Skip to content

Commit a6204ba

Browse files
committed
Another wave
1 parent df08228 commit a6204ba

156 files changed

Lines changed: 5696 additions & 2033 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintCollectorAccumulator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public interface UniConstraintCollectorAccumulator<ResultContainer_, A> {
2323
* {@link UniConstraintCollector#supplier()} will be used to supply a fresh instance of the container;
2424
* otherwise a pre-existing container will be used, carrying the accumulated state.
2525
* {@link UniConstraintCollector#finisher()} will be used to extract the final accumulated value from the container.
26-
* The container is the only state that the accumulator may rely on;
27-
* any other state will result in subtle score corruption issues.
26+
* The accumulator factory itself must not hold mutable cross-slot state;
27+
* any mutable state must live in the container or in the returned accumulated value.
2828
* @return the accumulator for the value, which will be used to insert the value to the group,
2929
* to update it while in the group, and to remove it from the group.
3030
*/

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ConnectedRangesCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractConnectedRangesSlot.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.ConnectedRangeTracker;
88
import ai.timefold.solver.core.impl.score.stream.collector.connected_ranges.Range;
99

10-
public final class ConnectedRangesCalculator<Interval_, Point_ extends Comparable<Point_>, Difference_ extends Comparable<Difference_>>
11-
implements ObjectCalculator<Interval_> {
10+
public abstract class AbstractConnectedRangesSlot<Interval_, Point_ extends Comparable<Point_>, Difference_ extends Comparable<Difference_>> {
1211

1312
public static final class State<Interval_, Point_ extends Comparable<Point_>, Difference_ extends Comparable<Difference_>> {
1413
private final ConnectedRangeTracker<Interval_, Point_, Difference_> context;
@@ -27,26 +26,23 @@ public ConnectedRangeChain<Interval_, Point_, Difference_> result() {
2726
private final State<Interval_, Point_, Difference_> state;
2827
private Range<Interval_, Point_> cachedRange;
2928

30-
public ConnectedRangesCalculator(State<Interval_, Point_, Difference_> state) {
29+
public AbstractConnectedRangesSlot(State<Interval_, Point_, Difference_> state) {
3130
this.state = state;
3231
}
3332

34-
@Override
35-
public void insert(Interval_ result) {
33+
protected void addMapped(Interval_ result) {
3634
final var saved = state.context.getRange(result);
3735
cachedRange = saved;
3836
state.context.add(saved);
3937
}
4038

41-
@Override
42-
public void update(Interval_ input) {
39+
protected void updateMapped(Interval_ input) {
4340
state.context.remove(cachedRange);
4441
cachedRange = state.context.getRange(input);
4542
state.context.add(cachedRange);
4643
}
4744

48-
@Override
49-
public void retract() {
45+
protected void removeMapped() {
5046
state.context.remove(cachedRange);
5147
}
5248
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package ai.timefold.solver.core.impl.score.stream.collector;
2+
3+
public abstract class AbstractCountSlot {
4+
5+
public static final class State {
6+
long count = 0;
7+
8+
public Long result() {
9+
return count;
10+
}
11+
}
12+
13+
private final State state;
14+
15+
public AbstractCountSlot(State state) {
16+
this.state = state;
17+
}
18+
19+
protected void addMapped() {
20+
state.count++;
21+
}
22+
23+
protected void updateMapped() {
24+
// count unchanged on update
25+
}
26+
27+
protected void removeMapped() {
28+
state.count--;
29+
}
30+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package ai.timefold.solver.core.impl.score.stream.collector;
2+
3+
import java.util.Objects;
4+
5+
import org.jspecify.annotations.Nullable;
6+
7+
public abstract class AbstractLoadBalanceSlot<Balanced_> {
8+
9+
private final DefaultLoadBalance<Balanced_> container;
10+
private @Nullable Balanced_ cachedBalanced;
11+
private long cachedLoad;
12+
13+
public AbstractLoadBalanceSlot(DefaultLoadBalance<Balanced_> container) {
14+
this.container = container;
15+
}
16+
17+
protected void addMapped(Balanced_ balanced, long load, long initialLoad) {
18+
cachedBalanced = balanced;
19+
cachedLoad = load;
20+
container.registerBalanced(balanced, load, initialLoad);
21+
}
22+
23+
protected void updateMapped(Balanced_ balanced, long load, long initialLoad) {
24+
if (Objects.equals(cachedBalanced, balanced) && cachedLoad == load) {
25+
return;
26+
}
27+
removeMapped();
28+
addMapped(balanced, load, initialLoad);
29+
}
30+
31+
protected void removeMapped() {
32+
container.unregisterBalanced(cachedBalanced, cachedLoad);
33+
}
34+
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongAverageCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractLongAverageSlot.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package ai.timefold.solver.core.impl.score.stream.collector;
22

3-
public final class LongAverageCalculator implements LongCalculator {
3+
public abstract class AbstractLongAverageSlot {
44

55
public static final class State {
66
long count = 0;
@@ -17,25 +17,22 @@ public Double result() {
1717
private final State state;
1818
private long cachedInput;
1919

20-
public LongAverageCalculator(State state) {
20+
public AbstractLongAverageSlot(State state) {
2121
this.state = state;
2222
}
2323

24-
@Override
25-
public void insert(long input) {
24+
protected void addMapped(long input) {
2625
cachedInput = input;
2726
state.count++;
2827
state.sum += input;
2928
}
3029

31-
@Override
32-
public void update(long input) {
30+
protected void updateMapped(long input) {
3331
state.sum += input - cachedInput;
3432
cachedInput = input;
3533
}
3634

37-
@Override
38-
public void retract() {
35+
protected void removeMapped() {
3936
state.count--;
4037
state.sum -= cachedInput;
4138
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongDistinctCountCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractLongDistinctSlot.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import org.jspecify.annotations.Nullable;
1010

11-
public final class LongDistinctCountCalculator<Input_> implements ObjectCalculator<Input_> {
11+
public abstract class AbstractLongDistinctSlot<Input_> {
1212

1313
public static final class State<Input_> {
1414
private final Map<Input_, MutableInt> countMap = new HashMap<>();
@@ -22,28 +22,25 @@ public Long result() {
2222
private @Nullable Input_ cachedInput;
2323
private @Nullable MutableInt cachedCounter;
2424

25-
public LongDistinctCountCalculator(State<Input_> state) {
25+
public AbstractLongDistinctSlot(State<Input_> state) {
2626
this.state = state;
2727
}
2828

29-
@Override
30-
public void insert(Input_ input) {
29+
protected void addMapped(Input_ input) {
3130
cachedInput = input;
3231
cachedCounter = state.countMap.computeIfAbsent(input, ignored -> new MutableInt());
3332
cachedCounter.increment();
3433
}
3534

36-
@Override
37-
public void update(Input_ input) {
35+
protected void updateMapped(Input_ input) {
3836
if (Objects.equals(cachedInput, input)) {
3937
return;
4038
}
41-
retract();
42-
insert(input);
39+
removeMapped();
40+
addMapped(input);
4341
}
4442

45-
@Override
46-
public void retract() {
43+
protected void removeMapped() {
4744
if (cachedCounter.decrement() == 0) {
4845
state.countMap.remove(cachedInput);
4946
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/LongSumCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractLongSumSlot.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package ai.timefold.solver.core.impl.score.stream.collector;
22

3-
public final class LongSumCalculator implements LongCalculator {
3+
public abstract class AbstractLongSumSlot {
44

55
public static final class State {
66
long sum = 0;
@@ -13,24 +13,21 @@ public Long result() {
1313
private final State state;
1414
private long cachedInput;
1515

16-
public LongSumCalculator(State state) {
16+
public AbstractLongSumSlot(State state) {
1717
this.state = state;
1818
}
1919

20-
@Override
21-
public void insert(long input) {
20+
protected void addMapped(long input) {
2221
cachedInput = input;
2322
state.sum += input;
2423
}
2524

26-
@Override
27-
public void update(long input) {
25+
protected void updateMapped(long input) {
2826
state.sum += input - cachedInput;
2927
cachedInput = input;
3028
}
3129

32-
@Override
33-
public void retract() {
30+
protected void removeMapped() {
3431
state.sum -= cachedInput;
3532
}
3633
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/MinMaxUndoableActionable.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractMinMaxSlot.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import ai.timefold.solver.core.impl.util.ConstantLambdaUtils;
1212
import ai.timefold.solver.core.impl.util.MutableInt;
1313

14-
public final class MinMaxUndoableActionable<Result_, Property_> implements UndoableActionable<Result_> {
14+
public abstract class AbstractMinMaxSlot<Result_, Property_> {
1515
public static final class State<Result_, Property_> {
1616
final boolean isMin;
1717
final NavigableMap<Property_, Map<Result_, MutableInt>> propertyToItemCountMap;
@@ -69,35 +69,42 @@ public static <Result, Property extends Comparable<? super Property>> State<Resu
6969
private Map<Result_, MutableInt> cachedInnerMap;
7070
private MutableInt cachedCount;
7171

72-
public MinMaxUndoableActionable(State<Result_, Property_> state) {
72+
public AbstractMinMaxSlot(State<Result_, Property_> state) {
7373
this.state = state;
7474
}
7575

76-
@Override
77-
public void insert(Result_ item) {
76+
protected void addMapped(Result_ item) {
7877
cachedItem = item;
7978
cachedKey = state.propertyFunction.apply(item);
8079
cachedInnerMap = state.propertyToItemCountMap.computeIfAbsent(cachedKey, ignored -> new LinkedHashMap<>());
8180
cachedCount = cachedInnerMap.computeIfAbsent(item, ignored -> new MutableInt());
8281
cachedCount.increment();
8382
}
8483

85-
@Override
86-
public void update(Result_ item) {
84+
protected void updateMapped(Result_ item) {
8785
var newKey = state.propertyFunction.apply(item);
8886
if (Objects.equals(cachedKey, newKey)) {
87+
if (Objects.equals(cachedItem, item)) {
88+
return;
89+
}
90+
// same key, different item: swap within the same inner map
91+
if (cachedCount.decrement() == 0) {
92+
cachedInnerMap.remove(cachedItem);
93+
}
94+
cachedItem = item;
95+
cachedCount = cachedInnerMap.computeIfAbsent(item, ignored -> new MutableInt());
96+
cachedCount.increment();
8997
return;
9098
}
91-
retract();
99+
removeMapped();
92100
cachedItem = item;
93101
cachedKey = newKey;
94102
cachedInnerMap = state.propertyToItemCountMap.computeIfAbsent(newKey, ignored -> new LinkedHashMap<>());
95103
cachedCount = cachedInnerMap.computeIfAbsent(item, ignored -> new MutableInt());
96104
cachedCount.increment();
97105
}
98106

99-
@Override
100-
public void retract() {
107+
protected void removeMapped() {
101108
if (cachedCount.decrement() == 0) {
102109
cachedInnerMap.remove(cachedItem);
103110
if (cachedInnerMap.isEmpty()) {

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceAverageCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractReferenceAverageSlot.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.function.BinaryOperator;
99
import java.util.function.Supplier;
1010

11-
public final class ReferenceAverageCalculator<Input_, Output_> implements ObjectCalculator<Input_> {
11+
public abstract class AbstractReferenceAverageSlot<Input_, Output_> {
1212

1313
public static final class State<Input_, Output_> {
1414
int count = 0;
@@ -51,7 +51,7 @@ public Output_ result() {
5151
return Duration.ofNanos(nanos / count);
5252
});
5353

54-
public ReferenceAverageCalculator(State<Input_, Output_> state) {
54+
public AbstractReferenceAverageSlot(State<Input_, Output_> state) {
5555
this.state = state;
5656
}
5757

@@ -67,15 +67,13 @@ public static Supplier<State<Duration, Duration>> durationState() {
6767
return DURATION;
6868
}
6969

70-
@Override
71-
public void insert(Input_ input) {
70+
protected void addMapped(Input_ input) {
7271
cachedValue = input;
7372
state.count++;
7473
state.sum = state.adder.apply(state.sum, input);
7574
}
7675

77-
@Override
78-
public void update(Input_ input) {
76+
protected void updateMapped(Input_ input) {
7977
if (cachedValue == input) {
8078
return;
8179
}
@@ -84,8 +82,7 @@ public void update(Input_ input) {
8482
cachedValue = input;
8583
}
8684

87-
@Override
88-
public void retract() {
85+
protected void removeMapped() {
8986
state.count--;
9087
state.sum = state.subtractor.apply(state.sum, cachedValue);
9188
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ReferenceSumCalculator.java renamed to core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/AbstractReferenceSumSlot.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.util.function.BinaryOperator;
44

5-
public final class ReferenceSumCalculator<Result_> implements ObjectCalculator<Result_> {
5+
public abstract class AbstractReferenceSumSlot<Result_> {
66

77
public static final class State<Result_> {
88
Result_ current;
@@ -23,18 +23,16 @@ public Result_ result() {
2323
private final State<Result_> state;
2424
private Result_ cachedValue;
2525

26-
public ReferenceSumCalculator(State<Result_> state) {
26+
public AbstractReferenceSumSlot(State<Result_> state) {
2727
this.state = state;
2828
}
2929

30-
@Override
31-
public void insert(Result_ input) {
30+
protected void addMapped(Result_ input) {
3231
cachedValue = input;
3332
state.current = state.adder.apply(state.current, input);
3433
}
3534

36-
@Override
37-
public void update(Result_ input) {
35+
protected void updateMapped(Result_ input) {
3836
if (cachedValue == input) {
3937
return;
4038
}
@@ -43,8 +41,7 @@ public void update(Result_ input) {
4341
state.current = state.adder.apply(state.current, input);
4442
}
4543

46-
@Override
47-
public void retract() {
44+
protected void removeMapped() {
4845
state.current = state.subtractor.apply(state.current, cachedValue);
4946
}
5047
}

0 commit comments

Comments
 (0)