Commit 6dd91a94 authored by Lukas Jelonek's avatar Lukas Jelonek
Browse files

Added union, intersect complement and shift operations for Intervals.

parent 5a86a9ca
......@@ -4,7 +4,7 @@
<groupId>de.cebitec.common</groupId>
<artifactId>intervals</artifactId>
<version>0.3.0</version>
<version>0.4.0</version>
<packaging>bundle</packaging>
<name>Intervals library</name>
......
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cebitec.common.sequencetools.intervals;
/**
*
* @author Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*/
public class IntegerIntervalOperations implements IntervalOperations<Integer> {
@Override
public boolean overlap(Interval<Integer> fst, Interval<Integer> snd) {
// normalize
fst = fst.as(Interval.Type.ZeroOpen);
snd = snd.as(Interval.Type.ZeroOpen);
if (fst.getStart() > snd.getStart()) {
Interval<Integer> tmp = fst;
fst = snd;
snd = tmp;
}
return !(fst.getEnd() - 1 < snd.getStart());
}
@Override
public boolean adjacent(Interval<Integer> fst, Interval<Integer> snd) {
// normalize
fst = fst.as(Interval.Type.ZeroOpen);
snd = snd.as(Interval.Type.ZeroOpen);
// sort by start
if (fst.getStart() > snd.getStart()) {
Interval<Integer> tmp = fst;
fst = snd;
snd = tmp;
}
return fst.getEnd() == snd.getStart();
}
@Override
public boolean overlapOrAdjacent(Interval<Integer> fst, Interval<Integer> snd) {
return overlap(fst, snd) || adjacent(fst, snd);
}
@Override
public Interval<Integer> union(Interval<Integer> fst, Interval<Integer> snd) {
if (!overlapOrAdjacent(fst, snd)) {
throw new IllegalArgumentException("Can't union non overlapping intervals. " + fst + "; " + snd);
}
// normalize
fst = fst.as(Interval.Type.OneClosed);
snd = snd.as(Interval.Type.OneClosed);
// sort by start
if (fst.getStart() > snd.getStart()) {
Interval<Integer> tmp = fst;
fst = snd;
snd = tmp;
}
int nstart = fst.getStart();
int nend = fst.getEnd() > snd.getEnd() ? fst.getEnd() : snd.getEnd();
return Intervals.createInterval(nstart, nend, Interval.Type.OneClosed);
}
@Override
public Interval<Integer> intersection(Interval<Integer> fst, Interval<Integer> snd) {
if (!overlap(fst, snd)) {
return Intervals.EMPTY;
}
// normalize
fst = fst.as(Interval.Type.OneClosed);
snd = snd.as(Interval.Type.OneClosed);
// sort by start
if (fst.getStart() > snd.getStart()) {
Interval<Integer> tmp = fst;
fst = snd;
snd = tmp;
}
int nstart = snd.getStart() > fst.getStart() ? snd.getStart() : fst.getStart();
int nend = fst.getEnd() < snd.getEnd() ? fst.getEnd() : snd.getEnd();
return Intervals.createInterval(nstart, nend, Interval.Type.OneClosed);
}
@Override
public Interval<Integer> complement(Interval<Integer> fst, Interval<Integer> snd) {
if (!overlap(fst, snd)) {
return fst;
}
if (fst.equals(snd)) {
return Intervals.EMPTY;
}
// normalize
fst = fst.as(Interval.Type.OneClosed);
snd = snd.as(Interval.Type.OneClosed);
return Intervals.createInterval(fst.getStart(), snd.getStart() - 1, Interval.Type.OneClosed);
}
@Override
public Interval<Integer> shift(Interval<Integer> i, Integer by) {
Interval<Integer> as = i.as(Interval.Type.ZeroOpen);
return Intervals.createInterval(as.getStart() + by, as.getEnd() + by);
}
}
......@@ -17,10 +17,11 @@
package de.cebitec.common.sequencetools.intervals;
/**
* An Interval describes a part on an indexed region by a start and a end coordinate.
* An interval has a type that determines the start position and whether the end position is
* included or excluded from the interval. To instantiate intervals and interact with them use
* the methods in the Intervals class.
* An Interval describes a part on an indexed region by a start and a end
* coordinate. An interval has a type that determines the start position and
* whether the end position is included or excluded from the interval. To
* instantiate intervals and interact with them use the methods in the Intervals
* class.
*
* @see Intervals
*
......@@ -31,29 +32,33 @@ public interface Interval<T extends Number> {
public enum Type {
/**
* The first position is 1 and the last position n is in the interval, e.g. [1..n]. This is
* a common case in biological data. Needs a lot of +1-1 juggling, so it should be avoided
* for calculations. But it is desired for presentation.
* The first position is 1 and the last position n is in the interval,
* e.g. [1..n]. This is a common case in biological data. Needs a lot of
* +1-1 juggling, so it should be avoided for calculations. But it is
* desired for presentation.
*/
OneClosed,
/**
* The first position is 0 and the last position n is not included in the interval, e.g.
* [0..n). Intervals are converted to this coordinate system by default.
* The first position is 0 and the last position n is not included in
* the interval, e.g. [0..n). Intervals are converted to this coordinate
* system by default.
*/
ZeroOpen,
/**
* The first position is 1 and the last position n is not included in the interval, e.g.
* [1..n).
* The first position is 1 and the last position n is not included in
* the interval, e.g. [1..n).
*/
OneOpen,
/**
* The first position is 0 and the last position n is included in the interval, e.g. [1..n]
* The first position is 0 and the last position n is included in the
* interval, e.g. [1..n]
*/
ZeroClosed
}
/**
* The type of the interval.
*
* @return
*/
Type getType();
......@@ -79,8 +84,16 @@ public interface Interval<T extends Number> {
*/
T getEnd();
/**
* Equivalent to getLength() == 0.
*
* @return true if this interval has zero length, false otherwise.
*/
boolean isEmpty();
/**
* Transforms the interval to the given type.
*
* @param newType
* @return The transformed interval.
*/
......
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cebitec.common.sequencetools.intervals;
/**
*
* @author Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*/
public interface IntervalOperations<L extends Number> {
public boolean overlap(Interval<L> fst, Interval<L> snd);
public boolean adjacent(Interval<L> fst, Interval<L> snd);
public boolean overlapOrAdjacent(Interval<L> fst, Interval<L> snd);
/**
* Shifts an interval by a given length, e.g. [2,3] shift by -2 results in [0,1].
*
* @param i
* @param by
* @return
*/
public Interval<L> shift(Interval<L> i, L by);
/* ********* Operations on overlapping or adjacent intervals ************ */
public Interval<L> union(Interval<L> fst, Interval<L> snd);
public Interval<L> intersection(Interval<L> fst, Interval<L> snd);
public Interval<L> complement(Interval<L> fst, Interval<L> snd);
}
......@@ -100,12 +100,25 @@ public class Intervals {
}
}
@Override
public boolean isEmpty() {
return getLength() == 0;
}
@Override
public Interval<java.lang.Integer> as(Type newType) {
return Intervals.transfromInterval(this, newType);
}
}
}
public static final Interval<Integer> EMPTY = emptyInterval();
public static Interval<Integer> emptyInterval() {
if (EMPTY == null) {
return createInterval(0, 0);
}
return EMPTY;
}
/**
* Creates an interval for the coordinates given in zeroOpen coordinates.
......@@ -119,8 +132,9 @@ public class Intervals {
}
/**
* Creates an interval ranging from start to end in zeroOpen coordinates. If type is not
* ZeroOpen the start and end positions of the interval will be transformed to be ZeroOpen.
* Creates an interval ranging from start to end in zeroOpen coordinates. If
* type is not ZeroOpen the start and end positions of the interval will be
* transformed to be ZeroOpen.
*
* @param start The start of the interval.
* @param end The end of the interval.
......@@ -128,6 +142,9 @@ public class Intervals {
* @return An interval in ZeroOpen coordinates.
*/
public static Interval<Integer> createInterval(int start, int end, Type type) {
// if (start > end) {
// throw new IllegalArgumentException("Start must be lower than end.");
// }
return new IntervalImpl.Integer(Type.ZeroOpen, normalizeStart(start, type), normalizeEnd(end, type));
}
......@@ -165,7 +182,8 @@ public class Intervals {
}
/**
* Corrects the start depending on the type so that it can be used with a java String.
* Corrects the start depending on the type so that it can be used with a
* java String.
*
* @param start
* @param t
......@@ -183,7 +201,8 @@ public class Intervals {
}
/**
* Corrects the end depending on the type so that it can be used with a java String.
* Corrects the end depending on the type so that it can be used with a java
* String.
*
* @param end an end position
* @param t the source type
......@@ -238,6 +257,7 @@ public class Intervals {
/**
* Sums up the total length of a list of intervals.
*
* @param totalLength
* @return
*/
......@@ -259,6 +279,4 @@ public class Intervals {
return o1.getStart().compareTo(o2.getStart());
}
}
}
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cebitec.common.sequencetools.intervals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/**
*
* @author Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*/
@RunWith(Parameterized.class)
public class IntegerIntervaComplementOperationTest {
private IntegerIntervalOperations ops = new IntegerIntervalOperations();
private Interval<Integer> fst;
private Interval<Integer> snd;
private Interval<Integer> result;
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
// ZeroOpen intervals
{Intervals.createInterval(0, 2), Intervals.createInterval(0, 2), Intervals.EMPTY},
{Intervals.createInterval(0, 2), Intervals.createInterval(2, 3), Intervals.createInterval(0, 2)},
{Intervals.createInterval(0, 3), Intervals.createInterval(2, 3), Intervals.createInterval(0, 2)},
// OneClosed intervals
{Intervals.createInterval(0, 1, Interval.Type.OneClosed),
Intervals.createInterval(0, 1, Interval.Type.OneClosed),
Intervals.EMPTY
},
{Intervals.createInterval(0, 1, Interval.Type.OneClosed),
Intervals.createInterval(2, 3, Interval.Type.OneClosed),
Intervals.createInterval(0, 1, Interval.Type.OneClosed)
},
{Intervals.createInterval(0, 2, Interval.Type.OneClosed),
Intervals.createInterval(2, 3, Interval.Type.OneClosed),
Intervals.createInterval(0, 1, Interval.Type.OneClosed)
}
});
}
public IntegerIntervaComplementOperationTest(Interval<Integer> fst, Interval<Integer> snd, Interval<Integer> result) {
this.fst = fst;
this.snd = snd;
this.result = result;
}
@Test
public void testComplement() {
assertThat(ops.complement(fst, snd), is(result));
}
}
\ No newline at end of file
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cebitec.common.sequencetools.intervals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/**
*
* @author Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*/
@RunWith(Parameterized.class)
public class IntegerIntervalCompareOperationsTest {
private IntegerIntervalOperations ops = new IntegerIntervalOperations();
private Interval<Integer> fst;
private Interval<Integer> snd;
private boolean adjacent;
private boolean overlap;
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
// ZeroOpen intervals
{Intervals.createInterval(0, 1), Intervals.createInterval(2, 3), false, false},
{Intervals.createInterval(0, 2), Intervals.createInterval(2, 3), true, false},
{Intervals.createInterval(0, 3), Intervals.createInterval(2, 3), false, true},
// OneClosed intervals
{Intervals.createInterval(0, 0, Interval.Type.OneClosed), Intervals.createInterval(2, 3, Interval.Type.OneClosed), false, false},
{Intervals.createInterval(0, 1, Interval.Type.OneClosed), Intervals.createInterval(2, 3, Interval.Type.OneClosed), true, false},
{Intervals.createInterval(0, 2, Interval.Type.OneClosed), Intervals.createInterval(2, 3, Interval.Type.OneClosed), false, true}
});
}
public IntegerIntervalCompareOperationsTest(Interval<Integer> fst, Interval<Integer> snd, boolean adjacent, boolean overlap) {
this.fst = fst;
this.snd = snd;
this.adjacent = adjacent;
this.overlap = overlap;
}
@Test
public void testOverlap() {
assertThat(ops.overlap(fst, snd), is(overlap));
}
@Test
public void testAdjacent() {
assertThat(ops.adjacent(fst, snd), is(adjacent));
}
@Test
public void testAdjacentOrOverlap() {
assertThat(ops.overlapOrAdjacent(fst, snd), is(adjacent || overlap));
}
}
\ No newline at end of file
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cebitec.common.sequencetools.intervals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/**
*
* @author Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*/
@RunWith(Parameterized.class)
public class IntegerIntervalIntersectionOperationTest {
private IntegerIntervalOperations ops = new IntegerIntervalOperations();
private Interval<Integer> fst;
private Interval<Integer> snd;
private Interval<Integer> result;
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
// ZeroOpen intervals
{Intervals.createInterval(0, 2), Intervals.createInterval(2, 3), Intervals.EMPTY},
{Intervals.createInterval(0, 3), Intervals.createInterval(2, 3), Intervals.createInterval(2, 3)},
// OneClosed intervals
{Intervals.createInterval(0, 1, Interval.Type.OneClosed),
Intervals.createInterval(2, 3, Interval.Type.OneClosed),
Intervals.EMPTY
},
{Intervals.createInterval(0, 2, Interval.Type.OneClosed),
Intervals.createInterval(2, 3, Interval.Type.OneClosed),
Intervals.createInterval(2, 2, Interval.Type.OneClosed)
}
});
}
public IntegerIntervalIntersectionOperationTest(Interval<Integer> fst, Interval<Integer> snd, Interval<Integer> result) {
this.fst = fst;
this.snd = snd;
this.result = result;
}
@Test
public void testIntersect() {
assertThat(ops.intersection(fst, snd), is(result));
}
}
\ No newline at end of file
/*
* Copyright (C) 2013 Lukas Jelonek <ljelonek at cebitec.uni-bielefeld.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.