diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2023-09-14 19:29:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-14 19:29:36 +0200 |
commit | 98ed3b6db5f4e51961a161050cc31c66015116e8 (patch) | |
tree | 8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java | |
parent | Merge pull request #38 from nagilooh/design-space-exploration (diff) | |
parent | Merge remote-tracking branch 'upstream/main' into partial-interpretation (diff) | |
download | refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip |
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
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.java | 560 |
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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.OptionalInt; | ||
19 | import 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 | */ | ||
28 | public 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 | } | ||