diff options
Diffstat (limited to 'subprojects/store/src/main/java/tools/refinery')
53 files changed, 924 insertions, 199 deletions
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java index 0a94d449..5e69e7af 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java | |||
@@ -5,6 +5,9 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.map; | 6 | package tools.refinery.store.map; |
7 | 7 | ||
8 | import java.util.Iterator; | ||
9 | import java.util.Map; | ||
10 | |||
8 | public final class Cursors { | 11 | public final class Cursors { |
9 | private Cursors() { | 12 | private Cursors() { |
10 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | 13 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); |
@@ -14,6 +17,18 @@ public final class Cursors { | |||
14 | return new Empty<>(); | 17 | return new Empty<>(); |
15 | } | 18 | } |
16 | 19 | ||
20 | public static <K, V> Cursor<K, V> singleton(K key, V value) { | ||
21 | return new Singleton<>(key, value); | ||
22 | } | ||
23 | |||
24 | public static <K, V> Cursor<K, V> of(Iterator<Map.Entry<K, V>> iterator) { | ||
25 | return new IteratorBasedCursor<>(iterator); | ||
26 | } | ||
27 | |||
28 | public static <K, V> Cursor<K, V> of(Map<K, V> map) { | ||
29 | return of(map.entrySet().iterator()); | ||
30 | } | ||
31 | |||
17 | private static class Empty<K, V> implements Cursor<K, V> { | 32 | private static class Empty<K, V> implements Cursor<K, V> { |
18 | private boolean terminated = false; | 33 | private boolean terminated = false; |
19 | 34 | ||
@@ -38,4 +53,53 @@ public final class Cursors { | |||
38 | return false; | 53 | return false; |
39 | } | 54 | } |
40 | } | 55 | } |
56 | |||
57 | private static class Singleton<K, V> implements Cursor<K, V> { | ||
58 | private State state = State.INITIAL; | ||
59 | private final K key; | ||
60 | private final V value; | ||
61 | |||
62 | public Singleton(K key, V value) { | ||
63 | this.key = key; | ||
64 | this.value = value; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public K getKey() { | ||
69 | if (state == State.STARTED) { | ||
70 | return key; | ||
71 | } | ||
72 | return null; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public V getValue() { | ||
77 | if (state == State.STARTED) { | ||
78 | return value; | ||
79 | } | ||
80 | return null; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public boolean isTerminated() { | ||
85 | return state == State.TERMINATED; | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public boolean move() { | ||
90 | if (state == State.INITIAL) { | ||
91 | state = State.STARTED; | ||
92 | return true; | ||
93 | } | ||
94 | state = State.TERMINATED; | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | |||
99 | private enum State { | ||
100 | INITIAL, | ||
101 | STARTED, | ||
102 | TERMINATED | ||
103 | } | ||
104 | } | ||
41 | } | 105 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java new file mode 100644 index 00000000..0ed9b730 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.map; | ||
7 | |||
8 | import java.util.Iterator; | ||
9 | import java.util.Map; | ||
10 | |||
11 | public class IteratorBasedCursor<K, V> implements Cursor<K, V> { | ||
12 | private final Iterator<Map.Entry<K, V>> iterator; | ||
13 | private Map.Entry<K, V> entry; | ||
14 | private boolean terminated; | ||
15 | |||
16 | public IteratorBasedCursor(Iterator<Map.Entry<K, V>> iterator) { | ||
17 | this.iterator = iterator; | ||
18 | } | ||
19 | |||
20 | @Override | ||
21 | public K getKey() { | ||
22 | return entry.getKey(); | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public V getValue() { | ||
27 | return entry.getValue(); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public boolean isTerminated() { | ||
32 | return terminated; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public boolean move() { | ||
37 | if (!terminated && iterator.hasNext()) { | ||
38 | entry = iterator.next(); | ||
39 | return true; | ||
40 | } | ||
41 | terminated = true; | ||
42 | return false; | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java index c19cc817..8be42712 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java | |||
@@ -5,10 +5,9 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.map.internal.delta; | 6 | package tools.refinery.store.map.internal.delta; |
7 | 7 | ||
8 | import java.util.*; | ||
9 | |||
10 | import tools.refinery.store.map.*; | 8 | import tools.refinery.store.map.*; |
11 | import tools.refinery.store.map.IteratorAsCursor; | 9 | |
10 | import java.util.*; | ||
12 | 11 | ||
13 | public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> { | 12 | public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> { |
14 | protected final VersionedMapStoreDeltaImpl<K, V> store; | 13 | protected final VersionedMapStoreDeltaImpl<K, V> store; |
@@ -24,7 +23,7 @@ public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> { | |||
24 | this.store = store; | 23 | this.store = store; |
25 | this.defaultValue = defaultValue; | 24 | this.defaultValue = defaultValue; |
26 | 25 | ||
27 | current = new HashMap<>(); | 26 | current = new LinkedHashMap<>(); |
28 | if (summarizeChanges) { | 27 | if (summarizeChanges) { |
29 | this.uncommittedStore = new UncommittedDeltaMapStore<>(this); | 28 | this.uncommittedStore = new UncommittedDeltaMapStore<>(this); |
30 | } else { | 29 | } else { |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java index f906b48a..d650bd06 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java | |||
@@ -13,4 +13,6 @@ public sealed interface AnyInterpretation permits Interpretation { | |||
13 | AnySymbol getSymbol(); | 13 | AnySymbol getSymbol(); |
14 | 14 | ||
15 | long getSize(); | 15 | long getSize(); |
16 | |||
17 | int getAdjacentSize(int slot, int node); | ||
16 | } | 18 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java index 72f188d3..1b15e4cf 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java | |||
@@ -19,6 +19,8 @@ public non-sealed interface Interpretation<T> extends AnyInterpretation { | |||
19 | 19 | ||
20 | Cursor<Tuple, T> getAll(); | 20 | Cursor<Tuple, T> getAll(); |
21 | 21 | ||
22 | Cursor<Tuple, T> getAdjacent(int slot, int node); | ||
23 | |||
22 | T put(Tuple key, T value); | 24 | T put(Tuple key, T value); |
23 | 25 | ||
24 | void putAll(Cursor<Tuple, T> cursor); | 26 | void putAll(Cursor<Tuple, T> cursor); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java index e2ab72e7..c4ce5207 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java | |||
@@ -8,11 +8,9 @@ package tools.refinery.store.model; | |||
8 | import tools.refinery.store.adapter.ModelAdapter; | 8 | import tools.refinery.store.adapter.ModelAdapter; |
9 | import tools.refinery.store.map.Version; | 9 | import tools.refinery.store.map.Version; |
10 | import tools.refinery.store.map.Versioned; | 10 | import tools.refinery.store.map.Versioned; |
11 | import tools.refinery.store.model.internal.VersionedInterpretation; | ||
12 | import tools.refinery.store.representation.AnySymbol; | 11 | import tools.refinery.store.representation.AnySymbol; |
13 | import tools.refinery.store.representation.Symbol; | 12 | import tools.refinery.store.representation.Symbol; |
14 | 13 | ||
15 | import java.util.Map; | ||
16 | import java.util.Optional; | 14 | import java.util.Optional; |
17 | 15 | ||
18 | public interface Model extends Versioned { | 16 | public interface Model extends Versioned { |
@@ -38,4 +36,6 @@ public interface Model extends Versioned { | |||
38 | void addListener(ModelListener listener); | 36 | void addListener(ModelListener listener); |
39 | 37 | ||
40 | void removeListener(ModelListener listener); | 38 | void removeListener(ModelListener listener); |
39 | |||
40 | void checkCancelled(); | ||
41 | } | 41 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java index 89382b3a..61abf126 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java | |||
@@ -26,6 +26,8 @@ public interface ModelStore { | |||
26 | 26 | ||
27 | <T extends ModelStoreAdapter> T getAdapter(Class<T> adapterType); | 27 | <T extends ModelStoreAdapter> T getAdapter(Class<T> adapterType); |
28 | 28 | ||
29 | void checkCancelled(); | ||
30 | |||
29 | static ModelStoreBuilder builder() { | 31 | static ModelStoreBuilder builder() { |
30 | return new ModelStoreBuilderImpl(); | 32 | return new ModelStoreBuilderImpl(); |
31 | } | 33 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java index 3a4024b5..9b2b38c3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java | |||
@@ -8,12 +8,15 @@ package tools.refinery.store.model; | |||
8 | import tools.refinery.store.adapter.ModelAdapterBuilder; | 8 | import tools.refinery.store.adapter.ModelAdapterBuilder; |
9 | import tools.refinery.store.representation.AnySymbol; | 9 | import tools.refinery.store.representation.AnySymbol; |
10 | import tools.refinery.store.representation.Symbol; | 10 | import tools.refinery.store.representation.Symbol; |
11 | import tools.refinery.store.util.CancellationToken; | ||
11 | 12 | ||
12 | import java.util.Collection; | 13 | import java.util.Collection; |
13 | import java.util.List; | 14 | import java.util.List; |
14 | import java.util.Optional; | 15 | import java.util.Optional; |
15 | 16 | ||
16 | public interface ModelStoreBuilder { | 17 | public interface ModelStoreBuilder { |
18 | ModelStoreBuilder cancellationToken(CancellationToken cancellationToken); | ||
19 | |||
17 | default ModelStoreBuilder symbols(AnySymbol... symbols) { | 20 | default ModelStoreBuilder symbols(AnySymbol... symbols) { |
18 | return symbols(List.of(symbols)); | 21 | return symbols(List.of(symbols)); |
19 | } | 22 | } |
@@ -29,7 +32,9 @@ public interface ModelStoreBuilder { | |||
29 | 32 | ||
30 | <T> ModelStoreBuilder symbol(Symbol<T> symbol); | 33 | <T> ModelStoreBuilder symbol(Symbol<T> symbol); |
31 | 34 | ||
32 | <T extends ModelAdapterBuilder> ModelStoreBuilder with(T adapterBuilder); | 35 | ModelStoreBuilder with(ModelAdapterBuilder adapterBuilder); |
36 | |||
37 | ModelStoreBuilder with(ModelStoreConfiguration configuration); | ||
33 | 38 | ||
34 | <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType); | 39 | <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType); |
35 | 40 | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java new file mode 100644 index 00000000..e94af5f8 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.model; | ||
7 | |||
8 | @FunctionalInterface | ||
9 | public interface ModelStoreConfiguration { | ||
10 | void apply(ModelStoreBuilder storeBuilder); | ||
11 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java new file mode 100644 index 00000000..3d7f59d7 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.model.internal; | ||
7 | |||
8 | import org.eclipse.collections.api.factory.Maps; | ||
9 | import org.eclipse.collections.api.factory.primitive.IntObjectMaps; | ||
10 | import org.eclipse.collections.api.map.MutableMap; | ||
11 | import org.eclipse.collections.api.map.primitive.MutableIntObjectMap; | ||
12 | import tools.refinery.store.map.*; | ||
13 | import tools.refinery.store.tuple.Tuple; | ||
14 | |||
15 | import java.util.Set; | ||
16 | |||
17 | class BaseIndexer<T> { | ||
18 | private final MutableIntObjectMap<MutableMap<Tuple, T>>[] maps; | ||
19 | private final VersionedMap<Tuple, T> versionedMap; | ||
20 | |||
21 | public BaseIndexer(int arity, VersionedMap<Tuple, T> map) { | ||
22 | if (arity < 2) { | ||
23 | throw new IllegalArgumentException("Only arity >= 2 symbols need to be indexed"); | ||
24 | } | ||
25 | // There is no way in Java to create a generic array in a checked way. | ||
26 | @SuppressWarnings({"unchecked", "squid:S1905"}) | ||
27 | var uncheckedMaps = (MutableIntObjectMap<MutableMap<Tuple, T>>[]) new MutableIntObjectMap[arity]; | ||
28 | maps = uncheckedMaps; | ||
29 | for (int i = 0; i < arity; i++) { | ||
30 | maps[i] = IntObjectMaps.mutable.empty(); | ||
31 | } | ||
32 | this.versionedMap = map; | ||
33 | if (map != null) { | ||
34 | var cursor = map.getAll(); | ||
35 | while (cursor.move()) { | ||
36 | put(cursor.getKey(), cursor.getValue()); | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | |||
41 | public void put(Tuple key, T value) { | ||
42 | for (int i = 0; i < maps.length; i++) { | ||
43 | var map = maps[i]; | ||
44 | int element = key.get(i); | ||
45 | var adjacentTuples = map.getIfAbsentPut(element, Maps.mutable::empty); | ||
46 | adjacentTuples.put(key, value); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | public void remove(Tuple key) { | ||
51 | for (int i = 0; i < maps.length; i++) { | ||
52 | var map = maps[i]; | ||
53 | int element = key.get(i); | ||
54 | var adjacentTuples = map.get(element); | ||
55 | if (adjacentTuples == null) { | ||
56 | continue; | ||
57 | } | ||
58 | adjacentTuples.remove(key); | ||
59 | if (adjacentTuples.isEmpty()) { | ||
60 | map.remove(element); | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | private MutableMap<Tuple, T> getAdjacentMap(int slot, int node) { | ||
66 | if (slot < 0 || slot >= maps.length) { | ||
67 | throw new IllegalArgumentException("Invalid index: " + slot); | ||
68 | } | ||
69 | var map = maps[slot]; | ||
70 | return map.get(node); | ||
71 | } | ||
72 | |||
73 | public int getAdjacentSize(int slot, int node) { | ||
74 | var adjacentTuples = getAdjacentMap(slot, node); | ||
75 | if (adjacentTuples == null) { | ||
76 | return 0; | ||
77 | } | ||
78 | return adjacentTuples.size(); | ||
79 | } | ||
80 | |||
81 | public Cursor<Tuple, T> getAdjacent(int slot, int node) { | ||
82 | var adjacentTuples = getAdjacentMap(slot, node); | ||
83 | if (adjacentTuples == null) { | ||
84 | return Cursors.empty(); | ||
85 | } | ||
86 | return new IndexCursor<>(adjacentTuples, versionedMap); | ||
87 | } | ||
88 | |||
89 | private static class IndexCursor<T> extends IteratorBasedCursor<Tuple, T> { | ||
90 | private final Set<AnyVersionedMap> dependingMaps; | ||
91 | |||
92 | public IndexCursor(MutableMap<Tuple, T> map, VersionedMap<Tuple, T> versionedMap) { | ||
93 | super(map.entrySet().iterator()); | ||
94 | dependingMaps = versionedMap == null ? Set.of() : Set.of(versionedMap); | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public Set<AnyVersionedMap> getDependingMaps() { | ||
99 | return dependingMaps; | ||
100 | } | ||
101 | } | ||
102 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java new file mode 100644 index 00000000..0be16f77 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.model.internal; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.VersionedMap; | ||
10 | import tools.refinery.store.representation.Symbol; | ||
11 | import tools.refinery.store.tuple.Tuple; | ||
12 | |||
13 | import java.util.Objects; | ||
14 | |||
15 | class IndexedVersionedInterpretation<T> extends VersionedInterpretation<T> { | ||
16 | private final BaseIndexer<T> indexer; | ||
17 | |||
18 | public IndexedVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { | ||
19 | super(model, symbol, map); | ||
20 | indexer = new BaseIndexer<>(symbol.arity(), map); | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public Cursor<Tuple, T> getAdjacent(int slot, int node) { | ||
25 | return indexer.getAdjacent(slot, node); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getAdjacentSize(int slot, int node) { | ||
30 | return indexer.getAdjacentSize(slot, node); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | protected boolean shouldNotifyRestoreListeners() { | ||
35 | // Always call the {@code valueChanged} method to update the index. | ||
36 | return true; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) { | ||
41 | if (Objects.equals(toValue, getSymbol().defaultValue())) { | ||
42 | indexer.remove(key); | ||
43 | } else { | ||
44 | indexer.put(key, toValue); | ||
45 | } | ||
46 | super.valueChanged(key, fromValue, toValue, restoring); | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java index 2b12d5a6..d11e431b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java | |||
@@ -13,23 +13,26 @@ import tools.refinery.store.model.*; | |||
13 | import tools.refinery.store.representation.AnySymbol; | 13 | import tools.refinery.store.representation.AnySymbol; |
14 | import tools.refinery.store.representation.Symbol; | 14 | import tools.refinery.store.representation.Symbol; |
15 | import tools.refinery.store.tuple.Tuple; | 15 | import tools.refinery.store.tuple.Tuple; |
16 | import tools.refinery.store.util.CancellationToken; | ||
16 | 17 | ||
17 | import java.util.*; | 18 | import java.util.*; |
18 | 19 | ||
19 | public class ModelImpl implements Model { | 20 | public class ModelImpl implements Model { |
20 | private final ModelStore store; | 21 | private final ModelStoreImpl store; |
21 | private Version state; | 22 | private Version state; |
22 | private LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations; | 23 | private LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations; |
23 | private final List<ModelAdapter> adapters; | 24 | private final List<ModelAdapter> adapters; |
24 | private final List<ModelListener> listeners = new ArrayList<>(); | 25 | private final List<ModelListener> listeners = new ArrayList<>(); |
26 | private final CancellationToken cancellationToken; | ||
25 | private boolean uncommittedChanges; | 27 | private boolean uncommittedChanges; |
26 | private ModelAction pendingAction = ModelAction.NONE; | 28 | private ModelAction pendingAction = ModelAction.NONE; |
27 | private Version restoringToState = null; | 29 | private Version restoringToState = null; |
28 | 30 | ||
29 | ModelImpl(ModelStore store, Version state, int adapterCount) { | 31 | ModelImpl(ModelStoreImpl store, Version state, int adapterCount) { |
30 | this.store = store; | 32 | this.store = store; |
31 | this.state = state; | 33 | this.state = state; |
32 | adapters = new ArrayList<>(adapterCount); | 34 | adapters = new ArrayList<>(adapterCount); |
35 | cancellationToken = store.getCancellationToken(); | ||
33 | } | 36 | } |
34 | 37 | ||
35 | void setInterpretations(LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations) { | 38 | void setInterpretations(LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations) { |
@@ -88,6 +91,7 @@ public class ModelImpl implements Model { | |||
88 | 91 | ||
89 | @Override | 92 | @Override |
90 | public Version commit() { | 93 | public Version commit() { |
94 | checkCancelled(); | ||
91 | if (hasPendingAction()) { | 95 | if (hasPendingAction()) { |
92 | throw pendingActionError("commit"); | 96 | throw pendingActionError("commit"); |
93 | } | 97 | } |
@@ -106,6 +110,7 @@ public class ModelImpl implements Model { | |||
106 | Version[] interpretationVersions = new Version[interpretations.size()]; | 110 | Version[] interpretationVersions = new Version[interpretations.size()]; |
107 | int j = 0; | 111 | int j = 0; |
108 | for (var interpretationEntry : interpretations.entrySet()) { | 112 | for (var interpretationEntry : interpretations.entrySet()) { |
113 | checkCancelled(); | ||
109 | interpretationVersions[j++] = interpretationEntry.getValue().commit(); | 114 | interpretationVersions[j++] = interpretationEntry.getValue().commit(); |
110 | } | 115 | } |
111 | ModelVersion modelVersion = new ModelVersion(interpretationVersions); | 116 | ModelVersion modelVersion = new ModelVersion(interpretationVersions); |
@@ -125,6 +130,7 @@ public class ModelImpl implements Model { | |||
125 | 130 | ||
126 | @Override | 131 | @Override |
127 | public void restore(Version version) { | 132 | public void restore(Version version) { |
133 | checkCancelled(); | ||
128 | if (hasPendingAction()) { | 134 | if (hasPendingAction()) { |
129 | throw pendingActionError("restore to %s".formatted(version)); | 135 | throw pendingActionError("restore to %s".formatted(version)); |
130 | } | 136 | } |
@@ -140,6 +146,7 @@ public class ModelImpl implements Model { | |||
140 | } | 146 | } |
141 | int j = 0; | 147 | int j = 0; |
142 | for (var interpretation : interpretations.values()) { | 148 | for (var interpretation : interpretations.values()) { |
149 | checkCancelled(); | ||
143 | interpretation.restore(ModelVersion.getInternalVersion(version, j++)); | 150 | interpretation.restore(ModelVersion.getInternalVersion(version, j++)); |
144 | } | 151 | } |
145 | 152 | ||
@@ -187,4 +194,9 @@ public class ModelImpl implements Model { | |||
187 | public void removeListener(ModelListener listener) { | 194 | public void removeListener(ModelListener listener) { |
188 | listeners.remove(listener); | 195 | listeners.remove(listener); |
189 | } | 196 | } |
197 | |||
198 | @Override | ||
199 | public void checkCancelled() { | ||
200 | cancellationToken.checkCancelled(); | ||
201 | } | ||
190 | } | 202 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java index 2bd187a8..53ecde5e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java | |||
@@ -12,21 +12,37 @@ import tools.refinery.store.map.VersionedMapStoreFactory; | |||
12 | import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; | 12 | import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; |
13 | import tools.refinery.store.model.ModelStore; | 13 | import tools.refinery.store.model.ModelStore; |
14 | import tools.refinery.store.model.ModelStoreBuilder; | 14 | import tools.refinery.store.model.ModelStoreBuilder; |
15 | import tools.refinery.store.model.ModelStoreConfiguration; | ||
15 | import tools.refinery.store.representation.AnySymbol; | 16 | import tools.refinery.store.representation.AnySymbol; |
16 | import tools.refinery.store.representation.Symbol; | 17 | import tools.refinery.store.representation.Symbol; |
17 | import tools.refinery.store.tuple.Tuple; | 18 | import tools.refinery.store.tuple.Tuple; |
19 | import tools.refinery.store.util.CancellationToken; | ||
18 | 20 | ||
19 | import java.util.*; | 21 | import java.util.*; |
20 | 22 | ||
21 | public class ModelStoreBuilderImpl implements ModelStoreBuilder { | 23 | public class ModelStoreBuilderImpl implements ModelStoreBuilder { |
24 | private CancellationToken cancellationToken; | ||
22 | private final LinkedHashSet<AnySymbol> allSymbols = new LinkedHashSet<>(); | 25 | private final LinkedHashSet<AnySymbol> allSymbols = new LinkedHashSet<>(); |
23 | private final LinkedHashMap<SymbolEquivalenceClass<?>, List<AnySymbol>> equivalenceClasses = new LinkedHashMap<>(); | 26 | private final LinkedHashMap<SymbolEquivalenceClass<?>, List<AnySymbol>> equivalenceClasses = new LinkedHashMap<>(); |
24 | private final List<ModelAdapterBuilder> adapters = new ArrayList<>(); | 27 | private final List<ModelAdapterBuilder> adapters = new ArrayList<>(); |
25 | 28 | ||
26 | @Override | 29 | @Override |
30 | public ModelStoreBuilder cancellationToken(CancellationToken cancellationToken) { | ||
31 | if (this.cancellationToken != null) { | ||
32 | throw new IllegalStateException("Cancellation token was already set"); | ||
33 | } | ||
34 | if (cancellationToken == null) { | ||
35 | throw new IllegalStateException("Cancellation token must not be null"); | ||
36 | } | ||
37 | this.cancellationToken = cancellationToken; | ||
38 | return this; | ||
39 | } | ||
40 | |||
41 | @Override | ||
27 | public <T> ModelStoreBuilder symbol(Symbol<T> symbol) { | 42 | public <T> ModelStoreBuilder symbol(Symbol<T> symbol) { |
28 | if (!allSymbols.add(symbol)) { | 43 | if (!allSymbols.add(symbol)) { |
29 | throw new IllegalArgumentException("Symbol %s already added".formatted(symbol)); | 44 | // No need to add symbol twice. |
45 | return this; | ||
30 | } | 46 | } |
31 | var equivalenceClass = new SymbolEquivalenceClass<>(symbol); | 47 | var equivalenceClass = new SymbolEquivalenceClass<>(symbol); |
32 | var symbolsInEquivalenceClass = equivalenceClasses.computeIfAbsent(equivalenceClass, | 48 | var symbolsInEquivalenceClass = equivalenceClasses.computeIfAbsent(equivalenceClass, |
@@ -36,7 +52,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { | |||
36 | } | 52 | } |
37 | 53 | ||
38 | @Override | 54 | @Override |
39 | public <T extends ModelAdapterBuilder> ModelStoreBuilder with(T adapterBuilder) { | 55 | public ModelStoreBuilder with(ModelAdapterBuilder adapterBuilder) { |
40 | for (var existingAdapter : adapters) { | 56 | for (var existingAdapter : adapters) { |
41 | if (existingAdapter.getClass().equals(adapterBuilder.getClass())) { | 57 | if (existingAdapter.getClass().equals(adapterBuilder.getClass())) { |
42 | throw new IllegalArgumentException("%s adapter was already configured for store builder" | 58 | throw new IllegalArgumentException("%s adapter was already configured for store builder" |
@@ -48,6 +64,12 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { | |||
48 | } | 64 | } |
49 | 65 | ||
50 | @Override | 66 | @Override |
67 | public ModelStoreBuilder with(ModelStoreConfiguration configuration) { | ||
68 | configuration.apply(this); | ||
69 | return this; | ||
70 | } | ||
71 | |||
72 | @Override | ||
51 | public <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType) { | 73 | public <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType) { |
52 | return AdapterUtils.tryGetAdapter(adapters, adapterType); | 74 | return AdapterUtils.tryGetAdapter(adapters, adapterType); |
53 | } | 75 | } |
@@ -59,6 +81,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { | |||
59 | 81 | ||
60 | @Override | 82 | @Override |
61 | public ModelStore build() { | 83 | public ModelStore build() { |
84 | // First configure adapters and let them register any symbols we don't know about yet. | ||
62 | for (int i = adapters.size() - 1; i >= 0; i--) { | 85 | for (int i = adapters.size() - 1; i >= 0; i--) { |
63 | adapters.get(i).configure(this); | 86 | adapters.get(i).configure(this); |
64 | } | 87 | } |
@@ -66,7 +89,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { | |||
66 | for (var entry : equivalenceClasses.entrySet()) { | 89 | for (var entry : equivalenceClasses.entrySet()) { |
67 | createStores(stores, entry.getKey(), entry.getValue()); | 90 | createStores(stores, entry.getKey(), entry.getValue()); |
68 | } | 91 | } |
69 | var modelStore = new ModelStoreImpl(stores, adapters.size()); | 92 | var modelStore = new ModelStoreImpl(stores, adapters.size(), cancellationToken == null ? |
93 | CancellationToken.NONE : cancellationToken); | ||
70 | for (var adapterBuilder : adapters) { | 94 | for (var adapterBuilder : adapters) { |
71 | var storeAdapter = adapterBuilder.build(modelStore); | 95 | var storeAdapter = adapterBuilder.build(modelStore); |
72 | modelStore.addAdapter(storeAdapter); | 96 | modelStore.addAdapter(storeAdapter); |
@@ -77,8 +101,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { | |||
77 | private <T> void createStores(Map<AnySymbol, VersionedMapStore<Tuple, ?>> stores, | 101 | private <T> void createStores(Map<AnySymbol, VersionedMapStore<Tuple, ?>> stores, |
78 | SymbolEquivalenceClass<T> equivalenceClass, List<AnySymbol> symbols) { | 102 | SymbolEquivalenceClass<T> equivalenceClass, List<AnySymbol> symbols) { |
79 | int size = symbols.size(); | 103 | int size = symbols.size(); |
80 | VersionedMapStoreFactory<Tuple,T> mapFactory = VersionedMapStore | 104 | VersionedMapStoreFactory<Tuple, T> mapFactory = VersionedMapStore |
81 | .<Tuple,T>builder() | 105 | .<Tuple, T>builder() |
82 | .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) | 106 | .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) |
83 | .defaultValue(equivalenceClass.defaultValue()) | 107 | .defaultValue(equivalenceClass.defaultValue()) |
84 | .build(); | 108 | .build(); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java index a320a618..fd4cc160 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java | |||
@@ -14,16 +14,20 @@ import tools.refinery.store.model.ModelDiffCursor; | |||
14 | import tools.refinery.store.model.ModelStore; | 14 | import tools.refinery.store.model.ModelStore; |
15 | import tools.refinery.store.representation.AnySymbol; | 15 | import tools.refinery.store.representation.AnySymbol; |
16 | import tools.refinery.store.tuple.Tuple; | 16 | import tools.refinery.store.tuple.Tuple; |
17 | import tools.refinery.store.util.CancellationToken; | ||
17 | 18 | ||
18 | import java.util.*; | 19 | import java.util.*; |
19 | 20 | ||
20 | public class ModelStoreImpl implements ModelStore { | 21 | public class ModelStoreImpl implements ModelStore { |
21 | private final LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores; | 22 | private final LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores; |
22 | private final List<ModelStoreAdapter> adapters; | 23 | private final List<ModelStoreAdapter> adapters; |
24 | private final CancellationToken cancellationToken; | ||
23 | 25 | ||
24 | ModelStoreImpl(LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores, int adapterCount) { | 26 | ModelStoreImpl(LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores, int adapterCount, |
27 | CancellationToken cancellationToken) { | ||
25 | this.stores = stores; | 28 | this.stores = stores; |
26 | adapters = new ArrayList<>(adapterCount); | 29 | adapters = new ArrayList<>(adapterCount); |
30 | this.cancellationToken = cancellationToken; | ||
27 | } | 31 | } |
28 | 32 | ||
29 | @Override | 33 | @Override |
@@ -100,4 +104,13 @@ public class ModelStoreImpl implements ModelStore { | |||
100 | void addAdapter(ModelStoreAdapter adapter) { | 104 | void addAdapter(ModelStoreAdapter adapter) { |
101 | adapters.add(adapter); | 105 | adapters.add(adapter); |
102 | } | 106 | } |
107 | |||
108 | @Override | ||
109 | public void checkCancelled() { | ||
110 | cancellationToken.checkCancelled(); | ||
111 | } | ||
112 | |||
113 | CancellationToken getCancellationToken() { | ||
114 | return cancellationToken; | ||
115 | } | ||
103 | } | 116 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java index f81386f1..c3e52084 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java | |||
@@ -11,11 +11,9 @@ import java.util.Arrays; | |||
11 | 11 | ||
12 | public class ModelVersion implements Version { | 12 | public class ModelVersion implements Version { |
13 | final Version[] mapVersions; | 13 | final Version[] mapVersions; |
14 | final int hash; | ||
15 | 14 | ||
16 | public ModelVersion(Version[] mapVersions) { | 15 | public ModelVersion(Version[] mapVersions) { |
17 | this.mapVersions = mapVersions; | 16 | this.mapVersions = mapVersions; |
18 | this.hash = Arrays.hashCode(mapVersions); | ||
19 | } | 17 | } |
20 | 18 | ||
21 | public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { | 19 | public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { |
@@ -23,16 +21,6 @@ public class ModelVersion implements Version { | |||
23 | } | 21 | } |
24 | 22 | ||
25 | @Override | 23 | @Override |
26 | public int hashCode() { | ||
27 | return hash; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public boolean equals(Object obj) { | ||
32 | return super.equals(obj); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public String toString() { | 24 | public String toString() { |
37 | return "ModelVersion{" + | 25 | return "ModelVersion{" + |
38 | "mapVersions=" + Arrays.toString(mapVersions) + | 26 | "mapVersions=" + Arrays.toString(mapVersions) + |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java new file mode 100644 index 00000000..4a8e6752 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.model.internal; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.VersionedMap; | ||
10 | import tools.refinery.store.representation.Symbol; | ||
11 | import tools.refinery.store.tuple.Tuple; | ||
12 | |||
13 | class NullaryVersionedInterpretation<T> extends VersionedInterpretation<T> { | ||
14 | public NullaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { | ||
15 | super(model, symbol, map); | ||
16 | } | ||
17 | |||
18 | @Override | ||
19 | public Cursor<Tuple, T> getAdjacent(int slot, int node) { | ||
20 | throw new IllegalArgumentException("Invalid index: " + slot); | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public int getAdjacentSize(int slot, int node) { | ||
25 | throw new IllegalArgumentException("Invalid index: " + slot); | ||
26 | } | ||
27 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java new file mode 100644 index 00000000..75946680 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.model.internal; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.Cursors; | ||
10 | import tools.refinery.store.map.VersionedMap; | ||
11 | import tools.refinery.store.representation.Symbol; | ||
12 | import tools.refinery.store.tuple.Tuple; | ||
13 | |||
14 | import java.util.Objects; | ||
15 | |||
16 | class UnaryVersionedInterpretation<T> extends VersionedInterpretation<T> { | ||
17 | public UnaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { | ||
18 | super(model, symbol, map); | ||
19 | } | ||
20 | |||
21 | private void validateSlot(int slot) { | ||
22 | if (slot != 0) { | ||
23 | throw new IllegalArgumentException("Invalid index: " + slot); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public Cursor<Tuple, T> getAdjacent(int slot, int node) { | ||
29 | validateSlot(slot); | ||
30 | var key = Tuple.of(node); | ||
31 | var value = get(key); | ||
32 | if (Objects.equals(value, getSymbol().defaultValue())) { | ||
33 | return Cursors.empty(); | ||
34 | } | ||
35 | return Cursors.singleton(key, value); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int getAdjacentSize(int slot, int node) { | ||
40 | validateSlot(slot); | ||
41 | var key = Tuple.of(node); | ||
42 | var value = get(key); | ||
43 | if (Objects.equals(value, getSymbol().defaultValue())) { | ||
44 | return 0; | ||
45 | } | ||
46 | return 1; | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java index 76e3baea..dcf0ad08 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java | |||
@@ -16,14 +16,14 @@ import tools.refinery.store.tuple.Tuple; | |||
16 | import java.util.ArrayList; | 16 | import java.util.ArrayList; |
17 | import java.util.List; | 17 | import java.util.List; |
18 | 18 | ||
19 | public class VersionedInterpretation<T> implements Interpretation<T> { | 19 | public abstract class VersionedInterpretation<T> implements Interpretation<T> { |
20 | private final ModelImpl model; | 20 | private final ModelImpl model; |
21 | private final Symbol<T> symbol; | 21 | private final Symbol<T> symbol; |
22 | private final VersionedMap<Tuple, T> map; | 22 | private final VersionedMap<Tuple, T> map; |
23 | private final List<InterpretationListener<T>> listeners = new ArrayList<>(); | 23 | private final List<InterpretationListener<T>> listeners = new ArrayList<>(); |
24 | private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>(); | 24 | private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>(); |
25 | 25 | ||
26 | private VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { | 26 | protected VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { |
27 | this.model = model; | 27 | this.model = model; |
28 | this.symbol = symbol; | 28 | this.symbol = symbol; |
29 | this.map = map; | 29 | this.map = map; |
@@ -50,6 +50,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
50 | .formatted(symbol, symbol.arity())); | 50 | .formatted(symbol, symbol.arity())); |
51 | } | 51 | } |
52 | } | 52 | } |
53 | |||
53 | @Override | 54 | @Override |
54 | public T get(Tuple key) { | 55 | public T get(Tuple key) { |
55 | checkKey(key); | 56 | checkKey(key); |
@@ -61,7 +62,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
61 | return map.getAll(); | 62 | return map.getAll(); |
62 | } | 63 | } |
63 | 64 | ||
64 | private void notifyListeners(Tuple key, T fromValue, T toValue, boolean restoring) { | 65 | protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) { |
65 | var listenerList = restoring ? restoreListeners : listeners; | 66 | var listenerList = restoring ? restoreListeners : listeners; |
66 | int listenerCount = listenerList.size(); | 67 | int listenerCount = listenerList.size(); |
67 | // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead. | 68 | // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead. |
@@ -74,23 +75,21 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
74 | @Override | 75 | @Override |
75 | public T put(Tuple key, T value) { | 76 | public T put(Tuple key, T value) { |
76 | checkKey(key); | 77 | checkKey(key); |
78 | model.checkCancelled(); | ||
77 | model.markAsChanged(); | 79 | model.markAsChanged(); |
78 | var oldValue = map.put(key, value); | 80 | var oldValue = map.put(key, value); |
79 | notifyListeners(key, oldValue, value, false); | 81 | valueChanged(key, oldValue, value, false); |
80 | return oldValue; | 82 | return oldValue; |
81 | } | 83 | } |
82 | 84 | ||
83 | @Override | 85 | @Override |
84 | public void putAll(Cursor<Tuple, T> cursor) { | 86 | public void putAll(Cursor<Tuple, T> cursor) { |
85 | if (listeners.isEmpty()) { | ||
86 | map.putAll(cursor); | ||
87 | return; | ||
88 | } | ||
89 | model.markAsChanged(); | 87 | model.markAsChanged(); |
90 | if (cursor.getDependingMaps().contains(map)) { | 88 | if (cursor.getDependingMaps().contains(map)) { |
91 | List<Tuple> keys = new ArrayList<>(); | 89 | List<Tuple> keys = new ArrayList<>(); |
92 | List<T> values = new ArrayList<>(); | 90 | List<T> values = new ArrayList<>(); |
93 | while (cursor.move()) { | 91 | while (cursor.move()) { |
92 | model.checkCancelled(); | ||
94 | keys.add(cursor.getKey()); | 93 | keys.add(cursor.getKey()); |
95 | values.add(cursor.getValue()); | 94 | values.add(cursor.getValue()); |
96 | } | 95 | } |
@@ -114,11 +113,16 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
114 | Version commit() { | 113 | Version commit() { |
115 | return map.commit(); | 114 | return map.commit(); |
116 | } | 115 | } |
117 | void restore(Version state) { | 116 | |
118 | if (!restoreListeners.isEmpty()) { | 117 | protected boolean shouldNotifyRestoreListeners() { |
118 | return !restoreListeners.isEmpty(); | ||
119 | } | ||
120 | |||
121 | public void restore(Version state) { | ||
122 | if (shouldNotifyRestoreListeners()) { | ||
119 | var diffCursor = getDiffCursor(state); | 123 | var diffCursor = getDiffCursor(state); |
120 | while (diffCursor.move()) { | 124 | while (diffCursor.move()) { |
121 | notifyListeners(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true); | 125 | valueChanged(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true); |
122 | } | 126 | } |
123 | } | 127 | } |
124 | map.restore(state); | 128 | map.restore(state); |
@@ -142,7 +146,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
142 | @SuppressWarnings("unchecked") | 146 | @SuppressWarnings("unchecked") |
143 | var typedSymbol = (Symbol<T>) symbol; | 147 | var typedSymbol = (Symbol<T>) symbol; |
144 | var map = store.createMap(); | 148 | var map = store.createMap(); |
145 | return new VersionedInterpretation<>(model, typedSymbol, map); | 149 | return of(model, typedSymbol, map); |
146 | } | 150 | } |
147 | 151 | ||
148 | static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store, | 152 | static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store, |
@@ -150,6 +154,15 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
150 | @SuppressWarnings("unchecked") | 154 | @SuppressWarnings("unchecked") |
151 | var typedSymbol = (Symbol<T>) symbol; | 155 | var typedSymbol = (Symbol<T>) symbol; |
152 | var map = store.createMap(state); | 156 | var map = store.createMap(state); |
153 | return new VersionedInterpretation<>(model, typedSymbol, map); | 157 | return of(model, typedSymbol, map); |
158 | } | ||
159 | |||
160 | private static <T> VersionedInterpretation<T> of(ModelImpl model, Symbol<T> typedSymbol, | ||
161 | VersionedMap<Tuple, T> map) { | ||
162 | return switch (typedSymbol.arity()) { | ||
163 | case 0 -> new NullaryVersionedInterpretation<>(model, typedSymbol, map); | ||
164 | case 1 -> new UnaryVersionedInterpretation<>(model, typedSymbol, map); | ||
165 | default -> new IndexedVersionedInterpretation<>(model, typedSymbol, map); | ||
166 | }; | ||
154 | } | 167 | } |
155 | } | 168 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java index 52c740e8..dfdb43bd 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java | |||
@@ -5,6 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.representation; | 6 | package tools.refinery.store.representation; |
7 | 7 | ||
8 | import java.util.Objects; | ||
8 | import java.util.Optional; | 9 | import java.util.Optional; |
9 | 10 | ||
10 | public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain { | 11 | public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain { |
@@ -22,7 +23,9 @@ public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain { | |||
22 | return toConcrete(abstractValue).isPresent(); | 23 | return toConcrete(abstractValue).isPresent(); |
23 | } | 24 | } |
24 | 25 | ||
25 | boolean isRefinement(A originalValue, A refinedValue); | 26 | default boolean isRefinement(A originalValue, A refinedValue) { |
27 | return Objects.equals(commonRefinement(originalValue, refinedValue), refinedValue); | ||
28 | } | ||
26 | 29 | ||
27 | A commonRefinement(A leftValue, A rightValue); | 30 | A commonRefinement(A leftValue, A rightValue); |
28 | 31 | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java index 40baf9a5..f81ee9a4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java | |||
@@ -36,6 +36,10 @@ public enum TruthValue { | |||
36 | return this != UNKNOWN; | 36 | return this != UNKNOWN; |
37 | } | 37 | } |
38 | 38 | ||
39 | public boolean isConcrete() { | ||
40 | return this == TRUE || this == FALSE; | ||
41 | } | ||
42 | |||
39 | public boolean must() { | 43 | public boolean must() { |
40 | return this == TRUE || this == ERROR; | 44 | return this == TRUE || this == ERROR; |
41 | } | 45 | } |
@@ -55,9 +59,18 @@ public enum TruthValue { | |||
55 | public TruthValue merge(TruthValue other) { | 59 | public TruthValue merge(TruthValue other) { |
56 | return switch (this) { | 60 | return switch (this) { |
57 | case TRUE -> other == UNKNOWN || other == TRUE ? TRUE : ERROR; | 61 | case TRUE -> other == UNKNOWN || other == TRUE ? TRUE : ERROR; |
58 | case FALSE -> other == TruthValue.UNKNOWN || other == TruthValue.FALSE ? FALSE : ERROR; | 62 | case FALSE -> other == UNKNOWN || other == FALSE ? FALSE : ERROR; |
59 | case UNKNOWN -> other; | 63 | case UNKNOWN -> other; |
60 | default -> ERROR; | 64 | case ERROR -> ERROR; |
65 | }; | ||
66 | } | ||
67 | |||
68 | public TruthValue join(TruthValue other) { | ||
69 | return switch (this) { | ||
70 | case TRUE -> other == ERROR || other == TRUE ? TRUE : UNKNOWN; | ||
71 | case FALSE -> other == ERROR || other == FALSE ? FALSE : UNKNOWN; | ||
72 | case UNKNOWN -> UNKNOWN; | ||
73 | case ERROR -> other; | ||
61 | }; | 74 | }; |
62 | } | 75 | } |
63 | } | 76 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java index 89f8dd19..61696dca 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java | |||
@@ -7,6 +7,8 @@ package tools.refinery.store.representation; | |||
7 | 7 | ||
8 | import java.util.Optional; | 8 | import java.util.Optional; |
9 | 9 | ||
10 | // Singleton pattern, because there is only one domain for truth values. | ||
11 | @SuppressWarnings("squid:S6548") | ||
10 | public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolean> { | 12 | public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolean> { |
11 | public static final TruthValueDomain INSTANCE = new TruthValueDomain(); | 13 | public static final TruthValueDomain INSTANCE = new TruthValueDomain(); |
12 | 14 | ||
@@ -15,51 +17,50 @@ public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolea | |||
15 | 17 | ||
16 | @Override | 18 | @Override |
17 | public Class<TruthValue> abstractType() { | 19 | public Class<TruthValue> abstractType() { |
18 | return null; | 20 | return TruthValue.class; |
19 | } | 21 | } |
20 | 22 | ||
21 | @Override | 23 | @Override |
22 | public Class<Boolean> concreteType() { | 24 | public Class<Boolean> concreteType() { |
23 | return null; | 25 | return Boolean.class; |
24 | } | 26 | } |
25 | 27 | ||
26 | @Override | 28 | @Override |
27 | public TruthValue toAbstract(Boolean concreteValue) { | 29 | public TruthValue toAbstract(Boolean concreteValue) { |
28 | return null; | 30 | return TruthValue.toTruthValue(concreteValue); |
29 | } | 31 | } |
30 | 32 | ||
31 | @Override | 33 | @Override |
32 | public Optional<Boolean> toConcrete(TruthValue abstractValue) { | 34 | public Optional<Boolean> toConcrete(TruthValue abstractValue) { |
33 | return Optional.empty(); | 35 | return switch (abstractValue) { |
36 | case TRUE -> Optional.of(true); | ||
37 | case FALSE -> Optional.of(false); | ||
38 | default -> Optional.empty(); | ||
39 | }; | ||
34 | } | 40 | } |
35 | 41 | ||
36 | @Override | 42 | @Override |
37 | public boolean isConcrete(TruthValue abstractValue) { | 43 | public boolean isConcrete(TruthValue abstractValue) { |
38 | return AbstractDomain.super.isConcrete(abstractValue); | 44 | return abstractValue.isConcrete(); |
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isRefinement(TruthValue originalValue, TruthValue refinedValue) { | ||
43 | return false; | ||
44 | } | 45 | } |
45 | 46 | ||
46 | @Override | 47 | @Override |
47 | public TruthValue commonRefinement(TruthValue leftValue, TruthValue rightValue) { | 48 | public TruthValue commonRefinement(TruthValue leftValue, TruthValue rightValue) { |
48 | return null; | 49 | return leftValue.merge(rightValue); |
49 | } | 50 | } |
50 | 51 | ||
51 | @Override | 52 | @Override |
52 | public TruthValue commonAncestor(TruthValue leftValue, TruthValue rightValue) { | 53 | public TruthValue commonAncestor(TruthValue leftValue, TruthValue rightValue) { |
53 | return null; | 54 | return leftValue.join(rightValue); |
54 | } | 55 | } |
55 | 56 | ||
56 | @Override | 57 | @Override |
57 | public TruthValue unknown() { | 58 | public TruthValue unknown() { |
58 | return null; | 59 | return TruthValue.UNKNOWN; |
59 | } | 60 | } |
60 | 61 | ||
61 | @Override | 62 | @Override |
62 | public boolean isError(TruthValue abstractValue) { | 63 | public boolean isError(TruthValue abstractValue) { |
63 | return false; | 64 | return !abstractValue.isConsistent(); |
64 | } | 65 | } |
65 | } | 66 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java new file mode 100644 index 00000000..7ae2d935 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.representation.cardinality; | ||
7 | |||
8 | import tools.refinery.store.representation.AbstractDomain; | ||
9 | |||
10 | import java.util.Optional; | ||
11 | |||
12 | // Singleton pattern, because there is only one domain for truth values. | ||
13 | @SuppressWarnings("squid:S6548") | ||
14 | public class CardinalityDomain implements AbstractDomain<CardinalityInterval, Integer> { | ||
15 | public static final CardinalityDomain INSTANCE = new CardinalityDomain(); | ||
16 | |||
17 | private CardinalityDomain() { | ||
18 | } | ||
19 | |||
20 | @Override | ||
21 | public Class<CardinalityInterval> abstractType() { | ||
22 | return CardinalityInterval.class; | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public Class<Integer> concreteType() { | ||
27 | return Integer.class; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public CardinalityInterval toAbstract(Integer concreteValue) { | ||
32 | return CardinalityIntervals.exactly(concreteValue); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Optional<Integer> toConcrete(CardinalityInterval abstractValue) { | ||
37 | return isConcrete(abstractValue) ? Optional.of(abstractValue.lowerBound()) : Optional.empty(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public boolean isConcrete(CardinalityInterval abstractValue) { | ||
42 | if (!(abstractValue instanceof NonEmptyCardinalityInterval nonEmptyValue) || | ||
43 | !((nonEmptyValue.upperBound()) instanceof FiniteUpperCardinality finiteUpperCardinality)) { | ||
44 | return false; | ||
45 | } | ||
46 | return nonEmptyValue.lowerBound() == finiteUpperCardinality.finiteUpperBound(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public CardinalityInterval commonRefinement(CardinalityInterval leftValue, CardinalityInterval rightValue) { | ||
51 | return leftValue.meet(rightValue); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public CardinalityInterval commonAncestor(CardinalityInterval leftValue, CardinalityInterval rightValue) { | ||
56 | return leftValue.join(rightValue); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public CardinalityInterval unknown() { | ||
61 | return CardinalityIntervals.SET; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public boolean isError(CardinalityInterval abstractValue) { | ||
66 | return abstractValue.isEmpty(); | ||
67 | } | ||
68 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java index 704ca2fc..b20c685a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java | |||
@@ -18,6 +18,8 @@ public sealed interface CardinalityInterval permits NonEmptyCardinalityInterval, | |||
18 | 18 | ||
19 | CardinalityInterval add(CardinalityInterval other); | 19 | CardinalityInterval add(CardinalityInterval other); |
20 | 20 | ||
21 | CardinalityInterval take(int count); | ||
22 | |||
21 | CardinalityInterval multiply(CardinalityInterval other); | 23 | CardinalityInterval multiply(CardinalityInterval other); |
22 | 24 | ||
23 | CardinalityInterval meet(CardinalityInterval other); | 25 | CardinalityInterval meet(CardinalityInterval other); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java index ad16a3e8..855fd248 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java | |||
@@ -30,7 +30,7 @@ public final class CardinalityIntervals { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | public static CardinalityInterval between(int lowerBound, int upperBound) { | 32 | public static CardinalityInterval between(int lowerBound, int upperBound) { |
33 | return between(lowerBound, UpperCardinalities.valueOf(upperBound)); | 33 | return between(lowerBound, UpperCardinalities.atMost(upperBound)); |
34 | } | 34 | } |
35 | 35 | ||
36 | public static CardinalityInterval atMost(UpperCardinality upperBound) { | 36 | public static CardinalityInterval atMost(UpperCardinality upperBound) { |
@@ -38,7 +38,7 @@ public final class CardinalityIntervals { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | public static CardinalityInterval atMost(int upperBound) { | 40 | public static CardinalityInterval atMost(int upperBound) { |
41 | return atMost(UpperCardinalities.valueOf(upperBound)); | 41 | return atMost(UpperCardinalities.atMost(upperBound)); |
42 | } | 42 | } |
43 | 43 | ||
44 | public static CardinalityInterval atLeast(int lowerBound) { | 44 | public static CardinalityInterval atLeast(int lowerBound) { |
@@ -46,6 +46,6 @@ public final class CardinalityIntervals { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | public static CardinalityInterval exactly(int lowerBound) { | 48 | public static CardinalityInterval exactly(int lowerBound) { |
49 | return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.valueOf(lowerBound)); | 49 | return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.atMost(lowerBound)); |
50 | } | 50 | } |
51 | } | 51 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java index 49911c29..9e371e21 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java | |||
@@ -5,6 +5,8 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.representation.cardinality; | 6 | package tools.refinery.store.representation.cardinality; |
7 | 7 | ||
8 | // Singleton implementation, because there is only a single empty interval. | ||
9 | @SuppressWarnings("squid:S6548") | ||
8 | public final class EmptyCardinalityInterval implements CardinalityInterval { | 10 | public final class EmptyCardinalityInterval implements CardinalityInterval { |
9 | static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval(); | 11 | static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval(); |
10 | 12 | ||
@@ -43,6 +45,11 @@ public final class EmptyCardinalityInterval implements CardinalityInterval { | |||
43 | } | 45 | } |
44 | 46 | ||
45 | @Override | 47 | @Override |
48 | public CardinalityInterval take(int count) { | ||
49 | return this; | ||
50 | } | ||
51 | |||
52 | @Override | ||
46 | public CardinalityInterval multiply(CardinalityInterval other) { | 53 | public CardinalityInterval multiply(CardinalityInterval other) { |
47 | return this; | 54 | return this; |
48 | } | 55 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java index 82afdbbc..b63a8637 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java | |||
@@ -6,6 +6,7 @@ | |||
6 | package tools.refinery.store.representation.cardinality; | 6 | package tools.refinery.store.representation.cardinality; |
7 | 7 | ||
8 | import org.jetbrains.annotations.NotNull; | 8 | import org.jetbrains.annotations.NotNull; |
9 | import org.jetbrains.annotations.Nullable; | ||
9 | 10 | ||
10 | import java.util.function.IntBinaryOperator; | 11 | import java.util.function.IntBinaryOperator; |
11 | 12 | ||
@@ -22,6 +23,15 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi | |||
22 | } | 23 | } |
23 | 24 | ||
24 | @Override | 25 | @Override |
26 | @Nullable | ||
27 | public UpperCardinality take(int count) { | ||
28 | if (finiteUpperBound < count) { | ||
29 | return null; | ||
30 | } | ||
31 | return new FiniteUpperCardinality(finiteUpperBound - count); | ||
32 | } | ||
33 | |||
34 | @Override | ||
25 | public UpperCardinality multiply(UpperCardinality other) { | 35 | public UpperCardinality multiply(UpperCardinality other) { |
26 | return lift(other, (a, b) -> a * b); | 36 | return lift(other, (a, b) -> a * b); |
27 | } | 37 | } |
@@ -49,7 +59,7 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi | |||
49 | 59 | ||
50 | private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) { | 60 | private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) { |
51 | if (other instanceof FiniteUpperCardinality finiteUpperCardinality) { | 61 | if (other instanceof FiniteUpperCardinality finiteUpperCardinality) { |
52 | return UpperCardinalities.valueOf(operator.applyAsInt(finiteUpperBound, | 62 | return UpperCardinalities.atMost(operator.applyAsInt(finiteUpperBound, |
53 | finiteUpperCardinality.finiteUpperBound)); | 63 | finiteUpperCardinality.finiteUpperBound)); |
54 | } | 64 | } |
55 | if (other instanceof UnboundedUpperCardinality) { | 65 | if (other instanceof UnboundedUpperCardinality) { |
@@ -57,4 +67,17 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi | |||
57 | } | 67 | } |
58 | throw new IllegalArgumentException("Unknown UpperCardinality: " + other); | 68 | throw new IllegalArgumentException("Unknown UpperCardinality: " + other); |
59 | } | 69 | } |
70 | |||
71 | @Override | ||
72 | public boolean equals(Object o) { | ||
73 | if (this == o) return true; | ||
74 | if (o == null || getClass() != o.getClass()) return false; | ||
75 | FiniteUpperCardinality that = (FiniteUpperCardinality) o; | ||
76 | return finiteUpperBound == that.finiteUpperBound; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public int hashCode() { | ||
81 | return finiteUpperBound; | ||
82 | } | ||
60 | } | 83 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java index 38bd53bf..6bd66df7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java | |||
@@ -5,6 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.representation.cardinality; | 6 | package tools.refinery.store.representation.cardinality; |
7 | 7 | ||
8 | import java.util.Objects; | ||
8 | import java.util.function.BinaryOperator; | 9 | import java.util.function.BinaryOperator; |
9 | import java.util.function.IntBinaryOperator; | 10 | import java.util.function.IntBinaryOperator; |
10 | 11 | ||
@@ -53,6 +54,16 @@ public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upper | |||
53 | return lift(other, Math::min, UpperCardinality::max, this); | 54 | return lift(other, Math::min, UpperCardinality::max, this); |
54 | } | 55 | } |
55 | 56 | ||
57 | @Override | ||
58 | public CardinalityInterval take(int count) { | ||
59 | int newLowerBound = Math.max(lowerBound - count, 0); | ||
60 | var newUpperBound = upperBound.take(count); | ||
61 | if (newUpperBound == null) { | ||
62 | return CardinalityIntervals.ERROR; | ||
63 | } | ||
64 | return CardinalityIntervals.between(newLowerBound, newUpperBound); | ||
65 | } | ||
66 | |||
56 | private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, | 67 | private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, |
57 | BinaryOperator<UpperCardinality> upperOperator, | 68 | BinaryOperator<UpperCardinality> upperOperator, |
58 | CardinalityInterval whenEmpty) { | 69 | CardinalityInterval whenEmpty) { |
@@ -73,7 +84,23 @@ public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upper | |||
73 | 84 | ||
74 | @Override | 85 | @Override |
75 | public String toString() { | 86 | public String toString() { |
76 | var closeBracket = upperBound instanceof UnboundedUpperCardinality ? ")" : "]"; | 87 | if (upperBound instanceof FiniteUpperCardinality finiteUpperCardinality && |
77 | return "[%d..%s%s".formatted(lowerBound, upperBound, closeBracket); | 88 | finiteUpperCardinality.finiteUpperBound() == lowerBound) { |
89 | return "[%d]".formatted(lowerBound); | ||
90 | } | ||
91 | return "[%d..%s]".formatted(lowerBound, upperBound); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public boolean equals(Object o) { | ||
96 | if (this == o) return true; | ||
97 | if (o == null || getClass() != o.getClass()) return false; | ||
98 | NonEmptyCardinalityInterval that = (NonEmptyCardinalityInterval) o; | ||
99 | return lowerBound == that.lowerBound && Objects.equals(upperBound, that.upperBound); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public int hashCode() { | ||
104 | return lowerBound * 31 + upperBound.hashCode(); | ||
78 | } | 105 | } |
79 | } | 106 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java index a5634020..03c701ae 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java | |||
@@ -7,6 +7,8 @@ package tools.refinery.store.representation.cardinality; | |||
7 | 7 | ||
8 | import org.jetbrains.annotations.NotNull; | 8 | import org.jetbrains.annotations.NotNull; |
9 | 9 | ||
10 | // Singleton implementation, because there is only a single countable infinity. | ||
11 | @SuppressWarnings("squid:S6548") | ||
10 | public final class UnboundedUpperCardinality implements UpperCardinality { | 12 | public final class UnboundedUpperCardinality implements UpperCardinality { |
11 | static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality(); | 13 | static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality(); |
12 | 14 | ||
@@ -20,10 +22,17 @@ public final class UnboundedUpperCardinality implements UpperCardinality { | |||
20 | } | 22 | } |
21 | 23 | ||
22 | @Override | 24 | @Override |
25 | public UpperCardinality take(int count) { | ||
26 | return this; | ||
27 | } | ||
28 | |||
29 | @Override | ||
23 | public UpperCardinality multiply(UpperCardinality other) { | 30 | public UpperCardinality multiply(UpperCardinality other) { |
24 | return this; | 31 | return this; |
25 | } | 32 | } |
26 | 33 | ||
34 | // This should always be greater than any finite cardinality. | ||
35 | @SuppressWarnings("ComparatorMethodParameterNotUsed") | ||
27 | @Override | 36 | @Override |
28 | public int compareTo(@NotNull UpperCardinality upperCardinality) { | 37 | public int compareTo(@NotNull UpperCardinality upperCardinality) { |
29 | if (upperCardinality instanceof FiniteUpperCardinality) { | 38 | if (upperCardinality instanceof FiniteUpperCardinality) { |
@@ -44,4 +53,14 @@ public final class UnboundedUpperCardinality implements UpperCardinality { | |||
44 | public String toString() { | 53 | public String toString() { |
45 | return "*"; | 54 | return "*"; |
46 | } | 55 | } |
56 | |||
57 | @Override | ||
58 | public boolean equals(Object obj) { | ||
59 | return this == obj || (obj != null && getClass() == obj.getClass()); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public int hashCode() { | ||
64 | return -1; | ||
65 | } | ||
47 | } | 66 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java index 1e18dde0..17d1b292 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java | |||
@@ -26,7 +26,7 @@ public final class UpperCardinalities { | |||
26 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | 26 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); |
27 | } | 27 | } |
28 | 28 | ||
29 | public static UpperCardinality valueOf(int upperBound) { | 29 | public static UpperCardinality atMost(int upperBound) { |
30 | if (upperBound < 0) { | 30 | if (upperBound < 0) { |
31 | return UNBOUNDED; | 31 | return UNBOUNDED; |
32 | } | 32 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java index 5dbaa922..3f0db028 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java | |||
@@ -5,6 +5,8 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.representation.cardinality; | 6 | package tools.refinery.store.representation.cardinality; |
7 | 7 | ||
8 | import org.jetbrains.annotations.Nullable; | ||
9 | |||
8 | public sealed interface UpperCardinality extends Comparable<UpperCardinality> permits FiniteUpperCardinality, | 10 | public sealed interface UpperCardinality extends Comparable<UpperCardinality> permits FiniteUpperCardinality, |
9 | UnboundedUpperCardinality { | 11 | UnboundedUpperCardinality { |
10 | default UpperCardinality min(UpperCardinality other) { | 12 | default UpperCardinality min(UpperCardinality other) { |
@@ -17,11 +19,14 @@ public sealed interface UpperCardinality extends Comparable<UpperCardinality> pe | |||
17 | 19 | ||
18 | UpperCardinality add(UpperCardinality other); | 20 | UpperCardinality add(UpperCardinality other); |
19 | 21 | ||
22 | @Nullable | ||
23 | UpperCardinality take(int count); | ||
24 | |||
20 | UpperCardinality multiply(UpperCardinality other); | 25 | UpperCardinality multiply(UpperCardinality other); |
21 | 26 | ||
22 | int compareToInt(int value); | 27 | int compareToInt(int value); |
23 | 28 | ||
24 | static UpperCardinality of(int upperBound) { | 29 | static UpperCardinality of(int upperBound) { |
25 | return UpperCardinalities.valueOf(upperBound); | 30 | return UpperCardinalities.atMost(upperBound); |
26 | } | 31 | } |
27 | } | 32 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java index 04e17a13..809205e4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java | |||
@@ -7,9 +7,10 @@ package tools.refinery.store.statecoding; | |||
7 | 7 | ||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | 8 | import org.eclipse.collections.api.set.primitive.IntSet; |
9 | import tools.refinery.store.model.Interpretation; | 9 | import tools.refinery.store.model.Interpretation; |
10 | import tools.refinery.store.model.Model; | ||
10 | 11 | ||
11 | import java.util.List; | 12 | import java.util.List; |
12 | 13 | ||
13 | public interface StateCodeCalculatorFactory { | 14 | public interface StateCodeCalculatorFactory { |
14 | StateCodeCalculator create(List<? extends Interpretation<?>> interpretations, IntSet individuals); | 15 | StateCodeCalculator create(Model model, List<? extends Interpretation<?>> interpretations, IntSet individuals); |
15 | } | 16 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java index 8cfd24d5..cb73d27e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java | |||
@@ -17,7 +17,7 @@ public interface StateCoderAdapter extends ModelAdapter { | |||
17 | return calculateStateCode().objectCode(); | 17 | return calculateStateCode().objectCode(); |
18 | } | 18 | } |
19 | 19 | ||
20 | static StateCoderBuilderImpl builder() { | 20 | static StateCoderBuilder builder() { |
21 | return new StateCoderBuilderImpl(); | 21 | return new StateCoderBuilderImpl(); |
22 | } | 22 | } |
23 | } | 23 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java index 3fd8c8d8..0f0023ed 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java | |||
@@ -6,7 +6,7 @@ | |||
6 | package tools.refinery.store.statecoding; | 6 | package tools.refinery.store.statecoding; |
7 | 7 | ||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | 8 | import org.eclipse.collections.api.set.primitive.IntSet; |
9 | import tools.refinery.store.model.Interpretation; | 9 | import tools.refinery.store.model.AnyInterpretation; |
10 | 10 | ||
11 | import java.util.List; | 11 | import java.util.List; |
12 | 12 | ||
@@ -16,9 +16,6 @@ public interface StateEquivalenceChecker { | |||
16 | } | 16 | } |
17 | 17 | ||
18 | EquivalenceResult constructMorphism( | 18 | EquivalenceResult constructMorphism( |
19 | IntSet individuals, | 19 | IntSet individuals, List<? extends AnyInterpretation> interpretations1, ObjectCode code1, |
20 | List<? extends Interpretation<?>> interpretations1, | 20 | List<? extends AnyInterpretation> interpretations2, ObjectCode code2); |
21 | ObjectCode code1, List<? | ||
22 | extends Interpretation<?>> interpretations2, | ||
23 | ObjectCode code2); | ||
24 | } | 21 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java index 05b47c52..eed591e7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java | |||
@@ -5,61 +5,61 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.internal; | 6 | package tools.refinery.store.statecoding.internal; |
7 | 7 | ||
8 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | 8 | import org.eclipse.collections.api.factory.primitive.IntSets; |
9 | import org.eclipse.collections.api.set.primitive.MutableIntSet; | ||
10 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; | ||
9 | import tools.refinery.store.model.ModelStore; | 11 | import tools.refinery.store.model.ModelStore; |
10 | import tools.refinery.store.model.ModelStoreBuilder; | ||
11 | import tools.refinery.store.representation.AnySymbol; | 12 | import tools.refinery.store.representation.AnySymbol; |
12 | import tools.refinery.store.representation.Symbol; | 13 | import tools.refinery.store.representation.Symbol; |
13 | import tools.refinery.store.statecoding.*; | 14 | import tools.refinery.store.statecoding.StateCodeCalculatorFactory; |
15 | import tools.refinery.store.statecoding.StateCoderBuilder; | ||
16 | import tools.refinery.store.statecoding.StateCoderStoreAdapter; | ||
17 | import tools.refinery.store.statecoding.StateEquivalenceChecker; | ||
14 | import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; | 18 | import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; |
15 | import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; | 19 | import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; |
16 | import tools.refinery.store.tuple.Tuple1; | 20 | import tools.refinery.store.tuple.Tuple1; |
17 | 21 | ||
18 | import java.util.*; | 22 | import java.util.HashSet; |
23 | import java.util.LinkedHashSet; | ||
24 | import java.util.Set; | ||
19 | 25 | ||
20 | public class StateCoderBuilderImpl implements StateCoderBuilder { | 26 | public class StateCoderBuilderImpl extends AbstractModelAdapterBuilder<StateCoderStoreAdapter> |
21 | Set<AnySymbol> excluded = new HashSet<>(); | 27 | implements StateCoderBuilder { |
22 | IntHashSet individuals = new IntHashSet(); | 28 | private final Set<AnySymbol> excluded = new HashSet<>(); |
23 | 29 | private final MutableIntSet individuals = IntSets.mutable.empty(); | |
24 | StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new; | 30 | private StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new; |
25 | StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); | 31 | private StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); |
26 | 32 | ||
27 | @Override | 33 | @Override |
28 | public StateCoderBuilder exclude(AnySymbol symbol) { | 34 | public StateCoderBuilder exclude(AnySymbol symbol) { |
35 | checkNotConfigured(); | ||
29 | excluded.add(symbol); | 36 | excluded.add(symbol); |
30 | return this; | 37 | return this; |
31 | } | 38 | } |
32 | 39 | ||
33 | @Override | 40 | @Override |
34 | public StateCoderBuilder individual(Tuple1 tuple) { | 41 | public StateCoderBuilder individual(Tuple1 tuple) { |
42 | checkNotConfigured(); | ||
35 | individuals.add(tuple.get(0)); | 43 | individuals.add(tuple.get(0)); |
36 | return this; | 44 | return this; |
37 | } | 45 | } |
38 | 46 | ||
39 | @Override | 47 | @Override |
40 | public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) { | 48 | public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) { |
49 | checkNotConfigured(); | ||
41 | this.checker = stateEquivalenceChecker; | 50 | this.checker = stateEquivalenceChecker; |
42 | return this; | 51 | return this; |
43 | } | 52 | } |
44 | 53 | ||
45 | @Override | 54 | @Override |
46 | public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) { | 55 | public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) { |
56 | checkNotConfigured(); | ||
47 | this.calculator = codeCalculatorFactory; | 57 | this.calculator = codeCalculatorFactory; |
48 | return this; | 58 | return this; |
49 | } | 59 | } |
50 | 60 | ||
51 | @Override | 61 | @Override |
52 | public boolean isConfigured() { | 62 | protected StateCoderStoreAdapter doBuild(ModelStore store) { |
53 | return true; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public void configure(ModelStoreBuilder storeBuilder) { | ||
58 | // It does not modify the build process | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public StateCoderStoreAdapter build(ModelStore store) { | ||
63 | Set<Symbol<?>> symbols = new LinkedHashSet<>(); | 63 | Set<Symbol<?>> symbols = new LinkedHashSet<>(); |
64 | for (AnySymbol symbol : store.getSymbols()) { | 64 | for (AnySymbol symbol : store.getSymbols()) { |
65 | if (!excluded.contains(symbol) && (symbol instanceof Symbol<?> typed)) { | 65 | if (!excluded.contains(symbol) && (symbol instanceof Symbol<?> typed)) { |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java index 89586bfb..2cb6f3c1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java | |||
@@ -68,7 +68,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { | |||
68 | @Override | 68 | @Override |
69 | public StateCoderAdapter createModelAdapter(Model model) { | 69 | public StateCoderAdapter createModelAdapter(Model model) { |
70 | var interpretations = symbols.stream().map(model::getInterpretation).toList(); | 70 | var interpretations = symbols.stream().map(model::getInterpretation).toList(); |
71 | var coder = codeCalculatorFactory.create(interpretations, individuals); | 71 | var coder = codeCalculatorFactory.create(model, interpretations, individuals); |
72 | return new StateCoderAdapterImpl(this, coder, model); | 72 | return new StateCoderAdapterImpl(this, coder, model); |
73 | } | 73 | } |
74 | } | 74 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java index 0a40a19d..4cef6786 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java | |||
@@ -5,9 +5,12 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.neighbourhood; | 6 | package tools.refinery.store.statecoding.neighbourhood; |
7 | 7 | ||
8 | import org.eclipse.collections.api.factory.primitive.IntLongMaps; | ||
9 | import org.eclipse.collections.api.map.primitive.MutableIntLongMap; | ||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | 10 | import org.eclipse.collections.api.set.primitive.IntSet; |
9 | import org.eclipse.collections.impl.map.mutable.primitive.IntLongHashMap; | 11 | import tools.refinery.store.model.AnyInterpretation; |
10 | import tools.refinery.store.model.Interpretation; | 12 | import tools.refinery.store.model.Interpretation; |
13 | import tools.refinery.store.model.Model; | ||
11 | import tools.refinery.store.statecoding.ObjectCode; | 14 | import tools.refinery.store.statecoding.ObjectCode; |
12 | import tools.refinery.store.tuple.Tuple; | 15 | import tools.refinery.store.tuple.Tuple; |
13 | import tools.refinery.store.tuple.Tuple0; | 16 | import tools.refinery.store.tuple.Tuple0; |
@@ -15,25 +18,28 @@ import tools.refinery.store.tuple.Tuple0; | |||
15 | import java.util.*; | 18 | import java.util.*; |
16 | 19 | ||
17 | public abstract class AbstractNeighbourhoodCalculator { | 20 | public abstract class AbstractNeighbourhoodCalculator { |
18 | protected final List<Interpretation<?>> nullImpactValues; | 21 | protected final Model model; |
19 | protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; | 22 | protected final List<AnyInterpretation> nullImpactValues; |
20 | protected final IntLongHashMap individualHashValues; | 23 | protected final LinkedHashMap<AnyInterpretation, long[]> impactValues; |
24 | protected final MutableIntLongMap individualHashValues = IntLongMaps.mutable.empty(); | ||
21 | 25 | ||
22 | protected static final long PRIME = 31; | 26 | protected static final long PRIME = 31; |
23 | 27 | ||
24 | protected AbstractNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { | 28 | protected AbstractNeighbourhoodCalculator(Model model, List<? extends AnyInterpretation> interpretations, |
29 | IntSet individuals) { | ||
30 | this.model = model; | ||
25 | this.nullImpactValues = new ArrayList<>(); | 31 | this.nullImpactValues = new ArrayList<>(); |
26 | this.impactValues = new LinkedHashMap<>(); | 32 | this.impactValues = new LinkedHashMap<>(); |
33 | // Random isn't used for cryptographical purposes but just to assign distinguishable identifiers to symbols. | ||
27 | @SuppressWarnings("squid:S2245") | 34 | @SuppressWarnings("squid:S2245") |
28 | Random random = new Random(1); | 35 | Random random = new Random(1); |
29 | 36 | ||
30 | individualHashValues = new IntLongHashMap(); | ||
31 | var individualsInOrder = individuals.toSortedList(Integer::compare); | 37 | var individualsInOrder = individuals.toSortedList(Integer::compare); |
32 | for(int i = 0; i<individualsInOrder.size(); i++) { | 38 | for(int i = 0; i<individualsInOrder.size(); i++) { |
33 | individualHashValues.put(individualsInOrder.get(i), random.nextLong()); | 39 | individualHashValues.put(individualsInOrder.get(i), random.nextLong()); |
34 | } | 40 | } |
35 | 41 | ||
36 | for (Interpretation<?> interpretation : interpretations) { | 42 | for (AnyInterpretation interpretation : interpretations) { |
37 | int arity = interpretation.getSymbol().arity(); | 43 | int arity = interpretation.getSymbol().arity(); |
38 | if (arity == 0) { | 44 | if (arity == 0) { |
39 | nullImpactValues.add(interpretation); | 45 | nullImpactValues.add(interpretation); |
@@ -86,7 +92,7 @@ public abstract class AbstractNeighbourhoodCalculator { | |||
86 | protected long calculateModelCode(long lastSum) { | 92 | protected long calculateModelCode(long lastSum) { |
87 | long result = 0; | 93 | long result = 0; |
88 | for (var nullImpactValue : nullImpactValues) { | 94 | for (var nullImpactValue : nullImpactValues) { |
89 | result = result * PRIME + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); | 95 | result = result * PRIME + Objects.hashCode(((Interpretation<?>) nullImpactValue).get(Tuple0.INSTANCE)); |
90 | } | 96 | } |
91 | result += lastSum; | 97 | result += lastSum; |
92 | return result; | 98 | return result; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java index 2ffbef5e..04335141 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java | |||
@@ -5,11 +5,14 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.neighbourhood; | 6 | package tools.refinery.store.statecoding.neighbourhood; |
7 | 7 | ||
8 | import org.eclipse.collections.api.factory.primitive.LongIntMaps; | ||
8 | import org.eclipse.collections.api.map.primitive.LongIntMap; | 9 | import org.eclipse.collections.api.map.primitive.LongIntMap; |
10 | import org.eclipse.collections.api.map.primitive.MutableLongIntMap; | ||
9 | import org.eclipse.collections.api.set.primitive.IntSet; | 11 | import org.eclipse.collections.api.set.primitive.IntSet; |
10 | import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; | ||
11 | import tools.refinery.store.map.Cursor; | 12 | import tools.refinery.store.map.Cursor; |
13 | import tools.refinery.store.model.AnyInterpretation; | ||
12 | import tools.refinery.store.model.Interpretation; | 14 | import tools.refinery.store.model.Interpretation; |
15 | import tools.refinery.store.model.Model; | ||
13 | import tools.refinery.store.statecoding.StateCodeCalculator; | 16 | import tools.refinery.store.statecoding.StateCodeCalculator; |
14 | import tools.refinery.store.statecoding.StateCoderResult; | 17 | import tools.refinery.store.statecoding.StateCoderResult; |
15 | import tools.refinery.store.tuple.Tuple; | 18 | import tools.refinery.store.tuple.Tuple; |
@@ -17,13 +20,14 @@ import tools.refinery.store.tuple.Tuple; | |||
17 | import java.util.List; | 20 | import java.util.List; |
18 | 21 | ||
19 | public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { | 22 | public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { |
20 | public LazyNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { | 23 | public LazyNeighbourhoodCalculator(Model model, List<? extends AnyInterpretation> interpretations, |
21 | super(interpretations, individuals); | 24 | IntSet individuals) { |
25 | super(model, interpretations, individuals); | ||
22 | } | 26 | } |
23 | 27 | ||
24 | public StateCoderResult calculateCodes() { | 28 | public StateCoderResult calculateCodes() { |
25 | ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); | 29 | ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); |
26 | LongIntHashMap prevHash2Amount = new LongIntHashMap(); | 30 | MutableLongIntMap prevHash2Amount = LongIntMaps.mutable.empty(); |
27 | 31 | ||
28 | long lastSum; | 32 | long lastSum; |
29 | // All hash code is 0, except to the individuals. | 33 | // All hash code is 0, except to the individuals. |
@@ -42,7 +46,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
42 | } | 46 | } |
43 | constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount); | 47 | constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount); |
44 | 48 | ||
45 | LongIntHashMap nextHash2Amount = new LongIntHashMap(); | 49 | MutableLongIntMap nextHash2Amount = LongIntMaps.mutable.empty(); |
46 | lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount); | 50 | lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount); |
47 | 51 | ||
48 | int nextSize = nextHash2Amount.size(); | 52 | int nextSize = nextHash2Amount.size(); |
@@ -60,7 +64,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
60 | } | 64 | } |
61 | 65 | ||
62 | private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, | 66 | private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, |
63 | LongIntHashMap nextHash2Amount) { | 67 | MutableLongIntMap nextHash2Amount) { |
64 | long lastSum = 0; | 68 | long lastSum = 0; |
65 | for (int i = 0; i < next.getSize(); i++) { | 69 | for (int i = 0; i < next.getSize(); i++) { |
66 | final long hash; | 70 | final long hash; |
@@ -84,7 +88,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
84 | 88 | ||
85 | private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount) { | 89 | private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount) { |
86 | for (var impactValueEntry : this.impactValues.entrySet()) { | 90 | for (var impactValueEntry : this.impactValues.entrySet()) { |
87 | Interpretation<?> interpretation = impactValueEntry.getKey(); | 91 | Interpretation<?> interpretation = (Interpretation<?>) impactValueEntry.getKey(); |
88 | var cursor = interpretation.getAll(); | 92 | var cursor = interpretation.getAll(); |
89 | int arity = interpretation.getSymbol().arity(); | 93 | int arity = interpretation.getSymbol().arity(); |
90 | long[] impactValue = impactValueEntry.getValue(); | 94 | long[] impactValue = impactValueEntry.getValue(); |
@@ -114,7 +118,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
114 | return amount == 1; | 118 | return amount == 1; |
115 | } | 119 | } |
116 | 120 | ||
117 | private void lazyImpactCalculation1(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 121 | private void lazyImpactCalculation1(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, |
122 | long[] impactValues, Cursor<Tuple, ?> cursor) { | ||
118 | 123 | ||
119 | Tuple tuple = cursor.getKey(); | 124 | Tuple tuple = cursor.getKey(); |
120 | int o = tuple.get(0); | 125 | int o = tuple.get(0); |
@@ -129,7 +134,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
129 | } | 134 | } |
130 | } | 135 | } |
131 | 136 | ||
132 | private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 137 | private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, |
138 | long[] impactValues, Cursor<Tuple, ?> cursor) { | ||
133 | final Tuple tuple = cursor.getKey(); | 139 | final Tuple tuple = cursor.getKey(); |
134 | final int o1 = tuple.get(0); | 140 | final int o1 = tuple.get(0); |
135 | final int o2 = tuple.get(1); | 141 | final int o2 = tuple.get(1); |
@@ -155,7 +161,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator | |||
155 | } | 161 | } |
156 | } | 162 | } |
157 | 163 | ||
158 | private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 164 | private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, |
165 | long[] impactValues, Cursor<Tuple, ?> cursor) { | ||
159 | final Tuple tuple = cursor.getKey(); | 166 | final Tuple tuple = cursor.getKey(); |
160 | 167 | ||
161 | final boolean[] uniques = new boolean[tuple.getSize()]; | 168 | final boolean[] uniques = new boolean[tuple.getSize()]; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java index 5b3e5ea3..5e6de53b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java | |||
@@ -8,6 +8,7 @@ package tools.refinery.store.statecoding.neighbourhood; | |||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | 8 | import org.eclipse.collections.api.set.primitive.IntSet; |
9 | import tools.refinery.store.map.Cursor; | 9 | import tools.refinery.store.map.Cursor; |
10 | import tools.refinery.store.model.Interpretation; | 10 | import tools.refinery.store.model.Interpretation; |
11 | import tools.refinery.store.model.Model; | ||
11 | import tools.refinery.store.statecoding.ObjectCode; | 12 | import tools.refinery.store.statecoding.ObjectCode; |
12 | import tools.refinery.store.statecoding.StateCodeCalculator; | 13 | import tools.refinery.store.statecoding.StateCodeCalculator; |
13 | import tools.refinery.store.statecoding.StateCoderResult; | 14 | import tools.refinery.store.statecoding.StateCoderResult; |
@@ -18,21 +19,27 @@ import java.util.List; | |||
18 | import java.util.Objects; | 19 | import java.util.Objects; |
19 | 20 | ||
20 | public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { | 21 | public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { |
21 | public NeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { | 22 | private ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); |
22 | super(interpretations, individuals); | 23 | private ObjectCodeImpl nextObjectCode = new ObjectCodeImpl(); |
24 | |||
25 | public NeighbourhoodCalculator(Model model, List<? extends Interpretation<?>> interpretations, IntSet individuals) { | ||
26 | super(model, interpretations, individuals); | ||
23 | } | 27 | } |
24 | 28 | ||
25 | public StateCoderResult calculateCodes() { | 29 | public StateCoderResult calculateCodes() { |
26 | ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); | 30 | model.checkCancelled(); |
31 | previousObjectCode.clear(); | ||
32 | nextObjectCode.clear(); | ||
27 | initializeWithIndividuals(previousObjectCode); | 33 | initializeWithIndividuals(previousObjectCode); |
28 | 34 | ||
29 | int rounds = 0; | 35 | int rounds = 0; |
30 | do { | 36 | do { |
31 | final ObjectCodeImpl nextObjectCode = rounds == 0 ? new ObjectCodeImpl() : | 37 | model.checkCancelled(); |
32 | new ObjectCodeImpl(previousObjectCode.getSize()); | ||
33 | |||
34 | constructNextObjectCodes(previousObjectCode, nextObjectCode); | 38 | constructNextObjectCodes(previousObjectCode, nextObjectCode); |
39 | var tempObjectCode = previousObjectCode; | ||
35 | previousObjectCode = nextObjectCode; | 40 | previousObjectCode = nextObjectCode; |
41 | nextObjectCode = tempObjectCode; | ||
42 | nextObjectCode.clear(); | ||
36 | rounds++; | 43 | rounds++; |
37 | } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize()); | 44 | } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize()); |
38 | 45 | ||
@@ -43,7 +50,7 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp | |||
43 | private long calculateLastSum(ObjectCode codes) { | 50 | private long calculateLastSum(ObjectCode codes) { |
44 | long result = 0; | 51 | long result = 0; |
45 | for (var nullImpactValue : nullImpactValues) { | 52 | for (var nullImpactValue : nullImpactValues) { |
46 | result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); | 53 | result = result * PRIME + Objects.hashCode(((Interpretation<?>) nullImpactValue).get(Tuple0.INSTANCE)); |
47 | } | 54 | } |
48 | 55 | ||
49 | for (int i = 0; i < codes.getSize(); i++) { | 56 | for (int i = 0; i < codes.getSize(); i++) { |
@@ -56,7 +63,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp | |||
56 | 63 | ||
57 | private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { | 64 | private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { |
58 | for (var impactValueEntry : this.impactValues.entrySet()) { | 65 | for (var impactValueEntry : this.impactValues.entrySet()) { |
59 | Interpretation<?> interpretation = impactValueEntry.getKey(); | 66 | model.checkCancelled(); |
67 | Interpretation<?> interpretation = (Interpretation<?>) impactValueEntry.getKey(); | ||
60 | var cursor = interpretation.getAll(); | 68 | var cursor = interpretation.getAll(); |
61 | int arity = interpretation.getSymbol().arity(); | 69 | int arity = interpretation.getSymbol().arity(); |
62 | long[] impactValue = impactValueEntry.getValue(); | 70 | long[] impactValue = impactValueEntry.getValue(); |
@@ -78,7 +86,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp | |||
78 | } | 86 | } |
79 | 87 | ||
80 | 88 | ||
81 | private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 89 | private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, |
90 | Cursor<Tuple, ?> cursor) { | ||
82 | 91 | ||
83 | Tuple tuple = cursor.getKey(); | 92 | Tuple tuple = cursor.getKey(); |
84 | int o = tuple.get(0); | 93 | int o = tuple.get(0); |
@@ -87,7 +96,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp | |||
87 | addHash(next, o, impactValues[0], tupleHash); | 96 | addHash(next, o, impactValues[0], tupleHash); |
88 | } | 97 | } |
89 | 98 | ||
90 | private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 99 | private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, |
100 | Cursor<Tuple, ?> cursor) { | ||
91 | final Tuple tuple = cursor.getKey(); | 101 | final Tuple tuple = cursor.getKey(); |
92 | final int o1 = tuple.get(0); | 102 | final int o1 = tuple.get(0); |
93 | final int o2 = tuple.get(1); | 103 | final int o2 = tuple.get(1); |
@@ -99,7 +109,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp | |||
99 | addHash(next, o2, impactValues[1], tupleHash); | 109 | addHash(next, o2, impactValues[1], tupleHash); |
100 | } | 110 | } |
101 | 111 | ||
102 | private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 112 | private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, |
113 | Cursor<Tuple, ?> cursor) { | ||
103 | final Tuple tuple = cursor.getKey(); | 114 | final Tuple tuple = cursor.getKey(); |
104 | 115 | ||
105 | Object value = cursor.getValue(); | 116 | Object value = cursor.getValue(); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java index 0cd7ff58..422e1d73 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java | |||
@@ -32,6 +32,13 @@ public class ObjectCodeImpl implements ObjectCode { | |||
32 | effectiveSize = copy.effectiveSize; | 32 | effectiveSize = copy.effectiveSize; |
33 | } | 33 | } |
34 | 34 | ||
35 | public void clear() { | ||
36 | effectiveSize = 0; | ||
37 | for (int i = 0; i < size; i++) { | ||
38 | vector[i] = 0; | ||
39 | } | ||
40 | } | ||
41 | |||
35 | public void ensureSize(int object) { | 42 | public void ensureSize(int object) { |
36 | if (object >= size) { | 43 | if (object >= size) { |
37 | size = object + 1; | 44 | size = object + 1; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java index decff1d5..0682e1a4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java | |||
@@ -8,18 +8,14 @@ package tools.refinery.store.statecoding.stateequivalence; | |||
8 | import org.eclipse.collections.api.factory.primitive.IntIntMaps; | 8 | import org.eclipse.collections.api.factory.primitive.IntIntMaps; |
9 | import org.eclipse.collections.api.map.primitive.IntIntMap; | 9 | import org.eclipse.collections.api.map.primitive.IntIntMap; |
10 | import org.eclipse.collections.api.set.primitive.IntSet; | 10 | import org.eclipse.collections.api.set.primitive.IntSet; |
11 | import org.eclipse.collections.impl.list.Interval; | ||
12 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | ||
13 | 11 | ||
14 | import java.util.ArrayList; | 12 | import java.util.*; |
15 | import java.util.Arrays; | ||
16 | import java.util.List; | ||
17 | 13 | ||
18 | public class CombinationNodePairing implements NodePairing { | 14 | public class CombinationNodePairing implements NodePairing { |
19 | private final int[] left; | 15 | private final int[] left; |
20 | private final int[] right; | 16 | private final int[] right; |
21 | 17 | ||
22 | CombinationNodePairing(IntSet left, IntHashSet right) { | 18 | CombinationNodePairing(IntSet left, IntSet right) { |
23 | this.left = left.toArray(); | 19 | this.left = left.toArray(); |
24 | this.right = right.toArray(); | 20 | this.right = right.toArray(); |
25 | 21 | ||
@@ -32,59 +28,33 @@ public class CombinationNodePairing implements NodePairing { | |||
32 | return left.length; | 28 | return left.length; |
33 | } | 29 | } |
34 | 30 | ||
35 | static final int LIMIT = 5; | 31 | private static final int LIMIT = 5; |
36 | static final List<List<int[]>> permutations = new ArrayList<>(); | 32 | // Enum-based singleton used to delay generating all permutations until they are first needed. |
37 | 33 | @SuppressWarnings("squid:S6548") | |
38 | /** | 34 | private enum PermutationsHolder { |
39 | * Generates and stores permutations up to a given size. If the number would be more than a limit, it provides a | 35 | INSTANCE; |
40 | * single permutation only. | 36 | |
41 | * | 37 | final CombinationNodePairingPermutations permutations = new CombinationNodePairingPermutations(LIMIT); |
42 | * @param max The max number in the permutation | ||
43 | * @return A complete list of permutations of numbers 0...max, or a single permutation. | ||
44 | */ | ||
45 | public static List<int[]> getPermutations(int max) { | ||
46 | if (max < permutations.size()) { | ||
47 | return permutations.get(max); | ||
48 | } | ||
49 | if (max == 0) { | ||
50 | List<int[]> result = new ArrayList<>(); | ||
51 | result.add(new int[1]); | ||
52 | permutations.add(result); | ||
53 | return result; | ||
54 | } | ||
55 | List<int[]> result = new ArrayList<>(); | ||
56 | List<int[]> previousPermutations = getPermutations(max - 1); | ||
57 | for (var permutation : previousPermutations) { | ||
58 | for (int pos = 0; pos <= max; pos++) { | ||
59 | int[] newPermutation = new int[max + 1]; | ||
60 | System.arraycopy(permutation, 0, newPermutation, 0, pos); | ||
61 | newPermutation[pos] = max; | ||
62 | if (max - (pos + 1) >= 0) | ||
63 | System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); | ||
64 | result.add(newPermutation); | ||
65 | } | ||
66 | } | ||
67 | permutations.add(result); | ||
68 | return result; | ||
69 | } | 38 | } |
70 | 39 | ||
71 | @Override | 40 | @Override |
72 | public List<IntIntMap> permutations() { | 41 | public List<IntIntMap> permutations() { |
73 | final var interval = Interval.zeroTo(this.size() - 1); | 42 | int limit = this.size(); |
43 | Iterable<Integer> interval = () -> new IntervalIterator(limit); | ||
74 | 44 | ||
75 | if (isComplete()) { | 45 | if (isComplete()) { |
76 | final List<int[]> p = getPermutations(this.size() - 1); | 46 | final List<int[]> p = PermutationsHolder.INSTANCE.permutations.getPermutations(this.size() - 1); |
77 | return p.stream().map(x -> constructPermutationMap(interval, x)).toList(); | 47 | return p.stream().map(x -> constructPermutationMap(interval, x)).toList(); |
78 | } else { | 48 | } else { |
79 | return List.of(constructTrivialMap(interval)); | 49 | return List.of(constructTrivialMap(interval)); |
80 | } | 50 | } |
81 | } | 51 | } |
82 | 52 | ||
83 | private IntIntMap constructTrivialMap(Interval interval) { | 53 | private IntIntMap constructTrivialMap(Iterable<Integer> interval) { |
84 | return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[r]); | 54 | return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[r]); |
85 | } | 55 | } |
86 | 56 | ||
87 | private IntIntMap constructPermutationMap(Interval interval, int[] permutation) { | 57 | private IntIntMap constructPermutationMap(Iterable<Integer> interval, int[] permutation) { |
88 | return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[permutation[r]]); | 58 | return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[permutation[r]]); |
89 | } | 59 | } |
90 | 60 | ||
@@ -92,4 +62,28 @@ public class CombinationNodePairing implements NodePairing { | |||
92 | public boolean isComplete() { | 62 | public boolean isComplete() { |
93 | return this.size() <= LIMIT; | 63 | return this.size() <= LIMIT; |
94 | } | 64 | } |
65 | |||
66 | private static class IntervalIterator implements Iterator<Integer> { | ||
67 | private final int limit; | ||
68 | private int value = 0; | ||
69 | |||
70 | private IntervalIterator(int max) { | ||
71 | this.limit = max; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public boolean hasNext() { | ||
76 | return value < limit; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public Integer next() { | ||
81 | if (value >= limit) { | ||
82 | throw new NoSuchElementException("End of interval"); | ||
83 | } | ||
84 | int next = value; | ||
85 | value++; | ||
86 | return next; | ||
87 | } | ||
88 | } | ||
95 | } | 89 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java new file mode 100644 index 00000000..eacd3a2a --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding.stateequivalence; | ||
7 | |||
8 | import java.util.ArrayList; | ||
9 | import java.util.List; | ||
10 | |||
11 | class CombinationNodePairingPermutations { | ||
12 | private final List<List<int[]>> permutations = new ArrayList<>(); | ||
13 | |||
14 | public CombinationNodePairingPermutations(int max) { | ||
15 | initializePermutations(max); | ||
16 | } | ||
17 | |||
18 | public List<int[]> getPermutations(int max) { | ||
19 | if (max >= permutations.size()) { | ||
20 | throw new IllegalArgumentException("Only permutations up to %d elements are supported".formatted(max)); | ||
21 | } | ||
22 | return permutations.get(max); | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Generates and stores permutations up to a given size. If the number would be more than a limit, it provides a | ||
27 | * single permutation only. | ||
28 | * | ||
29 | * @param max The max number in the permutation | ||
30 | * @return A complete list of permutations of numbers 0...max, or a single permutation. | ||
31 | */ | ||
32 | private List<int[]> initializePermutations(int max) { | ||
33 | if (max < permutations.size()) { | ||
34 | return permutations.get(max); | ||
35 | } | ||
36 | if (max == 0) { | ||
37 | List<int[]> result = new ArrayList<>(); | ||
38 | result.add(new int[1]); | ||
39 | permutations.add(result); | ||
40 | return result; | ||
41 | } | ||
42 | List<int[]> result = new ArrayList<>(); | ||
43 | List<int[]> previousPermutations = initializePermutations(max - 1); | ||
44 | for (var permutation : previousPermutations) { | ||
45 | for (int pos = 0; pos <= max; pos++) { | ||
46 | int[] newPermutation = new int[max + 1]; | ||
47 | System.arraycopy(permutation, 0, newPermutation, 0, pos); | ||
48 | newPermutation[pos] = max; | ||
49 | if (max - (pos + 1) >= 0) | ||
50 | System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); | ||
51 | result.add(newPermutation); | ||
52 | } | ||
53 | } | ||
54 | permutations.add(result); | ||
55 | return result; | ||
56 | } | ||
57 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java index 7e5db7a3..f45f0d2e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java | |||
@@ -6,18 +6,18 @@ | |||
6 | package tools.refinery.store.statecoding.stateequivalence; | 6 | package tools.refinery.store.statecoding.stateequivalence; |
7 | 7 | ||
8 | import org.eclipse.collections.api.map.primitive.IntIntMap; | 8 | import org.eclipse.collections.api.map.primitive.IntIntMap; |
9 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | 9 | import org.eclipse.collections.api.set.primitive.IntSet; |
10 | 10 | ||
11 | import java.util.List; | 11 | import java.util.List; |
12 | 12 | ||
13 | public interface NodePairing { | 13 | public interface NodePairing { |
14 | |||
15 | int size(); | 14 | int size(); |
15 | |||
16 | List<IntIntMap> permutations(); | 16 | List<IntIntMap> permutations(); |
17 | 17 | ||
18 | boolean isComplete(); | 18 | boolean isComplete(); |
19 | 19 | ||
20 | static NodePairing constructNodePairing(IntHashSet left, IntHashSet right){ | 20 | static NodePairing constructNodePairing(IntSet left, IntSet right){ |
21 | if(left.size() != right.size()) { | 21 | if(left.size() != right.size()) { |
22 | return null; | 22 | return null; |
23 | } | 23 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java index 6be0f3e7..bc4d723a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java | |||
@@ -12,12 +12,12 @@ import java.util.List; | |||
12 | 12 | ||
13 | public class PermutationMorphism implements Morphism { | 13 | public class PermutationMorphism implements Morphism { |
14 | private final IntIntMap object2PermutationGroup; | 14 | private final IntIntMap object2PermutationGroup; |
15 | private final List<List<IntIntMap>> permutationsGroups; | 15 | private final List<? extends List<? extends IntIntMap>> permutationsGroups; |
16 | private final int[] selection; | 16 | private final int[] selection; |
17 | private boolean hasNext; | 17 | private boolean hasNext; |
18 | 18 | ||
19 | PermutationMorphism(IntIntMap object2PermutationGroup, | 19 | PermutationMorphism(IntIntMap object2PermutationGroup, |
20 | List<List<IntIntMap>> permutationsGroups) { | 20 | List<? extends List<? extends IntIntMap>> permutationsGroups) { |
21 | this.object2PermutationGroup = object2PermutationGroup; | 21 | this.object2PermutationGroup = object2PermutationGroup; |
22 | this.permutationsGroups = permutationsGroups; | 22 | this.permutationsGroups = permutationsGroups; |
23 | 23 | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java index ef0d76a7..5a62d8a0 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java | |||
@@ -6,11 +6,14 @@ | |||
6 | package tools.refinery.store.statecoding.stateequivalence; | 6 | package tools.refinery.store.statecoding.stateequivalence; |
7 | 7 | ||
8 | import org.eclipse.collections.api.factory.primitive.IntIntMaps; | 8 | import org.eclipse.collections.api.factory.primitive.IntIntMaps; |
9 | import org.eclipse.collections.api.factory.primitive.IntSets; | ||
10 | import org.eclipse.collections.api.factory.primitive.LongObjectMaps; | ||
9 | import org.eclipse.collections.api.map.primitive.IntIntMap; | 11 | import org.eclipse.collections.api.map.primitive.IntIntMap; |
12 | import org.eclipse.collections.api.map.primitive.MutableIntIntMap; | ||
13 | import org.eclipse.collections.api.map.primitive.MutableLongObjectMap; | ||
10 | import org.eclipse.collections.api.set.primitive.IntSet; | 14 | import org.eclipse.collections.api.set.primitive.IntSet; |
11 | import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; | 15 | import org.eclipse.collections.api.set.primitive.MutableIntSet; |
12 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; | 16 | import tools.refinery.store.model.AnyInterpretation; |
13 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | ||
14 | import tools.refinery.store.model.Interpretation; | 17 | import tools.refinery.store.model.Interpretation; |
15 | import tools.refinery.store.statecoding.Morphism; | 18 | import tools.refinery.store.statecoding.Morphism; |
16 | import tools.refinery.store.statecoding.ObjectCode; | 19 | import tools.refinery.store.statecoding.ObjectCode; |
@@ -26,12 +29,11 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
26 | 29 | ||
27 | @Override | 30 | @Override |
28 | public EquivalenceResult constructMorphism(IntSet individuals, | 31 | public EquivalenceResult constructMorphism(IntSet individuals, |
29 | List<? extends Interpretation<?>> interpretations1, | 32 | List<? extends AnyInterpretation> interpretations1, |
30 | ObjectCode code1, | 33 | ObjectCode code1, |
31 | List<? extends Interpretation<?>> interpretations2, | 34 | List<? extends AnyInterpretation> interpretations2, |
32 | ObjectCode code2) | 35 | ObjectCode code2) { |
33 | { | 36 | MutableIntIntMap object2PermutationGroup = IntIntMaps.mutable.empty(); |
34 | IntIntHashMap object2PermutationGroup = new IntIntHashMap(); | ||
35 | List<List<IntIntMap>> permutationsGroups = new ArrayList<>(); | 37 | List<List<IntIntMap>> permutationsGroups = new ArrayList<>(); |
36 | 38 | ||
37 | final EquivalenceResult permutations = constructPermutationNavigation(individuals, | 39 | final EquivalenceResult permutations = constructPermutationNavigation(individuals, |
@@ -50,7 +52,7 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
50 | return permutations; | 52 | return permutations; |
51 | } | 53 | } |
52 | 54 | ||
53 | if(tried >= LIMIT) { | 55 | if (tried >= LIMIT) { |
54 | return EquivalenceResult.UNKNOWN; | 56 | return EquivalenceResult.UNKNOWN; |
55 | } | 57 | } |
56 | 58 | ||
@@ -58,22 +60,22 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
58 | tried++; | 60 | tried++; |
59 | } while (hasNext); | 61 | } while (hasNext); |
60 | 62 | ||
61 | if(permutations == EquivalenceResult.UNKNOWN) { | 63 | if (permutations == EquivalenceResult.UNKNOWN) { |
62 | return EquivalenceResult.UNKNOWN; | 64 | return EquivalenceResult.UNKNOWN; |
63 | } else { | 65 | } else { |
64 | return EquivalenceResult.DIFFERENT; | 66 | return EquivalenceResult.DIFFERENT; |
65 | } | 67 | } |
66 | } | 68 | } |
67 | 69 | ||
68 | private LongObjectHashMap<IntHashSet> indexByHash(ObjectCode code, IntSet individuals) { | 70 | private MutableLongObjectMap<MutableIntSet> indexByHash(ObjectCode code, IntSet individuals) { |
69 | LongObjectHashMap<IntHashSet> result = new LongObjectHashMap<>(); | 71 | MutableLongObjectMap<MutableIntSet> result = LongObjectMaps.mutable.empty(); |
70 | for (int o = 0; o < code.getSize(); o++) { | 72 | for (int o = 0; o < code.getSize(); o++) { |
71 | if(! individuals.contains(o)){ | 73 | if (!individuals.contains(o)) { |
72 | long hash = code.get(o); | 74 | long hash = code.get(o); |
73 | if(hash != 0) { | 75 | if (hash != 0) { |
74 | var equivalenceClass = result.get(hash); | 76 | var equivalenceClass = result.get(hash); |
75 | if (equivalenceClass == null) { | 77 | if (equivalenceClass == null) { |
76 | equivalenceClass = new IntHashSet(); | 78 | equivalenceClass = IntSets.mutable.empty(); |
77 | result.put(hash, equivalenceClass); | 79 | result.put(hash, equivalenceClass); |
78 | } | 80 | } |
79 | equivalenceClass.add(o); | 81 | equivalenceClass.add(o); |
@@ -83,11 +85,9 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
83 | return result; | 85 | return result; |
84 | } | 86 | } |
85 | 87 | ||
86 | private EquivalenceResult constructPermutationNavigation(IntSet individuals, | 88 | private EquivalenceResult constructPermutationNavigation( |
87 | LongObjectHashMap<IntHashSet> map1, | 89 | IntSet individuals, MutableLongObjectMap<MutableIntSet> map1, MutableLongObjectMap<MutableIntSet> map2, |
88 | LongObjectHashMap<IntHashSet> map2, | 90 | MutableIntIntMap object2OptionIndex, List<List<IntIntMap>> listOfOptions) { |
89 | IntIntHashMap object2OptionIndex, | ||
90 | List<List<IntIntMap>> listOfOptions) { | ||
91 | if (map1.size() != map2.size()) { | 91 | if (map1.size() != map2.size()) { |
92 | return EquivalenceResult.DIFFERENT; | 92 | return EquivalenceResult.DIFFERENT; |
93 | } | 93 | } |
@@ -116,27 +116,28 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
116 | listOfOptions.add(pairing.permutations()); | 116 | listOfOptions.add(pairing.permutations()); |
117 | } | 117 | } |
118 | 118 | ||
119 | individuals.forEach(o -> listOfOptions.add(o,List.of(IntIntMaps.immutable.of(o,o)))); | 119 | individuals.forEach(o -> listOfOptions.add(o, List.of(IntIntMaps.immutable.of(o, o)))); |
120 | 120 | ||
121 | if(allComplete) { | 121 | if (allComplete) { |
122 | return EquivalenceResult.ISOMORPHIC; | 122 | return EquivalenceResult.ISOMORPHIC; |
123 | } else { | 123 | } else { |
124 | return EquivalenceResult.UNKNOWN; | 124 | return EquivalenceResult.UNKNOWN; |
125 | } | 125 | } |
126 | } | 126 | } |
127 | 127 | ||
128 | private boolean testMorphism(List<? extends Interpretation<?>> s, List<? extends Interpretation<?>> t, Morphism m) { | 128 | private boolean testMorphism(List<? extends AnyInterpretation> s, List<? extends AnyInterpretation> t, |
129 | Morphism m) { | ||
129 | for (int interpretationIndex = 0; interpretationIndex < s.size(); interpretationIndex++) { | 130 | for (int interpretationIndex = 0; interpretationIndex < s.size(); interpretationIndex++) { |
130 | var sI = s.get(interpretationIndex); | 131 | var sI = s.get(interpretationIndex); |
131 | var tI = t.get(interpretationIndex); | 132 | var tI = t.get(interpretationIndex); |
132 | 133 | ||
133 | var cursor = sI.getAll(); | 134 | var cursor = ((Interpretation<?>) sI).getAll(); |
134 | while (cursor.move()) { | 135 | while (cursor.move()) { |
135 | final Tuple sTuple = cursor.getKey(); | 136 | final Tuple sTuple = cursor.getKey(); |
136 | final Object sValue = cursor.getValue(); | 137 | final Object sValue = cursor.getValue(); |
137 | 138 | ||
138 | final Tuple tTuple = apply(sTuple, m); | 139 | final Tuple tTuple = apply(sTuple, m); |
139 | final Object tValue = tI.get(tTuple); | 140 | final Object tValue = ((Interpretation<?>) tI).get(tTuple); |
140 | 141 | ||
141 | if (!Objects.equals(sValue, tValue)) { | 142 | if (!Objects.equals(sValue, tValue)) { |
142 | return false; | 143 | return false; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java index aae7b344..e9761763 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java | |||
@@ -12,6 +12,8 @@ public sealed interface Tuple extends Comparable<Tuple> permits Tuple0, Tuple1, | |||
12 | 12 | ||
13 | int get(int element); | 13 | int get(int element); |
14 | 14 | ||
15 | Tuple set(int element, int value); | ||
16 | |||
15 | @Override | 17 | @Override |
16 | default int compareTo(@NotNull Tuple other) { | 18 | default int compareTo(@NotNull Tuple other) { |
17 | int size = getSize(); | 19 | int size = getSize(); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java index a9aa9bf2..5f525798 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java | |||
@@ -29,6 +29,11 @@ public final class Tuple0 implements Tuple { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | @Override | 31 | @Override |
32 | public Tuple set(int element, int value) { | ||
33 | throw new IndexOutOfBoundsException(element); | ||
34 | } | ||
35 | |||
36 | @Override | ||
32 | public String toString() { | 37 | public String toString() { |
33 | return TUPLE_BEGIN + TUPLE_END; | 38 | return TUPLE_BEGIN + TUPLE_END; |
34 | } | 39 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java index 388ee3a9..fb9497d2 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java | |||
@@ -38,6 +38,14 @@ public final class Tuple1 implements Tuple { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | @Override | 40 | @Override |
41 | public Tuple set(int element, int value) { | ||
42 | if (element == 0) { | ||
43 | return Tuple.of(value); | ||
44 | } | ||
45 | throw new IndexOutOfBoundsException(element); | ||
46 | } | ||
47 | |||
48 | @Override | ||
41 | public String toString() { | 49 | public String toString() { |
42 | return TUPLE_BEGIN + value0 + TUPLE_END; | 50 | return TUPLE_BEGIN + value0 + TUPLE_END; |
43 | } | 51 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java index 6d886fd3..2213df97 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java | |||
@@ -25,6 +25,15 @@ public record Tuple2(int value0, int value1) implements Tuple { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | @Override | 27 | @Override |
28 | public Tuple set(int element, int value) { | ||
29 | return switch (element) { | ||
30 | case 0 -> Tuple.of(value, value1); | ||
31 | case 1 -> Tuple.of(value0, value); | ||
32 | default -> throw new ArrayIndexOutOfBoundsException(element); | ||
33 | }; | ||
34 | } | ||
35 | |||
36 | @Override | ||
28 | public String toString() { | 37 | public String toString() { |
29 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_END; | 38 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_END; |
30 | } | 39 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java index 734e45c2..417770e8 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java | |||
@@ -26,6 +26,16 @@ public record Tuple3(int value0, int value1, int value2) implements Tuple { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | @Override | 28 | @Override |
29 | public Tuple set(int element, int value) { | ||
30 | return switch (element) { | ||
31 | case 0 -> Tuple.of(value, value1, value2); | ||
32 | case 1 -> Tuple.of(value0, value, value2); | ||
33 | case 2 -> Tuple.of(value0, value1, value); | ||
34 | default -> throw new ArrayIndexOutOfBoundsException(element); | ||
35 | }; | ||
36 | } | ||
37 | |||
38 | @Override | ||
29 | public String toString() { | 39 | public String toString() { |
30 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_END; | 40 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_END; |
31 | } | 41 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java index e1b93e7b..c4915198 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java | |||
@@ -27,6 +27,17 @@ public record Tuple4(int value0, int value1, int value2, int value3) implements | |||
27 | } | 27 | } |
28 | 28 | ||
29 | @Override | 29 | @Override |
30 | public Tuple set(int element, int value) { | ||
31 | return switch (element) { | ||
32 | case 0 -> Tuple.of(value, value1, value2, value3); | ||
33 | case 1 -> Tuple.of(value0, value, value2, value3); | ||
34 | case 2 -> Tuple.of(value0, value1, value, value3); | ||
35 | case 3 -> Tuple.of(value0, value1, value2, value); | ||
36 | default -> throw new ArrayIndexOutOfBoundsException(element); | ||
37 | }; | ||
38 | } | ||
39 | |||
40 | @Override | ||
30 | public String toString() { | 41 | public String toString() { |
31 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_SEPARATOR + value3 + | 42 | return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_SEPARATOR + value3 + |
32 | TUPLE_END; | 43 | TUPLE_END; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java index b66af491..b42b4b6a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java | |||
@@ -32,6 +32,16 @@ public final class TupleN implements Tuple { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | @Override | 34 | @Override |
35 | public Tuple set(int element, int value) { | ||
36 | int size = getSize(); | ||
37 | var newValues = new int[size]; | ||
38 | for (int i = 0; i < size; i++) { | ||
39 | newValues[i] = element == i ? value : values[i]; | ||
40 | } | ||
41 | return Tuple.of(newValues); | ||
42 | } | ||
43 | |||
44 | @Override | ||
35 | public String toString() { | 45 | public String toString() { |
36 | var valuesString = Arrays.stream(values) | 46 | var valuesString = Arrays.stream(values) |
37 | .mapToObj(Integer::toString) | 47 | .mapToObj(Integer::toString) |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java new file mode 100644 index 00000000..be294013 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java | |||
@@ -0,0 +1,13 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.util; | ||
7 | |||
8 | @FunctionalInterface | ||
9 | public interface CancellationToken { | ||
10 | CancellationToken NONE = () -> {}; | ||
11 | |||
12 | void checkCancelled(); | ||
13 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java b/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java index 78ad2ad7..2e302663 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java +++ b/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java | |||
@@ -20,6 +20,10 @@ public class CycleDetectingMapper<T, R> { | |||
20 | 20 | ||
21 | private final Map<T, R> results = new HashMap<>(); | 21 | private final Map<T, R> results = new HashMap<>(); |
22 | 22 | ||
23 | public CycleDetectingMapper(Function<T, R> doMap) { | ||
24 | this(Objects::toString, doMap); | ||
25 | } | ||
26 | |||
23 | public CycleDetectingMapper(Function<T, String> getName, Function<T, R> doMap) { | 27 | public CycleDetectingMapper(Function<T, String> getName, Function<T, R> doMap) { |
24 | this.getName = getName; | 28 | this.getName = getName; |
25 | this.doMap = doMap; | 29 | this.doMap = doMap; |