diff options
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.java | 411 |
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.map.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import org.eclipse.viatra.solver.data.map.ContinousHashProvider; | ||
7 | |||
8 | public 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 | } | ||