diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ConnectedRangesCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ConnectedRangesCalculator.java index c49e11f76f..9395893bbf 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ConnectedRangesCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ConnectedRangesCalculator.java @@ -5,9 +5,11 @@ import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.ConnectedRangeTracker; +import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range; public final class ConnectedRangesCalculator, Difference_ extends Comparable> - implements ObjectCalculator> { + implements + ObjectCalculator, Range> { private final ConnectedRangeTracker context; @@ -21,13 +23,15 @@ public ConnectedRangesCalculator(Function s } @Override - public void insert(Interval_ result) { - context.add(context.getRange(result)); + public Range insert(Interval_ result) { + final var saved = context.getRange(result); + context.add(saved); + return saved; } @Override - public void retract(Interval_ result) { - context.remove(context.getRange(result)); + public void retract(Range range) { + context.remove(range); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/IntDistinctCountCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/IntDistinctCountCalculator.java index ea6866127d..0552e7dce1 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/IntDistinctCountCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/IntDistinctCountCalculator.java @@ -5,18 +5,19 @@ import ai.timefold.solver.core.impl.util.MutableInt; -public final class IntDistinctCountCalculator implements ObjectCalculator { +public final class IntDistinctCountCalculator implements ObjectCalculator { private final Map countMap = new HashMap<>(); @Override - public void insert(Input_ input) { + public Input_ insert(Input_ input) { countMap.computeIfAbsent(input, ignored -> new MutableInt()).increment(); + return input; } @Override - public void retract(Input_ input) { - if (countMap.get(input).decrement() == 0) { - countMap.remove(input); + public void retract(Input_ mapped) { + if (countMap.get(mapped).decrement() == 0) { + countMap.remove(mapped); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongDistinctCountCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongDistinctCountCalculator.java index e7f9332424..0a5eb0706f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongDistinctCountCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongDistinctCountCalculator.java @@ -5,18 +5,19 @@ import ai.timefold.solver.core.impl.util.MutableInt; -public final class LongDistinctCountCalculator implements ObjectCalculator { +public final class LongDistinctCountCalculator implements ObjectCalculator { private final Map countMap = new HashMap<>(); @Override - public void insert(Input_ input) { + public Input_ insert(Input_ input) { countMap.computeIfAbsent(input, ignored -> new MutableInt()).increment(); + return input; } @Override - public void retract(Input_ input) { - if (countMap.get(input).decrement() == 0) { - countMap.remove(input); + public void retract(Input_ mapped) { + if (countMap.get(mapped).decrement() == 0) { + countMap.remove(mapped); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ObjectCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ObjectCalculator.java index d3007f2c25..ae1927285f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ObjectCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ObjectCalculator.java @@ -1,11 +1,11 @@ package ai.timefold.solver.core.impl.score.stream.collector; -public sealed interface ObjectCalculator +public sealed interface ObjectCalculator permits ConnectedRangesCalculator, IntDistinctCountCalculator, LongDistinctCountCalculator, ReferenceAverageCalculator, ReferenceSumCalculator, SequenceCalculator { - void insert(Input_ input); + Mapped_ insert(Input_ input); - void retract(Input_ input); + void retract(Mapped_ mapped); Output_ result(); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceAverageCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceAverageCalculator.java index 1cbf2cd162..ce4963b1c8 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceAverageCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceAverageCalculator.java @@ -8,7 +8,7 @@ import java.util.function.BinaryOperator; import java.util.function.Supplier; -public final class ReferenceAverageCalculator implements ObjectCalculator { +public final class ReferenceAverageCalculator implements ObjectCalculator { int count = 0; Input_ sum; final BinaryOperator adder; @@ -51,15 +51,16 @@ public static Supplier> duration( } @Override - public void insert(Input_ input) { + public Input_ insert(Input_ input) { count++; sum = adder.apply(sum, input); + return input; } @Override - public void retract(Input_ input) { + public void retract(Input_ mapped) { count--; - sum = subtractor.apply(sum, input); + sum = subtractor.apply(sum, mapped); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceSumCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceSumCalculator.java index 95aeedef7b..dc361f153b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceSumCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceSumCalculator.java @@ -2,7 +2,7 @@ import java.util.function.BinaryOperator; -public final class ReferenceSumCalculator implements ObjectCalculator { +public final class ReferenceSumCalculator implements ObjectCalculator { private Result_ current; private final BinaryOperator adder; private final BinaryOperator subtractor; @@ -14,13 +14,14 @@ public ReferenceSumCalculator(Result_ current, BinaryOperator adder, Bi } @Override - public void insert(Result_ input) { + public Result_ insert(Result_ input) { current = adder.apply(current, input); + return input; } @Override - public void retract(Result_ input) { - current = subtractor.apply(current, input); + public void retract(Result_ mapped) { + current = subtractor.apply(current, mapped); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/SequenceCalculator.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/SequenceCalculator.java index 54db9dc5fb..2463fe4166 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/SequenceCalculator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/SequenceCalculator.java @@ -7,7 +7,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.consecutive.ConsecutiveSetTree; public final class SequenceCalculator - implements ObjectCalculator> { + implements ObjectCalculator, Result_> { private final ConsecutiveSetTree context = new ConsecutiveSetTree<>( (Integer a, Integer b) -> b - a, @@ -20,9 +20,10 @@ public SequenceCalculator(ToIntFunction indexMap) { } @Override - public void insert(Result_ result) { + public Result_ insert(Result_ result) { var value = indexMap.applyAsInt(result); context.add(result, value); + return result; } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/AverageReferenceBiCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/AverageReferenceBiCollector.java index 8ffb3ad19b..e11cb7a9ce 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/AverageReferenceBiCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/AverageReferenceBiCollector.java @@ -7,7 +7,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceAverageCalculator; final class AverageReferenceBiCollector - extends ObjectCalculatorBiCollector> { + extends ObjectCalculatorBiCollector> { private final Supplier> calculatorSupplier; AverageReferenceBiCollector(BiFunction mapper, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConnectedRangesBiConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConnectedRangesBiConstraintCollector.java index 5ec943825a..cd7efd836c 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConnectedRangesBiConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConnectedRangesBiConstraintCollector.java @@ -7,10 +7,11 @@ import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.impl.score.stream.collector.ConnectedRangesCalculator; +import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range; final class ConnectedRangesBiConstraintCollector, Difference_ extends Comparable> extends - ObjectCalculatorBiCollector, ConnectedRangesCalculator> { + ObjectCalculatorBiCollector, Range, ConnectedRangesCalculator> { private final Function startMap; private final Function endMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConsecutiveSequencesBiConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConsecutiveSequencesBiConstraintCollector.java index 775db3c578..4831d7bca4 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConsecutiveSequencesBiConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ConsecutiveSequencesBiConstraintCollector.java @@ -10,7 +10,7 @@ final class ConsecutiveSequencesBiConstraintCollector extends - ObjectCalculatorBiCollector, SequenceCalculator> { + ObjectCalculatorBiCollector, Result_, SequenceCalculator> { private final ToIntFunction indexMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctIntBiCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctIntBiCollector.java index 68c340fc7d..8775aa525a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctIntBiCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctIntBiCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.IntDistinctCountCalculator; final class CountDistinctIntBiCollector - extends ObjectCalculatorBiCollector> { + extends ObjectCalculatorBiCollector> { CountDistinctIntBiCollector(BiFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctLongBiCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctLongBiCollector.java index 20adc32787..4c83171952 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctLongBiCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/CountDistinctLongBiCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.LongDistinctCountCalculator; final class CountDistinctLongBiCollector - extends ObjectCalculatorBiCollector> { + extends ObjectCalculatorBiCollector> { CountDistinctLongBiCollector(BiFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ObjectCalculatorBiCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ObjectCalculatorBiCollector.java index 2e197c5138..418ac70bea 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ObjectCalculatorBiCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/ObjectCalculatorBiCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.api.score.stream.bi.BiConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.ObjectCalculator; -abstract sealed class ObjectCalculatorBiCollector> +abstract sealed class ObjectCalculatorBiCollector> implements BiConstraintCollector permits AverageReferenceBiCollector, ConnectedRangesBiConstraintCollector, ConsecutiveSequencesBiConstraintCollector, CountDistinctIntBiCollector, CountDistinctLongBiCollector, SumReferenceBiCollector { @@ -21,9 +21,9 @@ public ObjectCalculatorBiCollector(BiFunction accumulator() { return (calculator, a, b) -> { - final Input_ mapped = mapper.apply(a, b); - calculator.insert(mapped); - return () -> calculator.retract(mapped); + final var mapped = mapper.apply(a, b); + final var saved = calculator.insert(mapped); + return () -> calculator.retract(saved); }; } @@ -38,7 +38,7 @@ public boolean equals(Object object) { return true; if (object == null || getClass() != object.getClass()) return false; - var that = (ObjectCalculatorBiCollector) object; + var that = (ObjectCalculatorBiCollector) object; return Objects.equals(mapper, that.mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/SumReferenceBiCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/SumReferenceBiCollector.java index 2f8a7e9717..25e499ea66 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/SumReferenceBiCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/bi/SumReferenceBiCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceSumCalculator; final class SumReferenceBiCollector - extends ObjectCalculatorBiCollector> { + extends ObjectCalculatorBiCollector> { private final Result_ zero; private final BinaryOperator adder; private final BinaryOperator subtractor; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/connected_ranges/RangeGapImpl.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/connected_ranges/RangeGapImpl.java index efae2257b5..8edd9d1d7e 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/connected_ranges/RangeGapImpl.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/connected_ranges/RangeGapImpl.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.collector.connected_ranges; +import java.util.Objects; + import ai.timefold.solver.core.api.score.stream.common.ConnectedRange; import ai.timefold.solver.core.api.score.stream.common.RangeGap; @@ -51,6 +53,21 @@ void setLength(Difference_ length) { this.length = length; } + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof RangeGapImpl rangeGap)) + return false; + return Objects.equals(getPreviousRangeEnd(), rangeGap.getPreviousRangeEnd()) && + Objects.equals(getNextRangeStart(), rangeGap.getNextRangeStart()); + } + + @Override + public int hashCode() { + return Objects.hash(getPreviousRangeEnd(), getNextRangeStart()); + } + @Override public String toString() { return "RangeGap{" + diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/AverageReferenceQuadCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/AverageReferenceQuadCollector.java index c4e3c7fabc..c1e8998dde 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/AverageReferenceQuadCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/AverageReferenceQuadCollector.java @@ -7,7 +7,8 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceAverageCalculator; final class AverageReferenceQuadCollector - extends ObjectCalculatorQuadCollector> { + extends + ObjectCalculatorQuadCollector> { private final Supplier> calculatorSupplier; AverageReferenceQuadCollector(QuadFunction mapper, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConnectedRangesQuadConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConnectedRangesQuadConstraintCollector.java index 4902c48047..cf241a37bf 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConnectedRangesQuadConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConnectedRangesQuadConstraintCollector.java @@ -8,10 +8,11 @@ import ai.timefold.solver.core.api.function.QuadFunction; import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.impl.score.stream.collector.ConnectedRangesCalculator; +import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range; final class ConnectedRangesQuadConstraintCollector, Difference_ extends Comparable> extends - ObjectCalculatorQuadCollector, ConnectedRangesCalculator> { + ObjectCalculatorQuadCollector, Range, ConnectedRangesCalculator> { private final Function startMap; private final Function endMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConsecutiveSequencesQuadConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConsecutiveSequencesQuadConstraintCollector.java index 808b521783..48378a9817 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConsecutiveSequencesQuadConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ConsecutiveSequencesQuadConstraintCollector.java @@ -10,7 +10,7 @@ final class ConsecutiveSequencesQuadConstraintCollector extends - ObjectCalculatorQuadCollector, SequenceCalculator> { + ObjectCalculatorQuadCollector, Result_, SequenceCalculator> { private final ToIntFunction indexMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctIntQuadCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctIntQuadCollector.java index e5c1907eba..cb16d5bb31 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctIntQuadCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctIntQuadCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.IntDistinctCountCalculator; final class CountDistinctIntQuadCollector - extends ObjectCalculatorQuadCollector> { + extends ObjectCalculatorQuadCollector> { CountDistinctIntQuadCollector(QuadFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctLongQuadCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctLongQuadCollector.java index 16f1cb7d3b..f1dcc17573 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctLongQuadCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/CountDistinctLongQuadCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.LongDistinctCountCalculator; final class CountDistinctLongQuadCollector - extends ObjectCalculatorQuadCollector> { + extends ObjectCalculatorQuadCollector> { CountDistinctLongQuadCollector(QuadFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ObjectCalculatorQuadCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ObjectCalculatorQuadCollector.java index ee508a4a8f..78c300be1a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ObjectCalculatorQuadCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/ObjectCalculatorQuadCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.api.score.stream.quad.QuadConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.ObjectCalculator; -abstract sealed class ObjectCalculatorQuadCollector> +abstract sealed class ObjectCalculatorQuadCollector> implements QuadConstraintCollector permits AverageReferenceQuadCollector, ConnectedRangesQuadConstraintCollector, ConsecutiveSequencesQuadConstraintCollector, CountDistinctIntQuadCollector, CountDistinctLongQuadCollector, @@ -23,9 +23,9 @@ public ObjectCalculatorQuadCollector(QuadFunction accumulator() { return (calculator, a, b, c, d) -> { - final Input_ mapped = mapper.apply(a, b, c, d); - calculator.insert(mapped); - return () -> calculator.retract(mapped); + final var mapped = mapper.apply(a, b, c, d); + final var saved = calculator.insert(mapped); + return () -> calculator.retract(saved); }; } @@ -40,7 +40,7 @@ public boolean equals(Object object) { return true; if (object == null || getClass() != object.getClass()) return false; - var that = (ObjectCalculatorQuadCollector) object; + var that = (ObjectCalculatorQuadCollector) object; return Objects.equals(mapper, that.mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/SumReferenceQuadCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/SumReferenceQuadCollector.java index 03fb2c3d11..da49633c8e 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/SumReferenceQuadCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/quad/SumReferenceQuadCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceSumCalculator; final class SumReferenceQuadCollector - extends ObjectCalculatorQuadCollector> { + extends ObjectCalculatorQuadCollector> { private final Result_ zero; private final BinaryOperator adder; private final BinaryOperator subtractor; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/AverageReferenceTriCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/AverageReferenceTriCollector.java index 15a3438bcc..7c16f7cbf0 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/AverageReferenceTriCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/AverageReferenceTriCollector.java @@ -7,7 +7,8 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceAverageCalculator; final class AverageReferenceTriCollector - extends ObjectCalculatorTriCollector> { + extends + ObjectCalculatorTriCollector> { private final Supplier> calculatorSupplier; AverageReferenceTriCollector(TriFunction mapper, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConnectedRangesTriConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConnectedRangesTriConstraintCollector.java index 17851a680e..17a5299dda 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConnectedRangesTriConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConnectedRangesTriConstraintCollector.java @@ -8,10 +8,11 @@ import ai.timefold.solver.core.api.function.TriFunction; import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.impl.score.stream.collector.ConnectedRangesCalculator; +import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range; final class ConnectedRangesTriConstraintCollector, Difference_ extends Comparable> extends - ObjectCalculatorTriCollector, ConnectedRangesCalculator> { + ObjectCalculatorTriCollector, Range, ConnectedRangesCalculator> { private final Function startMap; private final Function endMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConsecutiveSequencesTriConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConsecutiveSequencesTriConstraintCollector.java index eabbb1b384..b1253b0f82 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConsecutiveSequencesTriConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ConsecutiveSequencesTriConstraintCollector.java @@ -10,7 +10,7 @@ final class ConsecutiveSequencesTriConstraintCollector extends - ObjectCalculatorTriCollector, SequenceCalculator> { + ObjectCalculatorTriCollector, Result_, SequenceCalculator> { private final ToIntFunction indexMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctIntTriCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctIntTriCollector.java index 03a739e02b..e798f38e4d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctIntTriCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctIntTriCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.IntDistinctCountCalculator; final class CountDistinctIntTriCollector - extends ObjectCalculatorTriCollector> { + extends ObjectCalculatorTriCollector> { CountDistinctIntTriCollector(TriFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctLongTriCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctLongTriCollector.java index 49a57d379e..69e07017a7 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctLongTriCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/CountDistinctLongTriCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.LongDistinctCountCalculator; final class CountDistinctLongTriCollector - extends ObjectCalculatorTriCollector> { + extends ObjectCalculatorTriCollector> { CountDistinctLongTriCollector(TriFunction mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ObjectCalculatorTriCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ObjectCalculatorTriCollector.java index d57b95b9f5..44484d0cb0 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ObjectCalculatorTriCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/ObjectCalculatorTriCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.api.score.stream.tri.TriConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.ObjectCalculator; -abstract sealed class ObjectCalculatorTriCollector> +abstract sealed class ObjectCalculatorTriCollector> implements TriConstraintCollector permits AverageReferenceTriCollector, ConnectedRangesTriConstraintCollector, ConsecutiveSequencesTriConstraintCollector, CountDistinctIntTriCollector, CountDistinctLongTriCollector, SumReferenceTriCollector { @@ -21,9 +21,9 @@ public ObjectCalculatorTriCollector(TriFunction accumulator() { return (calculator, a, b, c) -> { - final Input_ mapped = mapper.apply(a, b, c); - calculator.insert(mapped); - return () -> calculator.retract(mapped); + final var mapped = mapper.apply(a, b, c); + final var saved = calculator.insert(mapped); + return () -> calculator.retract(saved); }; } @@ -38,7 +38,7 @@ public boolean equals(Object object) { return true; if (object == null || getClass() != object.getClass()) return false; - var that = (ObjectCalculatorTriCollector) object; + var that = (ObjectCalculatorTriCollector) object; return Objects.equals(mapper, that.mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/SumReferenceTriCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/SumReferenceTriCollector.java index 056fd4c4b1..4aa7da1937 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/SumReferenceTriCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/tri/SumReferenceTriCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceSumCalculator; final class SumReferenceTriCollector - extends ObjectCalculatorTriCollector> { + extends ObjectCalculatorTriCollector> { private final Result_ zero; private final BinaryOperator adder; private final BinaryOperator subtractor; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/AverageReferenceUniCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/AverageReferenceUniCollector.java index cd43e197a2..329d727ba0 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/AverageReferenceUniCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/AverageReferenceUniCollector.java @@ -7,7 +7,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceAverageCalculator; final class AverageReferenceUniCollector - extends ObjectCalculatorUniCollector> { + extends ObjectCalculatorUniCollector> { private final Supplier> calculatorSupplier; AverageReferenceUniCollector(Function mapper, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConnectedRangesUniConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConnectedRangesUniConstraintCollector.java index 20557c211b..0eb267265a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConnectedRangesUniConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConnectedRangesUniConstraintCollector.java @@ -7,10 +7,11 @@ import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.impl.score.stream.collector.ConnectedRangesCalculator; +import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range; final class ConnectedRangesUniConstraintCollector, Difference_ extends Comparable> extends - ObjectCalculatorUniCollector, ConnectedRangesCalculator> { + ObjectCalculatorUniCollector, Range, ConnectedRangesCalculator> { private final Function startMap; private final Function endMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConsecutiveSequencesUniConstraintCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConsecutiveSequencesUniConstraintCollector.java index 45f641de5b..7887740990 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConsecutiveSequencesUniConstraintCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ConsecutiveSequencesUniConstraintCollector.java @@ -9,7 +9,7 @@ import ai.timefold.solver.core.impl.util.ConstantLambdaUtils; final class ConsecutiveSequencesUniConstraintCollector - extends ObjectCalculatorUniCollector, SequenceCalculator> { + extends ObjectCalculatorUniCollector, A, SequenceCalculator> { private final ToIntFunction indexMap; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctIntUniCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctIntUniCollector.java index 3965ed854e..dff9559f03 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctIntUniCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctIntUniCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.IntDistinctCountCalculator; final class CountDistinctIntUniCollector - extends ObjectCalculatorUniCollector> { + extends ObjectCalculatorUniCollector> { CountDistinctIntUniCollector(Function mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctLongUniCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctLongUniCollector.java index 6c482fac9c..5c9a2c0cac 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctLongUniCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/CountDistinctLongUniCollector.java @@ -6,7 +6,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.LongDistinctCountCalculator; final class CountDistinctLongUniCollector - extends ObjectCalculatorUniCollector> { + extends ObjectCalculatorUniCollector> { CountDistinctLongUniCollector(Function mapper) { super(mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ObjectCalculatorUniCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ObjectCalculatorUniCollector.java index a7f6c56811..746a303da2 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ObjectCalculatorUniCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/ObjectCalculatorUniCollector.java @@ -7,7 +7,7 @@ import ai.timefold.solver.core.api.score.stream.uni.UniConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.ObjectCalculator; -abstract sealed class ObjectCalculatorUniCollector> +abstract sealed class ObjectCalculatorUniCollector> implements UniConstraintCollector permits AverageReferenceUniCollector, ConnectedRangesUniConstraintCollector, ConsecutiveSequencesUniConstraintCollector, CountDistinctIntUniCollector, CountDistinctLongUniCollector, SumReferenceUniCollector { @@ -21,9 +21,9 @@ public ObjectCalculatorUniCollector(Function mapper @Override public BiFunction accumulator() { return (calculator, a) -> { - final Input_ mapped = mapper.apply(a); - calculator.insert(mapped); - return () -> calculator.retract(mapped); + final var mapped = mapper.apply(a); + final var saved = calculator.insert(mapped); + return () -> calculator.retract(saved); }; } @@ -38,7 +38,7 @@ public boolean equals(Object object) { return true; if (object == null || getClass() != object.getClass()) return false; - var that = (ObjectCalculatorUniCollector) object; + var that = (ObjectCalculatorUniCollector) object; return Objects.equals(mapper, that.mapper); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/SumReferenceUniCollector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/SumReferenceUniCollector.java index 68c2f16432..b5fb706639 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/SumReferenceUniCollector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/uni/SumReferenceUniCollector.java @@ -8,7 +8,7 @@ import ai.timefold.solver.core.impl.score.stream.collector.ReferenceSumCalculator; final class SumReferenceUniCollector - extends ObjectCalculatorUniCollector> { + extends ObjectCalculatorUniCollector> { private final Result_ zero; private final BinaryOperator adder; private final BinaryOperator subtractor; diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractConstraintCollectorsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractConstraintCollectorsTest.java index 40ca538877..535efdeb38 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractConstraintCollectorsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractConstraintCollectorsTest.java @@ -1,6 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.collector; import java.util.Arrays; +import java.util.Objects; import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.api.score.stream.common.SequenceChain; @@ -110,6 +111,9 @@ public abstract class AbstractConstraintCollectorsTest { @Test public abstract void consecutiveUsage(); + @Test + public abstract void consecutiveUsageDynamic(); + @Test public abstract void loadBalance(); @@ -134,8 +138,56 @@ protected ConnectedRangeChain buildConsecutiveUsage( }).getConnectedRangeChain(); } + protected ConnectedRangeChain buildDynamicConsecutiveUsage(DynamicInterval... data) { + return Arrays.stream(data).collect( + () -> new ConnectedRangeTracker<>(DynamicInterval::getStart, DynamicInterval::getEnd, (a, b) -> b - a), + (tree, datum) -> tree.add(tree.getRange(datum)), + (a, b) -> { + throw new UnsupportedOperationException(); + }).getConnectedRangeChain(); + } + public record Interval(int start, int end) { } + public static final class DynamicInterval { + int start; + + public DynamicInterval(int start) { + this.start = start; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return start + 10; + } + + public void setStart(int start) { + this.start = start; + } + + @Override + public String toString() { + return "DynamicInterval(%d, %d)".formatted(getStart(), getEnd()); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof DynamicInterval that)) + return false; + return start == that.start; + } + + @Override + public int hashCode() { + return Objects.hashCode(start); + } + } + } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/bi/InnerBiConstraintCollectorsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/bi/InnerBiConstraintCollectorsTest.java index dd780c1cd5..acc5279c68 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/bi/InnerBiConstraintCollectorsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/bi/InnerBiConstraintCollectorsTest.java @@ -32,7 +32,6 @@ import ai.timefold.solver.core.api.score.stream.ConstraintCollectors; import ai.timefold.solver.core.api.score.stream.bi.BiConstraintCollector; -import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.api.score.stream.common.LoadBalance; import ai.timefold.solver.core.impl.score.stream.collector.AbstractConstraintCollectorsTest; import ai.timefold.solver.core.impl.util.Pair; @@ -1071,19 +1070,19 @@ public void toConsecutiveSequences() { @Override @Test public void consecutiveUsage() { - BiConstraintCollector> collector = + var collector = ConstraintCollectors.toConnectedRanges(Interval::new, Interval::start, Interval::end, (a, b) -> b - a); var container = collector.supplier().get(); // Add first value, sequence is [(1,3)] - Runnable firstRetractor = accumulate(collector, container, 1, 3); + var firstRetractor = accumulate(collector, container, 1, 3); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3))); // Add second value, sequence is [(1,3),(2,4)] - Runnable secondRetractor = accumulate(collector, container, 2, 4); + var secondRetractor = accumulate(collector, container, 2, 4); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4))); // Add third value, same as the second. Sequence is [{1,1},2}] - Runnable thirdRetractor = accumulate(collector, container, 2, 4); + var thirdRetractor = accumulate(collector, container, 2, 4); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4), new Interval(2, 4))); // Retract one instance of the second value; we only have two values now. secondRetractor.run(); @@ -1096,6 +1095,42 @@ public void consecutiveUsage() { assertResult(collector, container, buildConsecutiveUsage()); } + @Override + @Test + public void consecutiveUsageDynamic() { + var dynamicCollector = + ConstraintCollectors.toConnectedRanges((DynamicInterval a, Object b) -> a, + DynamicInterval::getStart, + DynamicInterval::getEnd, (a, b) -> b - a); + + var first = new DynamicInterval(0); + var second = new DynamicInterval(10); + var third = new DynamicInterval(20); + var container = dynamicCollector.supplier().get(); + + // Add first value, sequence is [[(0, 10)]] + var firstRetractor = accumulate(dynamicCollector, container, first, null); + assertResult(dynamicCollector, container, buildDynamicConsecutiveUsage(new DynamicInterval(0))); + + // Add third value, sequence is [[(0, 10)], [(20, 30)]] + accumulate(dynamicCollector, container, third, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(20))); + + // Add second value, sequence is [[(0, 10), (10, 20), (20, 30)]] + accumulate(dynamicCollector, container, second, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(10), new DynamicInterval(20))); + + // Change first value, retract it, then re-add it + first.setStart(-5); + firstRetractor.run(); + accumulate(dynamicCollector, container, first, null); + + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(-5), new DynamicInterval(10), new DynamicInterval(20))); + } + @Override @Test public void loadBalance() { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/quad/InnerQuadConstraintCollectorsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/quad/InnerQuadConstraintCollectorsTest.java index 4b8abe8529..eaee6f4c34 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/quad/InnerQuadConstraintCollectorsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/quad/InnerQuadConstraintCollectorsTest.java @@ -30,7 +30,6 @@ import java.util.SortedSet; import ai.timefold.solver.core.api.score.stream.ConstraintCollectors; -import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.api.score.stream.common.LoadBalance; import ai.timefold.solver.core.api.score.stream.quad.QuadConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.AbstractConstraintCollectorsTest; @@ -1124,19 +1123,19 @@ public void toConsecutiveSequences() { @Override @Test public void consecutiveUsage() { - QuadConstraintCollector> collector = - ConstraintCollectors.toConnectedRanges((a, b, c, d) -> new Interval(a, b), + var collector = + ConstraintCollectors.toConnectedRanges((Integer a, Integer b, Object c, Object d) -> new Interval(a, b), Interval::start, Interval::end, (a, b) -> b - a); var container = collector.supplier().get(); // Add first value, sequence is [(1,3)] - Runnable firstRetractor = accumulate(collector, container, 1, 3, null, null); + var firstRetractor = accumulate(collector, container, 1, 3, null, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3))); // Add second value, sequence is [(1,3),(2,4)] - Runnable secondRetractor = accumulate(collector, container, 2, 4, null, null); + var secondRetractor = accumulate(collector, container, 2, 4, null, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4))); // Add third value, same as the second. Sequence is [{1,1},2}] - Runnable thirdRetractor = accumulate(collector, container, 2, 4, null, null); + var thirdRetractor = accumulate(collector, container, 2, 4, null, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4), new Interval(2, 4))); // Retract one instance of the second value; we only have two values now. secondRetractor.run(); @@ -1149,6 +1148,42 @@ public void consecutiveUsage() { assertResult(collector, container, buildConsecutiveUsage()); } + @Override + @Test + public void consecutiveUsageDynamic() { + var dynamicCollector = + ConstraintCollectors.toConnectedRanges((DynamicInterval a, Object b, Object c, Object d) -> a, + DynamicInterval::getStart, + DynamicInterval::getEnd, (a, b) -> b - a); + + var first = new DynamicInterval(0); + var second = new DynamicInterval(10); + var third = new DynamicInterval(20); + var container = dynamicCollector.supplier().get(); + + // Add first value, sequence is [[(0, 10)]] + var firstRetractor = accumulate(dynamicCollector, container, first, null, null, null); + assertResult(dynamicCollector, container, buildDynamicConsecutiveUsage(new DynamicInterval(0))); + + // Add third value, sequence is [[(0, 10)], [(20, 30)]] + accumulate(dynamicCollector, container, third, null, null, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(20))); + + // Add second value, sequence is [[(0, 10), (10, 20), (20, 30)]] + accumulate(dynamicCollector, container, second, null, null, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(10), new DynamicInterval(20))); + + // Change first value, retract it, then re-add it + first.setStart(-5); + firstRetractor.run(); + accumulate(dynamicCollector, container, first, null, null, null); + + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(-5), new DynamicInterval(10), new DynamicInterval(20))); + } + @Override @Test public void loadBalance() { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/tri/InnerTriConstraintCollectorsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/tri/InnerTriConstraintCollectorsTest.java index e31ba9e85a..26154932d1 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/tri/InnerTriConstraintCollectorsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/tri/InnerTriConstraintCollectorsTest.java @@ -30,7 +30,6 @@ import java.util.SortedSet; import ai.timefold.solver.core.api.score.stream.ConstraintCollectors; -import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.api.score.stream.common.LoadBalance; import ai.timefold.solver.core.api.score.stream.tri.TriConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.AbstractConstraintCollectorsTest; @@ -1076,19 +1075,19 @@ public void toConsecutiveSequences() { @Override @Test public void consecutiveUsage() { - TriConstraintCollector> collector = - ConstraintCollectors.toConnectedRanges((a, b, c) -> new Interval(a, b), + var collector = + ConstraintCollectors.toConnectedRanges((Integer a, Integer b, Object c) -> new Interval(a, b), Interval::start, Interval::end, (a, b) -> b - a); var container = collector.supplier().get(); // Add first value, sequence is [(1,3)] - Runnable firstRetractor = accumulate(collector, container, 1, 3, null); + var firstRetractor = accumulate(collector, container, 1, 3, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3))); // Add second value, sequence is [(1,3),(2,4)] - Runnable secondRetractor = accumulate(collector, container, 2, 4, null); + var secondRetractor = accumulate(collector, container, 2, 4, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4))); // Add third value, same as the second. Sequence is [{1,1},2}] - Runnable thirdRetractor = accumulate(collector, container, 2, 4, null); + var thirdRetractor = accumulate(collector, container, 2, 4, null); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4), new Interval(2, 4))); // Retract one instance of the second value; we only have two values now. secondRetractor.run(); @@ -1101,6 +1100,42 @@ public void consecutiveUsage() { assertResult(collector, container, buildConsecutiveUsage()); } + @Override + @Test + public void consecutiveUsageDynamic() { + var dynamicCollector = + ConstraintCollectors.toConnectedRanges((DynamicInterval a, Object b, Object c) -> a, + DynamicInterval::getStart, + DynamicInterval::getEnd, (a, b) -> b - a); + + var first = new DynamicInterval(0); + var second = new DynamicInterval(10); + var third = new DynamicInterval(20); + var container = dynamicCollector.supplier().get(); + + // Add first value, sequence is [[(0, 10)]] + var firstRetractor = accumulate(dynamicCollector, container, first, null, null); + assertResult(dynamicCollector, container, buildDynamicConsecutiveUsage(new DynamicInterval(0))); + + // Add third value, sequence is [[(0, 10)], [(20, 30)]] + accumulate(dynamicCollector, container, third, null, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(20))); + + // Add second value, sequence is [[(0, 10), (10, 20), (20, 30)]] + accumulate(dynamicCollector, container, second, null, null); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(10), new DynamicInterval(20))); + + // Change first value, retract it, then re-add it + first.setStart(-5); + firstRetractor.run(); + accumulate(dynamicCollector, container, first, null, null); + + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(-5), new DynamicInterval(10), new DynamicInterval(20))); + } + @Override @Test public void loadBalance() { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/uni/InnerUniConstraintCollectorsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/uni/InnerUniConstraintCollectorsTest.java index 22cc919a32..e83ec18d00 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/uni/InnerUniConstraintCollectorsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/collector/uni/InnerUniConstraintCollectorsTest.java @@ -33,7 +33,6 @@ import ai.timefold.solver.core.api.function.QuadFunction; import ai.timefold.solver.core.api.function.TriFunction; import ai.timefold.solver.core.api.score.stream.ConstraintCollectors; -import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain; import ai.timefold.solver.core.api.score.stream.common.LoadBalance; import ai.timefold.solver.core.api.score.stream.uni.UniConstraintCollector; import ai.timefold.solver.core.impl.score.stream.collector.AbstractConstraintCollectorsTest; @@ -1001,19 +1000,19 @@ public void toConsecutiveSequences() { @Override @Test public void consecutiveUsage() { - UniConstraintCollector> collector = + var collector = ConstraintCollectors.toConnectedRanges( Interval::start, Interval::end, (a, b) -> b - a); var container = collector.supplier().get(); // Add first value, sequence is [(1,3)] - Runnable firstRetractor = accumulate(collector, container, new Interval(1, 3)); + var firstRetractor = accumulate(collector, container, new Interval(1, 3)); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3))); // Add second value, sequence is [(1,3),(2,4)] - Runnable secondRetractor = accumulate(collector, container, new Interval(2, 4)); + var secondRetractor = accumulate(collector, container, new Interval(2, 4)); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4))); // Add third value, same as the second. Sequence is [{1,1},2}] - Runnable thirdRetractor = accumulate(collector, container, new Interval(2, 4)); + var thirdRetractor = accumulate(collector, container, new Interval(2, 4)); assertResult(collector, container, buildConsecutiveUsage(new Interval(1, 3), new Interval(2, 4), new Interval(2, 4))); // Retract one instance of the second value; we only have two values now. secondRetractor.run(); @@ -1026,6 +1025,42 @@ public void consecutiveUsage() { assertResult(collector, container, buildConsecutiveUsage()); } + @Override + @Test + public void consecutiveUsageDynamic() { + var dynamicCollector = + ConstraintCollectors.toConnectedRanges( + DynamicInterval::getStart, + DynamicInterval::getEnd, (a, b) -> b - a); + + var first = new DynamicInterval(0); + var second = new DynamicInterval(10); + var third = new DynamicInterval(20); + var container = dynamicCollector.supplier().get(); + + // Add first value, sequence is [[(0, 10)]] + var firstRetractor = accumulate(dynamicCollector, container, first); + assertResult(dynamicCollector, container, buildDynamicConsecutiveUsage(new DynamicInterval(0))); + + // Add third value, sequence is [[(0, 10)], [(20, 30)]] + accumulate(dynamicCollector, container, third); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(20))); + + // Add second value, sequence is [[(0, 10), (10, 20), (20, 30)]] + accumulate(dynamicCollector, container, second); + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(0), new DynamicInterval(10), new DynamicInterval(20))); + + // Change first value, retract it, then re-add it + first.setStart(-5); + firstRetractor.run(); + accumulate(dynamicCollector, container, first); + + assertResult(dynamicCollector, container, + buildDynamicConsecutiveUsage(new DynamicInterval(-5), new DynamicInterval(10), new DynamicInterval(20))); + } + @Override @Test public void loadBalance() {