aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java32
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/SymbolicParameter.java4
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java25
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java21
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java9
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java2
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java179
11 files changed, 74 insertions, 246 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
index 38fd017e..d51bc9fc 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
@@ -88,7 +88,7 @@ public class Dnf2PQuery {
88 List<PParameter> parameterList = new ArrayList<>(); 88 List<PParameter> parameterList = new ArrayList<>();
89 for (var parameter : dnfQuery.getSymbolicParameters()) { 89 for (var parameter : dnfQuery.getSymbolicParameters()) {
90 var direction = switch (parameter.getDirection()) { 90 var direction = switch (parameter.getDirection()) {
91 case OUT -> parameter.isUnifiable() ? PParameterDirection.INOUT : PParameterDirection.OUT; 91 case OUT -> PParameterDirection.INOUT;
92 case IN -> throw new IllegalArgumentException("Query %s with input parameter %s is not supported" 92 case IN -> throw new IllegalArgumentException("Query %s with input parameter %s is not supported"
93 .formatted(dnfQuery, parameter.getVariable())); 93 .formatted(dnfQuery, parameter.getVariable()));
94 }; 94 };
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
index c4f877c5..7f5c24fe 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
@@ -424,6 +424,42 @@ class FunctionalQueryTest {
424 } 424 }
425 425
426 @QueryEngineTest 426 @QueryEngineTest
427 void multipleAssignmentTest(QueryEvaluationHint hint) {
428 var query = Query.of("MultipleAssignment", Integer.class, (builder, p1, p2, output) -> builder
429 .clause(Integer.class, Integer.class, (x1, x2) -> List.of(
430 ageView.call(p1, x1),
431 ageView.call(p2, x2),
432 output.assign(mul(x1, constant(2))),
433 output.assign(mul(x2, constant(3)))
434 )));
435
436 var store = ModelStore.builder()
437 .symbols(age)
438 .with(ViatraModelQueryAdapter.builder()
439 .defaultHint(hint)
440 .queries(query))
441 .build();
442
443 var model = store.createEmptyModel();
444 var ageInterpretation = model.getInterpretation(age);
445 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
446 var queryResultSet = queryEngine.getResultSet(query);
447
448 ageInterpretation.put(Tuple.of(0), 3);
449 ageInterpretation.put(Tuple.of(1), 2);
450 ageInterpretation.put(Tuple.of(2), 15);
451 ageInterpretation.put(Tuple.of(3), 10);
452
453 queryEngine.flushChanges();
454 assertNullableResults(Map.of(
455 Tuple.of(0, 1), Optional.of(6),
456 Tuple.of(1, 0), Optional.empty(),
457 Tuple.of(2, 3), Optional.of(30),
458 Tuple.of(3, 2), Optional.empty()
459 ), queryResultSet);
460 }
461
462 @QueryEngineTest
427 void notFunctionalTest(QueryEvaluationHint hint) { 463 void notFunctionalTest(QueryEvaluationHint hint) {
428 var query = Query.of("NotFunctional", Integer.class, (builder, p1, output) -> builder.clause((p2) -> List.of( 464 var query = Query.of("NotFunctional", Integer.class, (builder, p1, output) -> builder.clause((p2) -> List.of(
429 personView.call(p1), 465 personView.call(p1),
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java
index f2749094..5d77b9aa 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java
@@ -11,7 +11,6 @@ import tools.refinery.store.query.literal.*;
11import tools.refinery.store.query.substitution.MapBasedSubstitution; 11import tools.refinery.store.query.substitution.MapBasedSubstitution;
12import tools.refinery.store.query.substitution.StatelessSubstitution; 12import tools.refinery.store.query.substitution.StatelessSubstitution;
13import tools.refinery.store.query.substitution.Substitution; 13import tools.refinery.store.query.substitution.Substitution;
14import tools.refinery.store.query.term.NodeVariable;
15import tools.refinery.store.query.term.ParameterDirection; 14import tools.refinery.store.query.term.ParameterDirection;
16import tools.refinery.store.query.term.Variable; 15import tools.refinery.store.query.term.Variable;
17 16
@@ -21,8 +20,8 @@ import java.util.function.Function;
21class ClausePostProcessor { 20class ClausePostProcessor {
22 private final Map<Variable, ParameterInfo> parameters; 21 private final Map<Variable, ParameterInfo> parameters;
23 private final List<Literal> literals; 22 private final List<Literal> literals;
24 private final Map<NodeVariable, NodeVariable> representatives = new LinkedHashMap<>(); 23 private final Map<Variable, Variable> representatives = new LinkedHashMap<>();
25 private final Map<NodeVariable, Set<NodeVariable>> equivalencePartition = new HashMap<>(); 24 private final Map<Variable, Set<Variable>> equivalencePartition = new HashMap<>();
26 private List<Literal> substitutedLiterals; 25 private List<Literal> substitutedLiterals;
27 private final Set<Variable> existentiallyQuantifiedVariables = new LinkedHashSet<>(); 26 private final Set<Variable> existentiallyQuantifiedVariables = new LinkedHashSet<>();
28 private Set<Variable> positiveVariables; 27 private Set<Variable> positiveVariables;
@@ -78,7 +77,7 @@ class ClausePostProcessor {
78 return literal instanceof EquivalenceLiteral equivalenceLiteral && equivalenceLiteral.isPositive(); 77 return literal instanceof EquivalenceLiteral equivalenceLiteral && equivalenceLiteral.isPositive();
79 } 78 }
80 79
81 private void mergeVariables(NodeVariable left, NodeVariable right) { 80 private void mergeVariables(Variable left, Variable right) {
82 var leftRepresentative = getRepresentative(left); 81 var leftRepresentative = getRepresentative(left);
83 var rightRepresentative = getRepresentative(right); 82 var rightRepresentative = getRepresentative(right);
84 var leftInfo = parameters.get(leftRepresentative); 83 var leftInfo = parameters.get(leftRepresentative);
@@ -91,7 +90,7 @@ class ClausePostProcessor {
91 } 90 }
92 } 91 }
93 92
94 private void doMergeVariables(NodeVariable parentRepresentative, NodeVariable newChildRepresentative) { 93 private void doMergeVariables(Variable parentRepresentative, Variable newChildRepresentative) {
95 var parentSet = getEquivalentVariables(parentRepresentative); 94 var parentSet = getEquivalentVariables(parentRepresentative);
96 var childSet = getEquivalentVariables(newChildRepresentative); 95 var childSet = getEquivalentVariables(newChildRepresentative);
97 parentSet.addAll(childSet); 96 parentSet.addAll(childSet);
@@ -101,18 +100,18 @@ class ClausePostProcessor {
101 } 100 }
102 } 101 }
103 102
104 private NodeVariable getRepresentative(NodeVariable variable) { 103 private Variable getRepresentative(Variable variable) {
105 return representatives.computeIfAbsent(variable, Function.identity()); 104 return representatives.computeIfAbsent(variable, Function.identity());
106 } 105 }
107 106
108 private Set<NodeVariable> getEquivalentVariables(NodeVariable variable) { 107 private Set<Variable> getEquivalentVariables(Variable variable) {
109 var representative = getRepresentative(variable); 108 var representative = getRepresentative(variable);
110 if (!representative.equals(variable)) { 109 if (!representative.equals(variable)) {
111 throw new AssertionError("NodeVariable %s already has a representative %s" 110 throw new AssertionError("NodeVariable %s already has a representative %s"
112 .formatted(variable, representative)); 111 .formatted(variable, representative));
113 } 112 }
114 return equivalencePartition.computeIfAbsent(variable, key -> { 113 return equivalencePartition.computeIfAbsent(variable, key -> {
115 var set = new HashSet<NodeVariable>(1); 114 var set = new HashSet<Variable>(1);
116 set.add(key); 115 set.add(key);
117 return set; 116 return set;
118 }); 117 });
@@ -123,7 +122,7 @@ class ClausePostProcessor {
123 var left = pair.getKey(); 122 var left = pair.getKey();
124 var right = pair.getValue(); 123 var right = pair.getValue();
125 if (!left.equals(right) && parameters.containsKey(left) && parameters.containsKey(right)) { 124 if (!left.equals(right) && parameters.containsKey(left) && parameters.containsKey(right)) {
126 substitutedLiterals.add(left.isEquivalent(right)); 125 substitutedLiterals.add(new EquivalenceLiteral(true, left, right));
127 } 126 }
128 } 127 }
129 } 128 }
@@ -149,20 +148,7 @@ class ClausePostProcessor {
149 148
150 private void computeExistentiallyQuantifiedVariables() { 149 private void computeExistentiallyQuantifiedVariables() {
151 for (var literal : substitutedLiterals) { 150 for (var literal : substitutedLiterals) {
152 for (var variable : literal.getOutputVariables()) { 151 existentiallyQuantifiedVariables.addAll(literal.getOutputVariables());
153 boolean added = existentiallyQuantifiedVariables.add(variable);
154 if (!variable.isUnifiable()) {
155 var parameterInfo = parameters.get(variable);
156 if (parameterInfo != null && parameterInfo.direction() == ParameterDirection.IN) {
157 throw new IllegalArgumentException("Trying to bind %s parameter %s"
158 .formatted(ParameterDirection.IN, variable));
159 }
160 if (!added) {
161 throw new IllegalArgumentException("Variable %s has multiple assigned values"
162 .formatted(variable));
163 }
164 }
165 }
166 } 152 }
167 } 153 }
168 154
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/SymbolicParameter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/SymbolicParameter.java
index 1e96cf27..fe9cefcc 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/SymbolicParameter.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/SymbolicParameter.java
@@ -24,10 +24,6 @@ public final class SymbolicParameter extends Parameter {
24 return variable; 24 return variable;
25 } 25 }
26 26
27 public boolean isUnifiable() {
28 return variable.isUnifiable();
29 }
30
31 public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) { 27 public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) {
32 return Objects.hash(super.hashCode(), helper.getVariableHashCode(variable)); 28 return Objects.hash(super.hashCode(), helper.getVariableHashCode(variable));
33 } 29 }
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
index a355dc3b..263c2e20 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
@@ -14,6 +14,8 @@ import tools.refinery.store.query.term.Variable;
14 14
15import java.util.*; 15import java.util.*;
16 16
17// {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}.
18@SuppressWarnings("squid:S2160")
17public abstract class AbstractCallLiteral extends AbstractLiteral { 19public abstract class AbstractCallLiteral extends AbstractLiteral {
18 private final Constraint target; 20 private final Constraint target;
19 private final List<Variable> arguments; 21 private final List<Variable> arguments;
@@ -42,16 +44,12 @@ public abstract class AbstractCallLiteral extends AbstractLiteral {
42 } 44 }
43 switch (parameter.getDirection()) { 45 switch (parameter.getDirection()) {
44 case IN -> { 46 case IN -> {
45 if (mutableOutArguments.remove(argument)) { 47 mutableOutArguments.remove(argument);
46 checkInOutUnifiable(argument);
47 }
48 mutableInArguments.add(argument); 48 mutableInArguments.add(argument);
49 } 49 }
50 case OUT -> { 50 case OUT -> {
51 if (mutableInArguments.contains(argument)) { 51 if (!mutableInArguments.contains(argument)) {
52 checkInOutUnifiable(argument); 52 mutableOutArguments.add(argument);
53 } else if (!mutableOutArguments.add(argument)) {
54 checkDuplicateOutUnifiable(argument);
55 } 53 }
56 } 54 }
57 } 55 }
@@ -60,19 +58,6 @@ public abstract class AbstractCallLiteral extends AbstractLiteral {
60 outArguments = Collections.unmodifiableSet(mutableOutArguments); 58 outArguments = Collections.unmodifiableSet(mutableOutArguments);
61 } 59 }
62 60
63 private static void checkInOutUnifiable(Variable argument) {
64 if (!argument.isUnifiable()) {
65 throw new IllegalArgumentException("Argument %s cannot appear with both %s and %s direction"
66 .formatted(argument, ParameterDirection.IN, ParameterDirection.OUT));
67 }
68 }
69
70 private static void checkDuplicateOutUnifiable(Variable argument) {
71 if (!argument.isUnifiable()) {
72 throw new IllegalArgumentException("Argument %s cannot be bound multiple times".formatted(argument));
73 }
74 }
75
76 public Constraint getTarget() { 61 public Constraint getTarget() {
77 return target; 62 return target;
78 } 63 }
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
index 61b7344f..9a0c22d1 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
@@ -8,18 +8,23 @@ package tools.refinery.store.query.literal;
8import tools.refinery.store.query.equality.LiteralEqualityHelper; 8import tools.refinery.store.query.equality.LiteralEqualityHelper;
9import tools.refinery.store.query.equality.LiteralHashCodeHelper; 9import tools.refinery.store.query.equality.LiteralHashCodeHelper;
10import tools.refinery.store.query.substitution.Substitution; 10import tools.refinery.store.query.substitution.Substitution;
11import tools.refinery.store.query.term.NodeVariable;
12import tools.refinery.store.query.term.Variable; 11import tools.refinery.store.query.term.Variable;
13 12
14import java.util.Objects; 13import java.util.Objects;
15import java.util.Set; 14import java.util.Set;
16 15
16// {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}.
17@SuppressWarnings("squid:S2160")
17public final class EquivalenceLiteral extends AbstractLiteral implements CanNegate<EquivalenceLiteral> { 18public final class EquivalenceLiteral extends AbstractLiteral implements CanNegate<EquivalenceLiteral> {
18 private final boolean positive; 19 private final boolean positive;
19 private final NodeVariable left; 20 private final Variable left;
20 private final NodeVariable right; 21 private final Variable right;
21 22
22 public EquivalenceLiteral(boolean positive, NodeVariable left, NodeVariable right) { 23 public EquivalenceLiteral(boolean positive, Variable left, Variable right) {
24 if (!left.tryGetType().equals(right.tryGetType())) {
25 throw new IllegalArgumentException("Variables %s and %s of different type cannot be equivalent"
26 .formatted(left, right));
27 }
23 this.positive = positive; 28 this.positive = positive;
24 this.left = left; 29 this.left = left;
25 this.right = right; 30 this.right = right;
@@ -29,11 +34,11 @@ public final class EquivalenceLiteral extends AbstractLiteral implements CanNega
29 return positive; 34 return positive;
30 } 35 }
31 36
32 public NodeVariable getLeft() { 37 public Variable getLeft() {
33 return left; 38 return left;
34 } 39 }
35 40
36 public NodeVariable getRight() { 41 public Variable getRight() {
37 return right; 42 return right;
38 } 43 }
39 44
@@ -59,8 +64,8 @@ public final class EquivalenceLiteral extends AbstractLiteral implements CanNega
59 64
60 @Override 65 @Override
61 public EquivalenceLiteral substitute(Substitution substitution) { 66 public EquivalenceLiteral substitute(Substitution substitution) {
62 return new EquivalenceLiteral(positive, substitution.getTypeSafeSubstitute(left), 67 return new EquivalenceLiteral(positive, substitution.getSubstitute(left),
63 substitution.getTypeSafeSubstitute(right)); 68 substitution.getSubstitute(right));
64 } 69 }
65 70
66 @Override 71 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
index ed6941da..4d88051b 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
@@ -47,11 +47,6 @@ public abstract sealed class AnyDataVariable extends Variable implements AnyTerm
47 } 47 }
48 48
49 @Override 49 @Override
50 public boolean isUnifiable() {
51 return false;
52 }
53
54 @Override
55 public abstract AnyDataVariable renew(@Nullable String name); 50 public abstract AnyDataVariable renew(@Nullable String name);
56 51
57 @Override 52 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
index e71c69ac..9b62e545 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
@@ -8,6 +8,7 @@ package tools.refinery.store.query.term;
8import org.jetbrains.annotations.Nullable; 8import org.jetbrains.annotations.Nullable;
9import tools.refinery.store.query.equality.LiteralEqualityHelper; 9import tools.refinery.store.query.equality.LiteralEqualityHelper;
10import tools.refinery.store.query.equality.LiteralHashCodeHelper; 10import tools.refinery.store.query.equality.LiteralHashCodeHelper;
11import tools.refinery.store.query.literal.EquivalenceLiteral;
11import tools.refinery.store.query.literal.Literal; 12import tools.refinery.store.query.literal.Literal;
12import tools.refinery.store.query.substitution.Substitution; 13import tools.refinery.store.query.substitution.Substitution;
13import tools.refinery.store.query.valuation.Valuation; 14import tools.refinery.store.query.valuation.Valuation;
@@ -90,4 +91,12 @@ public final class DataVariable<T> extends AnyDataVariable implements Term<T> {
90 public int hashCode() { 91 public int hashCode() {
91 return Objects.hash(super.hashCode(), type); 92 return Objects.hash(super.hashCode(), type);
92 } 93 }
94
95 public EquivalenceLiteral isEquivalent(DataVariable<T> other) {
96 return new EquivalenceLiteral(true, this, other);
97 }
98
99 public EquivalenceLiteral notEquivalent(DataVariable<T> other) {
100 return new EquivalenceLiteral(false, this, other);
101 }
93} 102}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
index e466f1c9..2f9c8bf1 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
@@ -22,11 +22,6 @@ public final class NodeVariable extends Variable {
22 } 22 }
23 23
24 @Override 24 @Override
25 public boolean isUnifiable() {
26 return true;
27 }
28
29 @Override
30 public NodeVariable renew(@Nullable String name) { 25 public NodeVariable renew(@Nullable String name) {
31 return Variable.of(name); 26 return Variable.of(name);
32 } 27 }
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
index 89ef0d89..1b553704 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
@@ -38,8 +38,6 @@ public abstract sealed class Variable permits AnyDataVariable, NodeVariable {
38 return uniqueName; 38 return uniqueName;
39 } 39 }
40 40
41 public abstract boolean isUnifiable();
42
43 public abstract Variable renew(@Nullable String name); 41 public abstract Variable renew(@Nullable String name);
44 42
45 public abstract Variable renew(); 43 public abstract Variable renew();
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
index c509da4d..bfeaa447 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
@@ -28,9 +28,8 @@ import static org.hamcrest.Matchers.hasItem;
28import static org.hamcrest.Matchers.not; 28import static org.hamcrest.Matchers.not;
29import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 29import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
30import static org.junit.jupiter.api.Assertions.assertThrows; 30import static org.junit.jupiter.api.Assertions.assertThrows;
31import static tools.refinery.store.query.literal.Literals.check;
32import static tools.refinery.store.query.literal.Literals.not; 31import static tools.refinery.store.query.literal.Literals.not;
33import static tools.refinery.store.query.term.int_.IntTerms.*; 32import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
34 33
35class VariableDirectionTest { 34class VariableDirectionTest {
36 private static final Symbol<Boolean> person = Symbol.of("Person", 1); 35 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
@@ -246,182 +245,6 @@ class VariableDirectionTest {
246 ); 245 );
247 } 246 }
248 247
249 @ParameterizedTest
250 @MethodSource("clausesWithDataVariableInput")
251 void unboundOutDataVariableTest(List<? extends Literal> clause) {
252 var builder = Dnf.builder().parameter(x, ParameterDirection.OUT).clause(clause);
253 assertThrows(IllegalArgumentException.class, builder::build);
254 }
255
256 @ParameterizedTest
257 @MethodSource("clausesWithDataVariableInput")
258 void unboundInDataVariableTest(List<? extends Literal> clause) {
259 var builder = Dnf.builder().parameter(x, ParameterDirection.IN).clause(clause);
260 var dnf = assertDoesNotThrow(builder::build);
261 var clauses = dnf.getClauses();
262 if (clauses.size() > 0) {
263 assertThat(clauses.get(0).positiveVariables(), hasItem(x));
264 }
265 }
266
267 @ParameterizedTest
268 @MethodSource("clausesWithDataVariableInput")
269 void boundPrivateDataVariableTest(List<? extends Literal> clause) {
270 var clauseWithBinding = new ArrayList<Literal>(clause);
271 clauseWithBinding.add(x.assign(constant(27)));
272 var builder = Dnf.builder().clause(clauseWithBinding);
273 var dnf = assertDoesNotThrow(builder::build);
274 var clauses = dnf.getClauses();
275 if (clauses.size() > 0) {
276 assertThat(clauses.get(0).positiveVariables(), hasItem(x));
277 }
278 }
279
280 static Stream<Arguments> clausesWithDataVariableInput() {
281 return Stream.concat(
282 clausesNotBindingDataVariable(),
283 literalToClauseArgumentStream(literalsWithRequiredDataVariableInput())
284 );
285 }
286
287 @ParameterizedTest
288 @MethodSource("clausesNotBindingDataVariable")
289 void unboundPrivateDataVariableTest(List<? extends Literal> clause) {
290 var builder = Dnf.builder().clause(clause);
291 var dnf = assertDoesNotThrow(builder::build);
292 var clauses = dnf.getClauses();
293 if (clauses.size() > 0) {
294 assertThat(clauses.get(0).positiveVariables(), not(hasItem(x)));
295 }
296 }
297
298 static Stream<Arguments> clausesNotBindingDataVariable() {
299 return Stream.concat(
300 Stream.of(
301 Arguments.of(List.of()),
302 Arguments.of(List.of(BooleanLiteral.TRUE)),
303 Arguments.of(List.of(BooleanLiteral.FALSE))
304 ),
305 literalToClauseArgumentStream(literalsWithPrivateDataVariable())
306 );
307 }
308
309 @ParameterizedTest
310 @MethodSource("literalsWithPrivateDataVariable")
311 void unboundTwicePrivateDataVariableTest(Literal literal) {
312 var builder = Dnf.builder().clause(not(ageView.call(p, x)), literal);
313 assertThrows(IllegalArgumentException.class, builder::build);
314 }
315
316 static Stream<Arguments> literalsWithPrivateDataVariable() {
317 var dnfWithOutput = Dnf.builder("WithDataOutput")
318 .parameter(y, ParameterDirection.OUT)
319 .parameter(q, ParameterDirection.OUT)
320 .clause(ageView.call(q, y))
321 .build();
322
323 return Stream.of(
324 Arguments.of(not(ageView.call(q, x))),
325 Arguments.of(y.assign(ageView.count(q, x))),
326 Arguments.of(not(dnfWithOutput.call(x, q)))
327 );
328 }
329
330 @ParameterizedTest
331 @MethodSource("literalsWithRequiredDataVariableInput")
332 void unboundPrivateDataVariableTest(Literal literal) {
333 var builder = Dnf.builder().clause(literal);
334 assertThrows(IllegalArgumentException.class, builder::build);
335 }
336
337 static Stream<Arguments> literalsWithRequiredDataVariableInput() {
338 var dnfWithInput = Dnf.builder("WithDataInput")
339 .parameter(y, ParameterDirection.IN)
340 .parameter(q, ParameterDirection.OUT)
341 .clause(ageView.call(q, x))
342 .build();
343 // We are passing {@code y} to the parameter named {@code right} of {@code greaterEq}.
344 @SuppressWarnings("SuspiciousNameCombination")
345 var dnfWithInputToAggregate = Dnf.builder("WithDataInputToAggregate")
346 .parameter(y, ParameterDirection.IN)
347 .parameter(q, ParameterDirection.OUT)
348 .parameter(x, ParameterDirection.OUT)
349 .clause(
350 friendView.call(p, q),
351 ageView.call(q, x),
352 check(greaterEq(x, y))
353 )
354 .build();
355
356 return Stream.of(
357 Arguments.of(dnfWithInput.call(x, q)),
358 Arguments.of(not(dnfWithInput.call(x, q))),
359 Arguments.of(y.assign(dnfWithInput.count(x, q))),
360 Arguments.of(y.assign(dnfWithInputToAggregate.aggregateBy(z, INT_SUM, x, q, z)))
361 );
362 }
363
364 @ParameterizedTest
365 @MethodSource("literalsWithDataVariableOutput")
366 void boundDataParameterTest(Literal literal) {
367 var builder = Dnf.builder().parameter(x, ParameterDirection.OUT).clause(literal);
368 var dnf = assertDoesNotThrow(builder::build);
369 assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(x));
370 }
371
372 @ParameterizedTest
373 @MethodSource("literalsWithDataVariableOutput")
374 void boundTwiceDataParameterTest(Literal literal) {
375 var builder = Dnf.builder().parameter(x, ParameterDirection.IN).clause(literal);
376 assertThrows(IllegalArgumentException.class, builder::build);
377 }
378
379 @ParameterizedTest
380 @MethodSource("literalsWithDataVariableOutput")
381 void boundPrivateDataVariableOutputTest(Literal literal) {
382 var dnfWithInput = Dnf.builder("WithInput")
383 .parameter(x, ParameterDirection.IN)
384 .clause(check(greaterEq(x, constant(24))))
385 .build();
386 var builder = Dnf.builder().clause(dnfWithInput.call(x), literal);
387 var dnf = assertDoesNotThrow(builder::build);
388 assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(x));
389 }
390
391 @ParameterizedTest
392 @MethodSource("literalsWithDataVariableOutput")
393 void boundTwicePrivateDataVariableOutputTest(Literal literal) {
394 var builder = Dnf.builder().clause(x.assign(constant(27)), literal);
395 assertThrows(IllegalArgumentException.class, builder::build);
396 }
397
398 static Stream<Arguments> literalsWithDataVariableOutput() {
399 var dnfWithOutput = Dnf.builder("WithOutput")
400 .parameter(q, ParameterDirection.OUT)
401 .clause(personView.call(q))
402 .build();
403 var dnfWithDataOutput = Dnf.builder("WithDataOutput")
404 .parameter(y, ParameterDirection.OUT)
405 .parameter(q, ParameterDirection.OUT)
406 .clause(ageView.call(q, y))
407 .build();
408 var dnfWithOutputToAggregate = Dnf.builder("WithDataOutputToAggregate")
409 .parameter(q, ParameterDirection.OUT)
410 .parameter(y, ParameterDirection.OUT)
411 .clause(ageView.call(q, y))
412 .build();
413
414 return Stream.of(
415 Arguments.of(x.assign(constant(24))),
416 Arguments.of(ageView.call(q, x)),
417 Arguments.of(x.assign(personView.count(q))),
418 Arguments.of(x.assign(ageView.aggregate(INT_SUM, q))),
419 Arguments.of(dnfWithDataOutput.call(x, q)),
420 Arguments.of(x.assign(dnfWithOutput.count(q))),
421 Arguments.of(x.assign(dnfWithOutputToAggregate.aggregateBy(z, INT_SUM, q, z)))
422 );
423 }
424
425 private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) { 248 private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) {
426 return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0]))); 249 return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0])));
427 } 250 }