3673e1c2b6bef9843f099c235e2577bc09798ece
java/com.sap.sse.common.test/src/com/sap/sse/common/test/KadaneExtremeSubarraysFinderTest.java
| ... | ... | @@ -1,71 +0,0 @@ |
| 1 | -package com.sap.sse.common.test; |
|
| 2 | - |
|
| 3 | -import static org.junit.jupiter.api.Assertions.assertEquals; |
|
| 4 | - |
|
| 5 | -import org.junit.jupiter.api.BeforeEach; |
|
| 6 | -import org.junit.jupiter.api.Test; |
|
| 7 | - |
|
| 8 | -import com.sap.sse.common.scalablevalue.KadaneExtremeSubarraysFinder; |
|
| 9 | -import com.sap.sse.common.scalablevalue.ScalableDouble; |
|
| 10 | - |
|
| 11 | -public class KadaneExtremeSubarraysFinderTest { |
|
| 12 | - private static final double EPSILON = 0.00000001; |
|
| 13 | - private KadaneExtremeSubarraysFinder<Double, Double, ScalableDouble> finder; |
|
| 14 | - |
|
| 15 | - @BeforeEach |
|
| 16 | - public void setUp() { |
|
| 17 | - finder = new KadaneExtremeSubarraysFinder<>(); |
|
| 18 | - } |
|
| 19 | - |
|
| 20 | - @Test |
|
| 21 | - public void testSimplePositiveSequence() { |
|
| 22 | - finder.add(new ScalableDouble(1)); |
|
| 23 | - finder.add(new ScalableDouble(2)); |
|
| 24 | - finder.add(new ScalableDouble(3)); |
|
| 25 | - assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 26 | - assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 27 | - assertEquals(2, finder.getEndIndexOfMaxSumSequence()); |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - @Test |
|
| 31 | - public void testSimplePositiveSequenceWithInsertInTheMiddle() { |
|
| 32 | - finder.add(new ScalableDouble(1)); |
|
| 33 | - finder.add(new ScalableDouble(3)); |
|
| 34 | - finder.add(1, new ScalableDouble(2)); |
|
| 35 | - assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 36 | - assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 37 | - assertEquals(2, finder.getEndIndexOfMaxSumSequence()); |
|
| 38 | - } |
|
| 39 | - |
|
| 40 | - @Test |
|
| 41 | - public void testSimpleSequenceWithPositiveAndNegative() { |
|
| 42 | - finder.add(new ScalableDouble(1)); |
|
| 43 | - finder.add(new ScalableDouble(2)); |
|
| 44 | - finder.add(new ScalableDouble(3)); |
|
| 45 | - finder.add(new ScalableDouble(-4)); |
|
| 46 | - finder.add(new ScalableDouble(5)); |
|
| 47 | - finder.add(new ScalableDouble(6)); |
|
| 48 | - finder.add(new ScalableDouble(-5)); |
|
| 49 | - assertEquals(13.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 50 | - assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 51 | - assertEquals(5, finder.getEndIndexOfMaxSumSequence()); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - @Test |
|
| 55 | - public void testSimplePositiveSequenceWithLaterNegativeInsertInTheMiddle() { |
|
| 56 | - finder.add(new ScalableDouble(1)); |
|
| 57 | - finder.add(new ScalableDouble(2)); |
|
| 58 | - finder.add(new ScalableDouble(3)); |
|
| 59 | - finder.add(new ScalableDouble(4)); |
|
| 60 | - finder.add(new ScalableDouble(5)); |
|
| 61 | - assertEquals(15.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 62 | - assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 63 | - assertEquals(4, finder.getEndIndexOfMaxSumSequence()); |
|
| 64 | - finder.add(3, new ScalableDouble(-7)); |
|
| 65 | - assertEquals(9.0, finder.getMaxSum().divide(1.0), EPSILON); // FIXME this test passes "coincidentally" because of the way getMaxSum() works while updating the aggregates... |
|
| 66 | - assertEquals(4, finder.getStartIndexOfMaxSumSequence()); |
|
| 67 | - assertEquals(5, finder.getEndIndexOfMaxSumSequence()); |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - // TODO add tests using the remove(...) method |
|
| 71 | -} |
java/com.sap.sse.common.test/src/com/sap/sse/common/test/KadaneExtremeSubsequenceFinderTest.java
| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | +package com.sap.sse.common.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
|
| 4 | + |
|
| 5 | +import org.junit.jupiter.api.BeforeEach; |
|
| 6 | +import org.junit.jupiter.api.Test; |
|
| 7 | + |
|
| 8 | +import com.sap.sse.common.scalablevalue.KadaneExtremeSubsequenceFinder; |
|
| 9 | +import com.sap.sse.common.scalablevalue.KadaneExtremeSubsequenceFinderLinkedListImpl; |
|
| 10 | +import com.sap.sse.common.scalablevalue.ScalableDouble; |
|
| 11 | + |
|
| 12 | +public class KadaneExtremeSubsequenceFinderTest { |
|
| 13 | + private static final double EPSILON = 0.00000001; |
|
| 14 | + private KadaneExtremeSubsequenceFinder<Double, Double, ScalableDouble> finder; |
|
| 15 | + |
|
| 16 | + @BeforeEach |
|
| 17 | + public void setUp() { |
|
| 18 | + finder = new KadaneExtremeSubsequenceFinderLinkedListImpl<>(); |
|
| 19 | + } |
|
| 20 | + |
|
| 21 | + @Test |
|
| 22 | + public void testSimplePositiveSequence() { |
|
| 23 | + finder.add(new ScalableDouble(1)); |
|
| 24 | + finder.add(new ScalableDouble(2)); |
|
| 25 | + finder.add(new ScalableDouble(3)); |
|
| 26 | + assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 27 | + assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 28 | + assertEquals(2, finder.getEndIndexOfMaxSumSequence()); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + @Test |
|
| 32 | + public void testSimplePositiveSequenceWithInsertInTheMiddle() { |
|
| 33 | + finder.add(new ScalableDouble(1)); |
|
| 34 | + finder.add(new ScalableDouble(3)); |
|
| 35 | + finder.add(1, new ScalableDouble(2)); |
|
| 36 | + assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 37 | + assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 38 | + assertEquals(2, finder.getEndIndexOfMaxSumSequence()); |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | + @Test |
|
| 42 | + public void testSimpleSequenceWithPositiveAndNegative() { |
|
| 43 | + finder.add(new ScalableDouble(1)); |
|
| 44 | + finder.add(new ScalableDouble(2)); |
|
| 45 | + finder.add(new ScalableDouble(3)); |
|
| 46 | + finder.add(new ScalableDouble(-4)); |
|
| 47 | + finder.add(new ScalableDouble(5)); |
|
| 48 | + finder.add(new ScalableDouble(6)); |
|
| 49 | + finder.add(new ScalableDouble(-5)); |
|
| 50 | + assertEquals(13.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 51 | + assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 52 | + assertEquals(5, finder.getEndIndexOfMaxSumSequence()); |
|
| 53 | + } |
|
| 54 | + |
|
| 55 | + @Test |
|
| 56 | + public void testSimplePositiveSequenceWithLaterNegativeInsertInTheMiddle() { |
|
| 57 | + finder.add(new ScalableDouble(1)); |
|
| 58 | + finder.add(new ScalableDouble(2)); |
|
| 59 | + finder.add(new ScalableDouble(3)); |
|
| 60 | + finder.add(new ScalableDouble(4)); |
|
| 61 | + finder.add(new ScalableDouble(5)); |
|
| 62 | + assertEquals(15.0, finder.getMaxSum().divide(1.0), EPSILON); |
|
| 63 | + assertEquals(0, finder.getStartIndexOfMaxSumSequence()); |
|
| 64 | + assertEquals(4, finder.getEndIndexOfMaxSumSequence()); |
|
| 65 | + finder.add(3, new ScalableDouble(-7)); |
|
| 66 | + assertEquals(9.0, finder.getMaxSum().divide(1.0), EPSILON); // FIXME this test passes "coincidentally" because of the way getMaxSum() works while updating the aggregates... |
|
| 67 | + assertEquals(4, finder.getStartIndexOfMaxSumSequence()); |
|
| 68 | + assertEquals(5, finder.getEndIndexOfMaxSumSequence()); |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + // TODO add tests using the remove(...) method |
|
| 72 | +} |
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubarraysFinder.java
| ... | ... | @@ -1,273 +0,0 @@ |
| 1 | -package com.sap.sse.common.scalablevalue; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | -import java.util.Iterator; |
|
| 5 | -import java.util.LinkedList; |
|
| 6 | -import java.util.List; |
|
| 7 | -import java.util.ListIterator; |
|
| 8 | - |
|
| 9 | -/** |
|
| 10 | - * In a sequence of {@link ComparableScalableValueWithDistance} objects, tells the contiguous sub-sequence with the |
|
| 11 | - * greatest and the contiguous sub-sequence with the least sum, according to the |
|
| 12 | - * {@link ComparableScalableValueWithDistance#add(ScalableValue) add} and the |
|
| 13 | - * {@link ComparableScalableValueWithDistance#compareTo(Object) compareTo} methods. |
|
| 14 | - * <p> |
|
| 15 | - * |
|
| 16 | - * The sequence is mutable. In particular, elements can be added at any position, also before the start or after the |
|
| 17 | - * end, and elements can be removed at least from the beginning of the sequence. Updating the sub-sequences with minimal |
|
| 18 | - * and maximal sums happens with complexity O(1) when adding to the end of the sequence, so with constant effort |
|
| 19 | - * regardless the size of the sequence. When inserting into or removing from the sequence at arbitrary positions, |
|
| 20 | - * constant effort can no longer be guaranteed as changes may need to get propagated onwards to following elements. |
|
| 21 | - * <p> |
|
| 22 | - * |
|
| 23 | - * See also <a href="https://en.wikipedia.org/wiki/Maximum_subarray_problem">here</a> for a description of the |
|
| 24 | - * algorithm. |
|
| 25 | - * |
|
| 26 | - * @author Axel Uhl (d043530) |
|
| 27 | - * |
|
| 28 | - */ |
|
| 29 | -public class KadaneExtremeSubarraysFinder<ValueType, AveragesTo extends Comparable<AveragesTo>, T extends ComparableScalableValueWithDistance<ValueType, AveragesTo>> |
|
| 30 | -implements Serializable, Iterable<T> { |
|
| 31 | - private static final long serialVersionUID = 2109193559337714286L; |
|
| 32 | - |
|
| 33 | - /** |
|
| 34 | - * The elements constituting the full sequence in which to find the contiguous sub-sequences |
|
| 35 | - */ |
|
| 36 | - private final List<T> sequence; |
|
| 37 | - |
|
| 38 | - /** |
|
| 39 | - * The element at index <tt>i</tt> holds the maximum value of the sum of any contiguous sub-sequence ending at index |
|
| 40 | - * <tt>i</tt>. Outside of {@code synchronized} blocks it holds as many elements as {@link #sequence}. The element |
|
| 41 | - * at index {@code i} is computed as {@code maxSumEndingAt.get(i-1)+sequence.get(i), max(sequence.get(i))}. This covers |
|
| 42 | - * the two cases extending the complete induction. Either, the sequence with the maximum sum ending at index {@code i} |
|
| 43 | - * includes prior elements; or the single element {@code sequence.get(i)} is greater than the sum of it and the maximum |
|
| 44 | - * sum ending at the previous element {@code i-1}. |
|
| 45 | - */ |
|
| 46 | - private final List<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAt; |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the maximum sum |
|
| 50 | - * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example: |
|
| 51 | - * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the maximum |
|
| 52 | - * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}. |
|
| 53 | - */ |
|
| 54 | - private List<Integer> startIndexOfMaxSumSequence; |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the maximum sum |
|
| 58 | - * of all such sub-sequences. This means that {@link #maxSumEndingAt}{@code .get(}{@link #endIndexOfMaxSumSequence}) |
|
| 59 | - * is minimal across all elements in {@link #maxSumEndingAt}. |
|
| 60 | - */ |
|
| 61 | - private int endIndexOfMaxSumSequence; |
|
| 62 | - |
|
| 63 | - /** |
|
| 64 | - * See {@code #maxSumEndingAt}, only for the minimum. |
|
| 65 | - */ |
|
| 66 | - private final List<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAt; |
|
| 67 | - |
|
| 68 | - /** |
|
| 69 | - * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the minimum sum |
|
| 70 | - * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example: |
|
| 71 | - * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the minimum |
|
| 72 | - * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}. |
|
| 73 | - */ |
|
| 74 | - private List<Integer> startIndexOfMinSumSequence; |
|
| 75 | - |
|
| 76 | - /** |
|
| 77 | - * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the minimum sum |
|
| 78 | - * of all such sub-sequences. This means that {@link #minSumEndingAt}{@code .get(}{@link #endIndexOfMinSumSequence}) |
|
| 79 | - * is minimal across all elements in {@link #minSumEndingAt}. |
|
| 80 | - */ |
|
| 81 | - private int endIndexOfMinSumSequence; |
|
| 82 | - |
|
| 83 | - public KadaneExtremeSubarraysFinder() { |
|
| 84 | - sequence = new LinkedList<>(); |
|
| 85 | - maxSumEndingAt = new LinkedList<>(); |
|
| 86 | - minSumEndingAt = new LinkedList<>(); |
|
| 87 | - startIndexOfMaxSumSequence = new LinkedList<>(); |
|
| 88 | - endIndexOfMaxSumSequence = -1; |
|
| 89 | - startIndexOfMinSumSequence = new LinkedList<>(); |
|
| 90 | - endIndexOfMinSumSequence = -1; |
|
| 91 | - } |
|
| 92 | - |
|
| 93 | - public synchronized void add(int index, T t) { |
|
| 94 | - final ScalableValueWithDistance<ValueType, AveragesTo> oldMaxSum = getMaxSum(); |
|
| 95 | - final ScalableValueWithDistance<ValueType, AveragesTo> oldMinSum = getMinSum(); |
|
| 96 | - final boolean insertingIntoMaxSumSequence = index <= endIndexOfMaxSumSequence && index > startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence); |
|
| 97 | - final boolean insertingIntoMinSumSequence = index <= endIndexOfMinSumSequence && index > startIndexOfMinSumSequence.get(endIndexOfMinSumSequence); |
|
| 98 | - sequence.add(index, t); |
|
| 99 | - final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAtIndex; |
|
| 100 | - final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMax = index == 0 ? null : t.add(maxSumEndingAt.get(index-1)); |
|
| 101 | - if (index == 0 || compare(t, sumWithMax) >= 0) { |
|
| 102 | - newMaxSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the maximum |
|
| 103 | - startIndexOfMaxSumSequence.add(index, index); |
|
| 104 | - } else { |
|
| 105 | - newMaxSumEndingAtIndex = sumWithMax; |
|
| 106 | - startIndexOfMaxSumSequence.add(index, startIndexOfMaxSumSequence.get(index-1)); |
|
| 107 | - } |
|
| 108 | - maxSumEndingAt.add(index, newMaxSumEndingAtIndex); |
|
| 109 | - if (oldMaxSum == null || compare(newMaxSumEndingAtIndex, oldMaxSum) > 0) { |
|
| 110 | - endIndexOfMaxSumSequence = index; |
|
| 111 | - } |
|
| 112 | - final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex; |
|
| 113 | - final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMin = index == 0 ? null : t.add(minSumEndingAt.get(index-1)); |
|
| 114 | - if (index == 0 || compare(t, sumWithMin) <= 0) { |
|
| 115 | - newMinSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the minimum |
|
| 116 | - startIndexOfMinSumSequence.add(index, index); |
|
| 117 | - } else { |
|
| 118 | - newMinSumEndingAtIndex = sumWithMin; |
|
| 119 | - startIndexOfMinSumSequence.add(index, startIndexOfMinSumSequence.get(index-1)); |
|
| 120 | - } |
|
| 121 | - minSumEndingAt.add(index, newMinSumEndingAtIndex); |
|
| 122 | - if (oldMinSum == null || compare(newMinSumEndingAtIndex, oldMinSum) < 0) { |
|
| 123 | - endIndexOfMinSumSequence = index; |
|
| 124 | - } |
|
| 125 | - if (index < sequence.size()) { |
|
| 126 | - update(index+1, newMaxSumEndingAtIndex, newMinSumEndingAtIndex); |
|
| 127 | - } |
|
| 128 | - if (insertingIntoMaxSumSequence) { // TODO probably also check whether a "positive" value was inserted; in this case, max can only grow further, and sub-sequence indices will stay unchanged |
|
| 129 | - updateMax(); |
|
| 130 | - } |
|
| 131 | - if (insertingIntoMinSumSequence) { // TODO probably also check whether a "negative" value was inserted; in this case, min can only shrink further, and sub-sequence indices will stay unchanged |
|
| 132 | - updateMin(); |
|
| 133 | - } |
|
| 134 | - } |
|
| 135 | - |
|
| 136 | - private void updateMin() { |
|
| 137 | - // TODO Auto-generated method stub |
|
| 138 | - |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - private void updateMax() { |
|
| 142 | - // TODO Auto-generated method stub |
|
| 143 | - |
|
| 144 | - } |
|
| 145 | - |
|
| 146 | - private int compare(final ScalableValueWithDistance<ValueType, AveragesTo> a, final ScalableValueWithDistance<ValueType, AveragesTo> b) { |
|
| 147 | - return a.divide(1).compareTo(b.divide(1)); |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - /** |
|
| 151 | - * For each element in {@link #sequence} starting at index {@code i}, this method checks whether the {@link #maxSumEndingAt}{@code [i]} |
|
| 152 | - * still is the maximum of {@link #maxSumEndingAt}{@code [i-1]+sequence[i]} and {@link #sequence}{@code [i]}. If yes, any change to |
|
| 153 | - * elements with index less than {@code i} do not have to be carried forward any further. Otherwise, {@link #maxSumEndingAt}{@code [i]} |
|
| 154 | - * is updated, and the process continues at {@code i+1} "recursively" (implemented iteratively, without recursion). |
|
| 155 | - */ |
|
| 156 | - private void update(int i, ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtPreviousIndex, ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex) { |
|
| 157 | - final ListIterator<T> sequenceIter = sequence.listIterator(i); |
|
| 158 | - final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAtIter = maxSumEndingAt.listIterator(i); |
|
| 159 | - final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAtIter = minSumEndingAt.listIterator(i); |
|
| 160 | - final ListIterator<Integer> startIndexOfMaxSumSequenceIter = startIndexOfMaxSumSequence.listIterator(i); |
|
| 161 | - final ListIterator<Integer> startIndexOfMinSumSequenceIter = startIndexOfMinSumSequence.listIterator(i); |
|
| 162 | - boolean finishedMax = false; |
|
| 163 | - boolean finishedMin = false; |
|
| 164 | - while (sequenceIter.hasNext() && (!finishedMax || !finishedMin)) { |
|
| 165 | - final T next = sequenceIter.next(); |
|
| 166 | - if (!finishedMax) { |
|
| 167 | - final ScalableValueWithDistance<ValueType, AveragesTo> nextMaxSumEndingAt = maxSumEndingAtIter.next(); |
|
| 168 | - startIndexOfMaxSumSequenceIter.next(); |
|
| 169 | - final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex); |
|
| 170 | - final boolean nextGreaterOrEqualsThanSum = compare(next, sum) >= 0; |
|
| 171 | - final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAt = nextGreaterOrEqualsThanSum ? next : sum; |
|
| 172 | - if (compare(nextMaxSumEndingAt, newMaxSumEndingAt) != 0) { |
|
| 173 | - maxSumEndingAtIter.remove(); |
|
| 174 | - maxSumEndingAtIter.add(newMaxSumEndingAt); |
|
| 175 | - maxSumEndingAtPreviousIndex = newMaxSumEndingAt; |
|
| 176 | - startIndexOfMaxSumSequenceIter.remove(); |
|
| 177 | - startIndexOfMaxSumSequenceIter.add(nextGreaterOrEqualsThanSum ? i : startIndexOfMaxSumSequence.get(i-1)); |
|
| 178 | - if (compare(newMaxSumEndingAt, getMaxSum()) > 0) { // FIXME getMaxSum() cannot be asked while we are still updating; indices may have moved left (remove) or right (add)! |
|
| 179 | - endIndexOfMaxSumSequence = i; |
|
| 180 | - } |
|
| 181 | - } else { |
|
| 182 | - finishedMax = true; // no more changes to propagate |
|
| 183 | - } |
|
| 184 | - } |
|
| 185 | - if (!finishedMin) { |
|
| 186 | - final ScalableValueWithDistance<ValueType, AveragesTo> nextMinSumEndingAt = minSumEndingAtIter.next(); |
|
| 187 | - startIndexOfMinSumSequenceIter.next(); |
|
| 188 | - final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex); |
|
| 189 | - final boolean nextLessOrEqualsThanSum = compare(next, sum) <= 0; |
|
| 190 | - final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAt = nextLessOrEqualsThanSum ? next : sum; |
|
| 191 | - if (compare(nextMinSumEndingAt, newMinSumEndingAt) != 0) { |
|
| 192 | - minSumEndingAtIter.remove(); |
|
| 193 | - minSumEndingAtIter.add(newMinSumEndingAt); |
|
| 194 | - maxSumEndingAtPreviousIndex = newMinSumEndingAt; |
|
| 195 | - startIndexOfMinSumSequenceIter.remove(); |
|
| 196 | - startIndexOfMinSumSequenceIter.add(nextLessOrEqualsThanSum ? i : startIndexOfMinSumSequence.get(i-1)); |
|
| 197 | - if (compare(newMinSumEndingAt, getMinSum()) < 0) { |
|
| 198 | - endIndexOfMinSumSequence = i; |
|
| 199 | - } |
|
| 200 | - } else { |
|
| 201 | - finishedMin = true; // no more changes to propagate |
|
| 202 | - } |
|
| 203 | - } |
|
| 204 | - i++; |
|
| 205 | - } |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - public synchronized void remove(int index) { |
|
| 209 | - sequence.remove(index); |
|
| 210 | - final ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtIndex = maxSumEndingAt.remove(index); |
|
| 211 | - startIndexOfMaxSumSequence.remove(index); |
|
| 212 | - if (endIndexOfMaxSumSequence > index) { |
|
| 213 | - endIndexOfMaxSumSequence--; |
|
| 214 | - } else if (endIndexOfMaxSumSequence == index) { |
|
| 215 | - // TODO but if endIndexOfMaxSumSequence == index, we have deleted the last element of the max sum sequence and need to re-evaluate |
|
| 216 | - } |
|
| 217 | - if (endIndexOfMinSumSequence > index) { |
|
| 218 | - endIndexOfMinSumSequence--; |
|
| 219 | - } else if (endIndexOfMinSumSequence == index) { |
|
| 220 | - // TODO but if endIndexOfMinSumSequence == index, we have deleted the last element of the min sum sequence and need to re-evaluate |
|
| 221 | - } |
|
| 222 | - final ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingAtIndex = minSumEndingAt.remove(index); |
|
| 223 | - startIndexOfMinSumSequence.remove(index); |
|
| 224 | - update(index+1, maxSumEndingAtIndex, minSumEndingAtIndex); |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - public synchronized void add(T t) { |
|
| 228 | - add(sequence.size(), t); |
|
| 229 | - } |
|
| 230 | - |
|
| 231 | - public synchronized void remove(T t) { |
|
| 232 | - remove(sequence.indexOf(t)); |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - public ScalableValueWithDistance<ValueType, AveragesTo> getMaxSum() { |
|
| 236 | - return endIndexOfMaxSumSequence == -1 ? null : maxSumEndingAt.get(endIndexOfMaxSumSequence); |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - public ScalableValueWithDistance<ValueType, AveragesTo> getMinSum() { |
|
| 240 | - return endIndexOfMinSumSequence == -1 ? null : minSumEndingAt.get(endIndexOfMinSumSequence); |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - public int getStartIndexOfMaxSumSequence() { |
|
| 244 | - return startIndexOfMaxSumSequence.isEmpty() ? -1 : startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence); |
|
| 245 | - } |
|
| 246 | - |
|
| 247 | - /** |
|
| 248 | - * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 249 | - * maximal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 250 | - * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 251 | - */ |
|
| 252 | - public int getEndIndexOfMaxSumSequence() { |
|
| 253 | - return endIndexOfMaxSumSequence; |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - public int getStartIndexOfMinSumSequence() { |
|
| 257 | - return startIndexOfMinSumSequence.isEmpty() ? -1 : startIndexOfMinSumSequence.get(endIndexOfMinSumSequence); |
|
| 258 | - } |
|
| 259 | - |
|
| 260 | - /** |
|
| 261 | - * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 262 | - * minimal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 263 | - * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 264 | - */ |
|
| 265 | - public int getEndIndexOfMinSumSequence() { |
|
| 266 | - return endIndexOfMinSumSequence; |
|
| 267 | - } |
|
| 268 | - |
|
| 269 | - @Override |
|
| 270 | - public Iterator<T> iterator() { |
|
| 271 | - return sequence.iterator(); |
|
| 272 | - } |
|
| 273 | -} |
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubsequenceFinder.java
| ... | ... | @@ -0,0 +1,69 @@ |
| 1 | +package com.sap.sse.common.scalablevalue; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | + |
|
| 6 | +/** |
|
| 7 | + * In a sequence of {@link ComparableScalableValueWithDistance} objects, tells the contiguous sub-sequence with the |
|
| 8 | + * greatest and the contiguous sub-sequence with the least sum, according to the |
|
| 9 | + * {@link ComparableScalableValueWithDistance#add(ScalableValue) add} and the |
|
| 10 | + * {@link ComparableScalableValueWithDistance#compareTo(Object) compareTo} methods. |
|
| 11 | + * <p> |
|
| 12 | + * |
|
| 13 | + * The sequence is mutable. In particular, elements can be added at any position, also before the start or after the |
|
| 14 | + * end, and elements can be removed at least from the beginning of the sequence. Updating the sub-sequences with minimal |
|
| 15 | + * and maximal sums happens with complexity O(1) when adding to the end of the sequence, so with constant effort |
|
| 16 | + * regardless the size of the sequence. When inserting into or removing from the sequence at arbitrary positions, |
|
| 17 | + * constant effort can no longer be guaranteed as changes may need to get propagated onwards to following elements. |
|
| 18 | + * <p> |
|
| 19 | + * |
|
| 20 | + * See also <a href="https://en.wikipedia.org/wiki/Maximum_subarray_problem">here</a> for a description of the |
|
| 21 | + * algorithm. |
|
| 22 | + * |
|
| 23 | + * @author Axel Uhl (d043530) |
|
| 24 | + * |
|
| 25 | + */ |
|
| 26 | +public interface KadaneExtremeSubsequenceFinder<ValueType, AveragesTo extends Comparable<AveragesTo>, T extends ComparableScalableValueWithDistance<ValueType, AveragesTo>> |
|
| 27 | + extends Serializable, Iterable<T> { |
|
| 28 | + int size(); |
|
| 29 | + |
|
| 30 | + default boolean isEmpty() { |
|
| 31 | + return size() == 0; |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + void add(int index, T t); |
|
| 35 | + |
|
| 36 | + default void add(T t) { |
|
| 37 | + add(size(), t); |
|
| 38 | + } |
|
| 39 | + |
|
| 40 | + void remove(int index); |
|
| 41 | + |
|
| 42 | + void remove(T t); |
|
| 43 | + |
|
| 44 | + ScalableValueWithDistance<ValueType, AveragesTo> getMinSum(); |
|
| 45 | + |
|
| 46 | + ScalableValueWithDistance<ValueType, AveragesTo> getMaxSum(); |
|
| 47 | + |
|
| 48 | + Iterator<T> getSubSequenceWithMaxSum(); |
|
| 49 | + |
|
| 50 | + Iterator<T> getSubSequenceWithMinSum(); |
|
| 51 | + |
|
| 52 | + int getStartIndexOfMaxSumSequence(); |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 56 | + * maximal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 57 | + * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 58 | + */ |
|
| 59 | + int getEndIndexOfMaxSumSequence(); |
|
| 60 | + |
|
| 61 | + int getStartIndexOfMinSumSequence(); |
|
| 62 | + |
|
| 63 | + /** |
|
| 64 | + * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 65 | + * minimal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 66 | + * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 67 | + */ |
|
| 68 | + int getEndIndexOfMinSumSequence(); |
|
| 69 | +} |
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubsequenceFinderImpl.java
| ... | ... | @@ -0,0 +1,349 @@ |
| 1 | +package com.sap.sse.common.scalablevalue; |
|
| 2 | + |
|
| 3 | +import java.util.Iterator; |
|
| 4 | +import java.util.function.BiFunction; |
|
| 5 | +import java.util.function.Consumer; |
|
| 6 | +import java.util.function.Function; |
|
| 7 | + |
|
| 8 | +public class KadaneExtremeSubsequenceFinderImpl<ValueType, AveragesTo extends Comparable<AveragesTo>, T extends ComparableScalableValueWithDistance<ValueType, AveragesTo>> |
|
| 9 | + implements KadaneExtremeSubsequenceFinder<ValueType, AveragesTo, T> { |
|
| 10 | + |
|
| 11 | + private static final long serialVersionUID = -8986609116472739636L; |
|
| 12 | + |
|
| 13 | + private Node<ValueType, AveragesTo, T> first; |
|
| 14 | + |
|
| 15 | + private Node<ValueType, AveragesTo, T> last; |
|
| 16 | + |
|
| 17 | + private int size; |
|
| 18 | + |
|
| 19 | + /** |
|
| 20 | + * Nodes of this type are used to construct a doubly-linked list, with each node holding a reference to the node |
|
| 21 | + * forming the start of the sequence with the maximum sum. |
|
| 22 | + * |
|
| 23 | + * @author Axel Uhl (d043530) |
|
| 24 | + */ |
|
| 25 | + private static class Node<ValueType, AveragesTo extends Comparable<AveragesTo>, T extends ComparableScalableValueWithDistance<ValueType, AveragesTo>> { |
|
| 26 | + private final T value; |
|
| 27 | + private Node<ValueType, AveragesTo, T> previous; |
|
| 28 | + private Node<ValueType, AveragesTo, T> next; |
|
| 29 | + private ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingHere; |
|
| 30 | + private Node<ValueType, AveragesTo, T> startOfMinSumSubSequenceEndingHere; |
|
| 31 | + private ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingHere; |
|
| 32 | + private Node<ValueType, AveragesTo, T> startOfMaxSumSubSequenceEndingHere; |
|
| 33 | + |
|
| 34 | + private Node(Node<ValueType, AveragesTo, T> previous, Node<ValueType, AveragesTo, T> next, T value) { |
|
| 35 | + super(); |
|
| 36 | + this.previous = previous; |
|
| 37 | + this.next = next; |
|
| 38 | + this.value = value; |
|
| 39 | + this.minSumEndingHere = null; |
|
| 40 | + this.startOfMinSumSubSequenceEndingHere = null; |
|
| 41 | + this.maxSumEndingHere = null; |
|
| 42 | + this.startOfMaxSumSubSequenceEndingHere = null; |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + private Node<ValueType, AveragesTo, T> getPrevious() { |
|
| 46 | + return previous; |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + private Node<ValueType, AveragesTo, T> getNext() { |
|
| 50 | + return next; |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + private void setPrevious(Node<ValueType, AveragesTo, T> previous) { |
|
| 54 | + this.previous = previous; |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + private void setNext(Node<ValueType, AveragesTo, T> next) { |
|
| 58 | + this.next = next; |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + private T getValue() { |
|
| 62 | + return value; |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + private ScalableValueWithDistance<ValueType, AveragesTo> getMinSumEndingHere() { |
|
| 66 | + return minSumEndingHere; |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + private Node<ValueType, AveragesTo, T> getStartOfMinSumSubSequenceEndingHere() { |
|
| 70 | + return startOfMinSumSubSequenceEndingHere; |
|
| 71 | + } |
|
| 72 | + |
|
| 73 | + private void setMinSumEndingHere(ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingHere) { |
|
| 74 | + this.minSumEndingHere = minSumEndingHere; |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + private void setStartOfMinSumSubSequenceEndingHere(Node<ValueType, AveragesTo, T> startOfMinSumSubSequenceEndingHere) { |
|
| 78 | + this.startOfMinSumSubSequenceEndingHere = startOfMinSumSubSequenceEndingHere; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + private void setMaxSumEndingHere(ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingHere) { |
|
| 82 | + this.maxSumEndingHere = maxSumEndingHere; |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + private void setStartOfMaxSumSubSequenceEndingHere(Node<ValueType, AveragesTo, T> startOfMaxSumSubSequenceEndingHere) { |
|
| 86 | + this.startOfMaxSumSubSequenceEndingHere = startOfMaxSumSubSequenceEndingHere; |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + private ScalableValueWithDistance<ValueType, AveragesTo> getMaxSumEndingHere() { |
|
| 90 | + return maxSumEndingHere; |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + private Node<ValueType, AveragesTo, T> getStartOfMaxSumSubSequenceEndingHere() { |
|
| 94 | + return startOfMaxSumSubSequenceEndingHere; |
|
| 95 | + } |
|
| 96 | + |
|
| 97 | + /** |
|
| 98 | + * Updates this node's extreme sum values of the sub-sequences ending at this node, considering the values |
|
| 99 | + * stored in the {@link #getPrevious() previous} element, if such an element exists. Furthermore, the |
|
| 100 | + * references to the nodes where these sub-sequences start are updated accordingly. |
|
| 101 | + * |
|
| 102 | + * @return whether the node has changed during this update; this may mean a change in the min/max sum and/or the |
|
| 103 | + * the start of the extreme sub-sequence(s) ending at this node. Any such change requires updating |
|
| 104 | + * {@link #getNext() following nodes} too. |
|
| 105 | + */ |
|
| 106 | + private boolean updateThisFromPrevious() { |
|
| 107 | + final boolean changedByMax = updateMaxFromPrevious(); |
|
| 108 | + final boolean changedByMin = updateMinFromPrevious(); |
|
| 109 | + return changedByMax || changedByMin; |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + /** |
|
| 113 | + * Updates this node's min sum values of the sub-sequences ending at this node, considering the values stored in |
|
| 114 | + * the {@link #getPrevious() previous} element, if such an element exists. Furthermore, the references to the |
|
| 115 | + * node where the min sum sub-sequence starts is updated accordingly. |
|
| 116 | + * |
|
| 117 | + * @return whether the node's min sum-related properties changed during this update; this may mean a change in |
|
| 118 | + * the min sum and/or the the start of the min sum sub-sequence(s) ending at this node. Any such change |
|
| 119 | + * requires updating {@link #getNext() following nodes} using this method, too. It does not happen |
|
| 120 | + * automatically by calling this method. This method updates only this node. |
|
| 121 | + */ |
|
| 122 | + private boolean updateMinFromPrevious() { |
|
| 123 | + return updateThisFromPrevious(Node::getMinSumEndingHere, |
|
| 124 | + Node::getStartOfMinSumSubSequenceEndingHere, this::setMinSumEndingHere, |
|
| 125 | + this::setStartOfMinSumSubSequenceEndingHere, (a, b)->compare(b, a)); |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + /** |
|
| 129 | + * Updates this node's max sum values of the sub-sequences ending at this node, considering the values stored in |
|
| 130 | + * the {@link #getPrevious() previous} element, if such an element exists. Furthermore, the references to the |
|
| 131 | + * node where the max sum sub-sequence starts is updated accordingly. |
|
| 132 | + * |
|
| 133 | + * @return whether the node's max sum-related properties changed during this update; this may mean a change in |
|
| 134 | + * the max sum and/or the the start of the max sum sub-sequence(s) ending at this node. Any such change |
|
| 135 | + * requires updating {@link #getNext() following nodes} using this method, too. It does not happen |
|
| 136 | + * automatically by calling this method. This method updates only this node. |
|
| 137 | + */ |
|
| 138 | + private boolean updateMaxFromPrevious() { |
|
| 139 | + return updateThisFromPrevious(Node::getMaxSumEndingHere, |
|
| 140 | + Node::getStartOfMaxSumSubSequenceEndingHere, this::setMaxSumEndingHere, |
|
| 141 | + this::setStartOfMaxSumSubSequenceEndingHere, this::compare); |
|
| 142 | + } |
|
| 143 | + |
|
| 144 | + private boolean updateThisFromPrevious(Function<Node<ValueType, AveragesTo, T>, ScalableValueWithDistance<ValueType, AveragesTo>> getExtremeSumEndingHere, |
|
| 145 | + Function<Node<ValueType, AveragesTo, T>, Node<ValueType, AveragesTo, T>> getStartOfExtremeSumSubSequenceEndingHere, |
|
| 146 | + Consumer<ScalableValueWithDistance<ValueType, AveragesTo>> setExtremeSumEndingHere, |
|
| 147 | + Consumer<Node<ValueType, AveragesTo, T>> setStartOfExtremeSubSubSequenceEndingHere, |
|
| 148 | + BiFunction<ScalableValueWithDistance<ValueType, AveragesTo>, ScalableValueWithDistance<ValueType, AveragesTo>, Integer> comparator) { |
|
| 149 | + boolean changed = false; |
|
| 150 | + final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAtIndex; |
|
| 151 | + final Node<ValueType, AveragesTo, T> newStartOfMaxSumSubSequenceEndingHere; |
|
| 152 | + final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMax = getPrevious() == null ? null : getValue().add(getExtremeSumEndingHere.apply(getPrevious())); |
|
| 153 | + if (getPrevious() == null || comparator.apply(getValue(), sumWithMax) >= 0) { |
|
| 154 | + newMaxSumEndingAtIndex = getValue(); // one-element sum consisting of element at "index" is the maximum |
|
| 155 | + newStartOfMaxSumSubSequenceEndingHere = this; |
|
| 156 | + } else { |
|
| 157 | + newMaxSumEndingAtIndex = sumWithMax; |
|
| 158 | + newStartOfMaxSumSubSequenceEndingHere = getStartOfExtremeSumSubSequenceEndingHere.apply(getPrevious()); |
|
| 159 | + } |
|
| 160 | + if (!newMaxSumEndingAtIndex.equals(getExtremeSumEndingHere.apply(this))) { |
|
| 161 | + changed = true; |
|
| 162 | + setExtremeSumEndingHere.accept(newMaxSumEndingAtIndex); |
|
| 163 | + } |
|
| 164 | + if (newStartOfMaxSumSubSequenceEndingHere != getStartOfExtremeSumSubSequenceEndingHere.apply(this)) { |
|
| 165 | + changed = true; |
|
| 166 | + setStartOfExtremeSubSubSequenceEndingHere.accept(newStartOfMaxSumSubSequenceEndingHere); |
|
| 167 | + } |
|
| 168 | + return changed; |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + private Integer compare(final ScalableValueWithDistance<ValueType, AveragesTo> a, final ScalableValueWithDistance<ValueType, AveragesTo> b) { |
|
| 172 | + return a.divide(1).compareTo(b.divide(1)); |
|
| 173 | + } |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + public KadaneExtremeSubsequenceFinderImpl() { |
|
| 177 | + this.size = 0; |
|
| 178 | + this.first = null; |
|
| 179 | + this.last = null; |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + @Override |
|
| 183 | + public int size() { |
|
| 184 | + return size; |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + @Override |
|
| 188 | + public boolean isEmpty() { |
|
| 189 | + return first == null; |
|
| 190 | + } |
|
| 191 | + |
|
| 192 | + @Override |
|
| 193 | + public Iterator<T> iterator() { |
|
| 194 | + return new Iterator<T>() { |
|
| 195 | + private Node<ValueType, AveragesTo, T> current = first; |
|
| 196 | + @Override |
|
| 197 | + public boolean hasNext() { |
|
| 198 | + return current != null; |
|
| 199 | + } |
|
| 200 | + |
|
| 201 | + @Override |
|
| 202 | + public T next() { |
|
| 203 | + final T result = current.getValue(); |
|
| 204 | + current = current.getNext(); |
|
| 205 | + return result; |
|
| 206 | + } |
|
| 207 | + }; |
|
| 208 | + } |
|
| 209 | + |
|
| 210 | + @Override |
|
| 211 | + public void add(int index, T t) { |
|
| 212 | + if (index < 0 || index > size()) { |
|
| 213 | + throw new IndexOutOfBoundsException("Trying to add at index "+index+" to a sequence of size "+size()); |
|
| 214 | + } |
|
| 215 | + if (isEmpty()) { |
|
| 216 | + final Node<ValueType, AveragesTo, T> node = new Node<>(/* previous */ null, /* next */ null, t); |
|
| 217 | + first = node; |
|
| 218 | + last = node; |
|
| 219 | + node.updateThisFromPrevious(); // no need to consider changes; it's the only element |
|
| 220 | + } else { |
|
| 221 | + final Node<ValueType, AveragesTo, T> nodeBeforeIndex; |
|
| 222 | + final Node<ValueType, AveragesTo, T> nodeAfterIndex; |
|
| 223 | + if (index>0) { |
|
| 224 | + nodeBeforeIndex = getNode(index-1); |
|
| 225 | + nodeAfterIndex = nodeBeforeIndex.getNext(); |
|
| 226 | + } else if (index<size()) { |
|
| 227 | + nodeAfterIndex = getNode(index); |
|
| 228 | + nodeBeforeIndex = nodeAfterIndex.getPrevious(); |
|
| 229 | + } else { |
|
| 230 | + throw new InternalError("This shouldn't have happened as it means the sequence is empty, and we shouldn't have arrived in this branch"); |
|
| 231 | + } |
|
| 232 | + final Node<ValueType, AveragesTo, T> node = new Node<>(nodeBeforeIndex, nodeAfterIndex, t); |
|
| 233 | + if (nodeBeforeIndex != null) { |
|
| 234 | + nodeBeforeIndex.setNext(node); |
|
| 235 | + } |
|
| 236 | + if (nodeAfterIndex != null) { |
|
| 237 | + nodeAfterIndex.setPrevious(node); |
|
| 238 | + } |
|
| 239 | + node.updateThisFromPrevious(); |
|
| 240 | + if (nodeBeforeIndex == null |
|
| 241 | + || !node.getMaxSumEndingHere().equals(nodeBeforeIndex.getMaxSumEndingHere()) |
|
| 242 | + || !node.getMinSumEndingHere().equals(nodeBeforeIndex.getMinSumEndingHere()) |
|
| 243 | + || node.getStartOfMaxSumSubSequenceEndingHere() != nodeBeforeIndex.getStartOfMaxSumSubSequenceEndingHere() |
|
| 244 | + || node.getStartOfMinSumSubSequenceEndingHere().equals(nodeBeforeIndex.getStartOfMinSumSubSequenceEndingHere())) { |
|
| 245 | + // the inserted node differs from the previous node in one of the extreme sums ending at it, and/or |
|
| 246 | + // regarding where those sub-sequences start, so we need to propagate the updates to subsequent nodes |
|
| 247 | + propagateChanges(node); |
|
| 248 | + } |
|
| 249 | + } |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + private void propagateChanges(Node<ValueType, AveragesTo, T> node) { |
|
| 253 | + Node<ValueType, AveragesTo, T> current = node.getNext(); |
|
| 254 | + boolean changedMin = true; |
|
| 255 | + boolean changedMax = true; |
|
| 256 | + while ((changedMin || changedMax) && current != null) { |
|
| 257 | + if (changedMin) { |
|
| 258 | + changedMin = current.updateMinFromPrevious(); |
|
| 259 | + } |
|
| 260 | + if (changedMax) { |
|
| 261 | + changedMax = current.updateMaxFromPrevious(); |
|
| 262 | + } |
|
| 263 | + current = current.getNext(); |
|
| 264 | + } |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + Node<ValueType, AveragesTo, T> getNode(int index) { |
|
| 268 | + final Node<ValueType, AveragesTo, T> result; |
|
| 269 | + if (isEmpty()) { |
|
| 270 | + result = null; |
|
| 271 | + } else { |
|
| 272 | + if (index > size()/2) { // search from the end |
|
| 273 | + result = step(last, size()-1-index, Node::getPrevious); |
|
| 274 | + } else { // search from the beginning |
|
| 275 | + result = step(first, index, Node::getNext); |
|
| 276 | + } |
|
| 277 | + } |
|
| 278 | + return result; |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + private Node<ValueType, AveragesTo, T> step(Node<ValueType, AveragesTo, T> start, int numberOfSteps, |
|
| 282 | + Function<Node<ValueType, AveragesTo, T>, Node<ValueType, AveragesTo, T>> stepper) { |
|
| 283 | + Node<ValueType, AveragesTo, T> current = start; |
|
| 284 | + for (int i=0; i<numberOfSteps; i++) { |
|
| 285 | + current = stepper.apply(current); |
|
| 286 | + } |
|
| 287 | + return current; |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + @Override |
|
| 291 | + public void remove(int index) { |
|
| 292 | + // TODO Auto-generated method stub |
|
| 293 | + |
|
| 294 | + } |
|
| 295 | + |
|
| 296 | + @Override |
|
| 297 | + public void remove(T t) { |
|
| 298 | + // TODO Auto-generated method stub |
|
| 299 | + |
|
| 300 | + } |
|
| 301 | + |
|
| 302 | + @Override |
|
| 303 | + public ScalableValueWithDistance<ValueType, AveragesTo> getMinSum() { |
|
| 304 | + // TODO Auto-generated method stub |
|
| 305 | + return null; |
|
| 306 | + } |
|
| 307 | + |
|
| 308 | + @Override |
|
| 309 | + public ScalableValueWithDistance<ValueType, AveragesTo> getMaxSum() { |
|
| 310 | + // TODO Auto-generated method stub |
|
| 311 | + return null; |
|
| 312 | + } |
|
| 313 | + |
|
| 314 | + @Override |
|
| 315 | + public int getStartIndexOfMaxSumSequence() { |
|
| 316 | + // TODO Auto-generated method stub |
|
| 317 | + return 0; |
|
| 318 | + } |
|
| 319 | + |
|
| 320 | + @Override |
|
| 321 | + public int getEndIndexOfMaxSumSequence() { |
|
| 322 | + // TODO Auto-generated method stub |
|
| 323 | + return 0; |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + @Override |
|
| 327 | + public int getStartIndexOfMinSumSequence() { |
|
| 328 | + // TODO Auto-generated method stub |
|
| 329 | + return 0; |
|
| 330 | + } |
|
| 331 | + |
|
| 332 | + @Override |
|
| 333 | + public int getEndIndexOfMinSumSequence() { |
|
| 334 | + // TODO Auto-generated method stub |
|
| 335 | + return 0; |
|
| 336 | + } |
|
| 337 | + |
|
| 338 | + @Override |
|
| 339 | + public Iterator<T> getSubSequenceWithMaxSum() { |
|
| 340 | + // TODO Auto-generated method stub |
|
| 341 | + return null; |
|
| 342 | + } |
|
| 343 | + |
|
| 344 | + @Override |
|
| 345 | + public Iterator<T> getSubSequenceWithMinSum() { |
|
| 346 | + // TODO Auto-generated method stub |
|
| 347 | + return null; |
|
| 348 | + } |
|
| 349 | +} |
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubsequenceFinderLinkedListImpl.java
| ... | ... | @@ -0,0 +1,286 @@ |
| 1 | +package com.sap.sse.common.scalablevalue; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | +import java.util.LinkedList; |
|
| 6 | +import java.util.List; |
|
| 7 | +import java.util.ListIterator; |
|
| 8 | + |
|
| 9 | +/** |
|
| 10 | + * This implementation uses {@link LinkedList}s to implement the full sequence and the max/min sum and start indices |
|
| 11 | + * collection. This requires index manipulations when elements are inserted to or removed from anywhere but the end of |
|
| 12 | + * the sequence. |
|
| 13 | + * |
|
| 14 | + * @author Axel Uhl (d043530) |
|
| 15 | + * |
|
| 16 | + */ |
|
| 17 | +public class KadaneExtremeSubsequenceFinderLinkedListImpl<ValueType, AveragesTo extends Comparable<AveragesTo>, T extends ComparableScalableValueWithDistance<ValueType, AveragesTo>> |
|
| 18 | +implements KadaneExtremeSubsequenceFinder<ValueType, AveragesTo, T>, Serializable, Iterable<T> { |
|
| 19 | + private static final long serialVersionUID = 2109193559337714286L; |
|
| 20 | + |
|
| 21 | + /** |
|
| 22 | + * The elements constituting the full sequence in which to find the contiguous sub-sequences |
|
| 23 | + */ |
|
| 24 | + private final List<T> sequence; |
|
| 25 | + |
|
| 26 | + /** |
|
| 27 | + * The element at index <tt>i</tt> holds the maximum value of the sum of any contiguous sub-sequence ending at index |
|
| 28 | + * <tt>i</tt>. Outside of {@code synchronized} blocks it holds as many elements as {@link #sequence}. The element |
|
| 29 | + * at index {@code i} is computed as {@code maxSumEndingAt.get(i-1)+sequence.get(i), max(sequence.get(i))}. This covers |
|
| 30 | + * the two cases extending the complete induction. Either, the sequence with the maximum sum ending at index {@code i} |
|
| 31 | + * includes prior elements; or the single element {@code sequence.get(i)} is greater than the sum of it and the maximum |
|
| 32 | + * sum ending at the previous element {@code i-1}. |
|
| 33 | + */ |
|
| 34 | + private final List<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAt; |
|
| 35 | + |
|
| 36 | + /** |
|
| 37 | + * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the maximum sum |
|
| 38 | + * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example: |
|
| 39 | + * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the maximum |
|
| 40 | + * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}. |
|
| 41 | + */ |
|
| 42 | + private List<Integer> startIndexOfMaxSumSequence; |
|
| 43 | + |
|
| 44 | + /** |
|
| 45 | + * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the maximum sum |
|
| 46 | + * of all such sub-sequences. This means that {@link #maxSumEndingAt}{@code .get(}{@link #endIndexOfMaxSumSequence}) |
|
| 47 | + * is minimal across all elements in {@link #maxSumEndingAt}. |
|
| 48 | + */ |
|
| 49 | + private int endIndexOfMaxSumSequence; |
|
| 50 | + |
|
| 51 | + /** |
|
| 52 | + * See {@code #maxSumEndingAt}, only for the minimum. |
|
| 53 | + */ |
|
| 54 | + private final List<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAt; |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the minimum sum |
|
| 58 | + * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example: |
|
| 59 | + * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the minimum |
|
| 60 | + * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}. |
|
| 61 | + */ |
|
| 62 | + private List<Integer> startIndexOfMinSumSequence; |
|
| 63 | + |
|
| 64 | + /** |
|
| 65 | + * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the minimum sum |
|
| 66 | + * of all such sub-sequences. This means that {@link #minSumEndingAt}{@code .get(}{@link #endIndexOfMinSumSequence}) |
|
| 67 | + * is minimal across all elements in {@link #minSumEndingAt}. |
|
| 68 | + */ |
|
| 69 | + private int endIndexOfMinSumSequence; |
|
| 70 | + |
|
| 71 | + public KadaneExtremeSubsequenceFinderLinkedListImpl() { |
|
| 72 | + sequence = new LinkedList<>(); |
|
| 73 | + maxSumEndingAt = new LinkedList<>(); |
|
| 74 | + minSumEndingAt = new LinkedList<>(); |
|
| 75 | + startIndexOfMaxSumSequence = new LinkedList<>(); |
|
| 76 | + endIndexOfMaxSumSequence = -1; |
|
| 77 | + startIndexOfMinSumSequence = new LinkedList<>(); |
|
| 78 | + endIndexOfMinSumSequence = -1; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + @Override |
|
| 82 | + public synchronized void add(int index, T t) { |
|
| 83 | + final ScalableValueWithDistance<ValueType, AveragesTo> oldMaxSum = getMaxSum(); |
|
| 84 | + final ScalableValueWithDistance<ValueType, AveragesTo> oldMinSum = getMinSum(); |
|
| 85 | + final boolean insertingIntoMaxSumSequence = index <= endIndexOfMaxSumSequence && index > startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence); |
|
| 86 | + final boolean insertingIntoMinSumSequence = index <= endIndexOfMinSumSequence && index > startIndexOfMinSumSequence.get(endIndexOfMinSumSequence); |
|
| 87 | + sequence.add(index, t); |
|
| 88 | + final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAtIndex; |
|
| 89 | + final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMax = index == 0 ? null : t.add(maxSumEndingAt.get(index-1)); |
|
| 90 | + if (index == 0 || compare(t, sumWithMax) >= 0) { |
|
| 91 | + newMaxSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the maximum |
|
| 92 | + startIndexOfMaxSumSequence.add(index, index); |
|
| 93 | + } else { |
|
| 94 | + newMaxSumEndingAtIndex = sumWithMax; |
|
| 95 | + startIndexOfMaxSumSequence.add(index, startIndexOfMaxSumSequence.get(index-1)); |
|
| 96 | + } |
|
| 97 | + maxSumEndingAt.add(index, newMaxSumEndingAtIndex); |
|
| 98 | + if (oldMaxSum == null || compare(newMaxSumEndingAtIndex, oldMaxSum) > 0) { |
|
| 99 | + endIndexOfMaxSumSequence = index; |
|
| 100 | + } |
|
| 101 | + final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex; |
|
| 102 | + final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMin = index == 0 ? null : t.add(minSumEndingAt.get(index-1)); |
|
| 103 | + if (index == 0 || compare(t, sumWithMin) <= 0) { |
|
| 104 | + newMinSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the minimum |
|
| 105 | + startIndexOfMinSumSequence.add(index, index); |
|
| 106 | + } else { |
|
| 107 | + newMinSumEndingAtIndex = sumWithMin; |
|
| 108 | + startIndexOfMinSumSequence.add(index, startIndexOfMinSumSequence.get(index-1)); |
|
| 109 | + } |
|
| 110 | + minSumEndingAt.add(index, newMinSumEndingAtIndex); |
|
| 111 | + if (oldMinSum == null || compare(newMinSumEndingAtIndex, oldMinSum) < 0) { |
|
| 112 | + endIndexOfMinSumSequence = index; |
|
| 113 | + } |
|
| 114 | + if (index < sequence.size()) { |
|
| 115 | + update(index+1, newMaxSumEndingAtIndex, newMinSumEndingAtIndex); |
|
| 116 | + } |
|
| 117 | + if (insertingIntoMaxSumSequence) { // TODO probably also check whether a "positive" value was inserted; in this case, max can only grow further, and sub-sequence indices will stay unchanged |
|
| 118 | + updateMax(); |
|
| 119 | + } |
|
| 120 | + if (insertingIntoMinSumSequence) { // TODO probably also check whether a "negative" value was inserted; in this case, min can only shrink further, and sub-sequence indices will stay unchanged |
|
| 121 | + updateMin(); |
|
| 122 | + } |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + private void updateMin() { |
|
| 126 | + // TODO Auto-generated method stub |
|
| 127 | + |
|
| 128 | + } |
|
| 129 | + |
|
| 130 | + private void updateMax() { |
|
| 131 | + // TODO Auto-generated method stub |
|
| 132 | + |
|
| 133 | + } |
|
| 134 | + |
|
| 135 | + private int compare(final ScalableValueWithDistance<ValueType, AveragesTo> a, final ScalableValueWithDistance<ValueType, AveragesTo> b) { |
|
| 136 | + return a.divide(1).compareTo(b.divide(1)); |
|
| 137 | + } |
|
| 138 | + |
|
| 139 | + /** |
|
| 140 | + * For each element in {@link #sequence} starting at index {@code i}, this method checks whether the {@link #maxSumEndingAt}{@code [i]} |
|
| 141 | + * still is the maximum of {@link #maxSumEndingAt}{@code [i-1]+sequence[i]} and {@link #sequence}{@code [i]}. If yes, any change to |
|
| 142 | + * elements with index less than {@code i} do not have to be carried forward any further. Otherwise, {@link #maxSumEndingAt}{@code [i]} |
|
| 143 | + * is updated, and the process continues at {@code i+1} "recursively" (implemented iteratively, without recursion). |
|
| 144 | + */ |
|
| 145 | + private void update(int i, ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtPreviousIndex, ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex) { |
|
| 146 | + final ListIterator<T> sequenceIter = sequence.listIterator(i); |
|
| 147 | + final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAtIter = maxSumEndingAt.listIterator(i); |
|
| 148 | + final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAtIter = minSumEndingAt.listIterator(i); |
|
| 149 | + final ListIterator<Integer> startIndexOfMaxSumSequenceIter = startIndexOfMaxSumSequence.listIterator(i); |
|
| 150 | + final ListIterator<Integer> startIndexOfMinSumSequenceIter = startIndexOfMinSumSequence.listIterator(i); |
|
| 151 | + boolean finishedMax = false; |
|
| 152 | + boolean finishedMin = false; |
|
| 153 | + while (sequenceIter.hasNext() && (!finishedMax || !finishedMin)) { |
|
| 154 | + final T next = sequenceIter.next(); |
|
| 155 | + if (!finishedMax) { |
|
| 156 | + final ScalableValueWithDistance<ValueType, AveragesTo> nextMaxSumEndingAt = maxSumEndingAtIter.next(); |
|
| 157 | + startIndexOfMaxSumSequenceIter.next(); |
|
| 158 | + final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex); |
|
| 159 | + final boolean nextGreaterOrEqualsThanSum = compare(next, sum) >= 0; |
|
| 160 | + final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAt = nextGreaterOrEqualsThanSum ? next : sum; |
|
| 161 | + if (compare(nextMaxSumEndingAt, newMaxSumEndingAt) != 0) { |
|
| 162 | + maxSumEndingAtIter.remove(); |
|
| 163 | + maxSumEndingAtIter.add(newMaxSumEndingAt); |
|
| 164 | + maxSumEndingAtPreviousIndex = newMaxSumEndingAt; |
|
| 165 | + startIndexOfMaxSumSequenceIter.remove(); |
|
| 166 | + startIndexOfMaxSumSequenceIter.add(nextGreaterOrEqualsThanSum ? i : startIndexOfMaxSumSequence.get(i-1)); |
|
| 167 | + if (compare(newMaxSumEndingAt, getMaxSum()) > 0) { // FIXME getMaxSum() cannot be asked while we are still updating; indices may have moved left (remove) or right (add)! |
|
| 168 | + endIndexOfMaxSumSequence = i; |
|
| 169 | + } |
|
| 170 | + } else { |
|
| 171 | + finishedMax = true; // no more changes to propagate |
|
| 172 | + } |
|
| 173 | + } |
|
| 174 | + if (!finishedMin) { |
|
| 175 | + final ScalableValueWithDistance<ValueType, AveragesTo> nextMinSumEndingAt = minSumEndingAtIter.next(); |
|
| 176 | + startIndexOfMinSumSequenceIter.next(); |
|
| 177 | + final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex); |
|
| 178 | + final boolean nextLessOrEqualsThanSum = compare(next, sum) <= 0; |
|
| 179 | + final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAt = nextLessOrEqualsThanSum ? next : sum; |
|
| 180 | + if (compare(nextMinSumEndingAt, newMinSumEndingAt) != 0) { |
|
| 181 | + minSumEndingAtIter.remove(); |
|
| 182 | + minSumEndingAtIter.add(newMinSumEndingAt); |
|
| 183 | + maxSumEndingAtPreviousIndex = newMinSumEndingAt; |
|
| 184 | + startIndexOfMinSumSequenceIter.remove(); |
|
| 185 | + startIndexOfMinSumSequenceIter.add(nextLessOrEqualsThanSum ? i : startIndexOfMinSumSequence.get(i-1)); |
|
| 186 | + if (compare(newMinSumEndingAt, getMinSum()) < 0) { |
|
| 187 | + endIndexOfMinSumSequence = i; |
|
| 188 | + } |
|
| 189 | + } else { |
|
| 190 | + finishedMin = true; // no more changes to propagate |
|
| 191 | + } |
|
| 192 | + } |
|
| 193 | + i++; |
|
| 194 | + } |
|
| 195 | + } |
|
| 196 | + |
|
| 197 | + @Override |
|
| 198 | + public synchronized void remove(int index) { |
|
| 199 | + sequence.remove(index); |
|
| 200 | + final ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtIndex = maxSumEndingAt.remove(index); |
|
| 201 | + startIndexOfMaxSumSequence.remove(index); |
|
| 202 | + if (endIndexOfMaxSumSequence > index) { |
|
| 203 | + endIndexOfMaxSumSequence--; |
|
| 204 | + } else if (endIndexOfMaxSumSequence == index) { |
|
| 205 | + // TODO but if endIndexOfMaxSumSequence == index, we have deleted the last element of the max sum sequence and need to re-evaluate |
|
| 206 | + } |
|
| 207 | + if (endIndexOfMinSumSequence > index) { |
|
| 208 | + endIndexOfMinSumSequence--; |
|
| 209 | + } else if (endIndexOfMinSumSequence == index) { |
|
| 210 | + // TODO but if endIndexOfMinSumSequence == index, we have deleted the last element of the min sum sequence and need to re-evaluate |
|
| 211 | + } |
|
| 212 | + final ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingAtIndex = minSumEndingAt.remove(index); |
|
| 213 | + startIndexOfMinSumSequence.remove(index); |
|
| 214 | + update(index+1, maxSumEndingAtIndex, minSumEndingAtIndex); |
|
| 215 | + } |
|
| 216 | + |
|
| 217 | + @Override |
|
| 218 | + public synchronized void add(T t) { |
|
| 219 | + add(sequence.size(), t); |
|
| 220 | + } |
|
| 221 | + |
|
| 222 | + @Override |
|
| 223 | + public synchronized void remove(T t) { |
|
| 224 | + remove(sequence.indexOf(t)); |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + @Override |
|
| 228 | + public ScalableValueWithDistance<ValueType, AveragesTo> getMaxSum() { |
|
| 229 | + return endIndexOfMaxSumSequence == -1 ? null : maxSumEndingAt.get(endIndexOfMaxSumSequence); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + @Override |
|
| 233 | + public ScalableValueWithDistance<ValueType, AveragesTo> getMinSum() { |
|
| 234 | + return endIndexOfMinSumSequence == -1 ? null : minSumEndingAt.get(endIndexOfMinSumSequence); |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + @Override |
|
| 238 | + public int getStartIndexOfMaxSumSequence() { |
|
| 239 | + return startIndexOfMaxSumSequence.isEmpty() ? -1 : startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence); |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + /** |
|
| 243 | + * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 244 | + * maximal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 245 | + * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 246 | + */ |
|
| 247 | + @Override |
|
| 248 | + public int getEndIndexOfMaxSumSequence() { |
|
| 249 | + return endIndexOfMaxSumSequence; |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + @Override |
|
| 253 | + public int getStartIndexOfMinSumSequence() { |
|
| 254 | + return startIndexOfMinSumSequence.isEmpty() ? -1 : startIndexOfMinSumSequence.get(endIndexOfMinSumSequence); |
|
| 255 | + } |
|
| 256 | + |
|
| 257 | + /** |
|
| 258 | + * @return the index into {@link #sequence} holding the last element of the contiguous sub-sequence that has the |
|
| 259 | + * minimal sum; note that pointing <em>to</em> and not <em>after</em> the last element of that sequence is |
|
| 260 | + * slightly different from how indices may be handled in some other from/to collection operations. |
|
| 261 | + */ |
|
| 262 | + @Override |
|
| 263 | + public int getEndIndexOfMinSumSequence() { |
|
| 264 | + return endIndexOfMinSumSequence; |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + @Override |
|
| 268 | + public Iterator<T> iterator() { |
|
| 269 | + return sequence.iterator(); |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + @Override |
|
| 273 | + public Iterator<T> getSubSequenceWithMaxSum() { |
|
| 274 | + return sequence.subList(getStartIndexOfMaxSumSequence(), getEndIndexOfMaxSumSequence()+1).iterator(); |
|
| 275 | + } |
|
| 276 | + |
|
| 277 | + @Override |
|
| 278 | + public Iterator<T> getSubSequenceWithMinSum() { |
|
| 279 | + return sequence.subList(getStartIndexOfMinSumSequence(), getEndIndexOfMinSumSequence()+1).iterator(); |
|
| 280 | + } |
|
| 281 | + |
|
| 282 | + @Override |
|
| 283 | + public int size() { |
|
| 284 | + return sequence.size(); |
|
| 285 | + } |
|
| 286 | +} |