aboutsummaryrefslogtreecommitdiffstats
path: root/store/src/main/java/org/eclipse/viatra/solver/data
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-09-29 02:45:57 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-09-29 03:16:01 +0200
commita155f6ba02e08a75ce6e474a86900b8363f506e8 (patch)
treeb78804c1c0f0968a9625f0656e08f5dadc16924c /store/src/main/java/org/eclipse/viatra/solver/data
parentSimplify branding (diff)
downloadrefinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.tar.gz
refinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.tar.zst
refinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.zip
build: migration to Gradle 7
Diffstat (limited to 'store/src/main/java/org/eclipse/viatra/solver/data')
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/ContinousHashProvider.java69
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/Cursor.java14
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/CursorAsIterator.java57
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/DiffCursor.java6
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/MapAsIterable.java26
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/Versioned.java7
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMap.java13
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStore.java14
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreConfiguration.java48
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreImpl.java135
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java379
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapCursor.java131
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapDiffCursor.java214
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java449
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/Node.java85
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/OldValueBox.java19
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/map/internal/VersionedMapImpl.java171
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/Model.java20
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/ModelCursor.java25
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/ModelDiffCursor.java26
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStore.java16
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStoreImpl.java127
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/Tuple.java148
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProvider.java65
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProviderBitMagic.java28
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/internal/ModelImpl.java124
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/internal/SimilarRelationEquivalenceClass.java33
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/representation/AuxilaryData.java22
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/representation/DataRepresentation.java24
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/representation/Relation.java31
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/model/representation/TruthValue.java44
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java34
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAnd.java37
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAtom.java33
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFPredicate.java72
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/EquivalenceAtom.java44
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateAtom.java57
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateBuilder_string.java107
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/RelationAtom.java45
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/building/Variable.java22
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java59
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/PredicateTranslator.java209
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java51
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java63
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java32
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java57
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java187
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/view/FilteredRelationView.java48
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/view/FunctionalRelationView.java50
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/view/KeyOnlyRelationView.java16
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/query/view/RelationView.java85
-rw-r--r--store/src/main/java/org/eclipse/viatra/solver/data/util/CollectionsUtil.java72
52 files changed, 3950 insertions, 0 deletions
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/ContinousHashProvider.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/ContinousHashProvider.java
new file mode 100644
index 00000000..dd64f901
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/ContinousHashProvider.java
@@ -0,0 +1,69 @@
1package org.eclipse.viatra.solver.data.map;
2
3import org.eclipse.viatra.solver.data.map.internal.Node;
4
5/**
6 * A class representing an equivalence relation for a type {@code K} with a
7 * continuous hash function.
8 *
9 * @author Oszkar Semerath
10 *
11 * @param <K> Target java type.
12 */
13public interface ContinousHashProvider<K> {
14 public static final int EFFECTIVE_BITS = Node.EFFECTIVE_BITS;
15 public static final int EFFECTIVE_BIT_MASK = (1 << (EFFECTIVE_BITS)) - 1;
16
17 /**
18 * Maximal practical depth for differentiating keys. If two keys have the same
19 * hash code until that depth, the algorithm can stop.
20 */
21 public static final int MAX_PRACTICAL_DEPTH = 500;
22
23 /**
24 * Provides a hash code for a object {@code key} with a given {@code index}. It
25 * has the following contracts:
26 * <ul>
27 * <li>If {@link #equals}{@code (key1,key2)}, then
28 * {@code getHash(key1, index) == getHash(key2, index)} for all values of
29 * {@code index}.</li>
30 * <li>If {@code getHash(key1,index) == getHash(key2, index)} for all values of
31 * {@code index}, then {@link #equals}{@code (key1, key2)}</li>
32 * <li>In current implementation, we use only the least significant
33 * {@link #EFFECTIVE_BITS}
34 * </ul>
35 * Check {@link #equals} for further details.
36 *
37 * @param key The target data object.
38 * @param index The depth of the the hash code. Needs to be non-negative.
39 * @return A hash code.
40 */
41 public int getHash(K key, int index);
42
43 public default int getEffectiveHash(K key, int index) {
44 return getHash(key, index) & EFFECTIVE_BIT_MASK;
45 }
46
47 public default int compare(K key1, K key2) {
48 if (key1.equals(key2)) {
49 return 0;
50 } else {
51 for (int i = 0; i < ContinousHashProvider.MAX_PRACTICAL_DEPTH; i++) {
52 int hash1 = getEffectiveHash(key1, i);
53 int hash2 = getEffectiveHash(key2, i);
54 for(int j = 0; j<Integer.SIZE/Node.BRANCHING_FACTOR_BITS; j++) {
55 final int factorMask = (1<<Node.BRANCHING_FACTOR_BITS)-1;
56 int hashFragment1 = (hash1>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask;
57 int hashFragment2 = (hash2>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask;
58 var result = Integer.compare(hashFragment1, hashFragment2);
59 if (result != 0) {
60 return result;
61 }
62 }
63 }
64 throw new IllegalArgumentException("Two different keys (" + key1 + " and " + key2
65 + ") have the same hashcode over the practical depth limitation ("
66 + ContinousHashProvider.MAX_PRACTICAL_DEPTH + ")!");
67 }
68 }
69}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/Cursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/Cursor.java
new file mode 100644
index 00000000..e45b1f20
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/Cursor.java
@@ -0,0 +1,14 @@
1package org.eclipse.viatra.solver.data.map;
2
3import java.util.List;
4
5public interface Cursor<K,V> {
6 public K getKey();
7 public V getValue();
8 public boolean isTerminated();
9 public boolean move();
10 public boolean isDirty();
11
12 @SuppressWarnings("squid:S1452")
13 public List<VersionedMap<?,?>> getDependingMaps();
14}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/CursorAsIterator.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/CursorAsIterator.java
new file mode 100644
index 00000000..b29b3119
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/CursorAsIterator.java
@@ -0,0 +1,57 @@
1package org.eclipse.viatra.solver.data.map;
2
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5import java.util.function.BiFunction;
6import java.util.function.BiPredicate;
7
8public class CursorAsIterator<K,V,D> implements Iterator<D> {
9 private final Cursor<K, V> internal;
10 private final BiFunction<K, V, D> entryTransformation;
11 private final BiPredicate<K,V> filtering;
12
13 D lastValidElement;
14
15 public CursorAsIterator(Cursor<K, V> internal, BiFunction<K, V, D> entryTransformation, BiPredicate<K,V> filtering) {
16 this.internal = internal;
17 this.entryTransformation = entryTransformation;
18 this.filtering = filtering;
19
20 moveToNext();
21 }
22 public CursorAsIterator(Cursor<K, V> internal, BiFunction<K, V, D> entryTransformation) {
23 this.internal = internal;
24 this.entryTransformation = entryTransformation;
25 this.filtering = ((k,v)->true);
26
27 moveToNext();
28 }
29
30 private void moveToNext() {
31 internal.move();
32 while(!internal.isTerminated() && !filtering.test(internal.getKey(), internal.getValue())) {
33 internal.move();
34 }
35 if(!internal.isTerminated()) {
36 lastValidElement = entryTransformation.apply(internal.getKey(), internal.getValue());
37 }
38 }
39
40
41 @Override
42 public boolean hasNext() {
43 return !internal.isTerminated();
44 }
45 @Override
46 public D next() {
47 if(hasNext()) {
48 D last = lastValidElement;
49 moveToNext();
50 return last;
51 } else {
52 throw new NoSuchElementException();
53 }
54
55 }
56
57}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/DiffCursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/DiffCursor.java
new file mode 100644
index 00000000..f0af1436
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/DiffCursor.java
@@ -0,0 +1,6 @@
1package org.eclipse.viatra.solver.data.map;
2
3public interface DiffCursor<K, V> extends Cursor<K,V> {
4 public V getFromValue();
5 public V getToValue();
6} \ No newline at end of file
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/MapAsIterable.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/MapAsIterable.java
new file mode 100644
index 00000000..22b5e6c1
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/MapAsIterable.java
@@ -0,0 +1,26 @@
1package org.eclipse.viatra.solver.data.map;
2
3import java.util.Iterator;
4import java.util.function.BiFunction;
5import java.util.function.BiPredicate;
6
7public class MapAsIterable<K,V,D> implements Iterable<D> {
8 private final VersionedMap<K, V> internal;
9 private final BiFunction<K, V, D> entryTransformation;
10 private final BiPredicate<K,V> filtering;
11
12 public MapAsIterable(VersionedMap<K, V> internal, BiFunction<K, V, D> entryTransformation, BiPredicate<K,V> filtering) {
13 this.internal = internal;
14 this.entryTransformation = entryTransformation;
15 this.filtering = filtering;
16 }
17 public MapAsIterable(VersionedMap<K, V> internal, BiFunction<K, V, D> entryTransformation) {
18 this.internal = internal;
19 this.entryTransformation = entryTransformation;
20 this.filtering = ((k,v)->true);
21 }
22 @Override
23 public Iterator<D> iterator() {
24 return new CursorAsIterator<>(internal.getAll(), entryTransformation, filtering);
25 }
26}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/Versioned.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/Versioned.java
new file mode 100644
index 00000000..e46be237
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/Versioned.java
@@ -0,0 +1,7 @@
1package org.eclipse.viatra.solver.data.map;
2
3public interface Versioned {
4 public long commit();
5 //maybe revert()?
6 public void restore(long state);
7}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMap.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMap.java
new file mode 100644
index 00000000..3a35b9f0
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMap.java
@@ -0,0 +1,13 @@
1package org.eclipse.viatra.solver.data.map;
2
3public interface VersionedMap<K,V> extends Versioned{
4 public V get(K key);
5 public Cursor<K,V> getAll();
6
7 public V put(K key, V value);
8 public void putAll(Cursor<K,V> cursor);
9
10 public long getSize();
11
12 public DiffCursor<K,V> getDiffCursor(long state);
13}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStore.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStore.java
new file mode 100644
index 00000000..0ff0773f
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStore.java
@@ -0,0 +1,14 @@
1package org.eclipse.viatra.solver.data.map;
2
3import java.util.Set;
4
5public interface VersionedMapStore<K, V> {
6
7 public VersionedMap<K, V> createMap();
8
9 public VersionedMap<K, V> createMap(long state);
10
11 public Set<Long> getStates();
12
13 public DiffCursor<K,V> getDiffCursor(long fromState, long toState);
14} \ No newline at end of file
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreConfiguration.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreConfiguration.java
new file mode 100644
index 00000000..be768e98
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreConfiguration.java
@@ -0,0 +1,48 @@
1package org.eclipse.viatra.solver.data.map;
2
3public class VersionedMapStoreConfiguration {
4
5 public VersionedMapStoreConfiguration() {
6
7 }
8 public VersionedMapStoreConfiguration(boolean immutableWhenCommiting, boolean sharedNodeCacheInStore,
9 boolean sharedNodeCacheInStoreGroups) {
10 super();
11 this.immutableWhenCommiting = immutableWhenCommiting;
12 this.sharedNodeCacheInStore = sharedNodeCacheInStore;
13 this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups;
14 }
15
16 /**
17 * If true root is replaced with immutable node when committed. Frees up memory
18 * by releasing immutable nodes, but it may decrease performance by recreating
19 * immutable nodes upon changes (some evidence).
20 */
21 private boolean immutableWhenCommiting = true;
22 public boolean isImmutableWhenCommiting() {
23 return immutableWhenCommiting;
24 }
25
26 /**
27 * If true, all subnodes are cached within a {@link VersionedMapStore}. It
28 * decreases the memory requirements. It may increase performance by discovering
29 * existing immutable copy of a node (some evidence). Additional overhead may
30 * decrease performance (no example found). The option permits the efficient
31 * implementation of version deletion.
32 */
33 private boolean sharedNodeCacheInStore = true;
34 public boolean isSharedNodeCacheInStore() {
35 return sharedNodeCacheInStore;
36 }
37
38 /**
39 * If true, all subnodes are cached within a group of
40 * {@link VersionedMapStoreImpl#createSharedVersionedMapStores(int, ContinousHashProvider, Object, VersionedMapStoreConfiguration)}.
41 * If {@link VersionedMapStoreConfiguration#sharedNodeCacheInStore} is
42 * <code>false</code>, then it has currently no impact.
43 */
44 private boolean sharedNodeCacheInStoreGroups = true;
45 public boolean isSharedNodeCacheInStoreGroups() {
46 return sharedNodeCacheInStoreGroups;
47 }
48}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreImpl.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreImpl.java
new file mode 100644
index 00000000..83d0e8cd
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/VersionedMapStoreImpl.java
@@ -0,0 +1,135 @@
1package org.eclipse.viatra.solver.data.map;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11
12import org.eclipse.viatra.solver.data.map.internal.ImmutableNode;
13import org.eclipse.viatra.solver.data.map.internal.MapDiffCursor;
14import org.eclipse.viatra.solver.data.map.internal.Node;
15import org.eclipse.viatra.solver.data.map.internal.VersionedMapImpl;
16
17public class VersionedMapStoreImpl<K, V> implements VersionedMapStore<K, V> {
18 // Configuration
19 private final boolean immutableWhenCommiting;
20
21 // Static data
22 protected final ContinousHashProvider<K> hashProvider;
23 protected final V defaultValue;
24
25 // Dynamic data
26 protected final Map<Long, ImmutableNode<K, V>> states = new HashMap<>();
27 protected final Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
28 protected long nextID = 0;
29
30 public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
31 VersionedMapStoreConfiguration config) {
32 this.immutableWhenCommiting = config.isImmutableWhenCommiting();
33 this.hashProvider = hashProvider;
34 this.defaultValue = defaultValue;
35 if (config.isSharedNodeCacheInStore()) {
36 nodeCache = new HashMap<>();
37 } else {
38 nodeCache = null;
39 }
40 }
41
42 private VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
43 Map<Node<K, V>, ImmutableNode<K, V>> nodeCache, VersionedMapStoreConfiguration config) {
44 this.immutableWhenCommiting = config.isImmutableWhenCommiting();
45 this.hashProvider = hashProvider;
46 this.defaultValue = defaultValue;
47 this.nodeCache = nodeCache;
48 }
49
50 public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue) {
51 this(hashProvider, defaultValue, new VersionedMapStoreConfiguration());
52 }
53
54 public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
55 ContinousHashProvider<K> hashProvider, V defaultValue,
56 VersionedMapStoreConfiguration config) {
57 List<VersionedMapStore<K, V>> result = new ArrayList<>(amount);
58 if (config.isSharedNodeCacheInStoreGroups()) {
59 Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
60 if (config.isSharedNodeCacheInStore()) {
61 nodeCache = new HashMap<>();
62 } else {
63 nodeCache = null;
64 }
65 for (int i = 0; i < amount; i++) {
66 result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, nodeCache, config));
67 }
68 } else {
69 for (int i = 0; i < amount; i++) {
70 result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, config));
71 }
72 }
73 return result;
74 }
75
76 public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
77 ContinousHashProvider<K> hashProvider, V defaultValue) {
78 return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreConfiguration());
79 }
80
81 @Override
82 public synchronized Set<Long> getStates() {
83 return new HashSet<>(states.keySet());
84 }
85
86 @Override
87 public VersionedMap<K, V> createMap() {
88 return new VersionedMapImpl<>(this, hashProvider, defaultValue);
89 }
90
91 @Override
92 public VersionedMap<K, V> createMap(long state) {
93 ImmutableNode<K, V> data = revert(state);
94 return new VersionedMapImpl<>(this, hashProvider, defaultValue, data);
95 }
96
97
98 public synchronized ImmutableNode<K, V> revert(long state) {
99 if (states.containsKey(state)) {
100 return states.get(state);
101 } else {
102 ArrayList<Long> existingKeys = new ArrayList<>(states.keySet());
103 Collections.sort(existingKeys);
104 throw new IllegalArgumentException("Store does not contain state " + state + "! Avaliable states: "
105 + Arrays.toString(existingKeys.toArray()));
106 }
107 }
108
109 public synchronized long commit(Node<K, V> data, VersionedMapImpl<K, V> mapToUpdateRoot) {
110 ImmutableNode<K, V> immutable;
111 if (data != null) {
112 immutable = data.toImmutable(this.nodeCache);
113 } else {
114 immutable = null;
115 }
116
117 if (nextID == Long.MAX_VALUE)
118 throw new IllegalStateException("Map store run out of Id-s");
119 long id = nextID++;
120 this.states.put(id, immutable);
121 if (this.immutableWhenCommiting) {
122 mapToUpdateRoot.setRoot(immutable);
123 }
124 return id;
125 }
126
127 @Override
128 public DiffCursor<K, V> getDiffCursor(long fromState, long toState) {
129 VersionedMap<K, V> map1 = createMap(fromState);
130 VersionedMap<K, V> map2 = createMap(toState);
131 Cursor<K, V> cursor1 = map1.getAll();
132 Cursor<K, V> cursor2 = map2.getAll();
133 return new MapDiffCursor<>(this.hashProvider, this.defaultValue, cursor1, cursor2);
134 }
135}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java
new file mode 100644
index 00000000..04a9b19a
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java
@@ -0,0 +1,379 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.Arrays;
4import java.util.Map;
5
6import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
7
8public class ImmutableNode<K, V> extends Node<K, V> {
9 /**
10 * Bitmap defining the stored key and values.
11 */
12 final int dataMap;
13 /**
14 * Bitmap defining the positions of further nodes.
15 */
16 final int nodeMap;
17 /**
18 * Stores Keys, Values, and subnodes. Structure: (K,V)*,NODE; NODES are stored backwards.
19 */
20 final Object[] content;
21
22 /**
23 * Hash code derived from immutable hash code
24 */
25 final int precalculatedHash;
26
27 private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) {
28 super();
29 this.dataMap = dataMap;
30 this.nodeMap = nodeMap;
31 this.content = content;
32 this.precalculatedHash = precalculatedHash;
33 }
34
35 /**
36 * Constructor that copies a mutable node to an immutable.
37 *
38 * @param node A mutable node.
39 * @param cache A cache of existing immutable nodes. It can be used to search
40 * and place reference immutable nodes. It can be null, if no cache
41 * available.
42 * @return an immutable version of the input node.
43 */
44 @SuppressWarnings("unchecked")
45 static <K,V> ImmutableNode<K,V> constructImmutable(MutableNode<K,V> node, Map<Node<K, V>, ImmutableNode<K, V>> cache) {
46 // 1. try to return from cache
47 if(cache != null) {
48 ImmutableNode<K, V> cachedResult = cache.get(node);
49 if(cachedResult != null) {
50 // 1.1 Already cached, return from cache.
51 return cachedResult;
52 }
53 }
54
55 // 2. otherwise construct a new ImmutableNode
56 int size = 0;
57 for(int i = 0; i<node.content.length; i++) {
58 if(node.content[i]!=null) {
59 size++;
60 }
61 }
62
63 int datas = 0;
64 int nodes = 0;
65 int resultDataMap = 0;
66 int resultNodeMap = 0;
67 final Object[] resultContent = new Object[size];
68 int bitposition = 1;
69 for(int i = 0; i<FACTOR; i++) {
70 Object key = node.content[i*2];
71 if(key != null) {
72 resultDataMap |= bitposition;
73 resultContent[datas*2] = key;
74 resultContent[datas*2+1] = node.content[i*2+1];
75 datas++;
76 } else {
77 Node<K,V> subnode = (Node<K, V>) node.content[i*2+1];
78 if(subnode != null) {
79 ImmutableNode<K, V> immutableSubnode = subnode.toImmutable(cache);
80 resultNodeMap |=bitposition;
81 resultContent[size-1-nodes] = immutableSubnode;
82 nodes++;
83 }
84 }
85 bitposition<<=1;
86 }
87 final int resultHash = node.hashCode();
88 ImmutableNode<K,V> newImmutable = new ImmutableNode<>(resultDataMap, resultNodeMap, resultContent, resultHash);
89
90 // 3. save new immutable.
91 if(cache != null) {
92 cache.put(newImmutable, newImmutable);
93 }
94 return newImmutable;
95 }
96
97 private int index(int bitmap, int bitpos) {
98 return Integer.bitCount(bitmap & (bitpos-1));
99 }
100
101 @SuppressWarnings("unchecked")
102 @Override
103 public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) {
104 int selectedHashFragment = hashFragment(hash,shiftDepth(depth));
105 int bitposition = 1 << selectedHashFragment;
106 // If the key is stored as a data
107 if((dataMap & bitposition) != 0) {
108 int keyIndex = 2*index(dataMap, bitposition);
109 K keyCandidate = (K) content[keyIndex];
110 if(keyCandidate.equals(key)) {
111 return (V) content[keyIndex+1];
112 } else {
113 return defaultValue;
114 }
115 }
116 // the key is stored as a node
117 else if((nodeMap & bitposition) != 0) {
118 int keyIndex = content.length-1-index(nodeMap, bitposition);
119 ImmutableNode<K,V> subNode = (ImmutableNode<K,V>) content[keyIndex];
120 int newDepth = depth+1;
121 int newHash = newHash(hashProvider, key, hash, newDepth);
122 return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth);
123 }
124 // the key is not stored at all
125 else {
126 return defaultValue;
127 }
128 }
129
130 @SuppressWarnings("unchecked")
131 @Override
132 public Node<K,V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) {
133 int selectedHashFragment = hashFragment(hash,shiftDepth(depth));
134 int bitposition = 1 << selectedHashFragment;
135 if((dataMap & bitposition) != 0) {
136 int keyIndex = 2*index(dataMap, bitposition);
137 K keyCandidate = (K) content[keyIndex];
138 if(keyCandidate.equals(key)) {
139 if(value == defaultValue) {
140 // delete
141 MutableNode<K, V> mutable = this.toMutable();
142 return mutable.removeEntry(selectedHashFragment,oldValue);
143 } else if(value == content[keyIndex+1]) {
144 // dont change
145 oldValue.setOldValue(value);
146 return this;
147 } else {
148 // update existing value
149 MutableNode<K, V> mutable = this.toMutable();
150 return mutable.updateValue(value, oldValue, selectedHashFragment);
151 }
152 } else {
153 if(value == defaultValue) {
154 // dont change
155 oldValue.setOldValue(defaultValue);
156 return this;
157 } else {
158 // add new key + value
159 MutableNode<K, V> mutable = this.toMutable();
160 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
161 }
162 }
163 } else if((nodeMap & bitposition)!=0) {
164
165 int keyIndex = content.length-1-index(nodeMap, bitposition);
166 ImmutableNode<K,V> subNode = (ImmutableNode<K,V>) content[keyIndex];
167 int newDepth = depth+1;
168 int newHash = newHash(hashProvider, key, hash, newDepth);
169 Node<K,V> newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth);
170
171 if(subNode == newsubNode) {
172 // nothing changed
173 return this;
174 } else {
175 MutableNode<K, V> mutable = toMutable();
176 return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue));
177 }
178 } else {
179 // add new key + value
180 MutableNode<K, V> mutable = this.toMutable();
181 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
182 }
183 }
184
185
186 @SuppressWarnings("unchecked")
187 @Override
188 public long getSize() {
189 int result = Integer.bitCount(this.dataMap);
190 for(int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) {
191 ImmutableNode<K,V> subnode =
192 (ImmutableNode<K, V>) this.content[this.content.length-1-subnodeIndex];
193 result += subnode.getSize();
194 }
195 return result;
196 }
197
198 @Override
199 protected MutableNode<K,V> toMutable() {
200 return new MutableNode<>(this);
201 }
202
203 @Override
204 public ImmutableNode<K, V> toImmutable(
205 Map<Node<K, V>, ImmutableNode<K, V>> cache) {
206 return this;
207 }
208
209 @Override
210 protected MutableNode<K, V> isMutable() {
211 return null;
212 }
213
214 @SuppressWarnings("unchecked")
215 @Override
216 boolean moveToNext(MapCursor<K, V> cursor) {
217 // 1. try to move to data
218 int datas = Integer.bitCount(this.dataMap);
219 if(cursor.dataIndex != MapCursor.INDEX_FINISH) {
220 int newDataIndex = cursor.dataIndex + 1;
221 if(newDataIndex < datas) {
222 cursor.dataIndex = newDataIndex;
223 cursor.key = (K) this.content[newDataIndex*2];
224 cursor.value = (V) this.content[newDataIndex*2+1];
225 return true;
226 } else {
227 cursor.dataIndex = MapCursor.INDEX_FINISH;
228 }
229 }
230
231 // 2. look inside the subnodes
232 int nodes = Integer.bitCount(this.nodeMap);
233 int newNodeIndex = cursor.nodeIndexStack.peek() + 1;
234 if(newNodeIndex < nodes) {
235 // 2.1 found next subnode, move down to the subnode
236 Node<K, V> subnode = (Node<K, V>) this.content[this.content.length-1-newNodeIndex];
237 cursor.dataIndex = MapCursor.INDEX_START;
238 cursor.nodeIndexStack.pop();
239 cursor.nodeIndexStack.push(newNodeIndex);
240 cursor.nodeIndexStack.push(MapCursor.INDEX_START);
241 cursor.nodeStack.push(subnode);
242 return subnode.moveToNext(cursor);
243 } else {
244 // 3. no subnode found, move up
245 cursor.nodeStack.pop();
246 cursor.nodeIndexStack.pop();
247 if(!cursor.nodeStack.isEmpty()) {
248 Node<K, V> supernode = cursor.nodeStack.peek();
249 return supernode.moveToNext(cursor);
250 } else {
251 cursor.key = null;
252 cursor.value = null;
253 return false;
254 }
255 }
256 }
257
258 @Override
259 public void prettyPrint(StringBuilder builder, int depth, int code) {
260 for(int i = 0; i<depth; i++) {
261 builder.append("\t");
262 }
263 if(code>=0) {
264 builder.append(code);
265 builder.append(":");
266 }
267 builder.append("Immutable(");
268 boolean hadContent = false;
269 int dataMask = 1;
270 for(int i = 0; i<FACTOR; i++) {
271 if((dataMask & dataMap) != 0) {
272 if(hadContent) {
273 builder.append(",");
274 }
275 builder.append(i);
276 builder.append(":[");
277 builder.append(content[2*index(dataMap, dataMask)].toString());
278 builder.append("]->[");
279 builder.append(content[2*index(dataMap, dataMask)+1].toString());
280 builder.append("]");
281 hadContent = true;
282 }
283 dataMask<<=1;
284 }
285 builder.append(")");
286 int nodeMask = 1;
287 for(int i = 0; i<FACTOR; i++) {
288 if((nodeMask & nodeMap)!=0) {
289 @SuppressWarnings("unchecked")
290 Node<K,V> subNode = (Node<K, V>) content[content.length-1-index(nodeMap, nodeMask)];
291 builder.append("\n");
292 subNode.prettyPrint(builder, depth+1, i);
293 }
294 nodeMask<<=1;
295 }
296 }
297
298 @SuppressWarnings("unchecked")
299 @Override
300 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {
301 if(depth>0) {
302 boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0;
303 if(orphaned) {
304 throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]);
305 }
306 }
307 // check the place of data
308
309 // check subnodes
310 for(int i = 0; i<Integer.bitCount(nodeMap); i++) {
311 Node<K,V> subnode = (Node<K, V>) this.content[this.content.length-1-i];
312 if(! (subnode instanceof ImmutableNode<?,?>)) {
313 throw new IllegalStateException("Immutable node contains mutable subnodes!");
314 } else {
315 subnode.checkIntegrity(hashProvider, defaultValue, depth+1);
316 }
317 }
318 }
319
320 @Override
321 public int hashCode() {
322 return this.precalculatedHash;
323 }
324
325 @Override
326 public boolean equals(Object obj) {
327 if (this == obj)
328 return true;
329 if (obj == null)
330 return false;
331 if (obj instanceof ImmutableNode<?,?>) {
332 ImmutableNode<?,?> other = (ImmutableNode<?,?>) obj;
333 if (precalculatedHash != other.precalculatedHash || dataMap != other.dataMap || nodeMap != other.nodeMap || !Arrays.deepEquals(content, other.content))
334 return false;
335 else return true;
336 } else if(obj instanceof MutableNode<?,?>) {
337 return ImmutableNode.compareImmutableMutable(this, (MutableNode<?, ?>) obj);
338 } else {
339 return false;
340 }
341 }
342 public static boolean compareImmutableMutable(
343 ImmutableNode<?, ?> immutable,
344 MutableNode<?, ?> mutable)
345 {
346 int datas = 0;
347 int nodes = 0;
348 final int immutableLength = immutable.content.length;
349 for(int i = 0; i<FACTOR; i++) {
350 Object key = mutable.content[i*2];
351 // For each key candidate
352 if(key != null) {
353 // Check whether a new Key-Value pair can fit into the immutable container
354 if(datas*2+nodes+2 <= immutableLength) {
355 if( !immutable.content[datas*2].equals(key) ||
356 !immutable.content[datas*2+1].equals(mutable.content[i*2+1]))
357 {
358 return false;
359 }
360 } else return false;
361 datas++;
362 } else {
363 Node<?,?> mutableSubnode = (Node<?, ?>) mutable.content[i*2+1];
364 if(mutableSubnode != null) {
365 if(datas*2+nodes+1 <= immutableLength) {
366 Object immutableSubnode = immutable.content[immutableLength-1-nodes];
367 if(!mutableSubnode.equals(immutableSubnode)) {
368 return false;
369 }
370 nodes++;
371 } else {
372 return false;
373 }
374 }
375 }
376 }
377 return true;
378 }
379}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapCursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapCursor.java
new file mode 100644
index 00000000..cc5a3982
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapCursor.java
@@ -0,0 +1,131 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.ArrayDeque;
4import java.util.ConcurrentModificationException;
5import java.util.Iterator;
6import java.util.List;
7
8import org.eclipse.viatra.solver.data.map.Cursor;
9import org.eclipse.viatra.solver.data.map.VersionedMap;
10
11public class MapCursor<K,V> implements Cursor<K,V> {
12 // Constants
13 static final int INDEX_START = -1;
14 static final int INDEX_FINISH = -2;
15
16 // Tree stack
17 ArrayDeque<Node<K,V>> nodeStack;
18 ArrayDeque<Integer> nodeIndexStack;
19 int dataIndex;
20
21 // Values
22 K key;
23 V value;
24
25 // Hash code for checking concurrent modifications
26 final VersionedMap<K,V> map;
27 final int creationHash;
28
29 public MapCursor(Node<K, V> root, VersionedMap<K,V> map) {
30 // Initializing tree stack
31 super();
32 this.nodeStack = new ArrayDeque<>();
33 this.nodeIndexStack = new ArrayDeque<>();
34 if(root != null) {
35 this.nodeStack.add(root);
36 this.nodeIndexStack.push(INDEX_START);
37 }
38
39 this.dataIndex = INDEX_START;
40
41 // Initializing cache
42 this.key = null;
43 this.value = null;
44
45 // Initializing state
46 this.map=map;
47 this.creationHash = map.hashCode();
48 }
49
50 public K getKey() {
51 return key;
52 }
53
54 public V getValue() {
55 return value;
56 }
57
58 public boolean isTerminated() {
59 return this.nodeStack.isEmpty();
60 }
61
62 public boolean move() {
63 if(isDirty()) {
64 throw new ConcurrentModificationException();
65 }
66 if(!isTerminated()) {
67 boolean result = this.nodeStack.peek().moveToNext(this);
68 if(this.nodeIndexStack.size() != this.nodeStack.size()) {
69 throw new IllegalArgumentException("Node stack is corrupted by illegal moves!");
70 }
71 return result;
72 }
73 return false;
74 }
75 public boolean skipCurrentNode() {
76 nodeStack.pop();
77 nodeIndexStack.pop();
78 dataIndex = INDEX_FINISH;
79 return move();
80 }
81 @Override
82 public boolean isDirty() {
83 return this.map.hashCode() != this.creationHash;
84 }
85 @Override
86 public List<VersionedMap<?, ?>> getDependingMaps() {
87 return List.of(this.map);
88 }
89
90 public static <K,V> boolean sameSubnode(MapCursor<K,V> cursor1, MapCursor<K,V> cursor2) {
91 Node<K, V> nodeOfCursor1 = cursor1.nodeStack.peek();
92 Node<K, V> nodeOfCursor2 = cursor2.nodeStack.peek();
93 if(nodeOfCursor1 != null && nodeOfCursor2 != null) {
94 return nodeOfCursor1.equals(nodeOfCursor2);
95 } else {
96 return false;
97 }
98 }
99
100 /**
101 *
102 * @param <K>
103 * @param <V>
104 * @param cursor1
105 * @param cursor2
106 * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the same position.
107 */
108 public static <K,V> int compare(MapCursor<K,V> cursor1, MapCursor<K,V> cursor2) {
109 // two cursors are equally deep
110 Iterator<Integer> stack1 = cursor1.nodeIndexStack.descendingIterator();
111 Iterator<Integer> stack2 = cursor2.nodeIndexStack.descendingIterator();
112 if(stack1.hasNext()) {
113 if(!stack2.hasNext()) {
114 // stack 2 has no more element, thus stack 1 is deeper
115 return 1;
116 }
117 int val1 = stack1.next();
118 int val2 = stack2.next();
119 if(val1 < val2) {
120 return -1;
121 } else if(val2 < val1) {
122 return 1;
123 }
124 }
125 if(stack2.hasNext()) {
126 // stack 2 has more element, thus stack 2 is deeper
127 return 1;
128 }
129 return Integer.compare(cursor1.dataIndex, cursor2.dataIndex);
130 }
131}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapDiffCursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapDiffCursor.java
new file mode 100644
index 00000000..e97e4aa1
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MapDiffCursor.java
@@ -0,0 +1,214 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.List;
4import java.util.stream.Collectors;
5import java.util.stream.Stream;
6
7import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
8import org.eclipse.viatra.solver.data.map.Cursor;
9import org.eclipse.viatra.solver.data.map.DiffCursor;
10import org.eclipse.viatra.solver.data.map.VersionedMap;
11
12/**
13 * A cursor representing the difference between two states of a map.
14 * @author Oszkar Semerath
15 *
16 */
17public class MapDiffCursor<K,V> implements DiffCursor<K, V>, Cursor<K,V>{
18 /**
19 * Default value representing missing elements.
20 */
21 private V defaultValue;
22 private MapCursor<K,V> cursor1;
23 private MapCursor<K,V> cursor2;
24 private ContinousHashProvider<? super K> hashProvider;
25
26 // Values
27 private K key;
28 private V fromValue;
29 private V toValue;
30
31 // State
32 /**
33 * Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the same position.
34 */
35 private int cursorRelation;
36 /**
37 * Denotes a state when two cursors are in the same position, but they contain different keys.
38 * Possible values:
39 * <ul>
40 * <li>0: not stuck</li>
41 * <li>1: hashClash, next it should return the key of cursor 1.</li>
42 * <li>2: hashClash, next it should return the key of cursor 2.</li>
43 * </ul>
44 */
45 private int hashClash=0;
46
47 public MapDiffCursor(ContinousHashProvider<? super K> hashProvider, V defaultValue, Cursor<K, V> cursor1, Cursor<K, V> cursor2) {
48 super();
49 this.hashProvider = hashProvider;
50 this.defaultValue = defaultValue;
51 this.cursor1 = (MapCursor<K, V>) cursor1;
52 this.cursor2 = (MapCursor<K, V>) cursor2;
53 }
54 @Override
55 public K getKey() {
56 return key;
57 }
58 @Override
59 public V getFromValue() {
60 return fromValue;
61 }
62 @Override
63 public V getToValue() {
64 return toValue;
65 }
66 @Override
67 public V getValue() {
68 return getToValue();
69 }
70 public boolean isTerminated() {
71 return cursor1.isTerminated() && cursor2.isTerminated();
72 }
73 @Override
74 public boolean isDirty() {
75 return this.cursor1.isDirty() || this.cursor2.isDirty();
76 }
77 @Override
78 public List<VersionedMap<?, ?>> getDependingMaps() {
79 return Stream.concat(
80 cursor1.getDependingMaps().stream(),
81 cursor2.getDependingMaps().stream()
82 ).collect(Collectors.toList());
83 }
84
85 protected void updateState() {
86 if(!isTerminated()) {
87 this.cursorRelation = MapCursor.compare(cursor1, cursor2);
88 if(cursorRelation > 0 || cursor2.isTerminated()) {
89 this.key = cursor1.getKey();
90 this.fromValue = cursor1.getValue();
91 this.toValue = defaultValue;
92 } else if(cursorRelation < 0 || cursor1.isTerminated()){
93 this.key = cursor2.getKey();
94 this.fromValue = defaultValue;
95 this.toValue = cursor1.getValue();
96 } else {
97 // cursor1 = cursor2
98 if(cursor1.getKey().equals(cursor2.getKey())) {
99 this.key = cursor1.getKey();
100 this.fromValue = cursor1.getValue();
101 this.toValue = defaultValue;
102 } else {
103 resolveHashClash1();
104 }
105 }
106 }
107 }
108 protected void resolveHashClash1() {
109 int compareResult = this.hashProvider.compare(cursor1.key, cursor2.key);
110 if(compareResult<0) {
111 this.hashClash = 2;
112 this.cursorRelation = 0;
113 this.key = cursor1.key;
114 this.fromValue = cursor1.value;
115 this.toValue = defaultValue;
116 } else if(compareResult>0) {
117 this.hashClash = 1;
118 this.cursorRelation = 0;
119 this.key = cursor2.key;
120 this.fromValue = defaultValue;
121 this.toValue = cursor2.value;
122 } else {
123 throw new IllegalArgumentException("Inconsistent compare result for diffcursor");
124 }
125 }
126 protected boolean isInHashClash() {
127 return this.hashClash != 0;
128 }
129 protected void resolveHashClash2() {
130 if(hashClash == 1) {
131 this.hashClash = 0;
132 this.cursorRelation = 0;
133 this.key = cursor1.key;
134 this.fromValue = cursor1.value;
135 this.toValue = defaultValue;
136 } else if(hashClash == 2) {
137 this.hashClash = 0;
138 this.cursorRelation = 0;
139 this.key = cursor2.key;
140 this.fromValue = defaultValue;
141 this.toValue = cursor2.value;
142 } else throw new IllegalArgumentException("Inconsistent compare result for diffcursor");
143 }
144
145
146 protected boolean sameValues() {
147 if(this.fromValue == null) {
148 return this.toValue == null;
149 } else {
150 return this.fromValue.equals(this.toValue);
151 }
152 }
153 protected boolean moveOne() {
154 if(isTerminated()) {
155 return false;
156 }
157 if(this.cursorRelation > 0 || cursor2.isTerminated()) {
158 return cursor1.move();
159 } else if(this.cursorRelation < 0 || cursor1.isTerminated()) {
160 return cursor2.move();
161 } else {
162 boolean moved1 = cursor1.move();
163 boolean moved2 = cursor2.move();
164 return moved1 && moved2;
165 }
166 }
167 private boolean skipNode() {
168 if(isTerminated()) {
169 throw new IllegalStateException("DiffCursor tries to skip when terminated!");
170 }
171 boolean update1 = cursor1.skipCurrentNode();
172 boolean update2 = cursor2.skipCurrentNode();
173 updateState();
174 return update1 && update2;
175 }
176
177 protected boolean moveToConsistentState() {
178 if(!isTerminated()) {
179 boolean changed;
180 boolean lastResult = true;
181 do {
182 changed = false;
183 if(MapCursor.sameSubnode(cursor1, cursor2)) {
184 lastResult = skipNode();
185 changed = true;
186 }
187 if(sameValues()) {
188 lastResult = moveOne();
189 changed = true;
190 }
191 updateState();
192 } while(changed && !isTerminated());
193 return lastResult;
194 } else {
195 return false;
196 }
197 }
198
199 public boolean move() {
200 if(!isTerminated()) {
201 if(isInHashClash()) {
202 this.resolveHashClash2();
203 return true;
204 } else {
205 if(moveOne()) {
206 return moveToConsistentState();
207 } else {
208 return false;
209 }
210 }
211
212 } else return false;
213 }
214}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java
new file mode 100644
index 00000000..9e63b941
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java
@@ -0,0 +1,449 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.Arrays;
4import java.util.Map;
5
6import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
7
8public class MutableNode<K, V> extends Node<K, V> {
9 int cachedHash;
10 protected Object[] content;
11
12 protected MutableNode() {
13 this.content = new Object[2 * FACTOR];
14 updateHash();
15 }
16
17 public static <K, V> MutableNode<K, V> initialize(K key, V value, ContinousHashProvider<? super K> hashProvider,
18 V defaultValue) {
19 if (value == defaultValue) {
20 return null;
21 } else {
22 int hash = hashProvider.getHash(key, 0);
23 int fragment = hashFragment(hash, 0);
24 MutableNode<K, V> res = new MutableNode<>();
25 res.content[2 * fragment] = key;
26 res.content[2 * fragment + 1] = value;
27 res.updateHash();
28 return res;
29 }
30 }
31
32 /**
33 * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode}
34 *
35 * @param node
36 */
37 protected MutableNode(ImmutableNode<K, V> node) {
38 this.content = new Object[2 * FACTOR];
39 int dataUsed = 0;
40 int nodeUsed = 0;
41 for (int i = 0; i < FACTOR; i++) {
42 int bitposition = 1 << i;
43 if ((node.dataMap & bitposition) != 0) {
44 content[2 * i] = node.content[dataUsed * 2];
45 content[2 * i + 1] = node.content[dataUsed * 2 + 1];
46 dataUsed++;
47 } else if ((node.nodeMap & bitposition) != 0) {
48 content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed];
49 nodeUsed++;
50 }
51 }
52 this.cachedHash = node.hashCode();
53 }
54
55 @SuppressWarnings("unchecked")
56 @Override
57 public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) {
58 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
59 K keyCandidate = (K) this.content[2 * selectedHashFragment];
60 if (keyCandidate != null) {
61 if (keyCandidate.equals(key)) {
62 return (V) this.content[2 * selectedHashFragment + 1];
63 } else {
64 return defaultValue;
65 }
66 } else {
67 Node<K, V> nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
68 if (nodeCandidate != null) {
69 int newDepth = depth + 1;
70 int newHash = newHash(hashProvider, key, hash, newDepth);
71 return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth);
72 } else {
73 return defaultValue;
74 }
75 }
76 }
77
78 @SuppressWarnings("unchecked")
79 @Override
80 public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash,
81 int depth) {
82 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
83 K keyCandidate = (K) content[2 * selectedHashFragment];
84 if (keyCandidate != null) {
85 // If has key
86 if (keyCandidate.equals(key)) {
87 // The key is equals to an existing key -> update entry
88 if (value == defaultValue) {
89 return removeEntry(selectedHashFragment, oldValue);
90 } else {
91 return updateValue(value, oldValue, selectedHashFragment);
92 }
93 } else {
94 // The key is not equivalent to an existing key on the same hash bin
95 // -> split entry if it is necessary
96 if (value == defaultValue) {
97 // Value is default -> do not need to add new node
98 oldValue.setOldValue(defaultValue);
99 return this;
100 } else {
101 // Value is not default -> Split entry data to a new node
102 oldValue.setOldValue(defaultValue);
103 return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment);
104 }
105 }
106 } else {
107 // If it does not have key, check for value
108 Node<K, V> nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
109 if (nodeCandidate != null) {
110 // If it has value, it is a subnode -> upate that
111 Node<K, V> newNode = nodeCandidate.putValue(key, value, oldValue, hashProvider, defaultValue,
112 newHash(hashProvider, key, hash, depth + 1), depth + 1);
113 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue));
114 } else {
115 // If it does not have value, put it in the empty place
116 if (value == defaultValue) {
117 // dont need to add new key-value pair
118 oldValue.setOldValue(defaultValue);
119 return this;
120 } else {
121 return addEntry(key, value, oldValue, selectedHashFragment);
122 }
123
124 }
125 }
126 }
127
128 @SuppressWarnings("unchecked")
129 private Node<K, V> addEntry(K key, V value, OldValueBox<V> oldValue, int selectedHashFragment) {
130 content[2 * selectedHashFragment] = key;
131 oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]);
132 content[2 * selectedHashFragment + 1] = value;
133 updateHash();
134 return this;
135 }
136
137 /**
138 * Updates an entry in a selected hash-fragment to a non-default value.
139 *
140 * @param value
141 * @param selectedHashFragment
142 * @return
143 */
144 @SuppressWarnings("unchecked")
145 Node<K, V> updateValue(V value, OldValueBox<V> oldValue, int selectedHashFragment) {
146 oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]);
147 content[2 * selectedHashFragment + 1] = value;
148 updateHash();
149 return this;
150 }
151
152 /**
153 *
154 * @param selectedHashFragment
155 * @param newNode
156 * @return
157 */
158 Node<K, V> updateWithSubNode(int selectedHashFragment, Node<K, V> newNode, boolean deletionHappened) {
159 if (deletionHappened) {
160 if (newNode == null) {
161 // Check whether this node become empty
162 content[2 * selectedHashFragment + 1] = null; // i.e. the new node
163 if (hasContent()) {
164 updateHash();
165 return this;
166 } else {
167 return null;
168 }
169 } else {
170 // check whether newNode is orphan
171 MutableNode<K, V> immutableNewNode = newNode.isMutable();
172 if (immutableNewNode != null) {
173 int orphaned = immutableNewNode.isOrphaned();
174 if (orphaned >= 0) {
175 // orphan subnode data is replaced with data
176 content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2];
177 content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1];
178 updateHash();
179 return this;
180 }
181 }
182 }
183 }
184 // normal behaviour
185 content[2 * selectedHashFragment + 1] = newNode;
186 updateHash();
187 return this;
188
189 }
190
191 private boolean hasContent() {
192 for (Object element : this.content) {
193 if (element != null)
194 return true;
195 }
196 return false;
197 }
198
199 @Override
200 protected MutableNode<K, V> isMutable() {
201 return this;
202 }
203
204 protected int isOrphaned() {
205 int dataFound = -2;
206 for (int i = 0; i < FACTOR; i++) {
207 if (content[i * 2] != null) {
208 if (dataFound >= 0) {
209 return -1;
210 } else {
211 dataFound = i;
212 }
213 } else if (content[i * 2 + 1] != null) {
214 return -3;
215 }
216 }
217 return dataFound;
218 }
219
220 @SuppressWarnings("unchecked")
221 private Node<K, V> moveDownAndSplit(ContinousHashProvider<? super K> hashProvider, K newKey, V newValue,
222 K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) {
223 V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1];
224
225 MutableNode<K, V> newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue,
226 hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, depth + 1);
227
228 content[2 * selectedHashFragmentOfCurrentDepth] = null;
229 content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode;
230 updateHash();
231 return this;
232 }
233
234 private MutableNode<K, V> newNodeWithTwoEntries(ContinousHashProvider<? super K> hashProvider, K key1, V value1,
235 int oldHash1, K key2, V value2, int oldHash2, int newdepth) {
236 int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth);
237 int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth);
238 int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth));
239 int newFragment2 = hashFragment(newHash2, shiftDepth(newdepth));
240
241 MutableNode<K, V> subNode = new MutableNode<>();
242 if (newFragment1 != newFragment2) {
243 subNode.content[newFragment1 * 2] = key1;
244 subNode.content[newFragment1 * 2 + 1] = value1;
245
246 subNode.content[newFragment2 * 2] = key2;
247 subNode.content[newFragment2 * 2 + 1] = value2;
248 } else {
249 MutableNode<K, V> subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2,
250 newHash2, newdepth + 1);
251 subNode.content[newFragment1 * 2 + 1] = subSubNode;
252 }
253 subNode.updateHash();
254 return subNode;
255 }
256
257 @SuppressWarnings("unchecked")
258 Node<K, V> removeEntry(int selectedHashFragment, OldValueBox<V> oldValue) {
259 content[2 * selectedHashFragment] = null;
260 oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]);
261 content[2 * selectedHashFragment + 1] = null;
262 if (hasContent()) {
263 updateHash();
264 return this;
265 } else {
266 return null;
267 }
268 }
269
270 @SuppressWarnings("unchecked")
271 @Override
272 public long getSize() {
273 int size = 0;
274 for (int i = 0; i < FACTOR; i++) {
275 if (content[i * 2] != null) {
276 size++;
277 } else {
278 Node<K, V> nodeCandidate = (Node<K, V>) content[i * 2 + 1];
279 if (nodeCandidate != null) {
280 size += nodeCandidate.getSize();
281 }
282 }
283 }
284 return size;
285 }
286
287 @Override
288 protected MutableNode<K, V> toMutable() {
289 return this;
290 }
291
292 @Override
293 public ImmutableNode<K, V> toImmutable(Map<Node<K, V>, ImmutableNode<K, V>> cache) {
294 return ImmutableNode.constructImmutable(this, cache);
295 }
296
297 @SuppressWarnings("unchecked")
298 @Override
299 boolean moveToNext(MapCursor<K, V> cursor) {
300 // 1. try to move to data
301 if (cursor.dataIndex != MapCursor.INDEX_FINISH) {
302 for (int index = cursor.dataIndex + 1; index < FACTOR; index++) {
303 if (this.content[index * 2] != null) {
304 // 1.1 found next data
305 cursor.dataIndex = index;
306 cursor.key = (K) this.content[index * 2];
307 cursor.value = (V) this.content[index * 2 + 1];
308 return true;
309 }
310 }
311 cursor.dataIndex = MapCursor.INDEX_FINISH;
312 }
313
314 // 2. look inside the subnodes
315 for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) {
316 if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) {
317 // 2.1 found next subnode, move down to the subnode
318 Node<K, V> subnode = (Node<K, V>) this.content[index * 2 + 1];
319
320 cursor.dataIndex = MapCursor.INDEX_START;
321 cursor.nodeIndexStack.pop();
322 cursor.nodeIndexStack.push(index);
323 cursor.nodeIndexStack.push(MapCursor.INDEX_START);
324 cursor.nodeStack.push(subnode);
325
326 return subnode.moveToNext(cursor);
327 }
328 }
329 // 3. no subnode found, move up
330 cursor.nodeStack.pop();
331 cursor.nodeIndexStack.pop();
332 if (!cursor.nodeStack.isEmpty()) {
333 Node<K, V> supernode = cursor.nodeStack.peek();
334 return supernode.moveToNext(cursor);
335 } else {
336 cursor.key = null;
337 cursor.value = null;
338 return false;
339 }
340 }
341
342 @Override
343 public void prettyPrint(StringBuilder builder, int depth, int code) {
344 for (int i = 0; i < depth; i++) {
345 builder.append("\t");
346 }
347 if (code >= 0) {
348 builder.append(code);
349 builder.append(":");
350 }
351 builder.append("Mutable(");
352 // print content
353 boolean hadContent = false;
354 for (int i = 0; i < FACTOR; i++) {
355 if (content[2 * i] != null) {
356 if (hadContent) {
357 builder.append(",");
358 }
359 builder.append(i);
360 builder.append(":[");
361 builder.append(content[2 * i].toString());
362 builder.append("]->[");
363 builder.append(content[2 * i + 1].toString());
364 builder.append("]");
365 hadContent = true;
366 }
367 }
368 builder.append(")");
369 // print subnodes
370 for (int i = 0; i < FACTOR; i++) {
371 if (content[2 * i] == null && content[2 * i + 1] != null) {
372 @SuppressWarnings("unchecked")
373 Node<K, V> subNode = (Node<K, V>) content[2 * i + 1];
374 builder.append("\n");
375 subNode.prettyPrint(builder, depth + 1, i);
376 }
377 }
378 }
379
380 @SuppressWarnings({ "unchecked" })
381 @Override
382 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {
383 // check for orphan nodes
384 if (depth > 0) {
385 int orphaned = isOrphaned();
386 if (orphaned >= 0) {
387 throw new IllegalStateException("Orphaned node! " + orphaned + ": " + content[2 * orphaned]);
388 }
389 }
390 // check the place of data
391 for (int i = 0; i < FACTOR; i++) {
392 if (this.content[2 * i] != null) {
393 K key = (K) this.content[2 * i];
394 V value = (V) this.content[2 * i + 1];
395
396 if (value == defaultValue) {
397 throw new IllegalStateException("Node contains default value!");
398 }
399 int hashCode = hashProvider.getHash(key, hashDepth(depth));
400 int shiftDepth = shiftDepth(depth);
401 int selectedHashFragment = hashFragment(hashCode, shiftDepth);
402 if (i != selectedHashFragment) {
403 throw new IllegalStateException("Key " + key + " with hash code " + hashCode
404 + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i);
405 }
406 }
407 }
408 // check subnodes
409 for (int i = 0; i < FACTOR; i++) {
410 if (this.content[2 * i + 1] != null && this.content[2 * i] == null) {
411 Node<K, V> subNode = (Node<K, V>) this.content[2 * i + 1];
412 subNode.checkIntegrity(hashProvider, defaultValue, depth + 1);
413 }
414 }
415 // check the hash
416 int oldHash = this.cachedHash;
417 updateHash();
418 int newHash = this.cachedHash;
419 if (oldHash != newHash) {
420 throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")");
421 }
422 }
423
424 protected void updateHash() {
425 this.cachedHash = Arrays.hashCode(content);
426 }
427
428 @Override
429 public int hashCode() {
430 return this.cachedHash;
431 }
432
433 @Override
434 public boolean equals(Object obj) {
435 if (this == obj)
436 return true;
437 if (obj == null)
438 return false;
439 if (obj instanceof MutableNode<?, ?>) {
440 MutableNode<?, ?> other = (MutableNode<?, ?>) obj;
441 return Arrays.deepEquals(this.content, other.content);
442 } else if (obj instanceof ImmutableNode<?, ?>) {
443 ImmutableNode<?, ?> other = (ImmutableNode<?, ?>) obj;
444 return ImmutableNode.compareImmutableMutable(other, this);
445 } else {
446 return false;
447 }
448 }
449}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/Node.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/Node.java
new file mode 100644
index 00000000..d40f980a
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/Node.java
@@ -0,0 +1,85 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.Map;
4
5import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
6
7public abstract class Node<K,V>{
8 public static final int BRANCHING_FACTOR_BITS = 5;
9 public static final int FACTOR = 1<<BRANCHING_FACTOR_BITS;
10 protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS;
11 protected static final int FACTOR_MASK = FACTOR-1;
12 public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS;
13
14 /**
15 * Calculates the index for the continuous hash.
16 * @param depth The depth of the node in the tree.
17 * @return The index of the continuous hash.
18 */
19 protected static int hashDepth(int depth) {
20 return depth/NUMBER_OF_FACTORS;
21 }
22
23 /**
24 * Calculates the which segment of a single hash should be used.
25 * @param depth The depth of the node in the tree.
26 * @return The segment of a hash code.
27 */
28 protected static int shiftDepth(int depth) {
29 return depth%NUMBER_OF_FACTORS;
30 }
31 /**
32 * Selects a segments from a complete hash for a given depth.
33 * @param hash A complete hash.
34 * @param shiftDepth The index of the segment.
35 * @return The segment as an integer.
36 */
37 protected static int hashFragment(int hash, int shiftDepth) {
38 if(shiftDepth<0 || Node.NUMBER_OF_FACTORS<shiftDepth) throw new IllegalArgumentException("Invalid shift depth! valid intervall=[0;5], input="+shiftDepth);
39 return (hash >>> shiftDepth*BRANCHING_FACTOR_BITS) & FACTOR_MASK;
40 }
41
42 /**
43 * Returns the hash code for a given depth. It may calculate new hash code, or reuse a hash code calculated for depth-1.
44 * @param key The key.
45 * @param hash Hash code for depth-1
46 * @param depth The depth.
47 * @return The new hash code.
48 */
49 protected int newHash(final ContinousHashProvider<? super K> hashProvider, K key, int hash, int depth) {
50 final int hashDepth = hashDepth(depth);
51 if(hashDepth>=ContinousHashProvider.MAX_PRACTICAL_DEPTH) {
52 throw new IllegalArgumentException("Key "+key+" have the clashing hashcode over the practical depth limitation ("+ContinousHashProvider.MAX_PRACTICAL_DEPTH+")!");
53 }
54 return depth%NUMBER_OF_FACTORS == 0?
55 hashProvider.getHash(key, hashDepth) :
56 hash;
57 }
58
59
60 public abstract V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
61 public abstract Node<K,V> putValue(K key, V value, OldValueBox<V> old, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
62 public abstract long getSize();
63
64 abstract MutableNode<K, V> toMutable();
65 public abstract ImmutableNode<K, V> toImmutable(
66 Map<Node<K, V>,ImmutableNode<K, V>> cache);
67 protected abstract MutableNode<K, V> isMutable();
68 /**
69 * Moves a {@link MapCursor} to its next position.
70 * @param cursor the cursor
71 * @return Whether there was a next value to move on.
72 */
73 abstract boolean moveToNext(MapCursor<K,V> cursor);
74
75 ///////// FOR printing
76 public abstract void prettyPrint(StringBuilder builder, int depth, int code);
77 @Override
78 public String toString() {
79 StringBuilder stringBuilder = new StringBuilder();
80 prettyPrint(stringBuilder, 0, -1);
81 return stringBuilder.toString();
82 }
83 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {}
84
85}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/OldValueBox.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/OldValueBox.java
new file mode 100644
index 00000000..23502857
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/OldValueBox.java
@@ -0,0 +1,19 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3public class OldValueBox<V>{
4 V oldValue;
5 boolean isSet = false;
6
7 public V getOldValue() {
8 if(!isSet) throw new IllegalStateException();
9 isSet = false;
10 return oldValue;
11 }
12
13 public void setOldValue(V ouldValue) {
14 if(isSet) throw new IllegalStateException();
15 this.oldValue = ouldValue;
16 isSet = true;
17 }
18
19}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/VersionedMapImpl.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/VersionedMapImpl.java
new file mode 100644
index 00000000..de41e602
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/VersionedMapImpl.java
@@ -0,0 +1,171 @@
1package org.eclipse.viatra.solver.data.map.internal;
2
3import java.util.Iterator;
4import java.util.LinkedList;
5import java.util.List;
6
7import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
8import org.eclipse.viatra.solver.data.map.Cursor;
9import org.eclipse.viatra.solver.data.map.DiffCursor;
10import org.eclipse.viatra.solver.data.map.VersionedMap;
11import org.eclipse.viatra.solver.data.map.VersionedMapStoreImpl;
12
13/**
14 * Not threadSafe in itself
15 * @author Oszkar Semerath
16 *
17 * @param <K>
18 * @param <V>
19 */
20public class VersionedMapImpl<K,V> implements VersionedMap<K,V>{
21 protected final VersionedMapStoreImpl<K,V> store;
22
23 protected final ContinousHashProvider<K> hashProvider;
24 protected final V defaultValue;
25 protected Node<K,V> root;
26
27 private OldValueBox<V> oldValueBox = new OldValueBox<>();
28
29 public VersionedMapImpl(
30 VersionedMapStoreImpl<K,V> store,
31 ContinousHashProvider<K> hashProvider,
32 V defaultValue)
33 {
34 this.store = store;
35 this.hashProvider = hashProvider;
36 this.defaultValue = defaultValue;
37 this.root = null;
38 }
39 public VersionedMapImpl(
40 VersionedMapStoreImpl<K,V> store,
41 ContinousHashProvider<K> hashProvider,
42 V defaultValue, Node<K,V> data)
43 {
44 this.store = store;
45 this.hashProvider = hashProvider;
46 this.defaultValue = defaultValue;
47 this.root = data;
48 }
49
50 public V getDefaultValue() {
51 return defaultValue;
52 }
53 public ContinousHashProvider<K> getHashProvider() {
54 return hashProvider;
55 }
56 @Override
57 public V put(K key, V value) {
58 if(root!=null) {
59 root = root.putValue(key, value, oldValueBox, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0);
60 return oldValueBox.getOldValue();
61 } else {
62 root = MutableNode.initialize(key, value, hashProvider, defaultValue);
63 return defaultValue;
64 }
65 }
66
67 @Override
68 public void putAll(Cursor<K, V> cursor) {
69 if(cursor.getDependingMaps().contains(this)) {
70 List<K> keys = new LinkedList<>();
71 List<V> values = new LinkedList<>();
72 while(cursor.move()) {
73 keys.add(cursor.getKey());
74 values.add(cursor.getValue());
75 }
76 Iterator<K> keyIterator = keys.iterator();
77 Iterator<V> valueIterator = values.iterator();
78 while(keyIterator.hasNext()) {
79 this.put(keyIterator.next(), valueIterator.next());
80 }
81 } else {
82 while(cursor.move()) {
83 this.put(cursor.getKey(), cursor.getValue());
84 }
85 }
86 }
87
88 @Override
89 public V get(K key) {
90 if(root!=null) {
91 return root.getValue(key, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0);
92 } else {
93 return defaultValue;
94 }
95 }
96 @Override
97 public long getSize() {
98 if(root == null) {
99 return 0;
100 } else {
101 return root.getSize();
102 }
103 }
104
105 @Override
106 public Cursor<K, V> getAll() {
107 return new MapCursor<>(this.root,this);
108 }
109 @Override
110 public DiffCursor<K, V> getDiffCursor(long toVersion) {
111 Cursor<K, V> fromCursor = this.getAll();
112 VersionedMap<K, V> toMap = this.store.createMap(toVersion);
113 Cursor<K, V> toCursor = toMap.getAll();
114 return new MapDiffCursor<>(this.hashProvider,this.defaultValue, fromCursor, toCursor);
115
116 }
117
118
119 @Override
120 public long commit() {
121 return this.store.commit(root,this);
122 }
123 public void setRoot(Node<K, V> root) {
124 this.root = root;
125 }
126
127 @Override
128 public void restore(long state) {
129 root = this.store.revert(state);
130 }
131
132 @Override
133 public int hashCode() {
134 final int prime = 31;
135 int result = 1;
136 result = prime * result + ((root == null) ? 0 : root.hashCode());
137 return result;
138 }
139
140 @Override
141 public boolean equals(Object obj) {
142 if (this == obj)
143 return true;
144 if (obj == null)
145 return false;
146 if (getClass() != obj.getClass())
147 return false;
148 VersionedMapImpl<?,?> other = (VersionedMapImpl<?,?>) obj;
149 if (root == null) {
150 if (other.root != null)
151 return false;
152 } else if (!root.equals(other.root))
153 return false;
154 return true;
155 }
156 public void prettyPrint() {
157 StringBuilder s = new StringBuilder();
158 if(this.root != null) {
159 this.root.prettyPrint(s, 0, -1);
160 System.out.println(s.toString());
161 } else {
162 System.out.println("empty tree");
163 }
164 }
165 public void checkIntegrity() {
166 if(this.root != null) {
167 this.root.checkIntegrity(hashProvider, defaultValue, 0);
168 }
169 }
170
171}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/Model.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/Model.java
new file mode 100644
index 00000000..2b21e3e7
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/Model.java
@@ -0,0 +1,20 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.Set;
4
5import org.eclipse.viatra.solver.data.map.Cursor;
6import org.eclipse.viatra.solver.data.map.Versioned;
7import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
8
9public interface Model extends Versioned{
10 @SuppressWarnings("squid:S1452")
11 Set<DataRepresentation<?, ?>> getDataRepresentations();
12
13 <K,V> V get(DataRepresentation<K,V> representation, K key);
14 <K,V> Cursor<K,V> getAll(DataRepresentation<K,V> representation);
15 <K,V> V put(DataRepresentation<K,V> representation, K key, V value);
16 <K,V> void putAll(DataRepresentation<K,V> representation, Cursor<K,V> cursor);
17 <K,V> long getSize(DataRepresentation<K,V> representation);
18
19 ModelDiffCursor getDiffCursor(long to);
20}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelCursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelCursor.java
new file mode 100644
index 00000000..3157c9f0
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelCursor.java
@@ -0,0 +1,25 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.Map;
4
5import org.eclipse.viatra.solver.data.map.Cursor;
6import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
7
8public class ModelCursor {
9 final Map<DataRepresentation<?, ?>,Cursor<?,?>> cursors;
10
11 public ModelCursor(Map<DataRepresentation<?, ?>, Cursor<?, ?>> cursors) {
12 super();
13 this.cursors = cursors;
14 }
15
16 @SuppressWarnings("unchecked")
17 public <K,V> Cursor<K,V> getCursor(DataRepresentation<K, V> representation) {
18 Cursor<?, ?> cursor = cursors.get(representation);
19 if(cursor != null) {
20 return (Cursor<K, V>) cursor;
21 } else {
22 throw new IllegalArgumentException("ModelCursor does not contain cursor for representation "+representation);
23 }
24 }
25}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelDiffCursor.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelDiffCursor.java
new file mode 100644
index 00000000..d3551e47
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelDiffCursor.java
@@ -0,0 +1,26 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.Map;
4
5import org.eclipse.viatra.solver.data.map.Cursor;
6import org.eclipse.viatra.solver.data.map.DiffCursor;
7import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
8
9public class ModelDiffCursor {
10 final Map<DataRepresentation<?, ?>,DiffCursor<?,?>> diffcursors;
11
12 public ModelDiffCursor(Map<DataRepresentation<?, ?>, DiffCursor<?, ?>> diffcursors) {
13 super();
14 this.diffcursors = diffcursors;
15 }
16
17 @SuppressWarnings("unchecked")
18 public <K,V> DiffCursor<K,V> getCursor(DataRepresentation<K, V> representation) {
19 Cursor<?, ?> cursor = diffcursors.get(representation);
20 if(cursor != null) {
21 return (DiffCursor<K, V>) cursor;
22 } else {
23 throw new IllegalArgumentException("ModelCursor does not contain cursor for representation "+representation);
24 }
25 }
26}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStore.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStore.java
new file mode 100644
index 00000000..35ac72b5
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStore.java
@@ -0,0 +1,16 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.Set;
4
5import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
6
7public interface ModelStore {
8 @SuppressWarnings("squid:S1452")
9 Set<DataRepresentation<?, ?>> getDataRepresentations();
10
11 Model createModel();
12 Model createModel(long state);
13
14 Set<Long> getStates();
15 ModelDiffCursor getDiffCursor(long from, long to);
16} \ No newline at end of file
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStoreImpl.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStoreImpl.java
new file mode 100644
index 00000000..f5e3b141
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/ModelStoreImpl.java
@@ -0,0 +1,127 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.Map;
8import java.util.Map.Entry;
9import java.util.Set;
10
11import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
12import org.eclipse.viatra.solver.data.map.DiffCursor;
13import org.eclipse.viatra.solver.data.map.VersionedMap;
14import org.eclipse.viatra.solver.data.map.VersionedMapStore;
15import org.eclipse.viatra.solver.data.map.VersionedMapStoreImpl;
16import org.eclipse.viatra.solver.data.model.internal.ModelImpl;
17import org.eclipse.viatra.solver.data.model.internal.SimilarRelationEquivalenceClass;
18import org.eclipse.viatra.solver.data.model.representation.AuxilaryData;
19import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
20import org.eclipse.viatra.solver.data.model.representation.Relation;
21
22public class ModelStoreImpl implements ModelStore {
23
24 private final Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> stores;
25
26 public ModelStoreImpl(Set<DataRepresentation<?, ?>> dataRepresentations) {
27 stores = initStores(dataRepresentations);
28 }
29
30 private Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> initStores(
31 Set<DataRepresentation<?, ?>> dataRepresentations) {
32 Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> result = new HashMap<>();
33
34 Map<SimilarRelationEquivalenceClass, List<Relation<?>>> symbolRepresentationsPerHashPerArity = new HashMap<>();
35
36 for (DataRepresentation<?, ?> dataRepresentation : dataRepresentations) {
37 if (dataRepresentation instanceof Relation<?>) {
38 Relation<?> symbolRepresentation = (Relation<?>) dataRepresentation;
39 addOrCreate(symbolRepresentationsPerHashPerArity,
40 new SimilarRelationEquivalenceClass(symbolRepresentation), symbolRepresentation);
41 } else if (dataRepresentation instanceof AuxilaryData<?, ?>) {
42 VersionedMapStoreImpl<?, ?> store = new VersionedMapStoreImpl<>(dataRepresentation.getHashProvider(),
43 dataRepresentation.getDefaultValue());
44 result.put(dataRepresentation, store);
45 } else {
46 throw new UnsupportedOperationException(
47 "Model store does not have strategy to use " + dataRepresentation.getClass() + "!");
48 }
49 }
50 for (List<Relation<?>> symbolGroup : symbolRepresentationsPerHashPerArity.values()) {
51 initRepresentationGroup(result, symbolGroup);
52 }
53
54 return result;
55 }
56
57 private void initRepresentationGroup(Map<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> result,
58 List<Relation<?>> symbolGroup) {
59 final ContinousHashProvider<Tuple> hashProvider = symbolGroup.get(0).getHashProvider();
60 final Object defaultValue = symbolGroup.get(0).getDefaultValue();
61
62 List<VersionedMapStore<Tuple, Object>> maps = VersionedMapStoreImpl
63 .createSharedVersionedMapStores(symbolGroup.size(), hashProvider, defaultValue);
64
65 for (int i = 0; i < symbolGroup.size(); i++) {
66 result.put(symbolGroup.get(i), maps.get(i));
67 }
68 }
69
70 private static <K, V> void addOrCreate(Map<K, List<V>> map, K key, V value) {
71 List<V> list;
72 if (map.containsKey(key)) {
73 list = map.get(key);
74 } else {
75 list = new LinkedList<>();
76 map.put(key, list);
77 }
78 list.add(value);
79 }
80
81 @Override
82 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
83 return this.stores.keySet();
84 }
85
86 @Override
87 public ModelImpl createModel() {
88 Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps = new HashMap<>();
89 for (Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : this.stores.entrySet()) {
90 maps.put(entry.getKey(), entry.getValue().createMap());
91 }
92 return new ModelImpl(this, maps);
93 }
94
95 @Override
96 public synchronized ModelImpl createModel(long state) {
97 Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps = new HashMap<>();
98 for (Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : this.stores.entrySet()) {
99 maps.put(entry.getKey(), entry.getValue().createMap(state));
100 }
101 return new ModelImpl(this, maps);
102 }
103
104 @Override
105 @SuppressWarnings("squid:S1751")
106 public synchronized Set<Long> getStates() {
107 // if not empty, return first
108 for(VersionedMapStore<?, ?> store : stores.values()) {
109 return new HashSet<>(store.getStates());
110 }
111 // if empty
112 Set<Long> result = new HashSet<>();
113 result.add(0l);
114 return result;
115 }
116
117 @Override
118 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
119 Map<DataRepresentation<?, ?>,DiffCursor<?,?>> diffcursors = new HashMap<>();
120 for(Entry<DataRepresentation<?, ?>, VersionedMapStore<?, ?>> entry : stores.entrySet()) {
121 DataRepresentation<?, ?> representation = entry.getKey();
122 DiffCursor<?, ?> diffCursor = entry.getValue().getDiffCursor(from, to);
123 diffcursors.put(representation, diffCursor);
124 }
125 return new ModelDiffCursor(diffcursors);
126 }
127}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/Tuple.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/Tuple.java
new file mode 100644
index 00000000..ca6548a4
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/Tuple.java
@@ -0,0 +1,148 @@
1package org.eclipse.viatra.solver.data.model;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.List;
6
7public abstract class Tuple {
8 private static final int CUSTOMTUPLESIZE = 2;
9 protected static final List<Tuple1> tuple1Cash = new ArrayList<>(1024);
10
11 public abstract int getSize();
12 public abstract int get(int element);
13 public abstract int[] toArray();
14
15 @Override
16 public String toString() {
17 StringBuilder b = new StringBuilder();
18 b.append("[");
19 for(int i = 0; i<getSize(); i++) {
20 if(i!=0) {
21 b.append(",");
22 }
23 b.append(get(i));
24 }
25 b.append("]");
26 return b.toString();
27 }
28
29 public static Tuple1 of1(int value) {
30 if(value < tuple1Cash.size()) {
31 return tuple1Cash.get(value);
32 } else {
33 Tuple1 newlyCreated = null;
34 while(value >= tuple1Cash.size()) {
35 newlyCreated = new Tuple1(tuple1Cash.size());
36 tuple1Cash.add(newlyCreated);
37 }
38 return newlyCreated;
39 }
40 }
41
42 public static Tuple of(int... values) {
43 if(values.length == 0) {
44 return new Tuple0();
45 } else if(values.length == 1) {
46 return of1(values[0]);
47 } else if(values.length == 2) {
48 return new Tuple2(values[0],values[1]);
49 } else return new TupleN(values);
50 }
51
52 protected IllegalArgumentException doesNotContain(int element) {
53 return new IllegalArgumentException("Tuple does not contain element "+element);
54 }
55
56 public static class Tuple0 extends Tuple{
57 protected Tuple0() { }
58 @Override public int getSize() { return 0; }
59 @Override public int get(int element) {
60 throw doesNotContain(element);
61 }
62 @Override public int[] toArray() {return new int[]{};}
63 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
64 @Override
65 public boolean equals(Object obj) {
66 if (this == obj)
67 return true;
68 if (obj == null)
69 return false;
70 if (getClass() != obj.getClass())
71 return false;
72 return true;
73 }
74 }
75 public static class Tuple1 extends Tuple{
76 final int value0;
77 protected Tuple1(int value0) { this.value0 = value0; }
78 @Override public int getSize() { return 1; }
79 @Override public int get(int element) {
80 if(element == 0) return value0;
81 throw doesNotContain(element);
82 }
83 @Override public int[] toArray() {return new int[]{ value0 };}
84 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
85 @Override
86 public boolean equals(Object obj) {
87 if (this == obj)
88 return true;
89 if (obj == null)
90 return false;
91 if (getClass() != obj.getClass())
92 return false;
93 Tuple1 other = (Tuple1) obj;
94 return value0 == other.value0;
95 }
96 }
97 public static class Tuple2 extends Tuple{
98 final int value0;
99 final int value1;
100 protected Tuple2(int value0, int value1) { this.value0 = value0; this.value1 = value1; }
101 @Override public int getSize() { return 2; }
102 @Override public int get(int element) {
103 if(element == 0) return value0;
104 else if(element == 1) return value1;
105 throw doesNotContain(element);
106 }
107 @Override public int[] toArray() {return new int[]{ value0,value1 };}
108 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
109 @Override
110 public boolean equals(Object obj) {
111 if (this == obj)
112 return true;
113 if (obj == null)
114 return false;
115 if (getClass() != obj.getClass())
116 return false;
117 Tuple2 other = (Tuple2) obj;
118 return value0 == other.value0 && value1 == other.value1;
119 }
120 }
121 public static class TupleN extends Tuple{
122 final int[] values;
123 protected TupleN(int[] values) {
124 if(values.length<CUSTOMTUPLESIZE)
125 throw new IllegalArgumentException();
126 this.values = Arrays.copyOf(values, values.length);
127 }
128 @Override public int getSize() { return values.length; }
129 @Override public int get(int element) {
130 if(0<=element && element < values.length) {
131 return values[element];
132 } else throw doesNotContain(element);
133 }
134 @Override public int[] toArray() { return values; }
135 @Override public int hashCode() { return TupleHashProvider.singleton().getHash(this, 0); }
136 @Override
137 public boolean equals(Object obj) {
138 if (this == obj)
139 return true;
140 if (obj == null)
141 return false;
142 if (getClass() != obj.getClass())
143 return false;
144 TupleN other = (TupleN) obj;
145 return Arrays.equals(values, other.values);
146 }
147 }
148}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProvider.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProvider.java
new file mode 100644
index 00000000..6c37aa37
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProvider.java
@@ -0,0 +1,65 @@
1package org.eclipse.viatra.solver.data.model;
2
3import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
4
5public class TupleHashProvider implements ContinousHashProvider<Tuple> {
6 protected static TupleHashProvider instance;
7
8 public static TupleHashProvider singleton() {
9 if (instance == null) {
10 instance = new TupleHashProvider();
11 }
12 return instance;
13 }
14
15 protected static final int[] primes = new int[] { 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
16 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
17 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337,
18 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
19 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
20 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739,
21 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881,
22 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
23 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
24 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283,
25 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429,
26 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549,
27 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
28 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
29 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973,
30 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089,
31 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
32 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377,
33 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
34 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677,
35 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
36 2797, 2801, 2803, 2819, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217,
37 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359,
38 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517,
39 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637,
40 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793,
41 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911 };
42
43 protected static final long LARGESTPRIME30BITS = 1073741789;
44
45 public TupleHashProvider() {
46 if (primes.length < MAX_PRACTICAL_DEPTH) {
47 throw new UnsupportedOperationException(
48 "Not enough prime numbers to support the practical depth of continuous hash!");
49 }
50 }
51
52 @Override
53 public int getHash(Tuple key, int index) {
54 if (index >= primes.length) {
55 throw new IllegalArgumentException("Not enough prime numbers to support index");
56 }
57 long accumulator = 0;
58 final int prime = primes[index];
59 for (int i = 0; i < key.getSize(); i++) {
60 accumulator = (prime * accumulator + key.get(i)) % LARGESTPRIME30BITS;
61 }
62
63 return (int) accumulator;
64 }
65}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProviderBitMagic.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProviderBitMagic.java
new file mode 100644
index 00000000..2a514d66
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/TupleHashProviderBitMagic.java
@@ -0,0 +1,28 @@
1package org.eclipse.viatra.solver.data.model;
2
3import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
4
5public class TupleHashProviderBitMagic implements ContinousHashProvider<Tuple> {
6
7 @Override
8 public int getHash(Tuple key, int index) {
9 if(key.getSize() == 1) {
10 return key.get(0);
11 }
12
13 int result = 0;
14 final int startBitIndex = index*30;
15 final int finalBitIndex = startBitIndex+30;
16 final int arity = key.getSize();
17
18 for(int i = startBitIndex; i<=finalBitIndex; i++) {
19 final int selectedKey = key.get(i%arity);
20 final int selectedPosition = 1<<(i/arity);
21 if((selectedKey&selectedPosition) != 0) {
22 result |= 1<<(i%30);
23 }
24 }
25
26 return result;
27 }
28}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/ModelImpl.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/ModelImpl.java
new file mode 100644
index 00000000..6d7f4e97
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/ModelImpl.java
@@ -0,0 +1,124 @@
1package org.eclipse.viatra.solver.data.model.internal;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Set;
6
7import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
8import org.eclipse.viatra.solver.data.map.Cursor;
9import org.eclipse.viatra.solver.data.map.DiffCursor;
10import org.eclipse.viatra.solver.data.map.VersionedMap;
11import org.eclipse.viatra.solver.data.map.internal.MapDiffCursor;
12import org.eclipse.viatra.solver.data.model.Model;
13import org.eclipse.viatra.solver.data.model.ModelDiffCursor;
14import org.eclipse.viatra.solver.data.model.ModelStore;
15import org.eclipse.viatra.solver.data.model.representation.DataRepresentation;
16
17public class ModelImpl implements Model {
18 private final ModelStore store;
19 private final Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps;
20
21 public ModelImpl(ModelStore store, Map<DataRepresentation<?, ?>, VersionedMap<?, ?>> maps) {
22 this.store = store;
23 this.maps = maps;
24 }
25
26 @Override
27 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
28 return maps.keySet();
29 }
30
31 @SuppressWarnings("unchecked")
32 private <K, V> VersionedMap<K, V> getMap(DataRepresentation<K, V> representation) {
33 if (maps.containsKey(representation)) {
34 return (VersionedMap<K, V>) maps.get(representation);
35 } else {
36 throw new IllegalArgumentException("Model does have representation " + representation);
37 }
38 }
39
40 private <K, V> VersionedMap<K, V> getMapValidateKey(DataRepresentation<K, V> representation, K key) {
41 if (representation.isValidKey(key)) {
42 return getMap(representation);
43 } else {
44 throw new IllegalArgumentException(
45 "Key is not valid for representation! (representation=" + representation + ", key=" + key + ");");
46 }
47 }
48
49 @Override
50 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
51 return getMapValidateKey(representation, key).get(key);
52 }
53
54 @Override
55 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
56 return getMap(representation).getAll();
57 }
58
59 @Override
60 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
61 return getMapValidateKey(representation, key).put(key, value);
62 }
63
64 @Override
65 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
66 getMap(representation).putAll(cursor);
67 }
68
69 @Override
70 public <K, V> long getSize(DataRepresentation<K, V> representation) {
71 return getMap(representation).getSize();
72 }
73
74 @Override
75 public ModelDiffCursor getDiffCursor(long to) {
76 Model toModel = store.createModel(to);
77 Map<DataRepresentation<?, ?>, DiffCursor<?, ?>> diffCursors = new HashMap<>();
78 for (DataRepresentation<?, ?> representation : this.maps.keySet()) {
79 MapDiffCursor<?, ?> diffCursor = constructDiffCursor(toModel, representation);
80 diffCursors.put(representation, diffCursor);
81 }
82 return new ModelDiffCursor(diffCursors);
83 }
84
85 private <K, V> MapDiffCursor<K, V> constructDiffCursor(Model toModel, DataRepresentation<K, V> representation) {
86 @SuppressWarnings("unchecked")
87 Cursor<K, V> fromCursor = (Cursor<K, V>) this.maps.get(representation).getAll();
88 Cursor<K, V> toCursor = toModel.getAll(representation);
89
90 ContinousHashProvider<K> hashProvider = representation.getHashProvider();
91 V defaultValue = representation.getDefaultValue();
92 return new MapDiffCursor<>(hashProvider, defaultValue, fromCursor, toCursor);
93 }
94
95 @Override
96 public long commit() {
97 long version = 0;
98 boolean versionSet = false;
99 for (VersionedMap<?, ?> map : maps.values()) {
100 long newVersion = map.commit();
101 if (versionSet) {
102 if (version != newVersion) {
103 throw new IllegalStateException(
104 "Maps in model have different versions! (" + version + " and" + newVersion + ")");
105 }
106 } else {
107 version = newVersion;
108 versionSet = true;
109 }
110 }
111 return version;
112 }
113
114 @Override
115 public void restore(long state) {
116 if(store.getStates().contains(state)) {
117 for (VersionedMap<?, ?> map : maps.values()) {
118 map.restore(state);
119 }
120 } else {
121 throw new IllegalArgumentException("Map does not contain state "+state+"!");
122 }
123 }
124}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/SimilarRelationEquivalenceClass.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/SimilarRelationEquivalenceClass.java
new file mode 100644
index 00000000..7054e4db
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/internal/SimilarRelationEquivalenceClass.java
@@ -0,0 +1,33 @@
1package org.eclipse.viatra.solver.data.model.internal;
2
3import java.util.Objects;
4
5import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
6import org.eclipse.viatra.solver.data.model.Tuple;
7import org.eclipse.viatra.solver.data.model.representation.Relation;
8
9public class SimilarRelationEquivalenceClass {
10 final ContinousHashProvider<Tuple> hashProvider;
11 final Object defaultValue;
12 final int arity;
13 public SimilarRelationEquivalenceClass(Relation<?> representation) {
14 this.hashProvider = representation.getHashProvider();
15 this.defaultValue = representation.getDefaultValue();
16 this.arity = representation.getArity();
17 }
18 @Override
19 public int hashCode() {
20 return Objects.hash(arity, defaultValue, hashProvider);
21 }
22 @Override
23 public boolean equals(Object obj) {
24 if (this == obj)
25 return true;
26 if (!(obj instanceof SimilarRelationEquivalenceClass))
27 return false;
28 SimilarRelationEquivalenceClass other = (SimilarRelationEquivalenceClass) obj;
29 return arity == other.arity && Objects.equals(defaultValue, other.defaultValue)
30 && Objects.equals(hashProvider, other.hashProvider);
31 }
32
33}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/AuxilaryData.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/AuxilaryData.java
new file mode 100644
index 00000000..7fc79348
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/AuxilaryData.java
@@ -0,0 +1,22 @@
1package org.eclipse.viatra.solver.data.model.representation;
2
3import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
4
5public class AuxilaryData<K,V> extends DataRepresentation<K, V> {
6 private final String name;
7
8 public AuxilaryData(String name, ContinousHashProvider<K> hashProvider, V defaultValue) {
9 super(hashProvider, defaultValue);
10 this.name = name;
11 }
12
13 @Override
14 public String getName() {
15 return name;
16 }
17
18 @Override
19 public boolean isValidKey(K key) {
20 return true;
21 }
22}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/DataRepresentation.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/DataRepresentation.java
new file mode 100644
index 00000000..fd48eb94
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/DataRepresentation.java
@@ -0,0 +1,24 @@
1package org.eclipse.viatra.solver.data.model.representation;
2
3import org.eclipse.viatra.solver.data.map.ContinousHashProvider;
4
5public abstract class DataRepresentation<K, V> {
6 protected final ContinousHashProvider<K> hashProvider;
7 protected final V defaultValue;
8
9 protected DataRepresentation(ContinousHashProvider<K> hashProvider, V defaultValue) {
10 this.hashProvider = hashProvider;
11 this.defaultValue = defaultValue;
12 }
13
14 public abstract String getName();
15
16 public ContinousHashProvider<K> getHashProvider() {
17 return hashProvider;
18 }
19 public abstract boolean isValidKey(K key);
20
21 public V getDefaultValue() {
22 return defaultValue;
23 }
24}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/Relation.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/Relation.java
new file mode 100644
index 00000000..eafb5c56
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/Relation.java
@@ -0,0 +1,31 @@
1package org.eclipse.viatra.solver.data.model.representation;
2
3import org.eclipse.viatra.solver.data.model.Tuple;
4import org.eclipse.viatra.solver.data.model.TupleHashProvider;
5
6public class Relation<D> extends DataRepresentation<Tuple, D> {
7 private final String name;
8 private final int arity;
9
10 public Relation(String name, int arity, D defaultValue) {
11 super(TupleHashProvider.singleton(), defaultValue);
12 this.name = name;
13 this.arity = arity;
14 }
15
16 @Override
17 public String getName() {
18 return name;
19 }
20
21 public int getArity() {
22 return arity;
23 }
24
25 @Override
26 public boolean isValidKey(Tuple key) {
27 if(key == null) {
28 return false;
29 } else return key.getSize() == getArity();
30 }
31}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/TruthValue.java b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/TruthValue.java
new file mode 100644
index 00000000..aeccde9e
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/model/representation/TruthValue.java
@@ -0,0 +1,44 @@
1package org.eclipse.viatra.solver.data.model.representation;
2
3public class TruthValue {
4 public static final TruthValue True = new TruthValue("true");
5 public static final TruthValue False = new TruthValue("false");
6 public static final TruthValue Unknown = new TruthValue("unknown");
7 public static final TruthValue Error = new TruthValue("error");
8
9 private final String name;
10 protected TruthValue(String name) {
11 this.name = name;
12 }
13
14 public String getName() {
15 return name;
16 }
17
18 public static TruthValue toTruthValue(boolean value) {
19 if(value) return True;
20 else return False;
21 }
22 public boolean isConsistent() {
23 return this != Error;
24 }
25 public boolean isComplete() {
26 return this != Unknown;
27 }
28 public boolean must() {
29 return this == True || this == Error;
30 }
31 public boolean may() {
32 return this == True || this == Unknown;
33 }
34
35 public TruthValue not() {
36 if(this == True) {
37 return False;
38 } else if(this == False) {
39 return True;
40 } else {
41 return this;
42 }
43 }
44}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java
new file mode 100644
index 00000000..97b33935
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java
@@ -0,0 +1,34 @@
1package org.eclipse.viatra.solver.data.query;
2
3import java.util.Set;
4
5import org.apache.log4j.Logger;
6import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
7import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
8import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
9import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
10import org.eclipse.viatra.solver.data.model.Model;
11import org.eclipse.viatra.solver.data.model.Tuple;
12import org.eclipse.viatra.solver.data.query.internal.RelationUpdateListener;
13import org.eclipse.viatra.solver.data.query.internal.RelationalEngineContext;
14import org.eclipse.viatra.solver.data.query.view.RelationView;
15
16public class RelationalScope extends QueryScope{
17 private final Model model;
18 private final RelationUpdateListener updateListener;
19
20 public RelationalScope(Model model, Set<RelationView<?>> relationViews) {
21 this.model = model;
22 updateListener = new RelationUpdateListener(relationViews);
23 }
24
25 public <D> void processUpdate(RelationView<D> relationView, Tuple key, D oldValue, D newValue) {
26 updateListener.processChange(relationView, key, oldValue, newValue);
27 }
28
29 @Override
30 protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener,
31 Logger logger) {
32 return new RelationalEngineContext(model, updateListener);
33 }
34}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAnd.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAnd.java
new file mode 100644
index 00000000..ff5a7848
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAnd.java
@@ -0,0 +1,37 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.List;
6import java.util.Map;
7import java.util.Set;
8
9public class DNFAnd {
10 private Set<Variable> existentiallyQuantified;
11 private List<DNFAtom> constraints;
12 public DNFAnd(Set<Variable> quantifiedVariables, List<DNFAtom> constraints) {
13 super();
14 this.existentiallyQuantified = quantifiedVariables;
15 this.constraints = constraints;
16 }
17 public Set<Variable> getExistentiallyQuantified() {
18 return existentiallyQuantified;
19 }
20 public List<DNFAtom> getConstraints() {
21 return constraints;
22 }
23 void unifyVariables(Map<String,Variable> uniqueVariableMap) {
24 Map<String,Variable> uniqueVariableMapForClause = new HashMap<>(uniqueVariableMap);
25 for(DNFAtom atom : constraints) {
26 atom.unifyVariables(uniqueVariableMapForClause);
27 }
28 }
29 void collectQuantifiedVariables(Set<Variable> parameters) {
30 Set<Variable> result = new HashSet<>();
31 for(DNFAtom constraint : constraints) {
32 constraint.collectAllVariables(result);
33 }
34 result.removeAll(parameters);
35 existentiallyQuantified = result;
36 }
37}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAtom.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAtom.java
new file mode 100644
index 00000000..05a3e3f8
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFAtom.java
@@ -0,0 +1,33 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.Collection;
4import java.util.Iterator;
5import java.util.Map;
6import java.util.Set;
7
8public interface DNFAtom {
9 void unifyVariables(Map<String,Variable> variables);
10 static Variable unifyVariables(Map<String,Variable> unifiedVariables, Variable variable) {
11 if(variable != null) {
12 if(variable.isNamed() && unifiedVariables.containsKey(variable.getName())) {
13 return unifiedVariables.get(variable.getName());
14 }
15 return variable;
16 } else {
17 return null;
18 }
19 }
20 void collectAllVariables(Set<Variable> variables);
21 static void addToCollection(Set<Variable> variables, Variable variable) {
22 if(variable != null) {
23 variables.add(variable);
24 }
25 }
26 static void addToCollection(Set<Variable> variables, Collection<Variable> variableCollection) {
27 Iterator<Variable> iterator = variableCollection.iterator();
28 while(iterator.hasNext()) {
29 Variable variable = iterator.next();
30 addToCollection(variables, variable);
31 }
32 }
33}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFPredicate.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFPredicate.java
new file mode 100644
index 00000000..8ee540ae
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/DNFPredicate.java
@@ -0,0 +1,72 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.List;
6import java.util.Map;
7import java.util.UUID;
8
9public class DNFPredicate {
10 private final String name;
11 private final String uniqueName;
12 private final List<Variable> parameters;
13 private final List<DNFAnd> clauses;
14
15 public DNFPredicate(String name, List<Variable> parameters, List<DNFAnd> clauses) {
16 this.name = name;
17 this.uniqueName = generateUniqueName(name,"predicate");
18 this.parameters = parameters;
19 this.clauses = clauses;
20
21 postProcess();
22 }
23
24 public static String generateUniqueName(String originalName, String defaultPrefix) {
25 UUID uuid = UUID.randomUUID();
26 String uniqueString = uuid.toString().replace('-', '_');
27 if(originalName == null) {
28 return defaultPrefix+uniqueString;
29 } else {
30 return originalName+uniqueString;
31 }
32 }
33
34 public String getName() {
35 return name;
36 }
37 public String getUniqueName() {
38 return uniqueName;
39 }
40 public List<Variable> getVariables() {
41 return parameters;
42 }
43 public List<DNFAnd> getClauses() {
44 return clauses;
45 }
46
47 public void unifyVariables() {
48 Map<String,Variable> uniqueVariableMap = new HashMap<>();
49 for(Variable parameter : this.parameters) {
50 if(parameter.isNamed()) {
51 String parameterName = parameter.getName();
52 if(uniqueVariableMap.containsKey(parameterName)) {
53 throw new IllegalArgumentException("Multiple parameters has the name "+parameterName);
54 } else {
55 uniqueVariableMap.put(parameterName, parameter);
56 }
57 }
58 }
59 for(DNFAnd clause : this.clauses) {
60 clause.unifyVariables(uniqueVariableMap);
61 }
62 }
63 public void collectQuantifiedVariables() {
64 for(DNFAnd clause : this.clauses) {
65 clause.collectQuantifiedVariables(new HashSet<>(parameters));
66 }
67 }
68 public void postProcess() {
69 unifyVariables();
70 collectQuantifiedVariables();
71 }
72}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/EquivalenceAtom.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/EquivalenceAtom.java
new file mode 100644
index 00000000..b47fe2a8
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/EquivalenceAtom.java
@@ -0,0 +1,44 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.Map;
4import java.util.Set;
5
6public class EquivalenceAtom implements DNFAtom{
7 private boolean positive;
8 private Variable left;
9 private Variable right;
10 public EquivalenceAtom(boolean positive, Variable left, Variable right) {
11 this.positive = positive;
12 this.left = left;
13 this.right = right;
14 }
15 public boolean isPositive() {
16 return positive;
17 }
18 public void setPositive(boolean positive) {
19 this.positive = positive;
20 }
21 public Variable getLeft() {
22 return left;
23 }
24 public void setLeft(Variable left) {
25 this.left = left;
26 }
27 public Variable getRight() {
28 return right;
29 }
30 public void setRight(Variable right) {
31 this.right = right;
32 }
33
34 @Override
35 public void unifyVariables(Map<String, Variable> variables) {
36 this.left = DNFAtom.unifyVariables(variables,left);
37 this.right = DNFAtom.unifyVariables(variables,right);
38 }
39 @Override
40 public void collectAllVariables(Set<Variable> variables) {
41 DNFAtom.addToCollection(variables, left);
42 DNFAtom.addToCollection(variables, right);
43 }
44}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateAtom.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateAtom.java
new file mode 100644
index 00000000..3e5ef88e
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateAtom.java
@@ -0,0 +1,57 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.List;
4import java.util.Map;
5import java.util.Set;
6
7public class PredicateAtom implements DNFAtom{
8 private DNFPredicate referred;
9 private List<Variable> substitution;
10 private boolean positive;
11 private boolean transitive;
12
13 public PredicateAtom(boolean positive, boolean transitive, DNFPredicate referred, List<Variable> substitution) {
14 this.positive = positive;
15 this.referred = referred;
16 this.substitution = substitution;
17 this.transitive = transitive;
18 }
19 public DNFPredicate getReferred() {
20 return referred;
21 }
22 public void setReferred(DNFPredicate referred) {
23 this.referred = referred;
24 }
25 public List<Variable> getSubstitution() {
26 return substitution;
27 }
28 public void setSubstitution(List<Variable> substitution) {
29 this.substitution = substitution;
30 }
31 public boolean isPositive() {
32 return positive;
33 }
34 public void setPositive(boolean positive) {
35 this.positive = positive;
36 }
37 public boolean isTransitive() {
38 return transitive;
39 }
40 public void setTransitive(boolean transitive) {
41 this.transitive = transitive;
42 }
43 @Override
44 public void unifyVariables(Map<String, Variable> variables) {
45 for(int i = 0; i<this.substitution.size(); i++) {
46 final Object term = this.substitution.get(i);
47 if(term instanceof Variable) {
48 Variable variableReference = (Variable) term;
49 this.substitution.set(i, DNFAtom.unifyVariables(variables, variableReference));
50 }
51 }
52 }
53 @Override
54 public void collectAllVariables(Set<Variable> variables) {
55 DNFAtom.addToCollection(variables, substitution);
56 }
57}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateBuilder_string.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateBuilder_string.java
new file mode 100644
index 00000000..41f85d39
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/PredicateBuilder_string.java
@@ -0,0 +1,107 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.HashSet;
6import java.util.List;
7
8import org.eclipse.viatra.solver.data.query.view.RelationView;
9
10public class PredicateBuilder_string {
11 private PredicateBuilder_string() {}
12
13 public static PredicateBuild1 predicate(String name) {
14 return new PredicateBuild1(name);
15 }
16 public static class PredicateBuild1 {
17 private String name;
18 public PredicateBuild1(String name) {
19 this.name = name;
20 }
21 public PredicateBuild2 parameters(String... parameters) {
22 return new PredicateBuild2(name, parameters);
23 }
24 }
25 public static class PredicateBuild2 {
26 private String name;
27 private String[] parameters;
28 public PredicateBuild2(String name, String[] parameters) {
29 this.name = name;
30 this.parameters = parameters;
31 }
32
33 public PredicateBuild3 clause(DNFAtom...constraints) {
34 return new PredicateBuild3(name,parameters,List.<DNFAtom[]>of(constraints));
35 }
36 }
37 public static class PredicateBuild3 {
38 String name;
39 String[] parameters;
40 List<DNFAtom[]> clauses;
41 public PredicateBuild3(String name, String[] parameters, List<DNFAtom[]> clauses) {
42 super();
43 this.name = name;
44 this.parameters = parameters;
45 this.clauses = clauses;
46 }
47
48 public PredicateBuild3 clause(DNFAtom...constraints) {
49 List<DNFAtom[]> newClauses = new ArrayList<>();
50 newClauses.addAll(clauses);
51 newClauses.add(constraints);
52 return new PredicateBuild3(name, parameters, newClauses);
53 }
54 public DNFPredicate build() {
55 List<Variable> newParameters = new ArrayList<>(this.parameters.length);
56 for(int i = 0; i<this.parameters.length; i++) {
57 newParameters.add(new Variable(parameters[i]));
58 }
59
60 List<DNFAnd> newClauses = new ArrayList<>(this.clauses.size());
61 for(DNFAtom[] clause : this.clauses) {
62 List<DNFAtom> constraints = new ArrayList<>(clause.length);
63 Collections.addAll(constraints, clause);
64 newClauses.add(new DNFAnd(new HashSet<>(), constraints));
65 }
66
67 return new DNFPredicate(name,newParameters,newClauses);
68 }
69 }
70
71 private static Variable stringToVariable(String name) {
72 if(name != null) {
73 return new Variable(name);
74 } else {
75 return null;
76 }
77 }
78 private static List<Variable> stringToVariable(String[] names) {
79 List<Variable> variables = new ArrayList<>();
80 for(int i = 0; i<names.length; i++) {
81 variables.add(stringToVariable(names[i]));
82 }
83 return variables;
84 }
85
86 public static EquivalenceAtom cEquals(String v1, String v2) {
87 return new EquivalenceAtom(true,stringToVariable(v1),stringToVariable(v2));
88 }
89 public static EquivalenceAtom cNotEquals(String v1, String v2) {
90 return new EquivalenceAtom(false,stringToVariable(v1),stringToVariable(v2));
91 }
92
93 public static RelationAtom cInRelation(RelationView<?> view, String... variables) {
94
95 return new RelationAtom(view, stringToVariable(variables));
96 }
97
98 public static PredicateAtom cInPredicate(DNFPredicate referred, String... variables) {
99 return new PredicateAtom(true, false, referred, stringToVariable(variables));
100 }
101 public static PredicateAtom cInTransitivePredicate(DNFPredicate referred, String... variables) {
102 return new PredicateAtom(true, true, referred, stringToVariable(variables));
103 }
104 public static PredicateAtom cNotInPredicate(DNFPredicate referred, String... variables) {
105 return new PredicateAtom(false, false, referred, stringToVariable(variables));
106 }
107}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/RelationAtom.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/RelationAtom.java
new file mode 100644
index 00000000..f7152bba
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/RelationAtom.java
@@ -0,0 +1,45 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3import java.util.List;
4import java.util.Map;
5import java.util.Set;
6
7import org.eclipse.viatra.solver.data.query.view.FilteredRelationView;
8import org.eclipse.viatra.solver.data.query.view.RelationView;
9
10public class RelationAtom implements DNFAtom{
11 RelationView<?> view;
12 List<Variable> substitution;
13
14 public RelationAtom(RelationView<?> view, List<Variable> substitution) {
15 this.view = view;
16 this.substitution = substitution;
17 }
18 public RelationView<?> getView() {
19 return view;
20 }
21 public void setView(FilteredRelationView<?> view) {
22 this.view = view;
23 }
24 public List<Variable> getSubstitution() {
25 return substitution;
26 }
27 public void setSubstitution(List<Variable> substitution) {
28 this.substitution = substitution;
29 }
30
31 @Override
32 public void unifyVariables(Map<String, Variable> variables) {
33 for(int i = 0; i<this.substitution.size(); i++) {
34 final Object term = this.substitution.get(i);
35 if(term instanceof Variable) {
36 Variable variableReference = (Variable) term;
37 this.substitution.set(i, DNFAtom.unifyVariables(variables, variableReference));
38 }
39 }
40 }
41 @Override
42 public void collectAllVariables(Set<Variable> variables) {
43 DNFAtom.addToCollection(variables, substitution);
44 }
45}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/building/Variable.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/Variable.java
new file mode 100644
index 00000000..29f9fc8b
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/building/Variable.java
@@ -0,0 +1,22 @@
1package org.eclipse.viatra.solver.data.query.building;
2
3public class Variable {
4 private final String name;
5 private final String uniqueName;
6
7 public Variable(String name) {
8 super();
9 this.name = name;
10 this.uniqueName = DNFPredicate.generateUniqueName(name, "variable");
11
12 }
13 public String getName() {
14 return name;
15 }
16 public String getUniqueName() {
17 return uniqueName;
18 }
19 public boolean isNamed() {
20 return name != null;
21 }
22}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java
new file mode 100644
index 00000000..042ec3dc
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java
@@ -0,0 +1,59 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import java.lang.reflect.InvocationTargetException;
4import java.util.concurrent.Callable;
5
6import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
7import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
8import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver;
9import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener;
10
11/**
12 * copied from org.eclipse.viatra.query.runtime.tabular.TabularEngineContext;
13 */
14public class DummyBaseIndexer implements IBaseIndex{
15
16 @Override
17 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
18 try {
19 return callable.call();
20 } catch (Exception e) {
21 throw new InvocationTargetException(e);
22 }
23 }
24
25 @Override
26 public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
27 // no notification support
28 }
29
30 @Override
31 public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
32 // no notification support
33 }
34
35 @Override
36 public void resampleDerivedFeatures() {
37 throw new UnsupportedOperationException();
38 }
39
40 @Override
41 public boolean addIndexingErrorListener(IIndexingErrorListener listener) {
42 return true;
43 }
44
45 @Override
46 public boolean removeIndexingErrorListener(IIndexingErrorListener listener) {
47 return true;
48 }
49
50 @Override
51 public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) {
52 return true;
53 }
54
55 @Override
56 public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) {
57 return true;
58 }
59}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/PredicateTranslator.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/PredicateTranslator.java
new file mode 100644
index 00000000..54cb4bab
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/PredicateTranslator.java
@@ -0,0 +1,209 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.LinkedHashSet;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10
11import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
12import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
13import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
14import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
15import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
16import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
17import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
18import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
19import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
20import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
21import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
22import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
23import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
24import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
25import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
26import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
27import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
28import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
29import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
30import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
31import org.eclipse.viatra.solver.data.query.RelationalScope;
32import org.eclipse.viatra.solver.data.query.view.RelationView;
33
34public class PredicateTranslator extends BasePQuery {
35
36 private final Map<String, PParameter> parameters = new HashMap<String, PParameter>();
37 private String fullyQualifiedName;
38 private LinkedList<PBody> bodies = new LinkedList<PBody>();
39 private List<ExportedParameter> symbolicParameters;
40
41 public PredicateTranslator(String fullyQualifiedName) {
42 super(PVisibility.PUBLIC);
43 this.fullyQualifiedName = fullyQualifiedName;
44 PBody body = new PBody(this);
45 bodies.add(body);
46 }
47
48 @Override
49 public String getFullyQualifiedName() {
50 return fullyQualifiedName;
51 }
52
53 public PredicateTranslator addParameter(String name, RelationView<?> type) {
54 PParameter parameter = new PParameter(name);
55 parameters.put(name, parameter);
56
57 PBody body = bodies.peekLast();
58 List<ExportedParameter> symbolicParameters = new ArrayList<>();
59 parameters.forEach((pName, pParameter) -> {
60 PVariable var = body.getOrCreateVariableByName(pName);
61 symbolicParameters.add(new ExportedParameter(body, var, pParameter));
62 });
63 body.setSymbolicParameters(symbolicParameters);
64
65 return this;
66 }
67
68 @Override
69 public List<PParameter> getParameters() {
70 return new ArrayList<PParameter>(parameters.values());
71 }
72 public <D> PredicateTranslator addConstraint(RelationView<D> view, String... name) {
73 if(name.length != view.getArity()) {
74 throw new IllegalArgumentException("Arity ("+view.getArity()+") does not match parameter numbers ("+name.length+")");
75 }
76 PBody body = bodies.peekLast();
77 Object[] variables = new Object[name.length];
78 for(int i = 0; i<name.length; i++) {
79 variables[i] = body.getOrCreateVariableByName(name[i]);
80 }
81 new TypeConstraint(body, Tuples.flatTupleOf(variables), view);
82 return this;
83 }
84
85// // Type constraint
86// public RelationQuery addConstraint(String type, String name) {
87// PBody body = bodies.peekLast();
88// PVariable var = body.getOrCreateVariableByName(name);
89// new TypeConstraint(body, Tuples.flatTupleOf(var), new StringExactInstancesKey(type));
90// return this;
91// }
92//
93// // Relation constraint
94// public RelationQuery addConstraint(String type, String sourceName, String targetName) {
95// PBody body = bodies.peekLast();
96// PVariable var_source = body.getOrCreateVariableByName(sourceName);
97// PVariable var_target = body.getOrCreateVariableByName(targetName);
98// new TypeConstraint(body, Tuples.flatTupleOf(var_source, var_target),
99// new StringStructuralFeatureInstancesKey(type));
100// return this;
101// }
102
103 // Create new Body
104 public PredicateTranslator or() {
105 PBody body = new PBody(this);
106 List<ExportedParameter> symbolicParameters = new ArrayList<>();
107 parameters.forEach((name, parameter) -> {
108 PVariable var = body.getOrCreateVariableByName(name);
109 symbolicParameters.add(new ExportedParameter(body, var, parameter));
110 });
111 body.setSymbolicParameters(symbolicParameters);
112 bodies.add(body);
113 return this;
114 }
115
116 // Equality constraint
117 public PredicateTranslator addEquality(String sourceName, String targetName) {
118 PBody body = bodies.peekLast();
119 PVariable var_source = body.getOrCreateVariableByName(sourceName);
120 PVariable var_target = body.getOrCreateVariableByName(targetName);
121 new Equality(body, var_source, var_target);
122 return this;
123 }
124
125 // Inequality constraint
126 public PredicateTranslator addInequality(String sourceName, String targetName) {
127 PBody body = bodies.peekLast();
128 PVariable var_source = body.getOrCreateVariableByName(sourceName);
129 PVariable var_target = body.getOrCreateVariableByName(targetName);
130 new Inequality(body, var_source, var_target);
131 return this;
132 }
133
134 // Positive pattern call
135 public PredicateTranslator addPatternCall(PQuery query, String... names) {
136 PBody body = bodies.peekLast();
137 PVariable[] vars = new PVariable[names.length];
138 for (int i = 0; i < names.length; i++) {
139 vars[i] = body.getOrCreateVariableByName(names[i]);
140 }
141 new PositivePatternCall(body, Tuples.flatTupleOf(vars), query);
142 return this;
143 }
144
145 // Negative pattern call
146 public PredicateTranslator addNegativePatternCall(PQuery query, String... names) {
147 PBody body = bodies.peekLast();
148 PVariable[] vars = new PVariable[names.length];
149 for (int i = 0; i < names.length; i++) {
150 vars[i] = body.getOrCreateVariableByName(names[i]);
151 }
152 new NegativePatternCall(body, Tuples.flatTupleOf(vars), query);
153 return this;
154 }
155
156 // Binary transitive closure pattern call
157 public PredicateTranslator addBinaryTransitiveClosure(PQuery query, String sourceName, String targetName) {
158 PBody body = bodies.peekLast();
159 PVariable var_source = body.getOrCreateVariableByName(sourceName);
160 PVariable var_target = body.getOrCreateVariableByName(targetName);
161 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(var_source, var_target), query);
162 return this;
163 }
164
165 // Binary reflexive transitive closure pattern call
166 public PredicateTranslator addBinaryReflexiveTransitiveClosure(PQuery query, String sourceName, String targetName) {
167 PBody body = bodies.peekLast();
168 PVariable var_source = body.getOrCreateVariableByName(sourceName);
169 PVariable var_target = body.getOrCreateVariableByName(targetName);
170 new BinaryReflexiveTransitiveClosure(body, Tuples.flatTupleOf(var_source, var_target), query,
171 query.getParameters().get(0).getDeclaredUnaryType());
172 return this;
173 }
174
175 @Override
176 public Set<PBody> doGetContainedBodies() {
177 setEvaluationHints(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.UNSPECIFIED));
178 return new LinkedHashSet<PBody>(bodies);
179 }
180
181 public void addSymbolicParameters(ExportedParameter symbolicParameter) {
182 checkMutability();
183 if (symbolicParameters == null) {
184 symbolicParameters = new ArrayList<>();
185 }
186 symbolicParameters.add(symbolicParameter);
187 }
188
189 public GenericQuerySpecification<GenericPatternMatcher> build() {
190 return new GenericQuerySpecification<GenericPatternMatcher>(this) {
191
192 @Override
193 public Class<? extends QueryScope> getPreferredScopeClass() {
194 return RelationalScope.class;
195 }
196
197 @Override
198 protected GenericPatternMatcher instantiate(ViatraQueryEngine engine) {
199 return defaultInstantiate(engine);
200 }
201
202 @Override
203 public GenericPatternMatcher instantiate() {
204 return new GenericPatternMatcher(this);
205 }
206
207 };
208 }
209} \ No newline at end of file
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java
new file mode 100644
index 00000000..c6d12614
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java
@@ -0,0 +1,51 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
9import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
10import org.eclipse.viatra.solver.data.model.Tuple;
11import org.eclipse.viatra.solver.data.query.view.RelationView;
12
13public class RelationUpdateListener {
14 private final Map<RelationView<?>,Set<RelationUpdateListenerEntry<?>>> view2Listeners;
15
16 public RelationUpdateListener(Set<RelationView<?>> relationViews) {
17 view2Listeners = new HashMap<>();
18 for(RelationView<?> relationView : relationViews) {
19 view2Listeners.put(relationView, new HashSet<>());
20 }
21 }
22 public boolean containsRelationalView(RelationView<?> relationalKey) {
23 RelationView<?> relationView = relationalKey.getWrappedKey();
24 return view2Listeners.containsKey(relationView);
25 }
26 public void addListener(RelationView<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) {
27 RelationView<?> relationView = relationalKey.getWrappedKey();
28 if(view2Listeners.containsKey(relationView)) {
29 RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener);
30 view2Listeners.get(relationView).add(entry);
31 } else throw new IllegalArgumentException();
32 }
33 public void removeListener(RelationView<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) {
34 RelationView<?> relationView = relationalKey.getWrappedKey();
35 if(view2Listeners.containsKey(relationView)) {
36 RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener);
37 view2Listeners.get(relationView).remove(entry);
38 } else throw new IllegalArgumentException();
39 }
40
41 public <D> void processChange(RelationView<D> relationView, Tuple tuple, D oldValue, D newValue) {
42 Set<RelationUpdateListenerEntry<?>> listeners = view2Listeners.get(relationView);
43 if(listeners != null) {
44 for(RelationUpdateListenerEntry<?> listener : listeners) {
45 @SuppressWarnings("unchecked")
46 RelationUpdateListenerEntry<D> typeCorrectListener = (RelationUpdateListenerEntry<D>) listener;
47 typeCorrectListener.processChange(tuple, oldValue, newValue);
48 }
49 } else throw new IllegalArgumentException("View was not indexed in constructor "+relationView);
50 }
51}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java
new file mode 100644
index 00000000..55aed7c8
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java
@@ -0,0 +1,63 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import java.util.Arrays;
4import java.util.Objects;
5
6import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
7import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
9import org.eclipse.viatra.solver.data.model.Tuple;
10import org.eclipse.viatra.solver.data.query.view.RelationView;
11
12public class RelationUpdateListenerEntry<D> {
13 final RelationView<D> key;
14 final ITuple filter;
15 final IQueryRuntimeContextListener listener;
16
17 public RelationUpdateListenerEntry(RelationView<D> key, ITuple filter, IQueryRuntimeContextListener listener) {
18 super();
19 this.key = key;
20 this.filter = filter;
21 this.listener = listener;
22 }
23
24 public void processChange(Tuple tuple, D oldValue, D newValue) {
25 Object[] oldTuple = isMatching(key.getWrappedKey().transform(tuple, oldValue), filter);
26 Object[] newTuple = isMatching(key.getWrappedKey().transform(tuple, newValue), filter);
27
28 if(!Arrays.equals(oldTuple, newTuple)) {
29 if(oldTuple != null) {
30 listener.update(key, Tuples.flatTupleOf(oldTuple), false);
31 }
32 if(newTuple != null) {
33 listener.update(key, Tuples.flatTupleOf(newTuple), true);
34 }
35 }
36 }
37
38 private Object[] isMatching(Object[] tuple, ITuple filter) {
39 for(int i = 0; i<filter.getSize(); i++) {
40 final Object filterObject = filter.get(i);
41 if(filterObject != null && !filterObject.equals(tuple[i])) {
42 return null;
43 }
44 }
45 return tuple;
46 }
47
48 @Override
49 public int hashCode() {
50 return Objects.hash(filter, key, listener);
51 }
52
53 @Override
54 public boolean equals(Object obj) {
55 if (this == obj)
56 return true;
57 if (!(obj instanceof RelationUpdateListenerEntry))
58 return false;
59 RelationUpdateListenerEntry<?> other = (RelationUpdateListenerEntry<?>) obj;
60 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key)
61 && Objects.equals(listener, other.listener);
62 }
63}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java
new file mode 100644
index 00000000..01948828
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java
@@ -0,0 +1,32 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
4import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
6import org.eclipse.viatra.solver.data.model.Model;
7
8public class RelationalEngineContext implements IEngineContext{
9 private final IBaseIndex baseIndex = new DummyBaseIndexer();
10 private final RelationalRuntimeContext runtimeContext;
11
12
13 public RelationalEngineContext(Model model, RelationUpdateListener updateListener) {
14 runtimeContext = new RelationalRuntimeContext(model, updateListener);
15 }
16
17 @Override
18 public IBaseIndex getBaseIndex() {
19 return this.baseIndex;
20 }
21
22 @Override
23 public void dispose() {
24 //lifecycle not controlled by engine
25 }
26
27 @Override
28 public IQueryRuntimeContext getQueryRuntimeContext() {
29 return runtimeContext;
30 }
31
32}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java
new file mode 100644
index 00000000..de500fc9
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java
@@ -0,0 +1,57 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import java.util.Collection;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.HashSet;
7import java.util.Map;
8import java.util.Set;
9
10import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
11import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
12import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
13import org.eclipse.viatra.solver.data.query.view.RelationView;
14
15/**
16 * The meta context information for String scopes.
17 */
18public final class RelationalQueryMetaContext extends AbstractQueryMetaContext {
19
20 @Override
21 public boolean isEnumerable(IInputKey key) {
22 ensureValidKey(key);
23 return key.isEnumerable();
24 }
25
26 @Override
27 public boolean isStateless(IInputKey key) {
28 ensureValidKey(key);
29 return key instanceof RelationView<?>;
30 }
31
32 @Override
33 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
34 ensureValidKey(implyingKey);
35 return new HashSet<InputKeyImplication>();
36 }
37
38 @Override
39 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
40 ensureValidKey(key);
41 if (key instanceof RelationView) {
42 return new HashMap<Set<Integer>, Set<Integer>>();
43 } else {
44 return Collections.emptyMap();
45 }
46 }
47
48 public void ensureValidKey(IInputKey key) {
49 if (! (key instanceof RelationView<?>))
50 illegalInputKey(key);
51 }
52
53 public void illegalInputKey(IInputKey key) {
54 throw new IllegalArgumentException("The input key " + key + " is not a valid input key.");
55 }
56
57}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java
new file mode 100644
index 00000000..7d1682b2
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java
@@ -0,0 +1,187 @@
1package org.eclipse.viatra.solver.data.query.internal;
2
3import static org.eclipse.viatra.solver.data.util.CollectionsUtil.filter;
4import static org.eclipse.viatra.solver.data.util.CollectionsUtil.map;
5
6import java.lang.reflect.InvocationTargetException;
7import java.util.Iterator;
8import java.util.Optional;
9import java.util.concurrent.Callable;
10
11import org.eclipse.viatra.query.runtime.base.core.NavigationHelperImpl;
12import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
13import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext;
14import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
15import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
16import org.eclipse.viatra.query.runtime.matchers.context.IndexingService;
17import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
19import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
20import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
21import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
22import org.eclipse.viatra.solver.data.model.Model;
23import org.eclipse.viatra.solver.data.query.view.RelationView;
24
25public class RelationalRuntimeContext implements IQueryRuntimeContext {
26 private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext();
27 private final RelationUpdateListener relationUpdateListener;
28 private final Model model;
29
30 public RelationalRuntimeContext(Model model, RelationUpdateListener relationUpdateListener) {
31 this.model = model;
32 this.relationUpdateListener = relationUpdateListener;
33 }
34
35 @Override
36 public IQueryMetaContext getMetaContext() {
37 return metaContext;
38 }
39
40 //
41 /**
42 * TODO: check {@link NavigationHelperImpl#coalesceTraversals(Callable)}
43 */
44 @Override
45 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
46 try {
47 return callable.call();
48 } catch (Exception e) {
49 throw new InvocationTargetException(e);
50 }
51 }
52
53 @Override
54 public boolean isCoalescing() {
55 return true;
56 }
57
58 @Override
59 public boolean isIndexed(IInputKey key, IndexingService service) {
60 if(key instanceof RelationView<?>) {
61 RelationView<?> relationalKey = (RelationView<?>) key;
62 return this.relationUpdateListener.containsRelationalView(relationalKey);
63 } else {
64 return false;
65 }
66 }
67
68 @Override
69 public void ensureIndexed(IInputKey key, IndexingService service) {
70 if(!isIndexed(key, service)) {
71 throw new IllegalStateException("Engine tries to index a new key " +key);
72 }
73 }
74
75 RelationView<?> checkKey(IInputKey key) {
76 if(key instanceof RelationView) {
77 RelationView<?> relationViewKey = (RelationView<?>) key;
78 if(relationUpdateListener.containsRelationalView(relationViewKey)) {
79 return relationViewKey;
80 } else {
81 throw new IllegalStateException("Query is asking for non-indexed key");
82 }
83 } else {
84 throw new IllegalStateException("Query is asking for non-relational key");
85 }
86 }
87
88 @Override
89 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
90 RelationView<?> relationalViewKey = checkKey(key);
91 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
92 Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed));
93 Iterator<Object[]> iterator = filteredBySeed.iterator();
94 int result = 0;
95 while(iterator.hasNext()) {
96 iterator.next();
97 result++;
98 }
99 return result;
100 }
101
102 @Override
103 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
104 return Optional.empty();
105 }
106
107 @Override
108 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
109 RelationView<?> relationalViewKey = checkKey(key);
110 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
111 Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed));
112 return map(filteredBySeed,Tuples::flatTupleOf);
113 }
114
115 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
116 for(int i=0; i<seedMask.indices.length; i++) {
117 final Object seedElement = seed.get(i);
118 final Object tupleElement = tuple[seedMask.indices[i]];
119 if(!tupleElement.equals(seedElement)) {
120 return false;
121 }
122 }
123 return true;
124 }
125// private Object[] toObjectMask(RelationViewKey<?> relationalViewKey, TupleMask seedMask, ITuple seed) {
126// final int arity = relationalViewKey.getArity();
127// Object[] result = new Object[arity];
128// for(int i = 0; i<seedMask.indices.length; i++) {
129// result[seedMask.indices[i]] = seed.get(i);
130// }
131// return result;
132// }
133
134 @Override
135 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
136 return enumerateTuples(key, seedMask, seed);
137 }
138
139 @Override
140 public boolean containsTuple(IInputKey key, ITuple seed) {
141 RelationView<?> relationalViewKey = checkKey(key);
142 return relationalViewKey.get(model,seed.getElements());
143 }
144
145 @Override
146 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
147 RelationView<?> relationalKey = checkKey(key);
148 this.relationUpdateListener.addListener(relationalKey, seed, listener);
149
150 }
151
152 @Override
153 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
154 RelationView<?> relationalKey = checkKey(key);
155 this.relationUpdateListener.removeListener(relationalKey, seed, listener);
156 }
157
158 @Override
159 public Object wrapElement(Object externalElement) {
160 return externalElement;
161 }
162
163 @Override
164 public Object unwrapElement(Object internalElement) {
165 return internalElement;
166 }
167
168 @Override
169 public Tuple wrapTuple(Tuple externalElements) {
170 return externalElements;
171 }
172
173 @Override
174 public Tuple unwrapTuple(Tuple internalElements) {
175 return internalElements;
176 }
177
178 @Override
179 public void ensureWildcardIndexing(IndexingService service) {
180 throw new UnsupportedOperationException();
181 }
182
183 @Override
184 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException {
185 runnable.run();
186 }
187}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FilteredRelationView.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FilteredRelationView.java
new file mode 100644
index 00000000..edc534b7
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FilteredRelationView.java
@@ -0,0 +1,48 @@
1package org.eclipse.viatra.solver.data.query.view;
2
3import java.util.function.BiPredicate;
4
5import org.eclipse.viatra.solver.data.model.Model;
6import org.eclipse.viatra.solver.data.model.Tuple;
7import org.eclipse.viatra.solver.data.model.Tuple.Tuple1;
8import org.eclipse.viatra.solver.data.model.representation.Relation;
9
10public class FilteredRelationView<D> extends RelationView<D>{
11 private final BiPredicate<Tuple,D> predicate;
12
13 public FilteredRelationView(Relation<D> representation, BiPredicate<Tuple,D> predicate) {
14 super(representation);
15 this.predicate = predicate;
16 }
17 @Override
18 protected Object[] forwardMap(Tuple key, D value) {
19 return toTuple1Array(key);
20 }
21 @Override
22 public boolean get(Model model, Object[] tuple) {
23 int[] content = new int[tuple.length];
24 for(int i = 0; i<tuple.length; i++) {
25 content[i] =((Tuple1)tuple[i]).get(0);
26 }
27 Tuple key = Tuple.of(content);
28 D value = model.get(representation, key);
29 return filter(key, value);
30 }
31
32 public static Object[] toTuple1Array(Tuple t) {
33 Object[] result = new Object[t.getSize()];
34 for(int i = 0; i<t.getSize(); i++) {
35 result[i] = t.get(i);
36 }
37 return result;
38 }
39
40 @Override
41 public int getArity() {
42 return this.representation.getArity();
43 }
44 @Override
45 protected boolean filter(Tuple key, D value) {
46 return this.predicate.test(key, value);
47 }
48}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FunctionalRelationView.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FunctionalRelationView.java
new file mode 100644
index 00000000..4aa7cfd0
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/FunctionalRelationView.java
@@ -0,0 +1,50 @@
1package org.eclipse.viatra.solver.data.query.view;
2
3import org.eclipse.viatra.solver.data.model.Model;
4import org.eclipse.viatra.solver.data.model.Tuple;
5import org.eclipse.viatra.solver.data.model.Tuple.Tuple1;
6import org.eclipse.viatra.solver.data.model.representation.Relation;
7
8public class FunctionalRelationView<D> extends RelationView<D> {
9
10 public FunctionalRelationView(Relation<D> representation) {
11 super(representation);
12 }
13
14 @Override
15 protected boolean filter(Tuple key, D value) {
16 return true;
17 }
18
19 @Override
20 protected Object[] forwardMap(Tuple key, D value) {
21 return toTuple1ArrayPlusValue(key, value);
22 }
23
24 @Override
25 public boolean get(Model model, Object[] tuple) {
26 int[] content = new int[tuple.length-1];
27 for(int i = 0; i<tuple.length-1; i++) {
28 content[i] =((Tuple1)tuple[i]).get(0);
29 }
30 Tuple key = Tuple.of(content);
31 @SuppressWarnings("unchecked")
32 D valueInTuple = (D) tuple[tuple.length-1];
33 D valueInMap = model.get(representation, key);
34 return valueInTuple.equals(valueInMap);
35 }
36
37 public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) {
38 Object[] result = new Object[t.getSize()+1];
39 for(int i = 0; i<t.getSize(); i++) {
40 result[i] = t.get(i);
41 }
42 result[t.getSize()] = value;
43 return result;
44 }
45
46 @Override
47 public int getArity() {
48 return this.representation.getArity()+1;
49 }
50}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/view/KeyOnlyRelationView.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/KeyOnlyRelationView.java
new file mode 100644
index 00000000..11a24fc8
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/KeyOnlyRelationView.java
@@ -0,0 +1,16 @@
1package org.eclipse.viatra.solver.data.query.view;
2
3import org.eclipse.viatra.solver.data.model.Tuple;
4import org.eclipse.viatra.solver.data.model.representation.Relation;
5
6public class KeyOnlyRelationView extends FilteredRelationView<Boolean>{
7
8 public KeyOnlyRelationView(Relation<Boolean> representation) {
9 super(representation, (k,v)->true);
10 }
11 @Override
12 protected boolean filter(Tuple key, Boolean value) {
13 return true;
14 }
15
16}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/query/view/RelationView.java b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/RelationView.java
new file mode 100644
index 00000000..c5bc5228
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/query/view/RelationView.java
@@ -0,0 +1,85 @@
1package org.eclipse.viatra.solver.data.query.view;
2
3import java.util.Objects;
4
5import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper;
6import org.eclipse.viatra.solver.data.map.CursorAsIterator;
7import org.eclipse.viatra.solver.data.model.Model;
8import org.eclipse.viatra.solver.data.model.Tuple;
9import org.eclipse.viatra.solver.data.model.representation.Relation;
10
11/**
12 * Represents a view of a {@link Relation} that can be queried.
13 *
14 * @author Oszkar Semerath
15 *
16 * @param <D>
17 */
18public abstract class RelationView<D> extends BaseInputKeyWrapper<RelationView<D>> {
19 protected final Relation<D> representation;
20
21 protected RelationView(Relation<D> representation) {
22 super(null);
23 this.wrappedKey = this;
24 this.representation = representation;
25 }
26
27 @Override
28 public String getPrettyPrintableName() {
29 return representation.getName();
30 }
31
32 @Override
33 public String getStringID() {
34 return representation.getName() + this.getClass().getName();
35 }
36
37 public Relation<D> getRepresentation() {
38 return representation;
39 }
40
41 @Override
42 public boolean isEnumerable() {
43 return true;
44 }
45
46 protected abstract boolean filter(Tuple key, D value);
47
48 protected abstract Object[] forwardMap(Tuple key, D value);
49
50 public abstract boolean get(Model model, Object[] tuple);
51
52 public Object[] transform(Tuple tuple, D value) {
53 if (filter(tuple, value)) {
54 return forwardMap(tuple, value);
55 } else
56 return null;
57 }
58
59 public Iterable<Object[]> getAll(Model model) {
60 return (() -> new CursorAsIterator<>(model.getAll(representation), (k, v) -> forwardMap(k, v),
61 (k, v) -> filter(k, v)));
62 }
63
64 @Override
65 public int hashCode() {
66 final int prime = 31;
67 int result = 1;
68 result = prime * result + Objects.hash(representation);
69 return result;
70 }
71
72 @Override
73 public boolean equals(Object obj) {
74 if (this == obj)
75 return true;
76 if (!super.equals(obj))
77 return false;
78 if (!(obj instanceof RelationView))
79 return false;
80 @SuppressWarnings("unchecked")
81 RelationView<D> other = ((RelationView<D>) obj);
82 return Objects.equals(representation, other.representation);
83 }
84
85}
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/util/CollectionsUtil.java b/store/src/main/java/org/eclipse/viatra/solver/data/util/CollectionsUtil.java
new file mode 100644
index 00000000..21b0a9df
--- /dev/null
+++ b/store/src/main/java/org/eclipse/viatra/solver/data/util/CollectionsUtil.java
@@ -0,0 +1,72 @@
1package org.eclipse.viatra.solver.data.util;
2
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5import java.util.function.Function;
6import java.util.function.Predicate;
7
8public final class CollectionsUtil {
9 private CollectionsUtil() {
10 throw new UnsupportedOperationException();
11 }
12
13 public static <S,T> Iterator<T> map(Iterator<S> source, Function<S, T> transformation) {
14 return new Iterator<T>() {
15
16 @Override
17 public boolean hasNext() {
18 return source.hasNext();
19 }
20
21 @Override
22 public T next() {
23 return transformation.apply(source.next());
24 }
25 };
26 }
27
28 public static <S,T> Iterable<T> map(Iterable<S> source, Function<S, T> transformation) {
29 return (()->map(source.iterator(),transformation));
30 }
31
32 public static <T> Iterator<T> filter(Iterator<T> source, Predicate<T> condition) {
33 return new Iterator<T>() {
34 T internalNext = move();
35 boolean internalHasNext;
36
37 private T move() {
38 internalHasNext = source.hasNext();
39 if(internalHasNext) {
40 internalNext = source.next();
41 }
42 while(internalHasNext && !condition.test(internalNext)) {
43 internalHasNext = source.hasNext();
44 if(internalHasNext) {
45 internalNext = source.next();
46 }
47 }
48 return internalNext;
49 }
50
51 @Override
52 public boolean hasNext() {
53 return internalHasNext;
54 }
55
56 @Override
57 public T next() {
58 if(!internalHasNext) {
59 throw new NoSuchElementException();
60 } else {
61 T result = internalNext;
62 move();
63 return result;
64 }
65 }
66 };
67 }
68
69 public static <T> Iterable<T> filter(Iterable<T> source, Predicate<T> condition) {
70 return (()->filter(source.iterator(),condition));
71 }
72}