From 6ae4346b6248198cb687a9cbbeba3bfb9c37c4b5 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Fri, 7 Apr 2023 19:49:23 +0200 Subject: refactor: remove TupleLike * Directly transform VIATRA tuples into Refinery tuples, since creating the additional wrapper object doesn't save any memory. * Adds static arity Tuple3 and Tuple4 implementations to be more aligned with VIATRA internals and save memory for queries with up to 4 parameters. * Makes sure no new objects are allocated (for varargs handling) when a static arity tuple is hashed. --- .../viatra/internal/matcher/FunctionalCursor.java | 13 +- .../internal/matcher/FunctionalViatraMatcher.java | 6 +- .../viatra/internal/matcher/MatcherUtils.java | 84 ++++++-- .../matcher/OmitOutputViatraTupleLike.java | 23 -- .../viatra/internal/matcher/RelationalCursor.java | 10 +- .../internal/matcher/RelationalViatraMatcher.java | 7 +- .../internal/matcher/UnsafeFunctionalCursor.java | 10 +- .../viatra/internal/matcher/ViatraTupleLike.java | 23 -- .../viatra/internal/matcher/MatcherUtilsTest.java | 234 +++++++++++++++++++++ .../store/query/viatra/tests/QueryAssertions.java | 2 +- .../tools/refinery/store/query/EmptyResultSet.java | 7 +- .../java/tools/refinery/store/query/ResultSet.java | 6 +- .../store/reasoning/rule/RuleActionExecutor.java | 4 +- .../store/reasoning/rule/RuleExecutor.java | 7 +- .../java/tools/refinery/store/tuple/Tuple.java | 25 ++- .../java/tools/refinery/store/tuple/Tuple0.java | 17 +- .../java/tools/refinery/store/tuple/Tuple1.java | 31 ++- .../java/tools/refinery/store/tuple/Tuple2.java | 20 +- .../java/tools/refinery/store/tuple/Tuple3.java | 41 ++++ .../java/tools/refinery/store/tuple/Tuple4.java | 44 ++++ .../tools/refinery/store/tuple/TupleConstants.java | 12 ++ .../java/tools/refinery/store/tuple/TupleLike.java | 35 --- .../java/tools/refinery/store/tuple/TupleN.java | 35 +-- 23 files changed, 522 insertions(+), 174 deletions(-) delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java create mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/tuple/TupleConstants.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java index 4daa14a1..52a83f69 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java @@ -1,17 +1,16 @@ package tools.refinery.store.query.viatra.internal.matcher; -import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer; import tools.refinery.store.map.Cursor; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; import java.util.Iterator; -class FunctionalCursor implements Cursor { +class FunctionalCursor implements Cursor { private final IterableIndexer indexer; - private final Iterator iterator; + private final Iterator iterator; private boolean terminated; - private TupleLike key; + private Tuple key; private T value; public FunctionalCursor(IterableIndexer indexer) { @@ -20,7 +19,7 @@ class FunctionalCursor implements Cursor { } @Override - public TupleLike getKey() { + public Tuple getKey() { return key; } @@ -38,7 +37,7 @@ class FunctionalCursor implements Cursor { public boolean move() { if (!terminated && iterator.hasNext()) { var match = iterator.next(); - key = new ViatraTupleLike(match); + key = MatcherUtils.toRefineryTuple(match); value = MatcherUtils.getSingleValue(indexer.get(match)); return true; } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java index 6aa45af2..adb34b8b 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java @@ -11,7 +11,7 @@ import tools.refinery.store.query.ResultSet; import tools.refinery.store.query.dnf.FunctionalQuery; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; /** * Directly access the tuples inside a VIATRA pattern matcher.

