aboutsummaryrefslogtreecommitdiffstats
path: root/model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java')
-rw-r--r--model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java411
1 files changed, 411 insertions, 0 deletions
diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java
new file mode 100644
index 00000000..963ce111
--- /dev/null
+++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java
@@ -0,0 +1,411 @@
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<KEY,VALUE> extends Node<KEY,VALUE> {
9 int cachedHash;
10 protected Object[] content;
11
12 protected MutableNode() {
13 this.content = new Object[2*factor];
14 updateHash();
15 }
16 public static <KEY,VALUE> MutableNode<KEY,VALUE> initialize(
17 KEY key, VALUE value,
18 ContinousHashProvider<? super KEY> hashProvider,
19 VALUE defaultValue)
20 {
21 if(value == defaultValue) {
22 return null;
23 } else {
24 int hash = hashProvider.getHash(key, 0);
25 int fragment = hashFragment(hash, 0);
26 MutableNode<KEY, VALUE> res = new MutableNode<KEY, VALUE>();
27 res.content[2*fragment] = key;
28 res.content[2*fragment+1] = value;
29 res.updateHash();
30 return res;
31 }
32 }
33
34 /**
35 * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode}
36 * @param node
37 */
38 protected MutableNode(ImmutableNode<KEY,VALUE> node) {
39 this.content = new Object[2*factor];
40 int dataUsed=0;
41 int nodeUsed=0;
42 for(int i=0; i<factor; i++) {
43 int bitposition = 1 << i;
44 if((node.dataMap & bitposition) != 0) {
45 content[2*i] = node.content[dataUsed*2];
46 content[2*i+1] = node.content[dataUsed*2+1];
47 dataUsed++;
48 } else if((node.nodeMap & bitposition) != 0) {
49 content[2*i+1] = node.content[node.content.length-1-nodeUsed];
50 nodeUsed++;
51 }
52 }
53 this.cachedHash = node.hashCode();
54 }
55
56 @SuppressWarnings("unchecked")
57 @Override
58 public VALUE getValue(KEY key, ContinousHashProvider<? super KEY> hashProvider, VALUE defaultValue, int hash, int depth) {
59 int selectedHashFragment = hashFragment(hash,shiftDepth(depth));
60 KEY keyCandidate = (KEY) this.content[2*selectedHashFragment];
61 if(keyCandidate != null) {
62 if(keyCandidate.equals(key)) {
63 return (VALUE) this.content[2*selectedHashFragment+1];
64 } else {
65 return defaultValue;
66 }
67 } else {
68 Node<KEY,VALUE> nodeCandidate = (Node<KEY,VALUE>) content[2*selectedHashFragment+1];
69 if(nodeCandidate != null) {
70 int newDepth = depth+1;
71 int newHash = newHash(hashProvider, key, hash, newDepth);
72 return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth);
73 } else {
74 return defaultValue;
75 }
76 }
77 }
78
79 private boolean hasContent() {
80 for(Object element : this.content) {
81 if(element != null) return true;
82 }
83 return false;
84 }
85
86 @SuppressWarnings("unchecked")
87 @Override
88 public Node<KEY,VALUE> putValue(KEY key, VALUE value, ContinousHashProvider<? super KEY> hashProvider, VALUE defaultValue, int hash, int depth) {
89 int selectedHashFragment = hashFragment(hash,shiftDepth(depth));
90 KEY keyCandidate = (KEY) content[2*selectedHashFragment];
91 if(keyCandidate != null) {
92 // If has key
93 if(keyCandidate.equals(key)) {
94 // The key is equals to an existing key -> update entry
95 if(value == defaultValue) {
96 return removeEntry(selectedHashFragment);
97 } else {
98 return updateValue(value,selectedHashFragment);
99 }
100 } else {
101 // The key is not equivalent to an existing key on the same hash bin
102 // -> split entry if it is necessary
103 if(value == defaultValue) {
104 // Value is default -> do not need to add new node
105 return this;
106 } else {
107 // Value is not default -> Split entry data to a new node
108 return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment);
109 }
110 }
111 } else {
112 // If it does not have key, check for value
113 Node<KEY,VALUE> nodeCandidate = (Node<KEY,VALUE>) content[2*selectedHashFragment+1];
114 if(nodeCandidate != null) {
115 // If it has value, it is a subnode -> upate that
116 Node<KEY, VALUE> newNode = nodeCandidate.putValue(key, value, hashProvider, defaultValue, newHash(hashProvider, key, hash, depth+1), depth+1);
117 return updateSubNode(selectedHashFragment,newNode);
118 } else {
119 // If it does not have value, put it in the empty place
120 if(value == defaultValue) {
121 // dont need to add new key-value pair
122 return this;
123 } else {
124 return addEntry(key, value, selectedHashFragment);
125 }
126
127 }
128 }
129 }
130
131
132
133 private Node<KEY, VALUE> addEntry(KEY key, VALUE value, int selectedHashFragment) {
134 content[2*selectedHashFragment] = key;
135 content[2*selectedHashFragment+1] = value;
136 updateHash();
137 return this;
138 }
139 /**
140 * Updates an entry in a selected hash-fragment to a non-default value.
141 * @param value
142 * @param selectedHashFragment
143 * @return
144 */
145 Node<KEY, VALUE> updateValue(VALUE value, int selectedHashFragment) {
146 content[2*selectedHashFragment+1] = value;
147 updateHash();
148 return this;
149 }
150
151 /**
152 *
153 * @param selectedHashFragment
154 * @param newNode
155 * @return
156 */
157 Node<KEY, VALUE> updateSubNode(int selectedHashFragment, Node<KEY, VALUE> newNode) {
158 content[2*selectedHashFragment+1] = newNode;
159 for(int i = 0; i<this.content.length; i++) {
160 if(content[i]!=null) {
161 updateHash();
162 return this;
163 }
164 }
165 return null;
166 }
167
168 @SuppressWarnings("unchecked")
169 private Node<KEY, VALUE> moveDownAndSplit(
170 ContinousHashProvider<? super KEY> hashProvider,
171 KEY newKey, VALUE newValue, KEY previousKey,
172 int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth)
173 {
174 VALUE previousValue = (VALUE) content[2*selectedHashFragmentOfCurrentDepth+1];
175
176 //final int newHash = newHash(hashProvider, newKey, hashOfNewKey, newDepth);
177 MutableNode<KEY,VALUE> newSubNode = newNodeWithTwoEntries(
178 hashProvider,
179 previousKey, previousValue, hashProvider.getHash(previousKey, hashDepth(depth)),
180 newKey, newValue, hashOfNewKey,
181 depth+1);
182
183 content[2*selectedHashFragmentOfCurrentDepth] = null;
184 content[2*selectedHashFragmentOfCurrentDepth+1] = newSubNode;
185 updateHash();
186 return this;
187 }
188 private MutableNode<KEY,VALUE> newNodeWithTwoEntries(
189 ContinousHashProvider<? super KEY> hashProvider,
190 KEY key1, VALUE value1, int oldHash1,
191 KEY key2, VALUE value2, int oldHash2,
192 int newdepth)
193 {
194// final int depthLimit = 4000;
195// if(newdepth>depthLimit) {
196// final int newHashes = 4000/numberOfFactors;
197// throw new IllegalArgumentException(
198// "Continuous hash was same " + newHashes + " times for non-equivalent objects " + key1 + " and " + key2 +".");
199// }
200 int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth);
201 int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth);
202 int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth));
203 int newFragment2 = hashFragment(newHash2, shiftDepth(newdepth));
204
205 MutableNode<KEY,VALUE> subNode = new MutableNode<KEY,VALUE>();
206 if(newFragment1 != newFragment2) {
207 subNode.content[newFragment1*2]=key1;
208 subNode.content[newFragment1*2+1]=value1;
209
210 subNode.content[newFragment2*2]=key2;
211 subNode.content[newFragment2*2+1]=value2;
212 } else {
213 MutableNode<KEY,VALUE> subSubNode = newNodeWithTwoEntries(
214 hashProvider,
215 key1, value1, newHash1,
216 key2, value2, newHash2,
217 newdepth+1);
218 subNode.content[newFragment1*2+1] = subSubNode;
219 }
220 subNode.updateHash();
221 return subNode;
222 }
223
224 Node<KEY, VALUE> removeEntry(int selectedHashFragment) {
225 content[2*selectedHashFragment] = null;
226 content[2*selectedHashFragment+1] = null;
227 if(hasContent()) {
228 updateHash();
229 return this;
230 } else {
231 return null;
232 }
233 }
234
235 @SuppressWarnings("unchecked")
236 @Override
237 public long getSize() {
238 int size = 0;
239 for(int i=0; i<factor; i++) {
240 if(content[i*2]!=null) {
241 size++;
242 } else{
243 Node<KEY,VALUE> nodeCandidate = (Node<KEY, VALUE>) content[i*2+1];
244 if(nodeCandidate!=null) {
245 size+=nodeCandidate.getSize();
246 }
247 }
248 }
249 return size;
250 }
251
252 @Override
253 protected MutableNode<KEY,VALUE> toMutable() {
254 return this;
255 }
256
257 @Override
258 public ImmutableNode<KEY,VALUE> toImmutable() {
259 return ImmutableNode.constructImmutable(this,null);
260 }
261
262 @Override
263 public ImmutableNode<KEY, VALUE> toImmutable(Map<Node<KEY, VALUE>, ImmutableNode<KEY, VALUE>> cache) {
264 return ImmutableNode.constructImmutable(this,cache);
265 }
266
267 @SuppressWarnings("unchecked")
268 @Override
269 boolean moveToNext(MapCursor<KEY, VALUE> cursor) {
270 // 1. try to move to data
271 if(cursor.dataIndex != MapCursor.IndexFinish) {
272 for(int index = cursor.dataIndex+1; index < factor; index++) {
273 if(this.content[index*2]!=null) {
274 // 1.1 found next data
275 cursor.dataIndex = index;
276 cursor.key = (KEY) this.content[index*2];
277 cursor.value = (VALUE) this.content[index*2+1];
278 return true;
279 }
280 }
281 cursor.dataIndex = MapCursor.IndexFinish;
282 }
283
284 // 2. look inside the subnodes
285 for(int index = cursor.nodeIndexStack.peek()+1; index < factor; index++) {
286 if(this.content[index*2]==null && this.content[index*2+1] !=null) {
287 // 2.1 found next subnode, move down to the subnode
288 Node<KEY, VALUE> subnode = (Node<KEY, VALUE>) this.content[index*2+1];
289
290 cursor.dataIndex = MapCursor.IndexStart;
291 cursor.nodeIndexStack.pop();
292 cursor.nodeIndexStack.push(index);
293 cursor.nodeIndexStack.push(MapCursor.IndexStart);
294 cursor.nodeStack.push(subnode);
295
296
297 return subnode.moveToNext(cursor);
298 }
299 }
300 // 3. no subnode found, move up
301 cursor.nodeStack.pop();
302 cursor.nodeIndexStack.pop();
303 if(!cursor.nodeStack.isEmpty()) {
304 Node<KEY, VALUE> supernode = cursor.nodeStack.peek();
305 return supernode.moveToNext(cursor);
306 } else {
307 cursor.key = null;
308 cursor.value = null;
309 return false;
310 }
311 }
312
313 @Override
314 public void prettyPrint(StringBuilder builder, int depth, int code) {
315 for(int i = 0; i<depth; i++) {
316 builder.append("\t");
317 }
318 if(code>=0) {
319 builder.append(code);
320 builder.append(":");
321 }
322 builder.append("Mutable(");
323 // print content
324 boolean hadContent = false;
325 for(int i = 0; i<factor; i++) {
326 if(content[2*i] != null) {
327 if(hadContent) {
328 builder.append(",");
329 }
330 builder.append(i);
331 builder.append(":[");
332 builder.append(content[2*i].toString());
333 builder.append("]->[");
334 builder.append(content[2*i+1].toString());
335 builder.append("]");
336 hadContent = true;
337 }
338 }
339 builder.append(")");
340 // print subnodes
341 for(int i = 0; i<factor; i++) {
342 if(content[2*i] == null && content[2*i+1]!=null) {
343 @SuppressWarnings("unchecked")
344 Node<KEY,VALUE> subNode = (Node<KEY, VALUE>) content[2*i+1];
345 builder.append("\n");
346 subNode.prettyPrint(builder, depth+1, i);
347 }
348 }
349 }
350 @SuppressWarnings({"unchecked"})
351 @Override
352 public void checkIntegrity(ContinousHashProvider<? super KEY> hashProvider, VALUE defaultValue, int depth) {
353 for(int i = 0; i<factor; i++) {
354 if(this.content[2*i] != null) {
355 KEY key = (KEY) this.content[2*i];
356 VALUE value = (VALUE) this.content[2*i+1];
357
358 if(value == defaultValue) {
359 throw new IllegalStateException("Node contains default value!");
360 }
361 int hashCode = hashProvider.getHash(key, hashDepth(depth));
362 int shiftDepth = shiftDepth(depth);
363 int selectedHashFragment = hashFragment(hashCode, shiftDepth);
364 if(i != selectedHashFragment) {
365 throw new IllegalStateException(
366 "Key "+key+" with hash code "+hashCode+" is in bad place! Fragment=" + selectedHashFragment +", Place="+i);
367 }
368 }
369 }
370 for(int i = 0; i<factor; i++) {
371 if(this.content[2*i+1] != null && this.content[2*i] == null) {
372 Node<KEY,VALUE> subNode = (Node<KEY, VALUE>) this.content[2*i+1];
373 subNode.checkIntegrity(hashProvider, defaultValue, depth+1);
374 }
375 }
376 int oldHash = this.cachedHash;
377 updateHash();
378 int newHash = this.cachedHash;
379 if(oldHash != newHash) {
380 throw new IllegalStateException("Hash code was not up to date! (old="+oldHash+",new="+newHash+")");
381 }
382 }
383
384
385 protected void updateHash() {
386 this.cachedHash = Arrays.hashCode(content);
387 }
388
389 @Override
390 public int hashCode() {
391 return this.cachedHash;
392 }
393
394
395 @Override
396 public boolean equals(Object obj) {
397 if (this == obj)
398 return true;
399 if (obj == null)
400 return false;
401 if (obj instanceof MutableNode<?, ?>) {
402 MutableNode<?,?> other = (MutableNode<?,?>) obj;
403 return Arrays.deepEquals(this.content, other.content);
404 } else if(obj instanceof ImmutableNode<?,?>) {
405 ImmutableNode<?,?> other = (ImmutableNode<?,?>) obj;
406 return ImmutableNode.compareImmutableMutable(other, this);
407 } else {
408 return false;
409 }
410 }
411}