aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java')
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java560
1 files changed, 560 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java
new file mode 100644
index 00000000..49c55fef
--- /dev/null
+++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java
@@ -0,0 +1,560 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
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 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.matchers.tuple;
11
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.HashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.OptionalInt;
19import java.util.Set;
20
21/**
22 *
23 * Specifies select indices of a tuple. If viewed through this mask (see {@link #transform(ITuple)}), the signature of the pattern will consist of its
24 * individual substitutions at the given positions, in the exact same order as they appear in indices[].
25 *
26 * @author Gabor Bergmann
27 */
28public class TupleMask {
29 /**
30 * indices[i] specifies the index of the substitution in the original tuple that occupies the i-th place in the
31 * masked signature.
32 */
33 public final int[] indices;
34 /**
35 * the size of the tuple this mask is applied to
36 */
37 public int sourceWidth;
38 /**
39 * indicesSorted is indices, sorted in ascending order.
40 * null by default, call {@link #ensureIndicesSorted()} before using
41 */
42 int[] indicesSorted;
43
44 /**
45 * true if no index occurs twice; computed on demand by {@link #isNonrepeating()}
46 */
47 Boolean isNonrepeating;
48
49 /**
50 * Creates a TupleMask instance with the given indices array
51 * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known.
52 * @since 2.0
53 */
54 protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) {
55 this.indices = indices;
56 this.sourceWidth = sourceWidth;
57 this.indicesSorted = indicesSorted;
58 this.isNonrepeating = isNonrepeating;
59 }
60
61 /**
62 * Creates a TupleMask instance that selects given positions.
63 * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards.
64 *
65 * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known.
66 * @since 2.0
67 */
68 protected static TupleMask fromSelectedIndicesInternal(
69 int[] selectedIndices, int sourceArity,
70 int[] indicesSorted, Boolean isNonrepeating)
71 {
72 if (selectedIndices.length == 0) // is it nullary?
73 return new TupleMask0(sourceArity);
74
75 // is it identity?
76 boolean identity = sourceArity == selectedIndices.length;
77 if (identity) {
78 for (int k=0; k < sourceArity; ++k) {
79 if (selectedIndices[k] != k) {
80 identity = false;
81 break;
82 }
83 }
84 }
85 if (identity)
86 return new TupleMaskIdentity(selectedIndices, sourceArity);
87
88 // generic case
89 return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating);
90 }
91
92 /**
93 * Creates a TupleMask instance that selects given positions in monotonically increasing order.
94 * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards.
95 * @since 2.0
96 */
97 protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity)
98 {
99 return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true);
100 }
101
102 /**
103 * Creates a TupleMask instance of the given size that maps the first 'size' elements intact
104 */
105 public static TupleMask linear(int size, int sourceWidth) {
106 if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth);
107 int[] indices = constructLinearSequence(size);
108 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
109 }
110
111 /**
112 * An array containing the first {@link size} nonnegative integers in order
113 * @since 2.0
114 */
115 protected static int[] constructLinearSequence(int size) {
116 int[] indices = new int[size];
117 for (int i = 0; i < size; i++)
118 indices[i] = i;
119 return indices;
120 }
121
122 /**
123 * Creates a TupleMask instance of the given size that maps every single element intact
124 */
125 public static TupleMask identity(int size) {
126 return new TupleMaskIdentity(size);
127 }
128
129 /**
130 * Creates a TupleMask instance of the given size that does not emit output.
131 */
132 public static TupleMask empty(int sourceWidth) {
133 return linear(0, sourceWidth);
134 }
135
136 /**
137 * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is
138 * omitted
139 */
140 public static TupleMask omit(int omission, int sourceWidth) {
141 int size = sourceWidth - 1;
142 int[] indices = new int[size];
143 for (int i = 0; i < omission; i++)
144 indices[i] = i;
145 for (int i = omission; i < size; i++)
146 indices[i] = i + 1;
147 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
148 }
149
150
151 /**
152 * Creates a TupleMask instance that selects positions where keep is true
153 * @since 1.7
154 */
155 public static TupleMask fromKeepIndicators(boolean[] keep) {
156 int size = 0;
157 for (int k = 0; k < keep.length; ++k)
158 if (keep[k])
159 size++;
160 if (size == keep.length) return new TupleMaskIdentity(size);
161 int[] indices = new int[size];
162 int l = 0;
163 for (int k = 0; k < keep.length; ++k)
164 if (keep[k])
165 indices[l++] = k;
166 return fromSelectedMonotonicIndicesInternal(indices, keep.length);
167 }
168
169 /**
170 * Creates a TupleMask instance that selects given positions.
171 * @since 1.7
172 */
173 public static TupleMask fromSelectedIndices(int sourceArity, Collection<Integer> selectedIndices) {
174 int[] selected = integersToIntArray(selectedIndices);
175 return fromSelectedIndicesInternal(selected, sourceArity, null, null);
176 }
177 /**
178 * Creates a TupleMask instance that selects given positions.
179 * @since 1.7
180 */
181 public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) {
182 return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null);
183 }
184 /**
185 * Creates a TupleMask instance that selects non-null positions of a given tuple
186 * @since 1.7
187 */
188 public static TupleMask fromNonNullIndices(ITuple tuple) {
189 List<Integer> indices = new ArrayList<>();
190 for (int i=0; i < tuple.getSize(); i++) {
191 if (tuple.get(i) != null) {
192 indices.add(i);
193 }
194 }
195 if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size());
196 return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize());
197 }
198 /**
199 * @since 1.7
200 */
201 public static int[] integersToIntArray(Collection<Integer> selectedIndices) {
202 int[] selected = new int[selectedIndices.size()];
203 int k=0;
204 for (Integer integer : selectedIndices) {
205 selected[k++] = integer;
206 }
207 return selected;
208 }
209
210
211 /**
212 * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary.
213 */
214 public static TupleMask displace(int from, int to, int sourceWidth) {
215 if (from == to) return new TupleMaskIdentity(sourceWidth);
216 int[] indices = new int[sourceWidth];
217 for (int i = 0; i < sourceWidth; i++)
218 if (i == to)
219 indices[i] = from;
220 else if (i >= from && i < to)
221 indices[i] = i + 1;
222 else if (i > to && i <= from)
223 indices[i] = i - 1;
224 else
225 indices[i] = i;
226 return fromSelectedIndicesInternal(indices, sourceWidth, null, true);
227 }
228
229 /**
230 * Creates a TupleMask instance that selects a single element of the tuple.
231 */
232 public static TupleMask selectSingle(int selected, int sourceWidth) {
233 int[] indices = { selected };
234 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
235 }
236
237 /**
238 * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by
239 * right. PRE: left and right have the same sourcewidth
240 */
241 public static TupleMask append(TupleMask left, TupleMask right) {
242 int leftLength = left.indices.length;
243 int rightLength = right.indices.length;
244 int[] indices = new int[leftLength + rightLength];
245 for (int i = 0; i < leftLength; ++i)
246 indices[i] = left.indices[i];
247 for (int i = 0; i < rightLength; ++i)
248 indices[i + leftLength] = right.indices[i];
249 return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null);
250 }
251
252 /**
253 * Generates indicesSorted from indices on demand
254 */
255 void ensureIndicesSorted() {
256 if (indicesSorted == null) {
257 indicesSorted = new int[indices.length];
258 List<Integer> list = new LinkedList<Integer>();
259 for (int i = 0; i < indices.length; ++i)
260 list.add(indices[i]);
261 java.util.Collections.sort(list);
262 int i = 0;
263 for (Integer a : list)
264 indicesSorted[i++] = a;
265 }
266 }
267
268
269
270 /**
271 * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected.
272 * <p> PRE: mask indices are all different
273 * @since 2.0
274 */
275 public OptionalInt getFirstOmittedIndex() {
276 ensureIndicesSorted();
277 int column = 0;
278 while (column < getSize() && indicesSorted[column] == column) column++;
279 if (column < getSourceWidth()) return OptionalInt.of(column);
280 else return OptionalInt.empty();
281 }
282
283
284 /**
285 * Returns a selected masked value from the selected tuple.
286 * @pre: 0 <= index < getSize()
287 * @since 1.7
288 */
289 public Object getValue(ITuple original, int index) {
290 return original.get(indices[index]);
291 }
292
293 /**
294 * Sets the selected value in the original tuple based on the mask definition
295 *
296 * @pre: 0 <= index < getSize()
297 * @since 1.7
298 */
299 public void set(IModifiableTuple tuple, int index, Object value) {
300 tuple.set(indices[index], value);
301 }
302
303 /**
304 * Generates an immutable, masked view of the original tuple.
305 * <p> The new tuple will have arity {@link #getSize()},
306 * and will consist of the elements of the original tuple, at positions indicated by this mask.
307 * @since 1.7
308 */
309 public Tuple transform(ITuple original) {
310 switch (indices.length) {
311 case 0:
312 return FlatTuple0.INSTANCE;
313 case 1:
314 return new FlatTuple1(original.get(indices[0]));
315 case 2:
316 return new FlatTuple2(original.get(indices[0]), original.get(indices[1]));
317 case 3:
318 return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]));
319 case 4:
320 return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3]));
321 default:
322 Object signature[] = new Object[indices.length];
323 for (int i = 0; i < indices.length; ++i)
324 signature[i] = original.get(indices[i]);
325 return new FlatTuple(signature);
326 }
327 }
328
329 /**
330 * @return true iff no two selected indices are the same
331 * @since 2.0
332 */
333 public boolean isNonrepeating() {
334 if (isNonrepeating == null) {
335 ensureIndicesSorted();
336 int previous = -1;
337 int i;
338 for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) {
339 previous = indicesSorted[i];
340 }
341 isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition
342 }
343 return isNonrepeating;
344 }
345
346 /**
347 * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple
348 * that are not determined this way will be filled with null.
349 *
350 * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true
351 * @since 1.7
352 */
353 public Tuple revertFrom(ITuple masked) {
354 Object[] signature = new Object[sourceWidth];
355 for (int i = 0; i < indices.length; ++i)
356 signature[indices[i]] = masked.get(i);
357 return Tuples.flatTupleOf(signature);
358 }
359
360 /**
361 * Returns a tuple `result`, same arity as the original tuple, that satisfies
362 * `this.transform(result).equals(this.transform(tuple))`.
363 * Positions of the result tuple that are not determined this way will be filled with null.
364 * <p> In other words, a copy of the original tuple is returned,
365 * with null substituted at each position that is <em>not</em> selected by this mask.
366 *
367 * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true
368 * @since 2.1
369 */
370 public Tuple keepSelectedIndices(ITuple original) {
371 Object[] signature = new Object[sourceWidth];
372 for (int i = 0; i < indices.length; ++i)
373 signature[indices[i]] = original.get(indices[i]);
374 return Tuples.flatTupleOf(signature);
375 }
376
377 /**
378 * Generates an immutable, masked view of the original tuple.
379 * <p> The list will have arity {@link #getSize()},
380 * and will consist of the elements of the original tuple, at positions indicated by this mask.
381 */
382 public <T> List<T> transform(List<T> original) {
383 List<T> signature = new ArrayList<T>(indices.length);
384 for (int i = 0; i < indices.length; ++i)
385 signature.add(original.get(indices[i]));
386 return signature;
387 }
388
389 /**
390 * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask.
391 *
392 * @return a mask that cascades the effects this mask after the mask provided as parameter.
393 */
394 public TupleMask transform(TupleMask mask) {
395 int[] cascadeIndices = new int[indices.length];
396 for (int i = 0; i < indices.length; ++i)
397 cascadeIndices[i] = mask.indices[indices[i]];
398 return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null);
399 }
400
401 // /**
402 // * Generates a complementer mask that maps those elements that were
403 // untouched by the original mask.
404 // * Ordering is left intact.
405 // * A Tuple is used for reference concerning possible equalities among
406 // elements.
407 // */
408 // public TupleMask complementer(Tuple reference)
409 // {
410 // HashSet<Object> touched = new HashSet<Object>();
411 // LinkedList<Integer> untouched = new LinkedList<Integer>();
412 //
413 // for (int index : indices) touched.add(reference.get(index));
414 // for (int index=0; index<reference.getSize(); ++index)
415 // {
416 // if (touched.add(reference.get(index))) untouched.addLast(index);
417 // }
418 //
419 // int[] complementer = new int[untouched.size()];
420 // int k = 0;
421 // for (Integer integer : untouched) complementer[k++] = integer;
422 // return new TupleMask(complementer, reference.getSize());
423 // }
424
425 /**
426 * Combines two substitutions. The new pattern will contain all substitutions of masked and unmasked, assuming that
427 * the elements of masked indicated by this mask are already matched against unmasked.
428 *
429 * POST: the result will start with an exact copy of unmasked
430 *
431 * @param unmasked
432 * primary pattern substitution that is left intact.
433 * @param masked
434 * secondary pattern substitution that is transformed to the end of the result.
435 * @param useInheritance
436 * whether to use inheritance or copy umasked into result instead.
437 * @param asComplementer
438 * whether this mask maps from the masked Tuple to the tail of the result or to the unmasked one.
439 * @return new pattern that is a combination of unmasked and masked.
440 */
441 public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) {
442
443 int combinedLength = asComplementer ? indices.length : masked.getSize() - indices.length;
444 if (!useInheritance)
445 combinedLength += unmasked.getSize();
446 Object combined[] = new Object[combinedLength];
447
448 int cPos = 0;
449 if (!useInheritance) {
450 for (int i = 0; i < unmasked.getSize(); ++i)
451 combined[cPos++] = unmasked.get(i);
452 }
453
454 if (asComplementer) {
455 for (int i = 0; i < indices.length; ++i)
456 combined[cPos++] = masked.get(indices[i]);
457 } else {
458 ensureIndicesSorted();
459 int mPos = 0;
460 for (int i = 0; i < masked.getSize(); ++i)
461 if (mPos < indicesSorted.length && i == indicesSorted[mPos])
462 mPos++;
463 else
464 combined[cPos++] = masked.get(i);
465 }
466
467 return useInheritance ? Tuples.leftInheritanceTupleOf(unmasked, combined) : Tuples.flatTupleOf(combined);
468 }
469
470 @Override
471 public int hashCode() {
472 final int PRIME = 31;
473 int result = sourceWidth;
474 for (int i : indices)
475 result = PRIME * result + i;
476 return result;
477 }
478
479 @Override
480 public boolean equals(Object obj) {
481 if (this == obj)
482 return true;
483 if (obj == null)
484 return false;
485 if (getClass() != obj.getClass())
486 return false;
487 final TupleMask other = (TupleMask) obj;
488 if (sourceWidth != other.sourceWidth)
489 return false;
490 if (indices.length != other.indices.length)
491 return false;
492 for (int k = 0; k < indices.length; k++)
493 if (indices[k] != other.indices[k])
494 return false;
495 return true;
496 }
497
498 @Override
499 public String toString() {
500 StringBuilder s = new StringBuilder();
501 s.append("M(" + sourceWidth + "->");
502 for (int i : indices) {
503 s.append(i);
504 s.append(',');
505 }
506 s.append(')');
507 return s.toString();
508 }
509
510 /**
511 * Returns the size of the masked tuples described by this mask
512 * @since 1.7
513 */
514 public int getSize() {
515 return indices.length;
516 }
517
518 /**
519 * Returns the size of the original tuples handled by this mask
520 * @since 1.7
521 */
522 public int getSourceWidth() {
523 return sourceWidth;
524 }
525
526
527 /**
528 * @return true iff this mask is a no-op
529 * @since 2.0
530 */
531 public boolean isIdentity() {
532 // Contract: if identity mask, a specialized subclass is constructed instead
533 return false;
534 }
535
536 /**
537 * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element
538 * in case of the mapping result in duplicate values.
539 *
540 * @since 1.7
541 */
542 public <T> Set<T> transformUnique(List<T> original) {
543 Set<T> signature = new HashSet<>();
544 for (int i = 0; i < indices.length; ++i)
545 signature.add(original.get(indices[i]));
546 return signature;
547 }
548
549 /**
550 * @return the list of selected indices
551 * @since 2.1
552 */
553 public List<Integer> getIndicesAsList() {
554 List<Integer> result = new ArrayList<Integer>(indices.length);
555 for (int i = 0; i < indices.length; ++i)
556 result.add(indices[i]);
557 return result;
558 }
559
560}