@@ -63,7 +63,7 @@ public class FunctionalViatraMatcher implements ResultSet { } @Override - public T get(TupleLike parameters) { + public T get(Tuple parameters) { var tuple = MatcherUtils.toViatraTuple(parameters); if (omitOutputIndexer == null) { return MatcherUtils.getSingleValue(backend.getAllMatches(omitOutputMask, tuple).iterator()); @@ -73,7 +73,7 @@ public class FunctionalViatraMatcher implements ResultSet { } @Override - public Cursor getAll() { + public Cursor getAll() { if (omitOutputIndexer == null) { var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf()); return new UnsafeFunctionalCursor<>(allMatches.iterator()); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java index 5d4be95d..d327c537 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java @@ -3,8 +3,7 @@ package tools.refinery.store.query.viatra.internal.matcher; import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; import org.jetbrains.annotations.Nullable; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.*; import java.util.Iterator; @@ -13,25 +12,80 @@ final class MatcherUtils { throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); } - public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(TupleLike tuple) { - if (tuple instanceof ViatraTupleLike viatraTupleLike) { - return viatraTupleLike.wrappedTuple().toImmutable(); + public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(Tuple refineryTuple) { + if (refineryTuple instanceof Tuple0) { + return Tuples.staticArityFlatTupleOf(); + } else if (refineryTuple instanceof Tuple1) { + return Tuples.staticArityFlatTupleOf(refineryTuple); + } else if (refineryTuple instanceof Tuple2 tuple2) { + return Tuples.staticArityFlatTupleOf(Tuple.of(tuple2.value0()), Tuple.of(tuple2.value1())); + } else if (refineryTuple instanceof Tuple3 tuple3) { + return Tuples.staticArityFlatTupleOf(Tuple.of(tuple3.value0()), Tuple.of(tuple3.value1()), + Tuple.of(tuple3.value2())); + } else if (refineryTuple instanceof Tuple4 tuple4) { + return Tuples.staticArityFlatTupleOf(Tuple.of(tuple4.value0()), Tuple.of(tuple4.value1()), + Tuple.of(tuple4.value2()), Tuple.of(tuple4.value3())); + } else { + int arity = refineryTuple.getSize(); + var values = new Object[arity]; + for (int i = 0; i < arity; i++) { + values[i] = Tuple.of(refineryTuple.get(i)); + } + return Tuples.flatTupleOf(values); } - int size = tuple.getSize(); - var array = new Object[size]; - for (int i = 0; i < size; i++) { - var value = tuple.get(i); - array[i] = Tuple.of(value); + } + + public static Tuple toRefineryTuple(ITuple viatraTuple) { + int arity = viatraTuple.getSize(); + if (arity == 1) { + return getWrapper(viatraTuple, 0); } - return Tuples.flatTupleOf(array); + return prefixToRefineryTuple(viatraTuple, viatraTuple.getSize()); + } + + public static Tuple keyToRefineryTuple(ITuple viatraTuple) { + return prefixToRefineryTuple(viatraTuple, viatraTuple.getSize() - 1); } + private static Tuple prefixToRefineryTuple(ITuple viatraTuple, int targetArity) { + if (targetArity < 0) { + throw new IllegalArgumentException("Requested negative prefix %d of %s" + .formatted(targetArity, viatraTuple)); + } + return switch (targetArity) { + case 0 -> Tuple.of(); + case 1 -> Tuple.of(unwrap(viatraTuple, 0)); + case 2 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1)); + case 3 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1), unwrap(viatraTuple, 2)); + case 4 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1), unwrap(viatraTuple, 2), + unwrap(viatraTuple, 3)); + default -> { + var entries = new int[targetArity]; + for (int i = 0; i < targetArity; i++) { + entries[i] = unwrap(viatraTuple, i); + } + yield Tuple.of(entries); + } + }; + } + + private static Tuple1 getWrapper(ITuple viatraTuple, int index) { + if (!((viatraTuple.get(index)) instanceof Tuple1 wrappedObjectId)) { + throw new IllegalArgumentException("Element %d of tuple %s is not an object id" + .formatted(index, viatraTuple)); + } + return wrappedObjectId; + } + + private static int unwrap(ITuple viatraTuple, int index) { + return getWrapper(viatraTuple, index).value0(); + } - public static T getSingleValue(@Nullable Iterable tuples) { - if (tuples == null) { + public static T getSingleValue(@Nullable Iterable viatraTuples) { + if (viatraTuples == null) { return null; } - return getSingleValue(tuples.iterator()); + return getSingleValue(viatraTuples.iterator()); } public static T getSingleValue(Iterator iterator) { @@ -42,7 +96,7 @@ final class MatcherUtils { @SuppressWarnings("unchecked") var result = (T) match.get(match.getSize() - 1); if (iterator.hasNext()) { - var input = new OmitOutputViatraTupleLike(match); + var input = keyToRefineryTuple(match); throw new IllegalStateException("Query is not functional for input tuple: " + input); } return result; diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java deleted file mode 100644 index bd9301ba..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java +++ /dev/null @@ -1,23 +0,0 @@ -package tools.refinery.store.query.viatra.internal.matcher; - -import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; -import tools.refinery.store.tuple.Tuple1; -import tools.refinery.store.tuple.TupleLike; - -record OmitOutputViatraTupleLike(ITuple wrappedTuple) implements TupleLike { - @Override - public int getSize() { - return wrappedTuple.getSize() - 1; - } - - @Override - public int get(int element) { - var wrappedValue = (Tuple1) wrappedTuple.get(element); - return wrappedValue.value0(); - } - - @Override - public String toString() { - return TupleLike.toString(this); - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java index c2dcc565..e3df0441 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java @@ -2,21 +2,21 @@ package tools.refinery.store.query.viatra.internal.matcher; import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; import tools.refinery.store.map.Cursor; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; import java.util.Iterator; -class RelationalCursor implements Cursor { +class RelationalCursor implements Cursor { private final Iterator tuplesIterator; private boolean terminated; - private TupleLike key; + private Tuple key; public RelationalCursor(Iterator tuplesIterator) { this.tuplesIterator = tuplesIterator; } @Override - public TupleLike getKey() { + public Tuple getKey() { return key; } @@ -33,7 +33,7 @@ class RelationalCursor implements Cursor { @Override public boolean move() { if (!terminated && tuplesIterator.hasNext()) { - key = new ViatraTupleLike(tuplesIterator.next()); + key = MatcherUtils.toRefineryTuple(tuplesIterator.next()); return true; } terminated = true; diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java index b9bc3f1e..9373709d 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java @@ -12,7 +12,7 @@ import tools.refinery.store.query.ResultSet; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; /** * Directly access the tuples inside a VIATRA pattern matcher.

@@ -58,7 +58,7 @@ public class RelationalViatraMatcher implements ResultSet { } @Override - public Boolean get(TupleLike parameters) { + public Boolean get(Tuple parameters) { var tuple = MatcherUtils.toViatraTuple(parameters); if (emptyMaskIndexer == null) { return backend.hasMatch(identityMask, tuple); @@ -68,7 +68,7 @@ public class RelationalViatraMatcher implements ResultSet { } @Override - public Cursor getAll() { + public Cursor getAll() { if (emptyMaskIndexer == null) { var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf()); return new RelationalCursor(allMatches.iterator()); @@ -85,5 +85,4 @@ public class RelationalViatraMatcher implements ResultSet { var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf()); return matches == null ? 0 : matches.size(); } - } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java index 6c53fff1..e9540d1d 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java @@ -2,7 +2,7 @@ package tools.refinery.store.query.viatra.internal.matcher; import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; import tools.refinery.store.map.Cursor; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; import java.util.Iterator; @@ -11,10 +11,10 @@ import java.util.Iterator; * functional dependency of the output on the inputs is obeyed. * @param The output type. */ -class UnsafeFunctionalCursor implements Cursor { +class UnsafeFunctionalCursor implements Cursor { private final Iterator tuplesIterator; private boolean terminated; - private TupleLike key; + private Tuple key; private T value; public UnsafeFunctionalCursor(Iterator tuplesIterator) { @@ -22,7 +22,7 @@ class UnsafeFunctionalCursor implements Cursor { } @Override - public TupleLike getKey() { + public Tuple getKey() { return key; } @@ -40,7 +40,7 @@ class UnsafeFunctionalCursor implements Cursor { public boolean move() { if (!terminated && tuplesIterator.hasNext()) { var match = tuplesIterator.next(); - key = new OmitOutputViatraTupleLike(match); + key = MatcherUtils.keyToRefineryTuple(match); @SuppressWarnings("unchecked") var typedValue = (T) match.get(match.getSize() - 1); value = typedValue; diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java deleted file mode 100644 index 76a3e40b..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java +++ /dev/null @@ -1,23 +0,0 @@ -package tools.refinery.store.query.viatra.internal.matcher; - -import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; -import tools.refinery.store.tuple.Tuple1; -import tools.refinery.store.tuple.TupleLike; - -record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike { - @Override - public int getSize() { - return wrappedTuple.getSize(); - } - - @Override - public int get(int element) { - var wrappedValue = (Tuple1) wrappedTuple.get(element); - return wrappedValue.value0(); - } - - @Override - public String toString() { - return TupleLike.toString(this); - } -} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java new file mode 100644 index 00000000..ea0b15ec --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java @@ -0,0 +1,234 @@ +package tools.refinery.store.query.viatra.internal.matcher; + +import org.eclipse.viatra.query.runtime.matchers.tuple.*; +import org.junit.jupiter.api.Test; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.*; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class MatcherUtilsTest { + @Test + void toViatra0Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of()); + assertThat(viatraTuple.getSize(), is(0)); + assertThat(viatraTuple, instanceOf(FlatTuple0.class)); + } + + @Test + void toViatra1Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2)); + assertThat(viatraTuple.getSize(), is(1)); + assertThat(viatraTuple.get(0), is(Tuple.of(2))); + assertThat(viatraTuple, instanceOf(FlatTuple1.class)); + } + + @Test + void toViatra2Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3)); + assertThat(viatraTuple.getSize(), is(2)); + assertThat(viatraTuple.get(0), is(Tuple.of(2))); + assertThat(viatraTuple.get(1), is(Tuple.of(3))); + assertThat(viatraTuple, instanceOf(FlatTuple2.class)); + } + + @Test + void toViatra3Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5)); + assertThat(viatraTuple.getSize(), is(3)); + assertThat(viatraTuple.get(0), is(Tuple.of(2))); + assertThat(viatraTuple.get(1), is(Tuple.of(3))); + assertThat(viatraTuple.get(2), is(Tuple.of(5))); + assertThat(viatraTuple, instanceOf(FlatTuple3.class)); + } + + @Test + void toViatra4Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5, 8)); + assertThat(viatraTuple.getSize(), is(4)); + assertThat(viatraTuple.get(0), is(Tuple.of(2))); + assertThat(viatraTuple.get(1), is(Tuple.of(3))); + assertThat(viatraTuple.get(2), is(Tuple.of(5))); + assertThat(viatraTuple.get(3), is(Tuple.of(8))); + assertThat(viatraTuple, instanceOf(FlatTuple4.class)); + } + + @Test + void toViatra5Test() { + var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5, 8, 13)); + assertThat(viatraTuple.getSize(), is(5)); + assertThat(viatraTuple.get(0), is(Tuple.of(2))); + assertThat(viatraTuple.get(1), is(Tuple.of(3))); + assertThat(viatraTuple.get(2), is(Tuple.of(5))); + assertThat(viatraTuple.get(3), is(Tuple.of(8))); + assertThat(viatraTuple.get(4), is(Tuple.of(13))); + assertThat(viatraTuple, instanceOf(FlatTuple.class)); + } + + @Test + void toRefinery0Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf()); + assertThat(refineryTuple.getSize(), is(0)); + assertThat(refineryTuple, instanceOf(Tuple0.class)); + } + + @Test + void toRefinery1Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2))); + assertThat(refineryTuple.getSize(), is(1)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple, instanceOf(Tuple1.class)); + } + + @Test + void toRefinery2Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3))); + assertThat(refineryTuple.getSize(), is(2)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple, instanceOf(Tuple2.class)); + } + + @Test + void toRefinery3Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5))); + assertThat(refineryTuple.getSize(), is(3)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple, instanceOf(Tuple3.class)); + } + + @Test + void toRefinery4Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5), + Tuple.of(8))); + assertThat(refineryTuple.getSize(), is(4)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple.get(3), is(8)); + assertThat(refineryTuple, instanceOf(Tuple4.class)); + } + + @Test + void toRefinery5Test() { + var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5), + Tuple.of(8), Tuple.of(13))); + assertThat(refineryTuple.getSize(), is(5)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple.get(3), is(8)); + assertThat(refineryTuple.get(4), is(13)); + assertThat(refineryTuple, instanceOf(TupleN.class)); + } + + @Test + void toRefineryInvalidValueTest() { + var viatraTuple = Tuples.flatTupleOf(Tuple.of(2), -98); + assertThrows(IllegalArgumentException.class, () -> MatcherUtils.toRefineryTuple(viatraTuple)); + } + + @Test + void keyToRefinery0Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(-99)); + assertThat(refineryTuple.getSize(), is(0)); + assertThat(refineryTuple, instanceOf(Tuple0.class)); + } + + @Test + void keyToRefinery1Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), -99)); + assertThat(refineryTuple.getSize(), is(1)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple, instanceOf(Tuple1.class)); + } + + @Test + void keyToRefinery2Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), -99)); + assertThat(refineryTuple.getSize(), is(2)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple, instanceOf(Tuple2.class)); + } + + @Test + void keyToRefinery3Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5), + -99)); + assertThat(refineryTuple.getSize(), is(3)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple, instanceOf(Tuple3.class)); + } + + @Test + void keyToRefinery4Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5), + Tuple.of(8), -99)); + assertThat(refineryTuple.getSize(), is(4)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple.get(3), is(8)); + assertThat(refineryTuple, instanceOf(Tuple4.class)); + } + + @Test + void keyToRefinery5Test() { + var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5), + Tuple.of(8), Tuple.of(13), -99)); + assertThat(refineryTuple.getSize(), is(5)); + assertThat(refineryTuple.get(0), is(2)); + assertThat(refineryTuple.get(1), is(3)); + assertThat(refineryTuple.get(2), is(5)); + assertThat(refineryTuple.get(3), is(8)); + assertThat(refineryTuple.get(4), is(13)); + assertThat(refineryTuple, instanceOf(TupleN.class)); + } + + @Test + void keyToRefineryTooShortTest() { + var viatraTuple = Tuples.flatTupleOf(); + assertThrows(IllegalArgumentException.class, () -> MatcherUtils.keyToRefineryTuple(viatraTuple)); + } + + @Test + void keyToRefineryInvalidValueTest() { + var viatraTuple = Tuples.flatTupleOf(Tuple.of(2), -98, -99); + assertThrows(IllegalArgumentException.class, () -> MatcherUtils.keyToRefineryTuple(viatraTuple)); + } + + @Test + void getSingleValueTest() { + var value = MatcherUtils.getSingleValue(List.of(Tuples.flatTupleOf(Tuple.of(2), -99))); + assertThat(value, is(-99)); + } + + // Static analysis accurately determines that the result is always {@code null}, but we check anyways. + @SuppressWarnings("ConstantValue") + @Test + void getSingleValueNullTest() { + var value = MatcherUtils.getSingleValue((Iterable) null); + assertThat(value, nullValue()); + } + + @Test + void getSingleValueEmptyTest() { + var value = MatcherUtils.getSingleValue(List.of()); + assertThat(value, nullValue()); + } + + @Test + void getSingleValueMultipleTest() { + var viatraTuples = List.of(Tuples.flatTupleOf(Tuple.of(2), -98), Tuples.flatTupleOf(Tuple.of(2), -99)); + assertThrows(IllegalStateException.class, () -> MatcherUtils.getSingleValue(viatraTuples)); + } +} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java index 6f50ec73..2769621d 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java @@ -42,7 +42,7 @@ public final class QueryAssertions { var cursor = resultSet.getAll(); while (cursor.move()) { var key = cursor.getKey(); - var previous = actual.put(key.toTuple(), cursor.getValue()); + var previous = actual.put(key, cursor.getValue()); assertThat("duplicate value for key " + key, previous, nullValue()); } executables.add(() -> assertThat("results cursor", actual, is(filteredExpected))); diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java index 9af73bdd..4c8eeab0 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java @@ -3,7 +3,7 @@ package tools.refinery.store.query; import tools.refinery.store.map.Cursor; import tools.refinery.store.map.Cursors; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; public record EmptyResultSet(ModelQueryAdapter adapter, Query query) implements ResultSet { @Override @@ -17,13 +17,12 @@ public record EmptyResultSet(ModelQueryAdapter adapter, Query query) imple } @Override - public T get(TupleLike parameters) { + public T get(Tuple parameters) { return query.defaultValue(); } - @Override - public Cursor getAll() { + public Cursor getAll() { return Cursors.empty(); } diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java index 3f6bc06f..2758c74f 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java @@ -2,12 +2,12 @@ package tools.refinery.store.query; import tools.refinery.store.map.Cursor; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; public non-sealed interface ResultSet extends AnyResultSet { Query getQuery(); - T get(TupleLike parameters); + T get(Tuple parameters); - Cursor getAll(); + Cursor getAll(); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleActionExecutor.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleActionExecutor.java index 80bfa6f8..7c51e3df 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleActionExecutor.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleActionExecutor.java @@ -1,9 +1,9 @@ package tools.refinery.store.reasoning.rule; import tools.refinery.store.reasoning.MergeResult; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.tuple.Tuple; @FunctionalInterface public interface RuleActionExecutor { - MergeResult execute(TupleLike activationTuple); + MergeResult execute(Tuple activationTuple); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleExecutor.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleExecutor.java index 1e5322b4..c20645fc 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleExecutor.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleExecutor.java @@ -1,8 +1,8 @@ package tools.refinery.store.reasoning.rule; -import tools.refinery.store.reasoning.MergeResult; import tools.refinery.store.model.Model; -import tools.refinery.store.tuple.TupleLike; +import tools.refinery.store.reasoning.MergeResult; +import tools.refinery.store.tuple.Tuple; import java.util.List; @@ -24,7 +24,8 @@ public final class RuleExecutor { public Model getModel() { return model; } - public MergeResult execute(TupleLike activationTuple) { + + public MergeResult execute(Tuple activationTuple) { MergeResult mergeResult = MergeResult.UNCHANGED; for (var actionExecutor : actionExecutors) { mergeResult = mergeResult.andAlso(actionExecutor.execute(activationTuple)); diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java index bf844c6d..51e2895a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java @@ -1,28 +1,37 @@ package tools.refinery.store.tuple; -public sealed interface Tuple extends TupleLike permits Tuple0, Tuple1, Tuple2, TupleN { - @Override - default Tuple toTuple() { - return this; - } +public sealed interface Tuple permits Tuple0, Tuple1, Tuple2, Tuple3, Tuple4, TupleN { + int getSize(); + + int get(int element); - static Tuple of() { + static Tuple0 of() { return Tuple0.INSTANCE; } - static Tuple of(int value) { + static Tuple1 of(int value) { return Tuple1.Cache.INSTANCE.getOrCreate(value); } - static Tuple of(int value1, int value2) { + static Tuple2 of(int value1, int value2) { return new Tuple2(value1, value2); } + static Tuple3 of(int value1, int value2, int value3) { + return new Tuple3(value1, value2, value3); + } + + static Tuple4 of(int value1, int value2, int value3, int value4) { + return new Tuple4(value1, value2, value3, value4); + } + static Tuple of(int... values) { return switch (values.length) { case 0 -> of(); case 1 -> of(values[0]); case 2 -> of(values[0], values[1]); + case 3 -> of(values[0], values[1], values[2]); + case 4 -> of(values[0], values[1], values[2], values[3]); default -> new TupleN(values); }; } diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java index 8eea5c3a..266e2cca 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java @@ -1,7 +1,13 @@ package tools.refinery.store.tuple; -public record Tuple0() implements Tuple { - public static Tuple0 INSTANCE = new Tuple0(); +import static tools.refinery.store.tuple.TupleConstants.TUPLE_BEGIN; +import static tools.refinery.store.tuple.TupleConstants.TUPLE_END; + +public final class Tuple0 implements Tuple { + public static final Tuple0 INSTANCE = new Tuple0(); + + private Tuple0() { + } @Override public int getSize() { @@ -13,13 +19,8 @@ public record Tuple0() implements Tuple { throw new IndexOutOfBoundsException(element); } - @Override - public int[] toArray() { - return new int[]{}; - } - @Override public String toString() { - return "[]"; + return TUPLE_BEGIN + TUPLE_END; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java index 07380966..bdcc47b5 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java @@ -4,7 +4,20 @@ import tools.refinery.store.model.TupleHashProvider; import java.util.Arrays; -public record Tuple1(int value0) implements Tuple { +import static tools.refinery.store.tuple.TupleConstants.TUPLE_BEGIN; +import static tools.refinery.store.tuple.TupleConstants.TUPLE_END; + +public final class Tuple1 implements Tuple { + private final int value0; + + private Tuple1(int value0) { + this.value0 = value0; + } + + public int value0() { + return value0; + } + @Override public int getSize() { return 1; @@ -19,13 +32,21 @@ public record Tuple1(int value0) implements Tuple { } @Override - public int[] toArray() { - return new int[]{value0}; + public String toString() { + return TUPLE_BEGIN + value0 + TUPLE_END; } @Override - public String toString() { - return "[" + value0 + "]"; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple1 tuple1 = (Tuple1) o; + return value0 == tuple1.value0; + } + + @Override + public int hashCode() { + return 31 + value0; } /** diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java index 0836a32d..fc0fbd8e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java @@ -1,5 +1,7 @@ package tools.refinery.store.tuple; +import static tools.refinery.store.tuple.TupleConstants.*; + public record Tuple2(int value0, int value1) implements Tuple { @Override public int getSize() { @@ -16,12 +18,22 @@ public record Tuple2(int value0, int value1) implements Tuple { } @Override - public int[] toArray() { - return new int[]{value0, value1}; + public String toString() { + return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_END; } @Override - public String toString() { - return "[" + value0 + ", " + value1 + "]"; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple2 tuple2 = (Tuple2) o; + return value0 == tuple2.value0 && value1 == tuple2.value1; + } + + @Override + public int hashCode() { + int hash = 31 + value0; + hash = 31 * hash + value1; + return hash; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java new file mode 100644 index 00000000..a6ec17b0 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java @@ -0,0 +1,41 @@ +package tools.refinery.store.tuple; + +import static tools.refinery.store.tuple.TupleConstants.*; + +public record Tuple3(int value0, int value1, int value2) implements Tuple { + @Override + public int getSize() { + return 3; + } + + @Override + public int get(int element) { + return switch (element) { + case 0 -> value0; + case 1 -> value1; + case 2 -> value2; + default -> throw new ArrayIndexOutOfBoundsException(element); + }; + } + + @Override + public String toString() { + return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_END; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple3 tuple3 = (Tuple3) o; + return value0 == tuple3.value0 && value1 == tuple3.value1 && value2 == tuple3.value2; + } + + @Override + public int hashCode() { + int hash = 31 + value0; + hash = 31 * hash + value1; + hash = 31 * hash + value2; + return hash; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java new file mode 100644 index 00000000..66cef32b --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java @@ -0,0 +1,44 @@ +package tools.refinery.store.tuple; + +import static tools.refinery.store.tuple.TupleConstants.*; + +public record Tuple4(int value0, int value1, int value2, int value3) implements Tuple { + @Override + public int getSize() { + return 4; + } + + @Override + public int get(int element) { + return switch (element) { + case 0 -> value0; + case 1 -> value1; + case 2 -> value2; + case 3 -> value3; + default -> throw new ArrayIndexOutOfBoundsException(element); + }; + } + + @Override + public String toString() { + return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_SEPARATOR + value3 + + TUPLE_END; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple4 tuple4 = (Tuple4) o; + return value0 == tuple4.value0 && value1 == tuple4.value1 && value2 == tuple4.value2 && value3 == tuple4.value3; + } + + @Override + public int hashCode() { + int hash = 31 + value0; + hash = 31 * hash + value1; + hash = 31 * hash + value2; + hash = 31 * hash + value3; + return hash; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleConstants.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleConstants.java new file mode 100644 index 00000000..3d95a655 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleConstants.java @@ -0,0 +1,12 @@ +package tools.refinery.store.tuple; + +final class TupleConstants { + public static final int MAX_STATIC_ARITY_TUPLE_SIZE = 4; + public static final String TUPLE_BEGIN = "["; + public static final String TUPLE_SEPARATOR = ", "; + public static final String TUPLE_END = "]"; + + private TupleConstants() { + throw new IllegalArgumentException("This is a static utility class an should not instantiated directly"); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java deleted file mode 100644 index 953ea9f8..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java +++ /dev/null @@ -1,35 +0,0 @@ -package tools.refinery.store.tuple; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public interface TupleLike { - int getSize(); - - int get(int element); - - default int[] toArray() { - int size = getSize(); - var array = new int[size]; - for (int i = 0; i < size; i++) { - array[i] = get(i); - } - return array; - } - - default Tuple toTuple() { - return switch (getSize()) { - case 0 -> Tuple.of(); - case 1 -> Tuple.of(get(0)); - case 2 -> Tuple.of(get(0), get(1)); - default -> Tuple.of(toArray()); - }; - } - - static String toString(TupleLike tuple) { - var valuesString = IntStream.range(0, tuple.getSize()) - .mapToObj(i -> Integer.toString(tuple.get(i))) - .collect(Collectors.joining(", ")); - return "[" + valuesString + "]"; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java index c3aed847..512bab49 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java +++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java @@ -1,13 +1,18 @@ package tools.refinery.store.tuple; import java.util.Arrays; +import java.util.stream.Collectors; -public record TupleN(int[] values) implements Tuple { - static final int CUSTOM_TUPLE_SIZE = 2; +import static tools.refinery.store.tuple.TupleConstants.*; - public TupleN(int[] values) { - if (values.length < CUSTOM_TUPLE_SIZE) - throw new IllegalArgumentException(); +public final class TupleN implements Tuple { + private final int[] values; + + TupleN(int[] values) { + if (values.length < MAX_STATIC_ARITY_TUPLE_SIZE) { + throw new IllegalArgumentException("Tuples of size at most %d must use static arity Tuple classes" + .formatted(MAX_STATIC_ARITY_TUPLE_SIZE)); + } this.values = Arrays.copyOf(values, values.length); } @@ -21,19 +26,12 @@ public record TupleN(int[] values) implements Tuple { return values[element]; } - @Override - public int[] toArray() { - return values; - } - @Override public String toString() { - return TupleLike.toString(this); - } - - @Override - public int hashCode() { - return Arrays.hashCode(values); + var valuesString = Arrays.stream(values) + .mapToObj(Integer::toString) + .collect(Collectors.joining(TUPLE_SEPARATOR)); + return TUPLE_BEGIN + valuesString + TUPLE_END; } @Override @@ -47,4 +45,9 @@ public record TupleN(int[] values) implements Tuple { TupleN other = (TupleN) obj; return Arrays.equals(values, other.values); } + + @Override + public int hashCode() { + return Arrays.hashCode(values); + } } -- cgit v1.2.3-54-g00ecf