Skip to content

Commit df08228

Browse files
committed
Third wave of incrementality
# Conflicts: # core/src/main/java/ai/timefold/solver/core/impl/score/stream/collector/ListUndoableActionable.java
1 parent 226ba50 commit df08228

60 files changed

Lines changed: 1247 additions & 322 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/impl/score/stream/collector/CustomCollectionUndoableActionable.java

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,41 @@
66
import java.util.function.IntFunction;
77

88
public final class CustomCollectionUndoableActionable<Mapped_, Result_ extends Collection<Mapped_>>
9-
implements UndoableActionable<Mapped_, Result_> {
10-
private final IntFunction<Result_> collectionFunction;
11-
private final List<Mapped_> resultList = new ArrayList<>();
9+
implements UndoableActionable<Mapped_> {
10+
public static final class State<Mapped_, Collection_ extends Collection<Mapped_>> {
11+
private final IntFunction<Collection_> collectionFunction;
12+
final List<Mapped_> list = new ArrayList<>();
1213

13-
public CustomCollectionUndoableActionable(IntFunction<Result_> collectionFunction) {
14-
this.collectionFunction = collectionFunction;
14+
public State(IntFunction<Collection_> collectionFunction) {
15+
this.collectionFunction = collectionFunction;
16+
}
17+
18+
public Collection_ result() {
19+
Collection_ out = collectionFunction.apply(list.size());
20+
if (list.isEmpty()) {
21+
// Avoid exception if out is an immutable collection
22+
return out;
23+
}
24+
out.addAll(list);
25+
return out;
26+
}
27+
}
28+
29+
private final State<Mapped_, Result_> state;
30+
private Mapped_ cachedValue;
31+
32+
public CustomCollectionUndoableActionable(State<Mapped_, Result_> state) {
33+
this.state = state;
1534
}
1635

1736
@Override
18-
public Runnable insert(Mapped_ result) {
19-
resultList.add(result);
20-
return () -> resultList.remove(result);
37+
public void insert(Mapped_ result) {
38+
cachedValue = result;
39+
state.list.add(result);
2140
}
2241

2342
@Override
24-
public Result_ result() {
25-
Result_ out = collectionFunction.apply(resultList.size());
26-
if (resultList.isEmpty()) {
27-
// Avoid exception if out is an immutable collection
28-
return out;
29-
}
30-
out.addAll(resultList);
31-
return out;
43+
public void retract() {
44+
state.list.remove(cachedValue);
3245
}
3346
}
Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,42 @@
11
package ai.timefold.solver.core.impl.score.stream.collector;
22

3+
import java.util.ArrayList;
34
import java.util.List;
5+
import java.util.Objects;
46

5-
import ai.timefold.solver.core.impl.util.ElementAwareArrayList;
7+
public final class ListUndoableActionable<Mapped_> implements UndoableActionable<Mapped_> {
8+
public static final class State<Mapped_> {
9+
final List<Mapped_> resultList = new ArrayList<>();
610

7-
public final class ListUndoableActionable<Mapped_> implements UndoableActionable<Mapped_, List<Mapped_>> {
11+
public List<Mapped_> result() {
12+
return resultList;
13+
}
14+
}
15+
16+
private final State<Mapped_> state;
17+
private Mapped_ cachedValue;
18+
19+
public ListUndoableActionable(State<Mapped_> state) {
20+
this.state = state;
21+
}
822

9-
/**
10-
* As long as additions and removals are performed using entry-based methods,
11-
* removals have O(1) performance.
12-
*/
13-
private final ElementAwareArrayList<Mapped_> resultList = new ElementAwareArrayList<>();
23+
@Override
24+
public void insert(Mapped_ mapped) {
25+
cachedValue = mapped;
26+
state.resultList.add(mapped);
27+
}
1428

1529
@Override
16-
public Runnable insert(Mapped_ result) {
17-
var entry = resultList.addEntry(result);
18-
return entry::remove;
30+
public void update(Mapped_ mapped) {
31+
if (Objects.equals(cachedValue, mapped)) {
32+
return;
33+
}
34+
state.resultList.set(state.resultList.indexOf(cachedValue), mapped);
35+
cachedValue = mapped;
1936
}
2037

2138
@Override
22-
public List<Mapped_> result() {
23-
return resultList;
39+
public void retract() {
40+
state.resultList.remove(cachedValue);
2441
}
2542
}
Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ai.timefold.solver.core.impl.score.stream.collector;
22

33
import java.util.Map;
4+
import java.util.Objects;
45
import java.util.Set;
56
import java.util.function.BinaryOperator;
67
import java.util.function.IntFunction;
@@ -9,33 +10,54 @@
910
import ai.timefold.solver.core.impl.util.Pair;
1011

1112
public final class MapUndoableActionable<Key_, Value_, ResultValue_, Result_ extends Map<Key_, ResultValue_>>
12-
implements UndoableActionable<Pair<Key_, Value_>, Result_> {
13-
ToMapResultContainer<Key_, Value_, ResultValue_, Result_> container;
13+
implements UndoableActionable<Pair<Key_, Value_>> {
14+
public static final class State<Key_, Value_, ResultValue_, Result_ extends Map<Key_, ResultValue_>> {
15+
final ToMapResultContainer<Key_, Value_, ResultValue_, Result_> container;
1416

15-
private MapUndoableActionable(ToMapResultContainer<Key_, Value_, ResultValue_, Result_> container) {
16-
this.container = container;
17+
State(ToMapResultContainer<Key_, Value_, ResultValue_, Result_> container) {
18+
this.container = container;
19+
}
20+
21+
public Result_ result() {
22+
return container.getResult();
23+
}
1724
}
1825

1926
public static <Key_, Value_, Set_ extends Set<Value_>, Result_ extends Map<Key_, Set_>>
20-
MapUndoableActionable<Key_, Value_, Set_, Result_> multiMap(
21-
Supplier<Result_> resultSupplier, IntFunction<Set_> setFunction) {
22-
return new MapUndoableActionable<>(new ToMultiMapResultContainer<>(resultSupplier, setFunction));
27+
State<Key_, Value_, Set_, Result_> multiMapState(Supplier<Result_> resultSupplier, IntFunction<Set_> setFunction) {
28+
return new State<>(new ToMultiMapResultContainer<>(resultSupplier, setFunction));
29+
}
30+
31+
public static <Key_, Value_, Result_ extends Map<Key_, Value_>>
32+
State<Key_, Value_, Value_, Result_>
33+
mergeMapState(Supplier<Result_> resultSupplier, BinaryOperator<Value_> mergeFunction) {
34+
return new State<>(new ToSimpleMapResultContainer<>(resultSupplier, mergeFunction));
2335
}
2436

25-
public static <Key_, Value_, Result_ extends Map<Key_, Value_>> MapUndoableActionable<Key_, Value_, Value_, Result_>
26-
mergeMap(
27-
Supplier<Result_> resultSupplier, BinaryOperator<Value_> mergeFunction) {
28-
return new MapUndoableActionable<>(new ToSimpleMapResultContainer<>(resultSupplier, mergeFunction));
37+
private final State<Key_, Value_, ResultValue_, Result_> state;
38+
private Pair<Key_, Value_> cachedEntry;
39+
40+
public MapUndoableActionable(State<Key_, Value_, ResultValue_, Result_> state) {
41+
this.state = state;
42+
}
43+
44+
@Override
45+
public void insert(Pair<Key_, Value_> entry) {
46+
this.cachedEntry = entry;
47+
state.container.add(entry.key(), entry.value());
2948
}
3049

3150
@Override
32-
public Runnable insert(Pair<Key_, Value_> entry) {
33-
container.add(entry.key(), entry.value());
34-
return () -> container.remove(entry.key(), entry.value());
51+
public void update(Pair<Key_, Value_> entry) {
52+
if (Objects.equals(cachedEntry, entry)) {
53+
return;
54+
}
55+
retract();
56+
insert(entry);
3557
}
3658

3759
@Override
38-
public Result_ result() {
39-
return container.getResult();
60+
public void retract() {
61+
state.container.remove(cachedEntry.key(), cachedEntry.value());
4062
}
4163
}

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

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,105 @@
44
import java.util.LinkedHashMap;
55
import java.util.Map;
66
import java.util.NavigableMap;
7+
import java.util.Objects;
78
import java.util.TreeMap;
89
import java.util.function.Function;
910

1011
import ai.timefold.solver.core.impl.util.ConstantLambdaUtils;
1112
import ai.timefold.solver.core.impl.util.MutableInt;
1213

13-
public final class MinMaxUndoableActionable<Result_, Property_> implements UndoableActionable<Result_, Result_> {
14-
private final boolean isMin;
15-
private final NavigableMap<Property_, Map<Result_, MutableInt>> propertyToItemCountMap;
16-
private final Function<? super Result_, ? extends Property_> propertyFunction;
17-
18-
private MinMaxUndoableActionable(boolean isMin,
19-
NavigableMap<Property_, Map<Result_, MutableInt>> propertyToItemCountMap,
20-
Function<? super Result_, ? extends Property_> propertyFunction) {
21-
this.isMin = isMin;
22-
this.propertyToItemCountMap = propertyToItemCountMap;
23-
this.propertyFunction = propertyFunction;
14+
public final class MinMaxUndoableActionable<Result_, Property_> implements UndoableActionable<Result_> {
15+
public static final class State<Result_, Property_> {
16+
final boolean isMin;
17+
final NavigableMap<Property_, Map<Result_, MutableInt>> propertyToItemCountMap;
18+
final Function<? super Result_, ? extends Property_> propertyFunction;
19+
20+
State(boolean isMin, NavigableMap<Property_, Map<Result_, MutableInt>> propertyToItemCountMap,
21+
Function<? super Result_, ? extends Property_> propertyFunction) {
22+
this.isMin = isMin;
23+
this.propertyToItemCountMap = propertyToItemCountMap;
24+
this.propertyFunction = propertyFunction;
25+
}
26+
27+
public Result_ result() {
28+
if (propertyToItemCountMap.isEmpty()) {
29+
return null;
30+
}
31+
return isMin ? getFirstKey(propertyToItemCountMap.firstEntry().getValue())
32+
: getFirstKey(propertyToItemCountMap.lastEntry().getValue());
33+
}
34+
35+
private static <Key_> Key_ getFirstKey(Map<Key_, ?> map) {
36+
return map.keySet().iterator().next();
37+
}
2438
}
2539

26-
public static <Result extends Comparable<? super Result>> MinMaxUndoableActionable<Result, Result> minCalculator() {
27-
return new MinMaxUndoableActionable<>(true, new TreeMap<>(), ConstantLambdaUtils.identity());
40+
public static <Result extends Comparable<? super Result>> State<Result, Result> minState() {
41+
return new State<>(true, new TreeMap<>(), ConstantLambdaUtils.identity());
2842
}
2943

30-
public static <Result extends Comparable<? super Result>> MinMaxUndoableActionable<Result, Result> maxCalculator() {
31-
return new MinMaxUndoableActionable<>(false, new TreeMap<>(), ConstantLambdaUtils.identity());
44+
public static <Result extends Comparable<? super Result>> State<Result, Result> maxState() {
45+
return new State<>(false, new TreeMap<>(), ConstantLambdaUtils.identity());
3246
}
3347

34-
public static <Result> MinMaxUndoableActionable<Result, Result> minCalculator(Comparator<? super Result> comparator) {
35-
return new MinMaxUndoableActionable<>(true, new TreeMap<>(comparator), ConstantLambdaUtils.identity());
48+
public static <Result> State<Result, Result> minState(Comparator<? super Result> comparator) {
49+
return new State<>(true, new TreeMap<>(comparator), ConstantLambdaUtils.identity());
3650
}
3751

38-
public static <Result> MinMaxUndoableActionable<Result, Result> maxCalculator(Comparator<? super Result> comparator) {
39-
return new MinMaxUndoableActionable<>(false, new TreeMap<>(comparator), ConstantLambdaUtils.identity());
52+
public static <Result> State<Result, Result> maxState(Comparator<? super Result> comparator) {
53+
return new State<>(false, new TreeMap<>(comparator), ConstantLambdaUtils.identity());
4054
}
4155

42-
public static <Result, Property extends Comparable<? super Property>> MinMaxUndoableActionable<Result, Property>
43-
minCalculator(
44-
Function<? super Result, ? extends Property> propertyMapper) {
45-
return new MinMaxUndoableActionable<>(true, new TreeMap<>(), propertyMapper);
56+
public static <Result, Property extends Comparable<? super Property>> State<Result, Property> minState(
57+
Function<? super Result, ? extends Property> propertyMapper) {
58+
return new State<>(true, new TreeMap<>(), propertyMapper);
4659
}
4760

48-
public static <Result, Property extends Comparable<? super Property>> MinMaxUndoableActionable<Result, Property>
49-
maxCalculator(
50-
Function<? super Result, ? extends Property> propertyMapper) {
51-
return new MinMaxUndoableActionable<>(false, new TreeMap<>(), propertyMapper);
61+
public static <Result, Property extends Comparable<? super Property>> State<Result, Property> maxState(
62+
Function<? super Result, ? extends Property> propertyMapper) {
63+
return new State<>(false, new TreeMap<>(), propertyMapper);
64+
}
65+
66+
private final State<Result_, Property_> state;
67+
private Result_ cachedItem;
68+
private Property_ cachedKey;
69+
private Map<Result_, MutableInt> cachedInnerMap;
70+
private MutableInt cachedCount;
71+
72+
public MinMaxUndoableActionable(State<Result_, Property_> state) {
73+
this.state = state;
5274
}
5375

5476
@Override
55-
public Runnable insert(Result_ item) {
56-
Property_ key = propertyFunction.apply(item);
57-
Map<Result_, MutableInt> itemCountMap = propertyToItemCountMap.computeIfAbsent(key, ignored -> new LinkedHashMap<>());
58-
MutableInt count = itemCountMap.computeIfAbsent(item, ignored -> new MutableInt());
59-
count.increment();
60-
61-
return () -> {
62-
if (count.decrement() == 0) {
63-
itemCountMap.remove(item);
64-
if (itemCountMap.isEmpty()) {
65-
propertyToItemCountMap.remove(key);
66-
}
67-
}
68-
};
77+
public void insert(Result_ item) {
78+
cachedItem = item;
79+
cachedKey = state.propertyFunction.apply(item);
80+
cachedInnerMap = state.propertyToItemCountMap.computeIfAbsent(cachedKey, ignored -> new LinkedHashMap<>());
81+
cachedCount = cachedInnerMap.computeIfAbsent(item, ignored -> new MutableInt());
82+
cachedCount.increment();
6983
}
7084

7185
@Override
72-
public Result_ result() {
73-
if (propertyToItemCountMap.isEmpty()) {
74-
return null;
86+
public void update(Result_ item) {
87+
var newKey = state.propertyFunction.apply(item);
88+
if (Objects.equals(cachedKey, newKey)) {
89+
return;
7590
}
76-
return isMin ? getFirstKey(propertyToItemCountMap.firstEntry().getValue())
77-
: getFirstKey(propertyToItemCountMap.lastEntry().getValue());
91+
retract();
92+
cachedItem = item;
93+
cachedKey = newKey;
94+
cachedInnerMap = state.propertyToItemCountMap.computeIfAbsent(newKey, ignored -> new LinkedHashMap<>());
95+
cachedCount = cachedInnerMap.computeIfAbsent(item, ignored -> new MutableInt());
96+
cachedCount.increment();
7897
}
7998

80-
private static <Key_> Key_ getFirstKey(Map<Key_, ?> map) {
81-
return map.keySet().iterator().next();
99+
@Override
100+
public void retract() {
101+
if (cachedCount.decrement() == 0) {
102+
cachedInnerMap.remove(cachedItem);
103+
if (cachedInnerMap.isEmpty()) {
104+
state.propertyToItemCountMap.remove(cachedKey);
105+
}
106+
}
82107
}
83108
}

0 commit comments

Comments
 (0)