aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java')
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java485
1 files changed, 485 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java
new file mode 100644
index 00000000..8b1944c1
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java
@@ -0,0 +1,485 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.matchers.util;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.NoSuchElementException;
14import java.util.Objects;
15import java.util.stream.Stream;
16import java.util.stream.StreamSupport;
17
18import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet;
19
20/**
21 * Specialized multimap implementation that saves memory
22 * by storing singleton value objects (multiplicity 1) instead of multiset buckets
23 * whenever there is only one value associated with a key.
24 *
25 * <p> See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types.
26 *
27 * <p> Implemented as a Key->Object map with invariant: <ul>
28 * <li> key maps to null if associated with no values;
29 * <li> key maps to a single Value iff it is associated with a single value of multiplicity +1;
30 * <li> key maps to Bucket otherwise
31 * </ul>
32 *
33 * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps.
34 *
35 * <p> Not intended as an API, but rather as a 'base class' for implementors.
36 * Realized as an interface with default implementations, instead of an abstract class,
37 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
38 *
39 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
40 * and bind the lowLevel* methods accordingly.
41 *
42 * @noreference This interface is not intended to be referenced by clients.
43 * @noimplement This interface is not intended to be implemented by clients.
44 *
45 * @author Gabor Bergmann
46 * @since 2.0
47 *
48 *
49 */
50public interface IMultiLookupAbstract<Key, Value, Bucket extends MarkedMemory<Value>> extends IMultiLookup<Key, Value> {
51
52 // the following methods must be bound to a concrete Map<Key,Object>-like structure (primitive implementation allowed)
53
54 /**
55 * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map
56 */
57 abstract Object lowLevelGet(Key key);
58
59 /**
60 * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map
61 */
62 abstract Object lowLevelGetUnsafe(Object key);
63
64 /**
65 * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map
66 */
67 abstract Object lowLevelRemove(Key key);
68
69 /**
70 * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map
71 */
72 abstract Object lowLevelPutIfAbsent(Key key, Value value);
73
74 /**
75 * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map
76 */
77 abstract void lowLevelPut(Key key, Object valueOrBucket);
78
79 /**
80 * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map
81 */
82 abstract Iterable<Object> lowLevelValues();
83
84 /**
85 * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map
86 */
87 abstract Iterable<Key> lowLevelKeySet();
88
89 /**
90 * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map
91 */
92 abstract int lowLevelSize();
93
94
95 // generic multi-lookup logic
96
97 @Override
98 default boolean lookupExists(Key key) {
99 Object object = lowLevelGet(key);
100 return null != object;
101 }
102
103 @Override
104 public default IMemoryView<Value> lookup(Key key) {
105 Object object = lowLevelGet(key);
106 if (object == null) return null;
107 if (object instanceof MarkedMemory) return (Bucket) object;
108 return yieldSingleton((Value)object);
109 }
110
111 @Override
112 default IMemoryView<Value> lookupAndRemoveAll(Key key) {
113 Object object = lowLevelRemove(key);
114 if (object == null) return EmptyMemory.instance();
115 if (object instanceof MarkedMemory) return (Bucket) object;
116 return yieldSingleton((Value)object);
117 }
118
119 @Override
120 public default IMemoryView<Value> lookupUnsafe(Object key) {
121 Object object = lowLevelGetUnsafe(key);
122 if (object == null) return null;
123 if (object instanceof MarkedMemory) return (Bucket) object;
124 return yieldSingleton((Value)object);
125 }
126
127 @Override
128 public default ChangeGranularity addPair(Key key, Value value) {
129 return addPairInternal(key, value, true);
130 }
131
132 @Override
133 default ChangeGranularity addPairOrNop(Key key, Value value) {
134 return addPairInternal(key, value, false);
135 }
136
137 public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) {
138 Object old = lowLevelPutIfAbsent(key, value);
139 boolean keyChange = (old == null);
140
141 if (keyChange) { // key was not present
142 return ChangeGranularity.KEY;
143 } else { // key was already present
144 Bucket bucket;
145 if (old instanceof MarkedMemory) { // ... as collection
146 bucket = (Bucket) old;
147 } else { // ... as singleton
148 if (!this.duplicatesAllowed() && Objects.equals(value, old)) {
149 if (throwIfImpossible)
150 throw new IllegalStateException();
151 else
152 return ChangeGranularity.DUPLICATE;
153 }
154 bucket = createSingletonBucket((Value) old);
155 lowLevelPut(key, bucket);
156 }
157 // will throw if forbidden duplicate, return false if allowed duplicate
158 if (addToBucket(bucket, value, throwIfImpossible)) {
159 // deltas may become empty or a singleton after addition!
160 if (negativesAllowed()) {
161 if (bucket.isEmpty()) {
162 lowLevelRemove(key);
163 return ChangeGranularity.KEY;
164 } else {
165 handleSingleton(key, bucket);
166 return ChangeGranularity.VALUE;
167 }
168 } else return ChangeGranularity.VALUE;
169 } else return ChangeGranularity.DUPLICATE;
170 }
171 }
172
173 @Override
174 // TODO deltas not supproted yet
175 default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) {
176 if (count == 1) return addPair(key, value);
177 // count > 1, always end up with non-singleton bucket
178
179 Object old = lowLevelGet(key);
180 boolean keyChange = (old == null);
181
182 Bucket bucket;
183 if (keyChange) { // ... nothing associated to key yet
184 bucket = createSingletonBucket(value);
185 lowLevelPut(key, bucket);
186 --count; // one less to increment later
187 } else if (old instanceof MarkedMemory) { // ... as collection
188 bucket = (Bucket) old;
189 } else { // ... as singleton
190 bucket = createSingletonBucket((Value) old);
191 lowLevelPut(key, bucket);
192 }
193
194 boolean newValue = bucket.addSigned(value, count);
195
196 if (keyChange) return ChangeGranularity.KEY;
197 else if (newValue) return ChangeGranularity.VALUE;
198 else return ChangeGranularity.DUPLICATE;
199 }
200
201 @Override
202 public default ChangeGranularity removePair(Key key, Value value) {
203 return removePairInternal(key, value, true);
204 }
205
206 @Override
207 default ChangeGranularity removePairOrNop(Key key, Value value) {
208 return removePairInternal(key, value, false);
209 }
210
211 public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) {
212 Object old = lowLevelGet(key);
213 if (old instanceof MarkedMemory) { // ... as collection
214 @SuppressWarnings("unchecked")
215 Bucket bucket = (Bucket) old;
216 // will throw if removing non-existent, return false if removing duplicate
217 boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible);
218 handleSingleton(key, bucket);
219 if (valueChange)
220 return ChangeGranularity.VALUE;
221 else
222 return ChangeGranularity.DUPLICATE;
223 } else if (value.equals(old)) { // matching singleton
224 lowLevelRemove(key);
225 return ChangeGranularity.KEY;
226 } else { // different singleton, will produce a delta if possible
227 if (negativesAllowed()) {
228 Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported
229 lowLevelPut(key, deltaBucket);
230 return ChangeGranularity.VALUE; // no key change
231 } else {
232 if (throwIfImpossible)
233 throw new IllegalStateException();
234 else
235 return ChangeGranularity.DUPLICATE;
236 }
237 }
238 }
239
240 public default void handleSingleton(Key key, Bucket bucket) {
241 Value remainingSingleton = asSingleton(bucket);
242 if (remainingSingleton != null) { // only one remains
243 lowLevelPut(key, remainingSingleton);
244 }
245 }
246
247 @Override
248 public default Iterable<Value> distinctValues() {
249 return new Iterable<Value>() {
250 private final Iterator<Value> EMPTY_ITERATOR = Collections.<Value>emptySet().iterator();
251 @Override
252 public Iterator<Value> iterator() {
253 return new Iterator<Value>() {
254 Iterator<Object> bucketIterator = lowLevelValues().iterator();
255 Iterator<Value> elementIterator = EMPTY_ITERATOR;
256
257 @Override
258 public boolean hasNext() {
259 return (elementIterator.hasNext() || bucketIterator.hasNext());
260 }
261
262 @Override
263 public Value next() {
264 if (elementIterator.hasNext())
265 return elementIterator.next();
266 else if (bucketIterator.hasNext()) {
267 Object bucket = bucketIterator.next();
268 if (bucket instanceof MarkedMemory) {
269 elementIterator =
270 ((MarkedMemory) bucket).distinctValues().iterator();
271 return elementIterator.next();
272 } else {
273 elementIterator = EMPTY_ITERATOR;
274 return (Value) bucket;
275 }
276 } else
277 throw new NoSuchElementException();
278 }
279
280 /**
281 * Not implemented
282 */
283 @Override
284 public void remove() {
285 throw new UnsupportedOperationException();
286 }
287
288 };
289 }
290 };
291 }
292
293 @Override
294 default Stream<Value> distinctValuesStream() {
295 return StreamSupport.stream(distinctValues().spliterator(), false);
296 }
297
298 @Override
299 default Iterable<Key> distinctKeys() {
300 return lowLevelKeySet();
301 }
302
303 @Override
304 default Stream<Key> distinctKeysStream() {
305 return StreamSupport.stream(distinctKeys().spliterator(), false);
306 }
307
308 @Override
309 default int countKeys() {
310 return lowLevelSize();
311 }
312
313 // the following methods are customized for bucket type
314
315 /**
316 * @return iff negative multiplicites are allowed
317 */
318 abstract boolean negativesAllowed();
319
320 /**
321 * @return iff larger-than-1 multiplicites are allowed
322 * @since 2.3
323 */
324 abstract boolean duplicatesAllowed();
325
326 /**
327 * Increases the multiplicity of the value in the bucket.
328 * @return true iff non-duplicate
329 * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified
330 */
331 abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible);
332
333 /**
334 * Decreases the multiplicity of the value in the bucket.
335 * @return false if removing duplicate value
336 * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified
337 */
338 abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible);
339
340 /**
341 * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1
342 * @return the singleton value, or null if the bucket is not singleton
343 */
344 abstract Value asSingleton(Bucket bucket);
345
346 /**
347 * @return a new bucket consisting of a sole value
348 */
349 abstract Bucket createSingletonBucket(Value value);
350 /**
351 * @return a read-only bucket consisting of a sole value, to be returned to the user
352 */
353 default IMemoryView<Value> yieldSingleton(Value value) {
354 return new SingletonMemoryView<>(value);
355 }
356
357 /**
358 * @param positive the previously existing value, or null if the delta is to contain a single negative tuple
359 * @return a new bucket consisting of a delta of two values
360 * @throws IllegalStateException if deltas not supported
361 */
362 abstract Bucket createDeltaBucket(Value positive, Value negative);
363
364 /**
365 * A multi-lookup whose buckets are sets.
366 *
367 * <p> Not intended as an API, but rather as a 'base class' for implementors.
368 * Realized as an interface with default implementations, instead of an abstract class,
369 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
370 *
371 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
372 * and bind the lowLevel* methods accordingly.
373 *
374 * @noreference This interface is not intended to be referenced by clients.
375 * @noimplement This interface is not intended to be implemented by clients.
376 * @author Gabor Bergmann
377 */
378 public static interface ToSetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedSet<Value>> {
379 /**
380 * @return a fresh, empty marked set
381 */
382 public MarkedSet<Value> createMarkedSet();
383
384 @Override
385 public default boolean negativesAllowed() {
386 return false;
387 }
388 @Override
389 default boolean duplicatesAllowed() {
390 return false;
391 }
392
393 @Override
394 public default boolean addToBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) {
395 if (bucket.addOne(value)) return true;
396 else if (throwIfImpossible) throw new IllegalStateException();
397 else return false;
398 }
399
400 @Override
401 public default boolean removeFromBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) {
402 return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value);
403 }
404
405 @Override
406 public default Value asSingleton(MarkedSet<Value> bucket) {
407 return bucket.size() == 1 ? bucket.iterator().next() : null;
408 }
409
410 @Override
411 public default MarkedSet<Value> createSingletonBucket(Value value) {
412 MarkedSet<Value> result = createMarkedSet();
413 result.addOne(value);
414 return result;
415 }
416
417 @Override
418 public default MarkedSet<Value> createDeltaBucket(Value positive, Value negative) {
419 throw new IllegalStateException();
420 }
421 }
422
423 /**
424 * A multi-lookup whose buckets are multisets.
425 *
426 * <p> Not intended as an API, but rather as a 'base class' for implementors.
427 * Realized as an interface with default implementations, instead of an abstract class,
428 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
429 *
430 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
431 * and bind the lowLevel* methods accordingly.
432 *
433 * @noreference This interface is not intended to be referenced by clients.
434 * @noimplement This interface is not intended to be implemented by clients.
435 * @author Gabor Bergmann
436 */
437 public static interface ToMultisetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedMultiset<Value>> {
438 /**
439 * @return a fresh, empty marked multiset
440 */
441 public MarkedMemory.MarkedMultiset<Value> createMarkedMultiset();
442
443 @Override
444 public default boolean negativesAllowed() {
445 return false;
446 }
447 @Override
448 default boolean duplicatesAllowed() {
449 return true;
450 }
451
452 @Override
453 public default boolean addToBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) {
454 return bucket.addOne(value);
455 }
456
457 @Override
458 public default boolean removeFromBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) {
459 return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value);
460 }
461
462 @Override
463 public default Value asSingleton(MarkedMemory.MarkedMultiset<Value> bucket) {
464 if (bucket.size() != 1) return null;
465 Value candidate = bucket.iterator().next();
466 return bucket.getCount(candidate) == 1 ? candidate : null;
467 }
468
469 @Override
470 public default MarkedMemory.MarkedMultiset<Value> createSingletonBucket(Value value) {
471 MarkedMemory.MarkedMultiset<Value> result = createMarkedMultiset();
472 result.addOne(value);
473 return result;
474 }
475
476 @Override
477 public default MarkedMemory.MarkedMultiset<Value> createDeltaBucket(Value positive, Value negative) {
478 throw new IllegalStateException();
479 }
480 }
481
482
483 // TODO add ToDeltaBagsAbstract
484
485} \ No newline at end of file