aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query/src/main/java/tools/refinery/store
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-07 16:26:26 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-31 15:38:59 +0200
commit372058e54825ab58a66c25ae528e81a656c22659 (patch)
tree3686057057ebcad2faae7233dc691ecacc3e9fe2 /subprojects/store-query/src/main/java/tools/refinery/store
parentrefactor: use Cursor in query result sets (diff)
downloadrefinery-372058e54825ab58a66c25ae528e81a656c22659.tar.gz
refinery-372058e54825ab58a66c25ae528e81a656c22659.tar.zst
refinery-372058e54825ab58a66c25ae528e81a656c22659.zip
feat: terms and improved query evaluation
* Implement data terms for computations in queries. * Function-like queries with computed results. * Improved query evaluation, including positive and negative diagonal cosntraints. * Preliminary local search support. * Changes to the DNF representation for count and aggregation support. feat: terms wip feat: query terms wip feat: query evaluation, diagonal constraints, local search wip fix reasoning compilation wip
Diffstat (limited to 'subprojects/store-query/src/main/java/tools/refinery/store')
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java65
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java22
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java13
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java63
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java)61
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java)41
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java)5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java)2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java)2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java103
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java46
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java71
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java93
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java4
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java80
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java113
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java44
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java53
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java105
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java83
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java40
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java4
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java10
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java13
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java33
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java26
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java56
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java51
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java93
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java20
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java63
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java52
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java29
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java87
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java103
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java48
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java80
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java20
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java21
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java68
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java76
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java78
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java48
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java34
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java81
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java85
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java81
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java6
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java38
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java22
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java13
98 files changed, 3001 insertions, 343 deletions
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java
new file mode 100644
index 00000000..6d411212
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.dnf.AnyQuery;
4
5public sealed interface AnyResultSet permits ResultSet {
6 ModelQueryAdapter getAdapter();
7
8 AnyQuery getQuery();
9
10 int size();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java
new file mode 100644
index 00000000..cec4c19f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java
@@ -0,0 +1,65 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.literal.*;
5import tools.refinery.store.query.term.*;
6
7import java.util.List;
8
9public interface Constraint {
10 String name();
11
12 List<Sort> getSorts();
13
14 default int arity() {
15 return getSorts().size();
16 }
17
18 default boolean invalidIndex(int i) {
19 return i < 0 || i >= arity();
20 }
21
22 default LiteralReduction getReduction() {
23 return LiteralReduction.NOT_REDUCIBLE;
24 }
25
26 default boolean equals(LiteralEqualityHelper helper, Constraint other) {
27 return equals(other);
28 }
29
30 String toReferenceString();
31
32 default CallLiteral call(CallPolarity polarity, List<Variable> arguments) {
33 return new CallLiteral(polarity, this, arguments);
34 }
35
36 default CallLiteral call(CallPolarity polarity, Variable... arguments) {
37 return call(polarity, List.of(arguments));
38 }
39
40 default CallLiteral call(Variable... arguments) {
41 return call(CallPolarity.POSITIVE, arguments);
42 }
43
44 default CallLiteral callTransitive(NodeVariable left, NodeVariable right) {
45 return call(CallPolarity.TRANSITIVE, List.of(left, right));
46 }
47
48 default AssignedValue<Integer> count(List<Variable> arguments) {
49 return targetVariable -> new CountLiteral(targetVariable, this, arguments);
50 }
51
52 default AssignedValue<Integer> count(Variable... arguments) {
53 return count(List.of(arguments));
54 }
55
56 default <R, T> AssignedValue<R> aggregate(DataVariable<T> inputVariable, Aggregator<R, T> aggregator,
57 List<Variable> arguments) {
58 return targetVariable -> new AggregationLiteral<>(targetVariable, aggregator, inputVariable, this, arguments);
59 }
60
61 default <R, T> AssignedValue<R> aggregate(DataVariable<T> inputVariable, Aggregator<R, T> aggregator,
62 Variable... arguments) {
63 return aggregate(inputVariable, aggregator, List.of(arguments));
64 }
65}
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 9ff6df26..9af73bdd 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
@@ -2,21 +2,33 @@ package tools.refinery.store.query;
2 2
3import tools.refinery.store.map.Cursor; 3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.map.Cursors; 4import tools.refinery.store.map.Cursors;
5import tools.refinery.store.query.dnf.Query;
5import tools.refinery.store.tuple.TupleLike; 6import tools.refinery.store.tuple.TupleLike;
6 7
7public class EmptyResultSet implements ResultSet { 8public record EmptyResultSet<T>(ModelQueryAdapter adapter, Query<T> query) implements ResultSet<T> {
8 @Override 9 @Override
9 public boolean hasResult(TupleLike parameters) { 10 public ModelQueryAdapter getAdapter() {
10 return false; 11 return adapter;
11 } 12 }
12 13
13 @Override 14 @Override
14 public Cursor<TupleLike, Boolean> allResults() { 15 public Query<T> getQuery() {
16 return query;
17 }
18
19 @Override
20 public T get(TupleLike parameters) {
21 return query.defaultValue();
22 }
23
24
25 @Override
26 public Cursor<TupleLike, T> getAll() {
15 return Cursors.empty(); 27 return Cursors.empty();
16 } 28 }
17 29
18 @Override 30 @Override
19 public int countResults() { 31 public int size() {
20 return 0; 32 return 0;
21 } 33 }
22} 34}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
index f7762444..2e30fec4 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
@@ -1,11 +1,17 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.adapter.ModelAdapter; 3import tools.refinery.store.adapter.ModelAdapter;
4import tools.refinery.store.query.dnf.AnyQuery;
5import tools.refinery.store.query.dnf.Query;
4 6
5public interface ModelQueryAdapter extends ModelAdapter { 7public interface ModelQueryAdapter extends ModelAdapter {
6 ModelQueryStoreAdapter getStoreAdapter(); 8 ModelQueryStoreAdapter getStoreAdapter();
7 9
8 ResultSet getResultSet(Dnf query); 10 default AnyResultSet getResultSet(AnyQuery query) {
11 return getResultSet((Query<?>) query);
12 }
13
14 <T> ResultSet<T> getResultSet(Query<T> query);
9 15
10 boolean hasPendingChanges(); 16 boolean hasPendingChanges();
11 17
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
index b3cfb4b4..4fdc9210 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
@@ -2,21 +2,23 @@ package tools.refinery.store.query;
2 2
3import tools.refinery.store.adapter.ModelAdapterBuilder; 3import tools.refinery.store.adapter.ModelAdapterBuilder;
4import tools.refinery.store.model.ModelStore; 4import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.dnf.AnyQuery;
5 6
6import java.util.Collection; 7import java.util.Collection;
7import java.util.List; 8import java.util.List;
8 9
10@SuppressWarnings("UnusedReturnValue")
9public interface ModelQueryBuilder extends ModelAdapterBuilder { 11public interface ModelQueryBuilder extends ModelAdapterBuilder {
10 default ModelQueryBuilder queries(Dnf... queries) { 12 default ModelQueryBuilder queries(AnyQuery... queries) {
11 return queries(List.of(queries)); 13 return queries(List.of(queries));
12 } 14 }
13 15
14 default ModelQueryBuilder queries(Collection<Dnf> queries) { 16 default ModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
15 queries.forEach(this::query); 17 queries.forEach(this::query);
16 return this; 18 return this;
17 } 19 }
18 20
19 ModelQueryBuilder query(Dnf query); 21 ModelQueryBuilder query(AnyQuery query);
20 22
21 @Override 23 @Override
22 ModelQueryStoreAdapter createStoreAdapter(ModelStore store); 24 ModelQueryStoreAdapter createStoreAdapter(ModelStore store);
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
index 091d6d06..514e582b 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
@@ -1,15 +1,16 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.query.view.AnyRelationView;
4import tools.refinery.store.adapter.ModelStoreAdapter; 3import tools.refinery.store.adapter.ModelStoreAdapter;
5import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
5import tools.refinery.store.query.dnf.AnyQuery;
6import tools.refinery.store.query.view.AnyRelationView;
6 7
7import java.util.Collection; 8import java.util.Collection;
8 9
9public interface ModelQueryStoreAdapter extends ModelStoreAdapter { 10public interface ModelQueryStoreAdapter extends ModelStoreAdapter {
10 Collection<AnyRelationView> getRelationViews(); 11 Collection<AnyRelationView> getRelationViews();
11 12
12 Collection<Dnf> getQueries(); 13 Collection<AnyQuery> getQueries();
13 14
14 @Override 15 @Override
15 ModelQueryAdapter createModelAdapter(Model model); 16 ModelQueryAdapter createModelAdapter(Model model);
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java
deleted file mode 100644
index 8c784d8b..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java
+++ /dev/null
@@ -1,11 +0,0 @@
1package tools.refinery.store.query;
2
3public interface RelationLike {
4 String name();
5
6 int arity();
7
8 default boolean invalidIndex(int i) {
9 return i < 0 || i >= arity();
10 }
11}
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 d2b8c9dd..3f6bc06f 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
@@ -1,16 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.map.Cursor; 3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.query.dnf.Query;
4import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
5 6
6public interface ResultSet { 7public non-sealed interface ResultSet<T> extends AnyResultSet {
7 default boolean hasResult() { 8 Query<T> getQuery();
8 return countResults() > 0;
9 }
10 9
11 boolean hasResult(TupleLike parameters); 10 T get(TupleLike parameters);
12 11
13 Cursor<TupleLike, Boolean> allResults(); 12 Cursor<TupleLike, T> getAll();
14
15 int countResults();
16} 13}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java
deleted file mode 100644
index d0e0dead..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java
+++ /dev/null
@@ -1,63 +0,0 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.literal.ConstantLiteral;
4import tools.refinery.store.query.literal.EquivalenceLiteral;
5
6import java.util.Objects;
7
8public class Variable {
9 private final String name;
10 private final String uniqueName;
11
12 public Variable() {
13 this(null);
14 }
15
16 public Variable(String name) {
17 super();
18 this.name = name;
19 this.uniqueName = DnfUtils.generateUniqueName(name);
20
21 }
22 public String getName() {
23 return name == null ? uniqueName : name;
24 }
25
26 public boolean isExplicitlyNamed() {
27 return name != null;
28 }
29
30 public String getUniqueName() {
31 return uniqueName;
32 }
33
34 public ConstantLiteral isConstant(int value) {
35 return new ConstantLiteral(this, value);
36 }
37
38 public EquivalenceLiteral isEquivalent(Variable other) {
39 return new EquivalenceLiteral(true, this, other);
40 }
41
42 public EquivalenceLiteral notEquivalent(Variable other) {
43 return new EquivalenceLiteral(false, this, other);
44 }
45
46 @Override
47 public String toString() {
48 return getName();
49 }
50
51 @Override
52 public boolean equals(Object o) {
53 if (this == o) return true;
54 if (o == null || getClass() != o.getClass()) return false;
55 Variable variable = (Variable) o;
56 return Objects.equals(uniqueName, variable.uniqueName);
57 }
58
59 @Override
60 public int hashCode() {
61 return Objects.hash(uniqueName);
62 }
63}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java
new file mode 100644
index 00000000..d0a2367f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.dnf;
2
3public sealed interface AnyQuery permits Query {
4 String name();
5
6 int arity();
7
8 Class<?> valueType();
9
10 Dnf getDnf();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java
index b6744b50..1b7759c7 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java
@@ -1,14 +1,18 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.equality.DnfEqualityChecker; 3import tools.refinery.store.query.equality.DnfEqualityChecker;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.literal.CallPolarity; 5import tools.refinery.store.query.Constraint;
6import tools.refinery.store.query.literal.DnfCallLiteral;
7import tools.refinery.store.query.literal.LiteralReduction; 6import tools.refinery.store.query.literal.LiteralReduction;
7import tools.refinery.store.query.term.Sort;
8import tools.refinery.store.query.term.Variable;
8 9
9import java.util.*; 10import java.util.Collection;
11import java.util.HashSet;
12import java.util.List;
13import java.util.Set;
10 14
11public final class Dnf implements RelationLike { 15public final class Dnf implements Constraint {
12 private static final String INDENTATION = " "; 16 private static final String INDENTATION = " ";
13 17
14 private final String name; 18 private final String name;
@@ -69,6 +73,11 @@ public final class Dnf implements RelationLike {
69 return parameters; 73 return parameters;
70 } 74 }
71 75
76 @Override
77 public List<Sort> getSorts() {
78 return parameters.stream().map(Variable::getSort).toList();
79 }
80
72 public List<FunctionalDependency<Variable>> getFunctionalDependencies() { 81 public List<FunctionalDependency<Variable>> getFunctionalDependencies() {
73 return functionalDependencies; 82 return functionalDependencies;
74 } 83 }
@@ -82,6 +91,15 @@ public final class Dnf implements RelationLike {
82 return clauses; 91 return clauses;
83 } 92 }
84 93
94 public RelationalQuery asRelation() {
95 return new RelationalQuery(this);
96 }
97
98 public <T> FunctionalQuery<T> asFunction(Class<T> type) {
99 return new FunctionalQuery<>(this, type);
100 }
101
102 @Override
85 public LiteralReduction getReduction() { 103 public LiteralReduction getReduction() {
86 if (clauses.isEmpty()) { 104 if (clauses.isEmpty()) {
87 return LiteralReduction.ALWAYS_FALSE; 105 return LiteralReduction.ALWAYS_FALSE;
@@ -94,22 +112,6 @@ public final class Dnf implements RelationLike {
94 return LiteralReduction.NOT_REDUCIBLE; 112 return LiteralReduction.NOT_REDUCIBLE;
95 } 113 }
96 114
97 public DnfCallLiteral call(CallPolarity polarity, List<Variable> arguments) {
98 return new DnfCallLiteral(polarity, this, arguments);
99 }
100
101 public DnfCallLiteral call(CallPolarity polarity, Variable... arguments) {
102 return call(polarity, List.of(arguments));
103 }
104
105 public DnfCallLiteral call(Variable... arguments) {
106 return call(CallPolarity.POSITIVE, arguments);
107 }
108
109 public DnfCallLiteral callTransitive(Variable left, Variable right) {
110 return call(CallPolarity.TRANSITIVE, List.of(left, right));
111 }
112
113 public boolean equalsWithSubstitution(DnfEqualityChecker callEqualityChecker, Dnf other) { 115 public boolean equalsWithSubstitution(DnfEqualityChecker callEqualityChecker, Dnf other) {
114 if (arity() != other.arity()) { 116 if (arity() != other.arity()) {
115 return false; 117 return false;
@@ -128,7 +130,24 @@ public final class Dnf implements RelationLike {
128 } 130 }
129 131
130 @Override 132 @Override
133 public boolean equals(LiteralEqualityHelper helper, Constraint other) {
134 if (other instanceof Dnf otherDnf) {
135 return helper.dnfEqual(this, otherDnf);
136 }
137 return false;
138 }
139
140 @Override
131 public String toString() { 141 public String toString() {
142 return "%s/%d".formatted(name, arity());
143 }
144
145 @Override
146 public String toReferenceString() {
147 return "@Dnf " + name;
148 }
149
150 public String toDefinitionString() {
132 var builder = new StringBuilder(); 151 var builder = new StringBuilder();
133 builder.append("pred ").append(name()).append("("); 152 builder.append("pred ").append(name()).append("(");
134 var parameterIterator = parameters.iterator(); 153 var parameterIterator = parameters.iterator();
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java
index ca47e979..aad5a85f 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java
@@ -1,11 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.literal.Literal; 3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.Variable;
4 6
5import java.util.*; 7import java.util.*;
6 8
7@SuppressWarnings("UnusedReturnValue") 9@SuppressWarnings("UnusedReturnValue")
8public class DnfBuilder { 10public final class DnfBuilder {
9 private final String name; 11 private final String name;
10 12
11 private final List<Variable> parameters = new ArrayList<>(); 13 private final List<Variable> parameters = new ArrayList<>();
@@ -19,6 +21,9 @@ public class DnfBuilder {
19 } 21 }
20 22
21 public DnfBuilder parameter(Variable variable) { 23 public DnfBuilder parameter(Variable variable) {
24 if (parameters.contains(variable)) {
25 throw new IllegalArgumentException("Duplicate parameter: " + variable);
26 }
22 parameters.add(variable); 27 parameters.add(variable);
23 return this; 28 return this;
24 } 29 }
@@ -27,7 +32,7 @@ public class DnfBuilder {
27 return parameters(List.of(variables)); 32 return parameters(List.of(variables));
28 } 33 }
29 34
30 public DnfBuilder parameters(Collection<Variable> variables) { 35 public DnfBuilder parameters(Collection<? extends Variable> variables) {
31 parameters.addAll(variables); 36 parameters.addAll(variables);
32 return this; 37 return this;
33 } 38 }
@@ -42,8 +47,8 @@ public class DnfBuilder {
42 return this; 47 return this;
43 } 48 }
44 49
45 public DnfBuilder functionalDependency(Set<Variable> forEach, Set<Variable> unique) { 50 public DnfBuilder functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
46 return functionalDependency(new FunctionalDependency<>(forEach, unique)); 51 return functionalDependency(new FunctionalDependency<>(Set.copyOf(forEach), Set.copyOf(unique)));
47 } 52 }
48 53
49 public DnfBuilder clause(Literal... literals) { 54 public DnfBuilder clause(Literal... literals) {
@@ -51,7 +56,7 @@ public class DnfBuilder {
51 return this; 56 return this;
52 } 57 }
53 58
54 public DnfBuilder clause(Collection<Literal> literals) { 59 public DnfBuilder clause(Collection<? extends Literal> literals) {
55 // Remove duplicates by using a hashed data structure. 60 // Remove duplicates by using a hashed data structure.
56 var filteredLiterals = new LinkedHashSet<Literal>(literals.size()); 61 var filteredLiterals = new LinkedHashSet<Literal>(literals.size());
57 for (var literal : literals) { 62 for (var literal : literals) {
@@ -73,21 +78,6 @@ public class DnfBuilder {
73 return this; 78 return this;
74 } 79 }
75 80
76 public DnfBuilder clause(DnfClause clause) {
77 return clause(clause.literals());
78 }
79
80 public DnfBuilder clauses(DnfClause... clauses) {
81 return clauses(List.of(clauses));
82 }
83
84 public DnfBuilder clauses(Collection<DnfClause> clauses) {
85 for (var clause : clauses) {
86 this.clause(clause);
87 }
88 return this;
89 }
90
91 public Dnf build() { 81 public Dnf build() {
92 var postProcessedClauses = postProcessClauses(); 82 var postProcessedClauses = postProcessClauses();
93 return new Dnf(name, Collections.unmodifiableList(parameters), 83 return new Dnf(name, Collections.unmodifiableList(parameters),
@@ -95,6 +85,11 @@ public class DnfBuilder {
95 Collections.unmodifiableList(postProcessedClauses)); 85 Collections.unmodifiableList(postProcessedClauses));
96 } 86 }
97 87
88 <T> void output(DataVariable<T> outputVariable) {
89 functionalDependency(Set.copyOf(parameters), Set.of(outputVariable));
90 parameter(outputVariable);
91 }
92
98 private List<DnfClause> postProcessClauses() { 93 private List<DnfClause> postProcessClauses() {
99 var postProcessedClauses = new ArrayList<DnfClause>(clauses.size()); 94 var postProcessedClauses = new ArrayList<DnfClause>(clauses.size());
100 for (var literals : clauses) { 95 for (var literals : clauses) {
@@ -103,8 +98,8 @@ public class DnfBuilder {
103 return List.of(new DnfClause(Set.of(), List.of())); 98 return List.of(new DnfClause(Set.of(), List.of()));
104 } 99 }
105 var variables = new HashSet<Variable>(); 100 var variables = new HashSet<Variable>();
106 for (var constraint : literals) { 101 for (var literal : literals) {
107 constraint.collectAllVariables(variables); 102 variables.addAll(literal.getBoundVariables());
108 } 103 }
109 parameters.forEach(variables::remove); 104 parameters.forEach(variables::remove);
110 postProcessedClauses.add(new DnfClause(Collections.unmodifiableSet(variables), 105 postProcessedClauses.add(new DnfClause(Collections.unmodifiableSet(variables),
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java
index c6e8b8c9..01830af1 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java
@@ -1,12 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.equality.LiteralEqualityHelper; 3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.literal.Literal; 4import tools.refinery.store.query.literal.Literal;
5import tools.refinery.store.query.term.Variable;
5 6
6import java.util.List; 7import java.util.List;
7import java.util.Set; 8import java.util.Set;
8 9
9public record DnfClause(Set<Variable> quantifiedVariables, List<Literal> literals) { 10public record DnfClause(Set<Variable> boundVariables, List<Literal> literals) {
10 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, DnfClause other) { 11 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, DnfClause other) {
11 int size = literals.size(); 12 int size = literals.size();
12 if (size != other.literals.size()) { 13 if (size != other.literals.size()) {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java
index c7a2849c..9bcf944c 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import java.util.UUID; 3import java.util.UUID;
4 4
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java
index 63a81713..f4cd109f 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import java.util.HashSet; 3import java.util.HashSet;
4import java.util.Set; 4import java.util.Set;
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java
new file mode 100644
index 00000000..5bf6f8c5
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java
@@ -0,0 +1,103 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.CallPolarity;
4import tools.refinery.store.query.term.*;
5
6import java.util.ArrayList;
7import java.util.List;
8import java.util.Objects;
9
10public final class FunctionalQuery<T> implements Query<T> {
11 private final Dnf dnf;
12 private final Class<T> type;
13
14 FunctionalQuery(Dnf dnf, Class<T> type) {
15 var parameters = dnf.getParameters();
16 int outputIndex = dnf.arity() - 1;
17 for (int i = 0; i < outputIndex; i++) {
18 var parameter = parameters.get(i);
19 if (!(parameter instanceof NodeVariable)) {
20 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
21 .formatted(parameter, dnf, NodeSort.INSTANCE, parameter.getSort()));
22 }
23 }
24 var outputParameter = parameters.get(outputIndex);
25 if (!(outputParameter instanceof DataVariable<?> dataOutputParameter) ||
26 !dataOutputParameter.getType().equals(type)) {
27 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
28 .formatted(outputParameter, dnf, type, outputParameter.getSort()));
29 }
30 this.dnf = dnf;
31 this.type = type;
32 }
33
34 @Override
35 public String name() {
36 return dnf.name();
37 }
38
39 @Override
40 public int arity() {
41 return dnf.arity() - 1;
42 }
43
44 @Override
45 public Class<T> valueType() {
46 return type;
47 }
48
49 @Override
50 public T defaultValue() {
51 return null;
52 }
53
54 @Override
55 public Dnf getDnf() {
56 return dnf;
57 }
58
59 public AssignedValue<T> call(List<NodeVariable> arguments) {
60 return targetVariable -> {
61 var argumentsWithTarget = new ArrayList<Variable>(arguments.size() + 1);
62 argumentsWithTarget.addAll(arguments);
63 argumentsWithTarget.add(targetVariable);
64 return dnf.call(CallPolarity.POSITIVE, argumentsWithTarget);
65 };
66 }
67
68 public AssignedValue<T> call(NodeVariable... arguments) {
69 return call(List.of(arguments));
70 }
71
72 public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, List<NodeVariable> arguments) {
73 return targetVariable -> {
74 var placeholderVariable = Variable.of(type);
75 var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1);
76 argumentsWithPlaceholder.addAll(arguments);
77 argumentsWithPlaceholder.add(placeholderVariable);
78 return dnf.aggregate(placeholderVariable, aggregator, argumentsWithPlaceholder).toLiteral(targetVariable);
79 };
80 }
81
82 public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, NodeVariable... arguments) {
83 return aggregate(aggregator, List.of(arguments));
84 }
85
86 @Override
87 public boolean equals(Object o) {
88 if (this == o) return true;
89 if (o == null || getClass() != o.getClass()) return false;
90 FunctionalQuery<?> that = (FunctionalQuery<?>) o;
91 return dnf.equals(that.dnf) && type.equals(that.type);
92 }
93
94 @Override
95 public int hashCode() {
96 return Objects.hash(dnf, type);
97 }
98
99 @Override
100 public String toString() {
101 return dnf.toString();
102 }
103}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java
new file mode 100644
index 00000000..ca2bc006
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.Variable;
5
6import java.util.Collection;
7import java.util.Set;
8
9public final class FunctionalQueryBuilder<T> {
10 private final DnfBuilder dnfBuilder;
11 private final Class<T> type;
12
13 FunctionalQueryBuilder(DnfBuilder dnfBuilder, Class<T> type) {
14 this.dnfBuilder = dnfBuilder;
15 this.type = type;
16 }
17
18 public FunctionalQueryBuilder<T> functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) {
19 dnfBuilder.functionalDependencies(functionalDependencies);
20 return this;
21 }
22
23 public FunctionalQueryBuilder<T> functionalDependency(FunctionalDependency<Variable> functionalDependency) {
24 dnfBuilder.functionalDependency(functionalDependency);
25 return this;
26 }
27
28 public FunctionalQueryBuilder<T> functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
29 dnfBuilder.functionalDependency(forEach, unique);
30 return this;
31 }
32
33 public FunctionalQueryBuilder<T> clause(Literal... literals) {
34 dnfBuilder.clause(literals);
35 return this;
36 }
37
38 public FunctionalQueryBuilder<T> clause(Collection<? extends Literal> literals) {
39 dnfBuilder.clause(literals);
40 return this;
41 }
42
43 public FunctionalQuery<T> build() {
44 return dnfBuilder.build().asFunction(type);
45 }
46}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java
new file mode 100644
index 00000000..32e33052
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.dnf;
2
3public sealed interface Query<T> extends AnyQuery permits RelationalQuery, FunctionalQuery {
4 @Override
5 Class<T> valueType();
6
7 T defaultValue();
8
9 static QueryBuilder builder() {
10 return new QueryBuilder();
11 }
12
13 static QueryBuilder builder(String name) {
14 return new QueryBuilder(name);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java
new file mode 100644
index 00000000..ed253cc9
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java
@@ -0,0 +1,71 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.Collection;
9import java.util.List;
10import java.util.Set;
11
12public final class QueryBuilder {
13 private final DnfBuilder dnfBuilder;
14
15 QueryBuilder(String name) {
16 dnfBuilder = Dnf.builder(name);
17 }
18
19 QueryBuilder() {
20 dnfBuilder = Dnf.builder();
21 }
22
23 public QueryBuilder parameter(NodeVariable variable) {
24 dnfBuilder.parameter(variable);
25 return this;
26 }
27
28 public QueryBuilder parameters(NodeVariable... variables) {
29 dnfBuilder.parameters(variables);
30 return this;
31 }
32
33 public QueryBuilder parameters(List<NodeVariable> variables) {
34 dnfBuilder.parameters(variables);
35 return this;
36 }
37
38 public <T> FunctionalQueryBuilder<T> output(DataVariable<T> outputVariable) {
39 dnfBuilder.output(outputVariable);
40 return new FunctionalQueryBuilder<>(dnfBuilder, outputVariable.getType());
41 }
42
43 public QueryBuilder functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) {
44 dnfBuilder.functionalDependencies(functionalDependencies);
45 return this;
46 }
47
48 public QueryBuilder functionalDependency(FunctionalDependency<Variable> functionalDependency) {
49 dnfBuilder.functionalDependency(functionalDependency);
50 return this;
51 }
52
53 public QueryBuilder functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
54 dnfBuilder.functionalDependency(forEach, unique);
55 return this;
56 }
57
58 public QueryBuilder clause(Literal... literals) {
59 dnfBuilder.clause(literals);
60 return this;
61 }
62
63 public QueryBuilder clause(Collection<? extends Literal> literals) {
64 dnfBuilder.clause(literals);
65 return this;
66 }
67
68 public RelationalQuery build() {
69 return dnfBuilder.build().asRelation();
70 }
71}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java
new file mode 100644
index 00000000..5307e509
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java
@@ -0,0 +1,93 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.CallLiteral;
4import tools.refinery.store.query.literal.CallPolarity;
5import tools.refinery.store.query.term.AssignedValue;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.NodeVariable;
8
9import java.util.Collections;
10import java.util.List;
11import java.util.Objects;
12
13public final class RelationalQuery implements Query<Boolean> {
14 private final Dnf dnf;
15
16 RelationalQuery(Dnf dnf) {
17 for (var parameter : dnf.getParameters()) {
18 if (!(parameter instanceof NodeVariable)) {
19 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
20 .formatted(parameter, dnf, NodeSort.INSTANCE, parameter.getSort()));
21 }
22 }
23 this.dnf = dnf;
24 }
25
26 @Override
27 public String name() {
28 return dnf.name();
29 }
30
31 @Override
32 public int arity() {
33 return dnf.arity();
34 }
35
36 @Override
37 public Class<Boolean> valueType() {
38 return Boolean.class;
39 }
40
41 @Override
42 public Boolean defaultValue() {
43 return false;
44 }
45
46 @Override
47 public Dnf getDnf() {
48 return dnf;
49 }
50
51 public CallLiteral call(CallPolarity polarity, List<NodeVariable> arguments) {
52 return dnf.call(polarity, Collections.unmodifiableList(arguments));
53 }
54
55 public CallLiteral call(CallPolarity polarity, NodeVariable... arguments) {
56 return dnf.call(polarity, arguments);
57 }
58
59 public CallLiteral call(NodeVariable... arguments) {
60 return dnf.call(arguments);
61 }
62
63 public CallLiteral callTransitive(NodeVariable left, NodeVariable right) {
64 return dnf.callTransitive(left, right);
65 }
66
67 public AssignedValue<Integer> count(List<NodeVariable> arguments) {
68 return dnf.count(Collections.unmodifiableList(arguments));
69 }
70
71 public AssignedValue<Integer> count(NodeVariable... arguments) {
72 return dnf.count(arguments);
73 }
74
75
76 @Override
77 public boolean equals(Object o) {
78 if (this == o) return true;
79 if (o == null || getClass() != o.getClass()) return false;
80 RelationalQuery that = (RelationalQuery) o;
81 return dnf.equals(that.dnf);
82 }
83
84 @Override
85 public int hashCode() {
86 return Objects.hash(dnf);
87 }
88
89 @Override
90 public String toString() {
91 return dnf.toString();
92 }
93}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
index ebd7f5b0..c3bc3ea3 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.util.CycleDetectingMapper; 4import tools.refinery.store.util.CycleDetectingMapper;
5 5
6import java.util.List; 6import java.util.List;
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
index eb77de17..6b1f2076 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4 4
5@FunctionalInterface 5@FunctionalInterface
6public interface DnfEqualityChecker { 6public interface DnfEqualityChecker {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
index 23f1acc7..07d261ea 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
@@ -1,7 +1,7 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.query.Variable; 4import tools.refinery.store.query.term.Variable;
5 5
6import java.util.HashMap; 6import java.util.HashMap;
7import java.util.List; 7import java.util.List;
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
new file mode 100644
index 00000000..657ca26b
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
@@ -0,0 +1,80 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.List;
9import java.util.Objects;
10
11public abstract class AbstractCallLiteral implements Literal {
12 private final Constraint target;
13 private final List<Variable> arguments;
14
15 protected AbstractCallLiteral(Constraint target, List<Variable> arguments) {
16 int arity = target.arity();
17 if (arguments.size() != arity) {
18 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(),
19 target.arity(), arguments.size()));
20 }
21 this.target = target;
22 this.arguments = arguments;
23 var sorts = target.getSorts();
24 for (int i = 0; i < arity; i++) {
25 var argument = arguments.get(i);
26 var sort = sorts.get(i);
27 if (!sort.isInstance(argument)) {
28 throw new IllegalArgumentException("Required argument %d of %s to be of sort %s, but got %s instead"
29 .formatted(i, target, sort, argument.getSort()));
30 }
31 }
32 }
33
34 public Constraint getTarget() {
35 return target;
36 }
37
38 public List<Variable> getArguments() {
39 return arguments;
40 }
41
42 @Override
43 public Literal substitute(Substitution substitution) {
44 var substitutedArguments = arguments.stream().map(substitution::getSubstitute).toList();
45 return doSubstitute(substitution, substitutedArguments);
46 }
47
48 protected abstract Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments);
49
50 @Override
51 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
52 if (other == null || getClass() != other.getClass()) {
53 return false;
54 }
55 var otherCallLiteral = (AbstractCallLiteral) other;
56 var arity = arguments.size();
57 if (arity != otherCallLiteral.arguments.size()) {
58 return false;
59 }
60 for (int i = 0; i < arity; i++) {
61 if (!helper.variableEqual(arguments.get(i), otherCallLiteral.arguments.get(i))) {
62 return false;
63 }
64 }
65 return target.equals(helper, otherCallLiteral.target);
66 }
67
68 @Override
69 public boolean equals(Object o) {
70 if (this == o) return true;
71 if (o == null || getClass() != o.getClass()) return false;
72 AbstractCallLiteral that = (AbstractCallLiteral) o;
73 return target.equals(that.target) && arguments.equals(that.arguments);
74 }
75
76 @Override
77 public int hashCode() {
78 return Objects.hash(target, arguments);
79 }
80}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java
new file mode 100644
index 00000000..df64839c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java
@@ -0,0 +1,113 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.Aggregator;
7import tools.refinery.store.query.term.DataVariable;
8import tools.refinery.store.query.term.Variable;
9
10import java.util.List;
11import java.util.Objects;
12import java.util.Set;
13
14public class AggregationLiteral<R, T> extends AbstractCallLiteral {
15 private final DataVariable<R> resultVariable;
16 private final DataVariable<T> inputVariable;
17 private final Aggregator<R, T> aggregator;
18
19 public AggregationLiteral(DataVariable<R> resultVariable, Aggregator<R, T> aggregator,
20 DataVariable<T> inputVariable, Constraint target, List<Variable> arguments) {
21 super(target, arguments);
22 if (!inputVariable.getType().equals(aggregator.getInputType())) {
23 throw new IllegalArgumentException("Input variable %s must of type %s, got %s instead".formatted(
24 inputVariable, aggregator.getInputType().getName(), inputVariable.getType().getName()));
25 }
26 if (!resultVariable.getType().equals(aggregator.getResultType())) {
27 throw new IllegalArgumentException("Result variable %s must of type %s, got %s instead".formatted(
28 resultVariable, aggregator.getResultType().getName(), resultVariable.getType().getName()));
29 }
30 if (!arguments.contains(inputVariable)) {
31 throw new IllegalArgumentException("Input variable %s must appear in the argument list".formatted(
32 inputVariable));
33 }
34 if (arguments.contains(resultVariable)) {
35 throw new IllegalArgumentException("Result variable %s must not appear in the argument list".formatted(
36 resultVariable));
37 }
38 this.resultVariable = resultVariable;
39 this.inputVariable = inputVariable;
40 this.aggregator = aggregator;
41 }
42
43 public DataVariable<R> getResultVariable() {
44 return resultVariable;
45 }
46
47 public DataVariable<T> getInputVariable() {
48 return inputVariable;
49 }
50
51 public Aggregator<R, T> getAggregator() {
52 return aggregator;
53 }
54
55 @Override
56 public Set<Variable> getBoundVariables() {
57 return Set.of(resultVariable);
58 }
59
60 @Override
61 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
62 return new AggregationLiteral<>(substitution.getTypeSafeSubstitute(resultVariable), aggregator,
63 substitution.getTypeSafeSubstitute(inputVariable), getTarget(), substitutedArguments);
64 }
65
66 @Override
67 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
68 if (!super.equalsWithSubstitution(helper, other)) {
69 return false;
70 }
71 var otherAggregationLiteral = (AggregationLiteral<?, ?>) other;
72 return helper.variableEqual(resultVariable, otherAggregationLiteral.resultVariable) &&
73 aggregator.equals(otherAggregationLiteral.aggregator) &&
74 helper.variableEqual(inputVariable, otherAggregationLiteral.inputVariable);
75 }
76
77 @Override
78 public boolean equals(Object o) {
79 if (this == o) return true;
80 if (o == null || getClass() != o.getClass()) return false;
81 if (!super.equals(o)) return false;
82 AggregationLiteral<?, ?> that = (AggregationLiteral<?, ?>) o;
83 return resultVariable.equals(that.resultVariable) && inputVariable.equals(that.inputVariable) &&
84 aggregator.equals(that.aggregator);
85 }
86
87 @Override
88 public int hashCode() {
89 return Objects.hash(super.hashCode(), resultVariable, inputVariable, aggregator);
90 }
91
92 @Override
93 public String toString() {
94 var builder = new StringBuilder();
95 builder.append(resultVariable);
96 builder.append(" is ");
97 builder.append(getTarget().toReferenceString());
98 builder.append("(");
99 var argumentIterator = getArguments().iterator();
100 if (argumentIterator.hasNext()) {
101 var argument = argumentIterator.next();
102 if (inputVariable.equals(argument)) {
103 builder.append("@Aggregate(\"").append(aggregator).append("\") ");
104 }
105 builder.append(argument);
106 while (argumentIterator.hasNext()) {
107 builder.append(", ").append(argumentIterator.next());
108 }
109 }
110 builder.append(")");
111 return builder.toString();
112 }
113}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java
new file mode 100644
index 00000000..52ac42d7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java
@@ -0,0 +1,44 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.DataVariable;
6import tools.refinery.store.query.term.Term;
7import tools.refinery.store.query.term.Variable;
8
9import java.util.Set;
10
11public record AssignLiteral<T>(DataVariable<T> variable, Term<T> term) implements Literal {
12 public AssignLiteral {
13 if (!term.getType().equals(variable.getType())) {
14 throw new IllegalArgumentException("Term %s must be of type %s, got %s instead".formatted(
15 term, variable.getType().getName(), term.getType().getName()));
16 }
17 }
18
19 @Override
20 public Set<Variable> getBoundVariables() {
21 return Set.of(variable);
22 }
23
24 @Override
25 public Literal substitute(Substitution substitution) {
26 return new AssignLiteral<>(substitution.getTypeSafeSubstitute(variable), term.substitute(substitution));
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
31 if (other == null || getClass() != other.getClass()) {
32 return false;
33 }
34 var otherLetLiteral = (AssignLiteral<?>) other;
35 return helper.variableEqual(variable, otherLetLiteral.variable) && term.equalsWithSubstitution(helper,
36 otherLetLiteral.term);
37 }
38
39
40 @Override
41 public String toString() {
42 return "%s is (%s)".formatted(variable, term);
43 }
44}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java
new file mode 100644
index 00000000..0b4267b4
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java
@@ -0,0 +1,53 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.Term;
6import tools.refinery.store.query.term.Variable;
7import tools.refinery.store.query.term.bool.BoolConstantTerm;
8
9import java.util.Set;
10
11public record AssumeLiteral(Term<Boolean> term) implements Literal {
12 public AssumeLiteral {
13 if (!term.getType().equals(Boolean.class)) {
14 throw new IllegalArgumentException("Term %s must be of type %s, got %s instead".formatted(
15 term, Boolean.class.getName(), term.getType().getName()));
16 }
17 }
18
19 @Override
20 public Set<Variable> getBoundVariables() {
21 return Set.of();
22 }
23
24 @Override
25 public Literal substitute(Substitution substitution) {
26 return new AssumeLiteral(term.substitute(substitution));
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
31 if (other == null || getClass() != other.getClass()) {
32 return false;
33 }
34 var otherAssumeLiteral = (AssumeLiteral) other;
35 return term.equalsWithSubstitution(helper, otherAssumeLiteral.term);
36 }
37
38 @Override
39 public LiteralReduction getReduction() {
40 if (BoolConstantTerm.TRUE.equals(term)) {
41 return LiteralReduction.ALWAYS_TRUE;
42 } else if (BoolConstantTerm.FALSE.equals(term)) {
43 return LiteralReduction.ALWAYS_FALSE;
44 } else {
45 return LiteralReduction.NOT_REDUCIBLE;
46 }
47 }
48
49 @Override
50 public String toString() {
51 return "(%s)".formatted(term);
52 }
53}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
index 6d751be8..38be61a4 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
@@ -1,12 +1,12 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6 6
7import java.util.Set; 7import java.util.Set;
8 8
9public enum BooleanLiteral implements PolarLiteral<BooleanLiteral> { 9public enum BooleanLiteral implements CanNegate<BooleanLiteral> {
10 TRUE(true), 10 TRUE(true),
11 FALSE(false); 11 FALSE(false);
12 12
@@ -17,8 +17,8 @@ public enum BooleanLiteral implements PolarLiteral<BooleanLiteral> {
17 } 17 }
18 18
19 @Override 19 @Override
20 public void collectAllVariables(Set<Variable> variables) { 20 public Set<Variable> getBoundVariables() {
21 // No variables to collect. 21 return Set.of();
22 } 22 }
23 23
24 @Override 24 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
index 091b4e04..78fae7f5 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
@@ -1,109 +1,78 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.RelationLike; 3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.Variable;
7 8
8import java.util.List; 9import java.util.List;
9import java.util.Objects; 10import java.util.Objects;
10import java.util.Set; 11import java.util.Set;
11 12
12public abstract class CallLiteral<T extends RelationLike> implements Literal { 13public final class CallLiteral extends AbstractCallLiteral implements CanNegate<CallLiteral> {
13 private final CallPolarity polarity; 14 private final CallPolarity polarity;
14 private final T target;
15 private final List<Variable> arguments;
16 15
17 protected CallLiteral(CallPolarity polarity, T target, List<Variable> arguments) { 16 public CallLiteral(CallPolarity polarity, Constraint target, List<Variable> arguments) {
18 if (arguments.size() != target.arity()) { 17 super(target, arguments);
19 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(), 18 if (polarity.isTransitive()) {
20 target.arity(), arguments.size())); 19 if (target.arity() != 2) {
21 } 20 throw new IllegalArgumentException("Transitive closures can only take binary relations");
22 if (polarity.isTransitive() && target.arity() != 2) { 21 }
23 throw new IllegalArgumentException("Transitive closures can only take binary relations"); 22 var sorts = target.getSorts();
23 if (!sorts.get(0).equals(NodeSort.INSTANCE) || !sorts.get(1).equals(NodeSort.INSTANCE)) {
24 throw new IllegalArgumentException("Transitive closures can only be computed over nodes");
25 }
24 } 26 }
25 this.polarity = polarity; 27 this.polarity = polarity;
26 this.target = target;
27 this.arguments = arguments;
28 } 28 }
29 29
30 public CallPolarity getPolarity() { 30 public CallPolarity getPolarity() {
31 return polarity; 31 return polarity;
32 } 32 }
33 33
34 public abstract Class<T> getTargetType();
35
36 public T getTarget() {
37 return target;
38 }
39
40 public List<Variable> getArguments() {
41 return arguments;
42 }
43
44 @Override 34 @Override
45 public void collectAllVariables(Set<Variable> variables) { 35 public Set<Variable> getBoundVariables() {
46 if (polarity.isPositive()) { 36 return polarity.isPositive() ? Set.copyOf(getArguments()) : Set.of();
47 variables.addAll(arguments);
48 }
49 } 37 }
50 38
51 protected List<Variable> substituteArguments(Substitution substitution) { 39 @Override
52 return arguments.stream().map(substitution::getSubstitute).toList(); 40 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
41 return new CallLiteral(polarity, getTarget(), substitutedArguments);
53 } 42 }
54 43
55 /** 44 @Override
56 * Compares the target of this call literal with another object. 45 public LiteralReduction getReduction() {
57 * 46 var reduction = getTarget().getReduction();
58 * @param helper Equality helper for comparing {@link Variable} and {@link tools.refinery.store.query.Dnf} 47 return polarity.isPositive() ? reduction : reduction.negate();
59 * instances.
60 * @param otherTarget The object to compare the target to.
61 * @return {@code true} if {@code otherTarget} is equal to the return value of {@link #getTarget()} according to
62 * {@code helper}, {@code false} otherwise.
63 */
64 protected boolean targetEquals(LiteralEqualityHelper helper, T otherTarget) {
65 return target.equals(otherTarget);
66 } 48 }
67 49
68 @Override 50 @Override
69 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) { 51 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
70 if (other.getClass() != getClass()) { 52 if (!super.equalsWithSubstitution(helper, other)) {
71 return false; 53 return false;
72 } 54 }
73 var otherCallLiteral = (CallLiteral<?>) other; 55 var otherCallLiteral = (CallLiteral) other;
74 if (getTargetType() != otherCallLiteral.getTargetType() || polarity != otherCallLiteral.polarity) { 56 return polarity.equals(otherCallLiteral.polarity);
75 return false; 57 }
76 } 58
77 var arity = arguments.size(); 59 @Override
78 if (arity != otherCallLiteral.arguments.size()) { 60 public CallLiteral negate() {
79 return false; 61 return new CallLiteral(polarity.negate(), getTarget(), getArguments());
80 }
81 for (int i = 0; i < arity; i++) {
82 if (!helper.variableEqual(arguments.get(i), otherCallLiteral.arguments.get(i))) {
83 return false;
84 }
85 }
86 @SuppressWarnings("unchecked")
87 var otherTarget = (T) otherCallLiteral.target;
88 return targetEquals(helper, otherTarget);
89 } 62 }
90 63
91 @Override 64 @Override
92 public boolean equals(Object o) { 65 public boolean equals(Object o) {
93 if (this == o) return true; 66 if (this == o) return true;
94 if (o == null || getClass() != o.getClass()) return false; 67 if (o == null || getClass() != o.getClass()) return false;
95 CallLiteral<?> callAtom = (CallLiteral<?>) o; 68 if (!super.equals(o)) return false;
96 return polarity == callAtom.polarity && Objects.equals(target, callAtom.target) && 69 CallLiteral that = (CallLiteral) o;
97 Objects.equals(arguments, callAtom.arguments); 70 return polarity == that.polarity;
98 } 71 }
99 72
100 @Override 73 @Override
101 public int hashCode() { 74 public int hashCode() {
102 return Objects.hash(polarity, target, arguments); 75 return Objects.hash(super.hashCode(), polarity);
103 }
104
105 protected String targetToString() {
106 return "@%s %s".formatted(getTargetType().getSimpleName(), target.name());
107 } 76 }
108 77
109 @Override 78 @Override
@@ -112,12 +81,12 @@ public abstract class CallLiteral<T extends RelationLike> implements Literal {
112 if (!polarity.isPositive()) { 81 if (!polarity.isPositive()) {
113 builder.append("!("); 82 builder.append("!(");
114 } 83 }
115 builder.append(targetToString()); 84 builder.append(getTarget().toReferenceString());
116 if (polarity.isTransitive()) { 85 if (polarity.isTransitive()) {
117 builder.append("+"); 86 builder.append("+");
118 } 87 }
119 builder.append("("); 88 builder.append("(");
120 var argumentIterator = arguments.iterator(); 89 var argumentIterator = getArguments().iterator();
121 if (argumentIterator.hasNext()) { 90 if (argumentIterator.hasNext()) {
122 builder.append(argumentIterator.next()); 91 builder.append(argumentIterator.next());
123 while (argumentIterator.hasNext()) { 92 while (argumentIterator.hasNext()) {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java
new file mode 100644
index 00000000..3e159c43
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java
@@ -0,0 +1,5 @@
1package tools.refinery.store.query.literal;
2
3public interface CanNegate<T extends CanNegate<T>> extends Literal {
4 T negate();
5}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
index d01c7d20..93fa3df0 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
@@ -1,20 +1,21 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 3import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
6 7
7import java.util.Set; 8import java.util.Set;
8 9
9public record ConstantLiteral(Variable variable, int nodeId) implements Literal { 10public record ConstantLiteral(NodeVariable variable, int nodeId) implements Literal {
10 @Override 11 @Override
11 public void collectAllVariables(Set<Variable> variables) { 12 public Set<Variable> getBoundVariables() {
12 variables.add(variable); 13 return Set.of(variable);
13 } 14 }
14 15
15 @Override 16 @Override
16 public ConstantLiteral substitute(Substitution substitution) { 17 public ConstantLiteral substitute(Substitution substitution) {
17 return new ConstantLiteral(substitution.getSubstitute(variable), nodeId); 18 return new ConstantLiteral(substitution.getTypeSafeSubstitute(variable), nodeId);
18 } 19 }
19 20
20 @Override 21 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java
new file mode 100644
index 00000000..32e7ba3a
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java
@@ -0,0 +1,83 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.DataVariable;
7import tools.refinery.store.query.term.Variable;
8
9import java.util.List;
10import java.util.Objects;
11import java.util.Set;
12
13public class CountLiteral extends AbstractCallLiteral {
14 private final DataVariable<Integer> resultVariable;
15
16 public CountLiteral(DataVariable<Integer> resultVariable, Constraint target, List<Variable> arguments) {
17 super(target, arguments);
18 if (!resultVariable.getType().equals(Integer.class)) {
19 throw new IllegalArgumentException("Count result variable %s must be of type %s, got %s instead".formatted(
20 resultVariable, Integer.class.getName(), resultVariable.getType().getName()));
21 }
22 if (arguments.contains(resultVariable)) {
23 throw new IllegalArgumentException("Count result variable %s must not appear in the argument list"
24 .formatted(resultVariable));
25 }
26 this.resultVariable = resultVariable;
27 }
28
29 public DataVariable<Integer> getResultVariable() {
30 return resultVariable;
31 }
32
33 @Override
34 public Set<Variable> getBoundVariables() {
35 return Set.of(resultVariable);
36 }
37
38 @Override
39 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
40 return new CountLiteral(substitution.getTypeSafeSubstitute(resultVariable), getTarget(), substitutedArguments);
41 }
42
43 @Override
44 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
45 if (!super.equalsWithSubstitution(helper, other)) {
46 return false;
47 }
48 var otherCountLiteral = (CountLiteral) other;
49 return helper.variableEqual(resultVariable, otherCountLiteral.resultVariable);
50 }
51
52 @Override
53 public boolean equals(Object o) {
54 if (this == o) return true;
55 if (o == null || getClass() != o.getClass()) return false;
56 if (!super.equals(o)) return false;
57 CountLiteral that = (CountLiteral) o;
58 return resultVariable.equals(that.resultVariable);
59 }
60
61 @Override
62 public int hashCode() {
63 return Objects.hash(super.hashCode(), resultVariable);
64 }
65
66 @Override
67 public String toString() {
68 var builder = new StringBuilder();
69 builder.append(resultVariable);
70 builder.append(" is count ");
71 builder.append(getTarget().toReferenceString());
72 builder.append("(");
73 var argumentIterator = getArguments().iterator();
74 if (argumentIterator.hasNext()) {
75 builder.append(argumentIterator.next());
76 while (argumentIterator.hasNext()) {
77 builder.append(", ").append(argumentIterator.next());
78 }
79 }
80 builder.append(")");
81 return builder.toString();
82 }
83}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java
deleted file mode 100644
index 27917265..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java
+++ /dev/null
@@ -1,40 +0,0 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Dnf;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.substitution.Substitution;
7
8import java.util.List;
9
10public final class DnfCallLiteral extends CallLiteral<Dnf> implements PolarLiteral<DnfCallLiteral> {
11 public DnfCallLiteral(CallPolarity polarity, Dnf target, List<Variable> arguments) {
12 super(polarity, target, arguments);
13 }
14
15 @Override
16 public Class<Dnf> getTargetType() {
17 return Dnf.class;
18 }
19
20 @Override
21 public DnfCallLiteral substitute(Substitution substitution) {
22 return new DnfCallLiteral(getPolarity(), getTarget(), substituteArguments(substitution));
23 }
24
25 @Override
26 public DnfCallLiteral negate() {
27 return new DnfCallLiteral(getPolarity().negate(), getTarget(), getArguments());
28 }
29
30 @Override
31 public LiteralReduction getReduction() {
32 var dnfReduction = getTarget().getReduction();
33 return getPolarity().isPositive() ? dnfReduction : dnfReduction.negate();
34 }
35
36 @Override
37 protected boolean targetEquals(LiteralEqualityHelper helper, Dnf otherTarget) {
38 return helper.dnfEqual(getTarget(), otherTarget);
39 }
40}
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 61c753c3..4dc86b98 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
@@ -1,17 +1,19 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.NodeVariable;
4import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 5import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 6import tools.refinery.store.query.substitution.Substitution;
6 7
7import java.util.Set; 8import java.util.Set;
8 9
9public record EquivalenceLiteral(boolean positive, Variable left, Variable right) 10public record EquivalenceLiteral(boolean positive, NodeVariable left, NodeVariable right)
10 implements PolarLiteral<EquivalenceLiteral> { 11 implements CanNegate<EquivalenceLiteral> {
11 @Override 12 @Override
12 public void collectAllVariables(Set<Variable> variables) { 13 public Set<Variable> getBoundVariables() {
13 variables.add(left); 14 // If one side of a {@code positive} equivalence is bound, it may bind its other side, but we under-approximate
14 variables.add(right); 15 // this behavior by not binding any of the sides by default.
16 return Set.of();
15 } 17 }
16 18
17 @Override 19 @Override
@@ -21,7 +23,8 @@ public record EquivalenceLiteral(boolean positive, Variable left, Variable right
21 23
22 @Override 24 @Override
23 public EquivalenceLiteral substitute(Substitution substitution) { 25 public EquivalenceLiteral substitute(Substitution substitution) {
24 return new EquivalenceLiteral(positive, substitution.getSubstitute(left), substitution.getSubstitute(right)); 26 return new EquivalenceLiteral(positive, substitution.getTypeSafeSubstitute(left),
27 substitution.getTypeSafeSubstitute(right));
25 } 28 }
26 29
27 @Override 30 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
index ddd91775..6347410e 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
@@ -1,13 +1,13 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6 6
7import java.util.Set; 7import java.util.Set;
8 8
9public interface Literal { 9public interface Literal {
10 void collectAllVariables(Set<Variable> variables); 10 Set<Variable> getBoundVariables();
11 11
12 Literal substitute(Substitution substitution); 12 Literal substitute(Substitution substitution);
13 13
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
index 2c7e893f..89039352 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
@@ -1,11 +1,17 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.term.Term;
4
3public final class Literals { 5public final class Literals {
4 private Literals() { 6 private Literals() {
5 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 7 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
6 } 8 }
7 9
8 public static <T extends PolarLiteral<T>> T not(PolarLiteral<T> literal) { 10 public static <T extends CanNegate<T>> T not(CanNegate<T> literal) {
9 return literal.negate(); 11 return literal.negate();
10 } 12 }
13
14 public static AssumeLiteral assume(Term<Boolean> term) {
15 return new AssumeLiteral(term);
16 }
11} 17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java
deleted file mode 100644
index 32523675..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java
+++ /dev/null
@@ -1,5 +0,0 @@
1package tools.refinery.store.query.literal;
2
3public interface PolarLiteral<T extends PolarLiteral<T>> extends Literal {
4 T negate();
5}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java
deleted file mode 100644
index fb8b3332..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java
+++ /dev/null
@@ -1,35 +0,0 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.view.AnyRelationView;
6
7import java.util.List;
8
9public final class RelationViewLiteral extends CallLiteral<AnyRelationView>
10 implements PolarLiteral<RelationViewLiteral> {
11 public RelationViewLiteral(CallPolarity polarity, AnyRelationView target, List<Variable> arguments) {
12 super(polarity, target, arguments);
13 }
14
15 @Override
16 public Class<AnyRelationView> getTargetType() {
17 return AnyRelationView.class;
18 }
19
20 @Override
21 protected String targetToString() {
22 var target = getTarget();
23 return "@RelationView(\"%s\") %s".formatted(target.getViewName(), target.getSymbol().name());
24 }
25
26 @Override
27 public RelationViewLiteral substitute(Substitution substitution) {
28 return new RelationViewLiteral(getPolarity(), getTarget(), substituteArguments(substitution));
29 }
30
31 @Override
32 public RelationViewLiteral negate() {
33 return new RelationViewLiteral(getPolarity().negate(), getTarget(), getArguments());
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java
new file mode 100644
index 00000000..f8064ca2
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java
@@ -0,0 +1,10 @@
1package tools.refinery.store.query.substitution;
2
3import tools.refinery.store.query.term.Variable;
4
5public record CompositeSubstitution(Substitution first, Substitution second) implements Substitution {
6 @Override
7 public Variable getSubstitute(Variable variable) {
8 return second.getSubstitute(first.getSubstitute(variable));
9 }
10}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
index ffc65047..c7754619 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5import java.util.Map; 5import java.util.Map;
6 6
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
index 54d18a3f..7847e582 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5import java.util.HashMap; 5import java.util.HashMap;
6import java.util.Map; 6import java.util.Map;
@@ -10,10 +10,6 @@ public class RenewingSubstitution implements Substitution {
10 10
11 @Override 11 @Override
12 public Variable getSubstitute(Variable variable) { 12 public Variable getSubstitute(Variable variable) {
13 return alreadyRenewed.computeIfAbsent(variable, RenewingSubstitution::renew); 13 return alreadyRenewed.computeIfAbsent(variable, Variable::renew);
14 }
15
16 private static Variable renew(Variable variable) {
17 return variable.isExplicitlyNamed() ? new Variable(variable.getName()) : new Variable();
18 } 14 }
19} 15}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
index d33ad6fb..eed414d9 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5public enum StatelessSubstitution implements Substitution { 5public enum StatelessSubstitution implements Substitution {
6 FAILING { 6 FAILING {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
index 9d086bf5..99f84b9e 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
@@ -1,8 +1,29 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.AnyDataVariable;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
4 7
5@FunctionalInterface 8@FunctionalInterface
6public interface Substitution { 9public interface Substitution {
7 Variable getSubstitute(Variable variable); 10 Variable getSubstitute(Variable variable);
11
12 default NodeVariable getTypeSafeSubstitute(NodeVariable variable) {
13 var substitute = getSubstitute(variable);
14 return substitute.asNodeVariable();
15 }
16
17 default AnyDataVariable getTypeSafeSubstitute(AnyDataVariable variable) {
18 return getTypeSafeSubstitute((DataVariable<?>) variable);
19 }
20
21 default <T> DataVariable<T> getTypeSafeSubstitute(DataVariable<T> variable) {
22 var substitute = getSubstitute(variable);
23 return substitute.asDataVariable(variable.getType());
24 }
25
26 default Substitution andThen(Substitution second) {
27 return new CompositeSubstitution(this, second);
28 }
8} 29}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
index 26cf1a20..5d4654da 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
@@ -1,6 +1,8 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import org.jetbrains.annotations.NotNull;
4import org.jetbrains.annotations.Nullable;
5import tools.refinery.store.query.term.Variable;
4 6
5import java.util.Map; 7import java.util.Map;
6 8
@@ -24,4 +26,8 @@ public final class Substitutions {
24 public static Substitution renewing() { 26 public static Substitution renewing() {
25 return new RenewingSubstitution(); 27 return new RenewingSubstitution();
26 } 28 }
29
30 public static Substitution compose(@Nullable Substitution first, @NotNull Substitution second) {
31 return first == null ? second : first.andThen(second);
32 }
27} 33}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java
new file mode 100644
index 00000000..47421a94
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java
@@ -0,0 +1,13 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface Aggregator<R, T> {
6 Class<R> getResultType();
7
8 Class<T> getInputType();
9
10 R aggregateStream(Stream<T> stream);
11
12 R getEmptyResult();
13}
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
new file mode 100644
index 00000000..ecfefcf9
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5
6import java.util.Set;
7
8public abstract sealed class AnyDataVariable extends Variable implements AnyTerm permits DataVariable {
9 protected AnyDataVariable(String name) {
10 super(name);
11 }
12
13 @Override
14 public NodeVariable asNodeVariable() {
15 throw new IllegalStateException("%s is a data variable".formatted(this));
16 }
17
18 @Override
19 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
20 return other instanceof AnyDataVariable dataVariable && helper.variableEqual(this, dataVariable);
21 }
22
23 @Override
24 public Set<AnyDataVariable> getInputVariables() {
25 return Set.of(this);
26 }
27
28 @Override
29 public abstract AnyDataVariable renew(@Nullable String name);
30
31 @Override
32 public abstract AnyDataVariable renew();
33}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java
new file mode 100644
index 00000000..8f998d45
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5
6import java.util.Set;
7
8public sealed interface AnyTerm permits AnyDataVariable, Term {
9 Class<?> getType();
10
11 AnyTerm substitute(Substitution substitution);
12
13 boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other);
14
15 Set<AnyDataVariable> getInputVariables();
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java
new file mode 100644
index 00000000..8706a046
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java
@@ -0,0 +1,26 @@
1package tools.refinery.store.query.term;
2
3public enum ArithmeticBinaryOperator {
4 ADD("+", true),
5 SUB("-", true),
6 MUL("*", true),
7 DIV("/", true),
8 POW("**", true),
9 MIN("min", false),
10 MAX("max", false);
11
12 private final String text;
13 private final boolean infix;
14
15 ArithmeticBinaryOperator(String text, boolean infix) {
16 this.text = text;
17 this.infix = infix;
18 }
19
20 public String formatString(String left, String right) {
21 if (infix) {
22 return "(%s) %s (%s)".formatted(left, text, right);
23 }
24 return "%s(%s, %s)".formatted(text, left, right);
25 }
26}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java
new file mode 100644
index 00000000..887a1e6e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java
@@ -0,0 +1,56 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ArithmeticBinaryTerm<T> extends BinaryTerm<T, T, T> {
8 private final ArithmeticBinaryOperator operator;
9
10 protected ArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<T> left, Term<T> right) {
11 super(left, right);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<T> getLeftType() {
17 return getType();
18 }
19
20 @Override
21 public Class<T> getRightType() {
22 return getType();
23 }
24
25 public ArithmeticBinaryOperator getOperator() {
26 return operator;
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
31 if (!super.equalsWithSubstitution(helper, other)) {
32 return false;
33 }
34 var otherArithmeticBinaryTerm = (ArithmeticBinaryTerm<?>) other;
35 return operator == otherArithmeticBinaryTerm.operator;
36 }
37
38 @Override
39 public String toString() {
40 return operator.formatString(getLeft().toString(), getRight().toString());
41 }
42
43 @Override
44 public boolean equals(Object o) {
45 if (this == o) return true;
46 if (o == null || getClass() != o.getClass()) return false;
47 if (!super.equals(o)) return false;
48 ArithmeticBinaryTerm<?> that = (ArithmeticBinaryTerm<?>) o;
49 return operator == that.operator;
50 }
51
52 @Override
53 public int hashCode() {
54 return Objects.hash(super.hashCode(), operator);
55 }
56}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java
new file mode 100644
index 00000000..6a7c25db
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term;
2
3public enum ArithmeticUnaryOperator {
4 PLUS("+"),
5 MINUS("-");
6
7 private final String prefix;
8
9 ArithmeticUnaryOperator(String prefix) {
10 this.prefix = prefix;
11 }
12
13 public String formatString(String body) {
14 return "%s(%s)".formatted(prefix, body);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java
new file mode 100644
index 00000000..b78239c7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java
@@ -0,0 +1,51 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ArithmeticUnaryTerm<T> extends UnaryTerm<T, T> {
8 private final ArithmeticUnaryOperator operator;
9
10 protected ArithmeticUnaryTerm(ArithmeticUnaryOperator operator, Term<T> body) {
11 super(body);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<T> getBodyType() {
17 return getType();
18 }
19
20 public ArithmeticUnaryOperator getOperator() {
21 return operator;
22 }
23
24 @Override
25 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
26 if (!super.equalsWithSubstitution(helper, other)) {
27 return false;
28 }
29 var otherArithmeticUnaryTerm = (ArithmeticUnaryTerm<?>) other;
30 return operator == otherArithmeticUnaryTerm.operator;
31 }
32
33 @Override
34 public String toString() {
35 return operator.formatString(getBody().toString());
36 }
37
38 @Override
39 public boolean equals(Object o) {
40 if (this == o) return true;
41 if (o == null || getClass() != o.getClass()) return false;
42 if (!super.equals(o)) return false;
43 ArithmeticUnaryTerm<?> that = (ArithmeticUnaryTerm<?>) o;
44 return operator == that.operator;
45 }
46
47 @Override
48 public int hashCode() {
49 return Objects.hash(super.hashCode(), operator);
50 }
51}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java
new file mode 100644
index 00000000..465e690f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java
@@ -0,0 +1,8 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.literal.Literal;
4
5@FunctionalInterface
6public interface AssignedValue<T> {
7 Literal toLiteral(DataVariable<T> targetVariable);
8}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java
new file mode 100644
index 00000000..34f48ccc
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java
@@ -0,0 +1,93 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.Objects;
10import java.util.Set;
11
12public abstract class BinaryTerm<R, T1, T2> implements Term<R> {
13 private final Term<T1> left;
14 private final Term<T2> right;
15
16 protected BinaryTerm(Term<T1> left, Term<T2> right) {
17 if (!left.getType().equals(getLeftType())) {
18 throw new IllegalArgumentException("Expected left %s to be of type %s, got %s instead".formatted(left,
19 getLeftType().getName(), left.getType().getName()));
20 }
21 if (!right.getType().equals(getRightType())) {
22 throw new IllegalArgumentException("Expected right %s to be of type %s, got %s instead".formatted(right,
23 getRightType().getName(), right.getType().getName()));
24 }
25 this.left = left;
26 this.right = right;
27 }
28
29 public abstract Class<T1> getLeftType();
30
31 public abstract Class<T2> getRightType();
32
33 public Term<T1> getLeft() {
34 return left;
35 }
36
37 public Term<T2> getRight() {
38 return right;
39 }
40
41 @Override
42 public R evaluate(Valuation valuation) {
43 var leftValue = left.evaluate(valuation);
44 if (leftValue == null) {
45 return null;
46 }
47 var rightValue = right.evaluate(valuation);
48 if (rightValue == null) {
49 return null;
50 }
51 return doEvaluate(leftValue, rightValue);
52 }
53
54 protected abstract R doEvaluate(T1 leftValue, T2 rightValue);
55
56 @Override
57 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
58 if (getClass() != other.getClass()) {
59 return false;
60 }
61 var otherBinaryTerm = (BinaryTerm<?, ?, ?>) other;
62 return left.equalsWithSubstitution(helper, otherBinaryTerm.left) && right.equalsWithSubstitution(helper,
63 otherBinaryTerm.right);
64 }
65
66 @Override
67 public Term<R> substitute(Substitution substitution) {
68 return doSubstitute(substitution, left.substitute(substitution), right.substitute(substitution));
69 }
70
71 public abstract Term<R> doSubstitute(Substitution substitution, Term<T1> substitutedLeft,
72 Term<T2> substitutedRight);
73
74 @Override
75 public Set<AnyDataVariable> getInputVariables() {
76 var inputVariables = new HashSet<>(left.getInputVariables());
77 inputVariables.addAll(right.getInputVariables());
78 return Collections.unmodifiableSet(inputVariables);
79 }
80
81 @Override
82 public boolean equals(Object o) {
83 if (this == o) return true;
84 if (o == null || getClass() != o.getClass()) return false;
85 BinaryTerm<?, ?, ?> that = (BinaryTerm<?, ?, ?>) o;
86 return left.equals(that.left) && right.equals(that.right);
87 }
88
89 @Override
90 public int hashCode() {
91 return Objects.hash(getClass(), left, right);
92 }
93}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java
new file mode 100644
index 00000000..44dcce10
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java
@@ -0,0 +1,20 @@
1package tools.refinery.store.query.term;
2
3public enum ComparisonOperator {
4 EQ("=="),
5 NOT_EQ("!="),
6 LESS("<"),
7 LESS_EQ("<="),
8 GREATER(">"),
9 GREATER_EQ(">=");
10
11 private final String text;
12
13 ComparisonOperator(String text) {
14 this.text = text;
15 }
16
17 public String formatString(String left, String right) {
18 return "(%s) %s (%s)".formatted(left, text, right);
19 }
20}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java
new file mode 100644
index 00000000..320d42df
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java
@@ -0,0 +1,63 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ComparisonTerm<T> extends BinaryTerm<Boolean, T, T> {
8 private final ComparisonOperator operator;
9
10 protected ComparisonTerm(ComparisonOperator operator, Term<T> left, Term<T> right) {
11 super(left, right);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<Boolean> getType() {
17 return Boolean.class;
18 }
19
20 public abstract Class<T> getOperandType();
21
22 @Override
23 public Class<T> getLeftType() {
24 return getOperandType();
25 }
26
27 @Override
28 public Class<T> getRightType() {
29 return getOperandType();
30 }
31
32 public ComparisonOperator getOperator() {
33 return operator;
34 }
35
36 @Override
37 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
38 if (!super.equalsWithSubstitution(helper, other)) {
39 return false;
40 }
41 var otherComparisonTerm = (ComparisonTerm<?>) other;
42 return operator == otherComparisonTerm.operator;
43 }
44
45 @Override
46 public String toString() {
47 return operator.formatString(getLeft().toString(), getRight().toString());
48 }
49
50 @Override
51 public boolean equals(Object o) {
52 if (this == o) return true;
53 if (o == null || getClass() != o.getClass()) return false;
54 if (!super.equals(o)) return false;
55 ComparisonTerm<?> that = (ComparisonTerm<?>) o;
56 return operator == that.operator;
57 }
58
59 @Override
60 public int hashCode() {
61 return Objects.hash(super.hashCode(), operator);
62 }
63}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java
new file mode 100644
index 00000000..2185fe37
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Set;
8
9public record ConstantTerm<T>(Class<T> type, T value) implements Term<T> {
10 public ConstantTerm {
11 if (value == null) {
12 throw new IllegalArgumentException("value should not be null");
13 }
14 if (!type.isInstance(value)) {
15 throw new IllegalArgumentException("value %s is not an instance of %s".formatted(value, type.getName()));
16 }
17 }
18
19 @Override
20 public Class<T> getType() {
21 return type;
22 }
23
24 public T getValue() {
25 return value;
26 }
27
28 @Override
29 public T evaluate(Valuation valuation) {
30 return getValue();
31 }
32
33 @Override
34 public Term<T> substitute(Substitution substitution) {
35 return this;
36 }
37
38 @Override
39 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
40 return equals(other);
41 }
42
43 @Override
44 public Set<AnyDataVariable> getInputVariables() {
45 return Set.of();
46 }
47
48 @Override
49 public String toString() {
50 return getValue().toString();
51 }
52}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java
new file mode 100644
index 00000000..4fb44492
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java
@@ -0,0 +1,29 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public record DataSort<T>(Class<T> type) implements Sort {
6 public static final DataSort<Integer> INT = new DataSort<>(Integer.class);
7
8 public static final DataSort<Boolean> BOOL = new DataSort<>(Boolean.class);
9
10 @Override
11 public boolean isInstance(Variable variable) {
12 return variable instanceof DataVariable<?> dataVariable && type.equals(dataVariable.getType());
13 }
14
15 @Override
16 public DataVariable<T> newInstance(@Nullable String name) {
17 return Variable.of(name, type);
18 }
19
20 @Override
21 public DataVariable<T> newInstance() {
22 return newInstance(null);
23 }
24
25 @Override
26 public String toString() {
27 return type.getName();
28 }
29}
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
new file mode 100644
index 00000000..af070ca7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
@@ -0,0 +1,87 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.literal.Literal;
6import tools.refinery.store.query.substitution.Substitution;
7import tools.refinery.store.query.valuation.Valuation;
8
9import java.util.Objects;
10
11public final class DataVariable<T> extends AnyDataVariable implements Term<T> {
12 private final Class<T> type;
13
14 DataVariable(String name, Class<T> type) {
15 super(name);
16 this.type = type;
17 }
18
19 @Override
20 public DataSort<T> getSort() {
21 return new DataSort<>(getType());
22 }
23
24 @Override
25 public Class<T> getType() {
26 return type;
27 }
28
29 @Override
30 public DataVariable<T> renew(@Nullable String name) {
31 return new DataVariable<>(name, type);
32 }
33
34 @Override
35 public DataVariable<T> renew() {
36 return renew(getExplicitName());
37 }
38
39 @Override
40 public NodeVariable asNodeVariable() {
41 throw new IllegalStateException("%s is a data variable".formatted(this));
42 }
43
44 @Override
45 public <U> DataVariable<U> asDataVariable(Class<U> newType) {
46 if (!getType().equals(newType)) {
47 throw new IllegalStateException("%s is not of type %s but of type %s".formatted(this, newType.getName(),
48 getType().getName()));
49 }
50 @SuppressWarnings("unchecked")
51 var result = (DataVariable<U>) this;
52 return result;
53 }
54
55 @Override
56 public T evaluate(Valuation valuation) {
57 return valuation.getValue(this);
58 }
59
60 @Override
61 public Term<T> substitute(Substitution substitution) {
62 return substitution.getTypeSafeSubstitute(this);
63 }
64
65 @Override
66 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
67 return other instanceof DataVariable<?> dataVariable && helper.variableEqual(this, dataVariable);
68 }
69
70 public Literal assign(AssignedValue<T> value) {
71 return value.toLiteral(this);
72 }
73
74 @Override
75 public boolean equals(Object o) {
76 if (this == o) return true;
77 if (o == null || getClass() != o.getClass()) return false;
78 if (!super.equals(o)) return false;
79 DataVariable<?> that = (DataVariable<?>) o;
80 return type.equals(that.type);
81 }
82
83 @Override
84 public int hashCode() {
85 return Objects.hash(super.hashCode(), type);
86 }
87}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java
new file mode 100644
index 00000000..57ff597c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java
@@ -0,0 +1,103 @@
1package tools.refinery.store.query.term;
2
3import java.util.Comparator;
4import java.util.Objects;
5import java.util.SortedMap;
6import java.util.TreeMap;
7
8public class ExtremeValueAggregator<T> implements StatefulAggregator<T, T> {
9 private final Class<T> type;
10 private final T emptyResult;
11 private final Comparator<T> comparator;
12
13 public ExtremeValueAggregator(Class<T> type, T emptyResult) {
14 this(type, emptyResult, null);
15 }
16
17 public ExtremeValueAggregator(Class<T> type, T emptyResult, Comparator<T> comparator) {
18 this.type = type;
19 this.emptyResult = emptyResult;
20 this.comparator = comparator;
21 }
22
23 @Override
24 public Class<T> getResultType() {
25 return getInputType();
26 }
27
28 @Override
29 public Class<T> getInputType() {
30 return type;
31 }
32
33 @Override
34 public StatefulAggregate<T, T> createEmptyAggregate() {
35 return new Aggregate();
36 }
37
38 @Override
39 public T getEmptyResult() {
40 return emptyResult;
41 }
42
43 @Override
44 public boolean equals(Object o) {
45 if (this == o) return true;
46 if (o == null || getClass() != o.getClass()) return false;
47 ExtremeValueAggregator<?> that = (ExtremeValueAggregator<?>) o;
48 return type.equals(that.type) && Objects.equals(emptyResult, that.emptyResult) && Objects.equals(comparator,
49 that.comparator);
50 }
51
52 @Override
53 public int hashCode() {
54 return Objects.hash(type, emptyResult, comparator);
55 }
56
57 private class Aggregate implements StatefulAggregate<T, T> {
58 private final SortedMap<T, Integer> values;
59
60 private Aggregate() {
61 values = new TreeMap<>(comparator);
62 }
63
64 private Aggregate(Aggregate other) {
65 values = new TreeMap<>(other.values);
66 }
67
68 @Override
69 public void add(T value) {
70 values.compute(value, (ignoredValue, currentCount) -> currentCount == null ? 1 : currentCount + 1);
71 }
72
73 @Override
74 public void remove(T value) {
75 values.compute(value, (theValue, currentCount) -> {
76 if (currentCount == null || currentCount <= 0) {
77 throw new IllegalStateException("Invalid count %d for value %s".formatted(currentCount, theValue));
78 }
79 return currentCount.equals(1) ? null : currentCount - 1;
80 });
81 }
82
83 @Override
84 public T getResult() {
85 return isEmpty() ? emptyResult : values.firstKey();
86 }
87
88 @Override
89 public boolean isEmpty() {
90 return values.isEmpty();
91 }
92
93 @Override
94 public StatefulAggregate<T, T> deepCopy() {
95 return new Aggregate(this);
96 }
97
98 @Override
99 public boolean contains(T value) {
100 return StatefulAggregate.super.contains(value);
101 }
102 }
103}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java
new file mode 100644
index 00000000..1a4b2d4b
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public final class NodeSort implements Sort {
6 public static final NodeSort INSTANCE = new NodeSort();
7
8 private NodeSort() {
9 }
10
11 @Override
12 public boolean isInstance(Variable variable) {
13 return variable instanceof NodeVariable;
14 }
15
16 @Override
17 public NodeVariable newInstance(@Nullable String name) {
18 return new NodeVariable(name);
19 }
20
21 @Override
22 public NodeVariable newInstance() {
23 return newInstance(null);
24 }
25
26 @Override
27 public String toString() {
28 return "<node>";
29 }
30}
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
new file mode 100644
index 00000000..7419aaad
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.literal.ConstantLiteral;
5import tools.refinery.store.query.literal.EquivalenceLiteral;
6
7public final class NodeVariable extends Variable {
8 NodeVariable(@Nullable String name) {
9 super(name);
10 }
11
12 @Override
13 public NodeSort getSort() {
14 return NodeSort.INSTANCE;
15 }
16
17 @Override
18 public NodeVariable renew(@Nullable String name) {
19 return Variable.of(name);
20 }
21
22 @Override
23 public NodeVariable renew() {
24 return renew(getExplicitName());
25 }
26
27 @Override
28 public NodeVariable asNodeVariable() {
29 return this;
30 }
31
32 @Override
33 public <T> DataVariable<T> asDataVariable(Class<T> type) {
34 throw new IllegalStateException("%s is a node variable".formatted(this));
35 }
36
37 public ConstantLiteral isConstant(int value) {
38 return new ConstantLiteral(this, value);
39 }
40
41 public EquivalenceLiteral isEquivalent(NodeVariable other) {
42 return new EquivalenceLiteral(true, this, other);
43 }
44
45 public EquivalenceLiteral notEquivalent(NodeVariable other) {
46 return new EquivalenceLiteral(false, this, other);
47 }
48}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java
new file mode 100644
index 00000000..8faa9c75
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java
@@ -0,0 +1,80 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.substitution.Substitutions;
6import tools.refinery.store.query.valuation.Valuation;
7
8import java.util.Objects;
9import java.util.Set;
10import java.util.function.Function;
11import java.util.stream.Collectors;
12
13public final class OpaqueTerm<T> implements Term<T> {
14 private final Class<T> type;
15 private final Function<? super Valuation, ? extends T> evaluator;
16 private final Set<AnyDataVariable> variables;
17 private final Substitution substitution;
18
19 public OpaqueTerm(Class<T> type, Function<? super Valuation, ? extends T> evaluator,
20 Set<? extends AnyDataVariable> variables) {
21 this(type, evaluator, variables, null);
22 }
23
24 private OpaqueTerm(Class<T> type, Function<? super Valuation, ? extends T> evaluator,
25 Set<? extends AnyDataVariable> variables, Substitution substitution) {
26 this.type = type;
27 this.evaluator = evaluator;
28 this.variables = Set.copyOf(variables);
29 this.substitution = substitution;
30 }
31
32 @Override
33 public Class<T> getType() {
34 return type;
35 }
36
37 @Override
38 public Set<AnyDataVariable> getInputVariables() {
39 return variables;
40 }
41
42 @Override
43 public T evaluate(Valuation valuation) {
44 return evaluator.apply(valuation.substitute(substitution));
45 }
46
47 @Override
48 public Term<T> substitute(Substitution newSubstitution) {
49 var substitutedVariables = variables.stream()
50 .map(newSubstitution::getTypeSafeSubstitute)
51 .collect(Collectors.toUnmodifiableSet());
52 return new OpaqueTerm<>(type, evaluator, substitutedVariables,
53 Substitutions.compose(substitution, newSubstitution));
54 }
55
56 @Override
57 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
58 // Cannot inspect the opaque evaluator for deep equality.
59 return equals(other);
60 }
61
62 @Override
63 public String toString() {
64 return "<opaque>";
65 }
66
67 @Override
68 public boolean equals(Object o) {
69 if (this == o) return true;
70 if (o == null || getClass() != o.getClass()) return false;
71 OpaqueTerm<?> that = (OpaqueTerm<?>) o;
72 return type.equals(that.type) && evaluator.equals(that.evaluator) && Objects.equals(substitution,
73 that.substitution);
74 }
75
76 @Override
77 public int hashCode() {
78 return Objects.hash(type, evaluator, substitution);
79 }
80}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java
new file mode 100644
index 00000000..622bcfce
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public sealed interface Sort permits DataSort, NodeSort {
6 boolean isInstance(Variable variable);
7
8 Variable newInstance(@Nullable String name);
9
10 Variable newInstance();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java
new file mode 100644
index 00000000..7ce91305
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term;
2
3public interface StatefulAggregate<R, T> {
4 void add(T value);
5
6 void remove(T value);
7
8 R getResult();
9
10 boolean isEmpty();
11
12 StatefulAggregate<R, T> deepCopy();
13
14 default boolean contains(T value) {
15 throw new UnsupportedOperationException();
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java
new file mode 100644
index 00000000..c215a511
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface StatefulAggregator<R, T> extends Aggregator<R, T> {
6 StatefulAggregate<R, T> createEmptyAggregate();
7
8 @Override
9 default R aggregateStream(Stream<T> stream) {
10 var accumulator = createEmptyAggregate();
11 var iterator = stream.iterator();
12 while (iterator.hasNext()) {
13 var value = iterator.next();
14 accumulator.add(value);
15 }
16 return accumulator.getResult();
17 }
18
19 @Override
20 default R getEmptyResult() {
21 return createEmptyAggregate().getResult();
22 }
23}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java
new file mode 100644
index 00000000..74dbd335
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java
@@ -0,0 +1,20 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface StatelessAggregator<R, T> extends Aggregator<R, T> {
6 R add(R current, T value);
7
8 R remove(R current, T value);
9
10 @Override
11 default R aggregateStream(Stream<T> stream) {
12 var accumulator = getEmptyResult();
13 var iterator = stream.iterator();
14 while (iterator.hasNext()) {
15 var value = iterator.next();
16 accumulator = add(accumulator, value);
17 }
18 return accumulator;
19 }
20}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java
new file mode 100644
index 00000000..95434db2
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java
@@ -0,0 +1,21 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.literal.AssignLiteral;
4import tools.refinery.store.query.literal.Literal;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.valuation.Valuation;
7
8public non-sealed interface Term<T> extends AnyTerm, AssignedValue<T> {
9 @Override
10 Class<T> getType();
11
12 T evaluate(Valuation valuation);
13
14 @Override
15 Term<T> substitute(Substitution substitution);
16
17 @Override
18 default Literal toLiteral(DataVariable<T> targetVariable) {
19 return new AssignLiteral<>(targetVariable, this);
20 }
21}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java
new file mode 100644
index 00000000..4083111a
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java
@@ -0,0 +1,68 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Objects;
8import java.util.Set;
9
10public abstract class UnaryTerm<R, T> implements Term<R> {
11 private final Term<T> body;
12
13 protected UnaryTerm(Term<T> body) {
14 if (!body.getType().equals(getBodyType())) {
15 throw new IllegalArgumentException("Expected body %s to be of type %s, got %s instead".formatted(body,
16 getBodyType().getName(), body.getType().getName()));
17 }
18 this.body = body;
19 }
20
21 public abstract Class<T> getBodyType();
22
23 public Term<T> getBody() {
24 return body;
25 }
26
27 @Override
28 public R evaluate(Valuation valuation) {
29 var bodyValue = body.evaluate(valuation);
30 return bodyValue == null ? null : doEvaluate(bodyValue);
31 }
32
33 protected abstract R doEvaluate(T bodyValue);
34
35 @Override
36 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
37 if (getClass() != other.getClass()) {
38 return false;
39 }
40 var otherUnaryTerm = (UnaryTerm<?, ?>) other;
41 return body.equalsWithSubstitution(helper, otherUnaryTerm.body);
42 }
43
44 @Override
45 public Term<R> substitute(Substitution substitution) {
46 return doSubstitute(substitution, body.substitute(substitution));
47 }
48
49 protected abstract Term<R> doSubstitute(Substitution substitution, Term<T> substitutedBody);
50
51 @Override
52 public Set<AnyDataVariable> getInputVariables() {
53 return body.getInputVariables();
54 }
55
56 @Override
57 public boolean equals(Object o) {
58 if (this == o) return true;
59 if (o == null || getClass() != o.getClass()) return false;
60 UnaryTerm<?, ?> unaryTerm = (UnaryTerm<?, ?>) o;
61 return body.equals(unaryTerm.body);
62 }
63
64 @Override
65 public int hashCode() {
66 return Objects.hash(getClass(), body);
67 }
68}
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
new file mode 100644
index 00000000..957e10f8
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
@@ -0,0 +1,76 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.dnf.DnfUtils;
5
6import java.util.Objects;
7
8public abstract sealed class Variable permits AnyDataVariable, NodeVariable {
9 private final String explicitName;
10 private final String uniqueName;
11
12 protected Variable(String name) {
13 this.explicitName = name;
14 uniqueName = DnfUtils.generateUniqueName(name);
15 }
16
17 public abstract Sort getSort();
18
19 public String getName() {
20 return explicitName == null ? uniqueName : explicitName;
21 }
22
23 protected String getExplicitName() {
24 return explicitName;
25 }
26
27 public boolean isExplicitlyNamed() {
28 return explicitName != null;
29 }
30
31 public String getUniqueName() {
32 return uniqueName;
33 }
34
35 public abstract Variable renew(@Nullable String name);
36
37 public abstract Variable renew();
38
39 public abstract NodeVariable asNodeVariable();
40
41 public abstract <T> DataVariable<T> asDataVariable(Class<T> type);
42
43 @Override
44 public String toString() {
45 return getName();
46 }
47
48 @Override
49 public boolean equals(Object o) {
50 if (this == o) return true;
51 if (o == null || getClass() != o.getClass()) return false;
52 Variable variable = (Variable) o;
53 return Objects.equals(uniqueName, variable.uniqueName);
54 }
55
56 @Override
57 public int hashCode() {
58 return Objects.hash(uniqueName);
59 }
60
61 public static NodeVariable of(@Nullable String name) {
62 return new NodeVariable(name);
63 }
64
65 public static NodeVariable of() {
66 return of((String) null);
67 }
68
69 public static <T> DataVariable<T> of(@Nullable String name, Class<T> type) {
70 return new DataVariable<>(name, type);
71 }
72
73 public static <T> DataVariable<T> of(Class<T> type) {
74 return of(null, type);
75 }
76}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java
new file mode 100644
index 00000000..5079f1ce
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.term.ConstantTerm;
4
5public final class BoolConstantTerm {
6 public static final ConstantTerm<Boolean> TRUE = new ConstantTerm<>(Boolean.class, true);
7 public static final ConstantTerm<Boolean> FALSE = new ConstantTerm<>(Boolean.class, false);
8
9 private BoolConstantTerm() {
10 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
11 }
12
13 public static ConstantTerm<Boolean> valueOf(boolean boolValue) {
14 return boolValue ? TRUE : FALSE;
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java
new file mode 100644
index 00000000..d85f864d
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java
@@ -0,0 +1,78 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.*;
6
7import java.util.Objects;
8
9public class BoolLogicBinaryTerm extends BinaryTerm<Boolean, Boolean, Boolean> {
10 private final LogicBinaryOperator operator;
11
12 protected BoolLogicBinaryTerm(LogicBinaryOperator operator, Term<Boolean> left, Term<Boolean> right) {
13 super(left, right);
14 this.operator = operator;
15 }
16
17 @Override
18 public Class<Boolean> getType() {
19 return Boolean.class;
20 }
21
22 @Override
23 public Class<Boolean> getLeftType() {
24 return getType();
25 }
26
27 @Override
28 public Class<Boolean> getRightType() {
29 return getType();
30 }
31
32 public LogicBinaryOperator getOperator() {
33 return operator;
34 }
35
36 @Override
37 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
38 if (!super.equalsWithSubstitution(helper, other)) {
39 return false;
40 }
41 var otherBoolLogicBinaryTerm = (BoolLogicBinaryTerm) other;
42 return operator == otherBoolLogicBinaryTerm.operator;
43 }
44
45 @Override
46 public Term<Boolean> doSubstitute(Substitution substitution, Term<Boolean> substitutedLeft,
47 Term<Boolean> substitutedRight) {
48 return new BoolLogicBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
49 }
50
51 @Override
52 protected Boolean doEvaluate(Boolean leftValue, Boolean rightValue) {
53 return switch (getOperator()) {
54 case AND -> leftValue && rightValue;
55 case OR -> leftValue || rightValue;
56 case XOR -> leftValue ^ rightValue;
57 };
58 }
59
60 @Override
61 public String toString() {
62 return operator.formatString(getLeft().toString(), getRight().toString());
63 }
64
65 @Override
66 public boolean equals(Object o) {
67 if (this == o) return true;
68 if (o == null || getClass() != o.getClass()) return false;
69 if (!super.equals(o)) return false;
70 BoolLogicBinaryTerm that = (BoolLogicBinaryTerm) o;
71 return operator == that.operator;
72 }
73
74 @Override
75 public int hashCode() {
76 return Objects.hash(super.hashCode(), operator);
77 }
78}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java
new file mode 100644
index 00000000..855139b5
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class BoolNotTerm extends UnaryTerm<Boolean, Boolean> {
8 protected BoolNotTerm(Term<Boolean> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Boolean> getType() {
14 return Boolean.class;
15 }
16
17 @Override
18 public Class<Boolean> getBodyType() {
19 return getType();
20 }
21
22 @Override
23 protected Term<Boolean> doSubstitute(Substitution substitution, Term<Boolean> substitutedBody) {
24 return new BoolNotTerm(substitutedBody);
25 }
26
27 @Override
28 protected Boolean doEvaluate(Boolean bodyValue) {
29 return !bodyValue;
30 }
31
32 @Override
33 public String toString() {
34 return "!(%s)".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java
new file mode 100644
index 00000000..3d6c8d9d
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.term.ConstantTerm;
4import tools.refinery.store.query.term.Term;
5
6public final class BoolTerms {
7 private BoolTerms() {
8 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
9 }
10
11 public static ConstantTerm<Boolean> constant(boolean value) {
12 return BoolConstantTerm.valueOf(value);
13 }
14
15 public static BoolNotTerm not(Term<Boolean> body) {
16 return new BoolNotTerm(body);
17 }
18
19 public static BoolLogicBinaryTerm and(Term<Boolean> left, Term<Boolean> right) {
20 return new BoolLogicBinaryTerm(LogicBinaryOperator.AND, left, right);
21 }
22
23 public static BoolLogicBinaryTerm or(Term<Boolean> left, Term<Boolean> right) {
24 return new BoolLogicBinaryTerm(LogicBinaryOperator.OR, left, right);
25 }
26
27 public static BoolLogicBinaryTerm xor(Term<Boolean> left, Term<Boolean> right) {
28 return new BoolLogicBinaryTerm(LogicBinaryOperator.XOR, left, right);
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java
new file mode 100644
index 00000000..ca9ac66e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.bool;
2
3public enum LogicBinaryOperator {
4 AND("&&"),
5 OR("||"),
6 XOR("^^");
7
8 private final String text;
9
10 LogicBinaryOperator(String text) {
11 this.text = text;
12 }
13
14 public String formatString(String left, String right) {
15 return "(%s) %s (%s)".formatted(left, text, right);
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java
new file mode 100644
index 00000000..32e41718
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticBinaryOperator;
5import tools.refinery.store.query.term.ArithmeticBinaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class IntArithmeticBinaryTerm extends ArithmeticBinaryTerm<Integer> {
9 public IntArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<Integer> left, Term<Integer> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Integer> getType() {
15 return Integer.class;
16 }
17
18 @Override
19 public Term<Integer> doSubstitute(Substitution substitution, Term<Integer> substitutedLeft,
20 Term<Integer> substitutedRight) {
21 return new IntArithmeticBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Integer doEvaluate(Integer leftValue, Integer rightValue) {
26 return switch (getOperator()) {
27 case ADD -> leftValue + rightValue;
28 case SUB -> leftValue - rightValue;
29 case MUL -> leftValue * rightValue;
30 case DIV -> rightValue == 0 ? null : leftValue / rightValue;
31 case POW -> rightValue < 0 ? null : power(leftValue, rightValue);
32 case MIN -> Math.min(leftValue, rightValue);
33 case MAX -> Math.max(leftValue, rightValue);
34 };
35 }
36
37 private static int power(int base, int exponent) {
38 int accum = 1;
39 while (exponent > 0) {
40 if (exponent % 2 == 1) {
41 accum = accum * base;
42 }
43 base = base * base;
44 exponent = exponent / 2;
45 }
46 return accum;
47 }
48}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java
new file mode 100644
index 00000000..1e769259
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.ArithmeticUnaryOperator;
6import tools.refinery.store.query.term.ArithmeticUnaryTerm;
7
8public class IntArithmeticUnaryTerm extends ArithmeticUnaryTerm<Integer> {
9 public IntArithmeticUnaryTerm(ArithmeticUnaryOperator operation, Term<Integer> body) {
10 super(operation, body);
11 }
12
13 @Override
14 public Class<Integer> getType() {
15 return Integer.class;
16 }
17
18 @Override
19 protected Term<Integer> doSubstitute(Substitution substitution, Term<Integer> substitutedBody) {
20 return new IntArithmeticUnaryTerm(getOperator(), substitutedBody);
21 }
22
23 @Override
24 protected Integer doEvaluate(Integer bodyValue) {
25 return switch(getOperator()) {
26 case PLUS -> bodyValue;
27 case MINUS -> -bodyValue;
28 };
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java
new file mode 100644
index 00000000..322d2b80
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java
@@ -0,0 +1,34 @@
1package tools.refinery.store.query.term.int_;
2import tools.refinery.store.query.substitution.Substitution;
3import tools.refinery.store.query.term.ComparisonOperator;
4import tools.refinery.store.query.term.ComparisonTerm;
5import tools.refinery.store.query.term.Term;
6
7public class IntComparisonTerm extends ComparisonTerm<Integer> {
8 public IntComparisonTerm(ComparisonOperator operator, Term<Integer> left, Term<Integer> right) {
9 super(operator, left, right);
10 }
11
12 @Override
13 public Class<Integer> getOperandType() {
14 return Integer.class;
15 }
16
17 @Override
18 public Term<Boolean> doSubstitute(Substitution substitution, Term<Integer> substitutedLeft,
19 Term<Integer> substitutedRight) {
20 return new IntComparisonTerm(getOperator(), substitutedLeft, substitutedRight);
21 }
22
23 @Override
24 protected Boolean doEvaluate(Integer leftValue, Integer rightValue) {
25 return switch (getOperator()) {
26 case EQ -> leftValue.equals(rightValue);
27 case NOT_EQ -> !leftValue.equals(rightValue);
28 case LESS -> leftValue < rightValue;
29 case LESS_EQ -> leftValue <= rightValue;
30 case GREATER -> leftValue > rightValue;
31 case GREATER_EQ -> leftValue >= rightValue;
32 };
33 }
34}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java
new file mode 100644
index 00000000..d5a6add0
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.ExtremeValueAggregator;
4
5import java.util.Comparator;
6
7public final class IntExtremeValueAggregator {
8 public static final ExtremeValueAggregator<Integer> MINIMUM = new ExtremeValueAggregator<>(Integer.class,
9 Integer.MAX_VALUE);
10
11 public static final ExtremeValueAggregator<Integer> MAXIMUM = new ExtremeValueAggregator<>(Integer.class,
12 Integer.MIN_VALUE, Comparator.reverseOrder());
13
14 private IntExtremeValueAggregator() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java
new file mode 100644
index 00000000..65024f52
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java
@@ -0,0 +1,35 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.StatelessAggregator;
4
5public final class IntSumAggregator implements StatelessAggregator<Integer, Integer> {
6 public static final IntSumAggregator INSTANCE = new IntSumAggregator();
7
8 private IntSumAggregator() {
9 }
10
11 @Override
12 public Class<Integer> getResultType() {
13 return Integer.class;
14 }
15
16 @Override
17 public Class<Integer> getInputType() {
18 return Integer.class;
19 }
20
21 @Override
22 public Integer getEmptyResult() {
23 return 0;
24 }
25
26 @Override
27 public Integer add(Integer current, Integer value) {
28 return current + value;
29 }
30
31 @Override
32 public Integer remove(Integer current, Integer value) {
33 return current - value;
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java
new file mode 100644
index 00000000..86594deb
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java
@@ -0,0 +1,81 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.*;
4
5public final class IntTerms {
6 public static final Aggregator<Integer, Integer> INT_SUM = IntSumAggregator.INSTANCE;
7 public static final Aggregator<Integer, Integer> INT_MIN = IntExtremeValueAggregator.MINIMUM;
8 public static final Aggregator<Integer, Integer> INT_MAX = IntExtremeValueAggregator.MAXIMUM;
9
10 private IntTerms() {
11 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static ConstantTerm<Integer> constant(int value) {
15 return new ConstantTerm<>(Integer.class, value);
16 }
17
18 public static IntArithmeticUnaryTerm plus(Term<Integer> body) {
19 return new IntArithmeticUnaryTerm(ArithmeticUnaryOperator.PLUS, body);
20 }
21
22 public static IntArithmeticUnaryTerm minus(Term<Integer> body) {
23 return new IntArithmeticUnaryTerm(ArithmeticUnaryOperator.MINUS, body);
24 }
25
26 public static IntArithmeticBinaryTerm add(Term<Integer> left, Term<Integer> right) {
27 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.ADD, left, right);
28 }
29
30 public static IntArithmeticBinaryTerm sub(Term<Integer> left, Term<Integer> right) {
31 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.SUB, left, right);
32 }
33
34 public static IntArithmeticBinaryTerm mul(Term<Integer> left, Term<Integer> right) {
35 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MUL, left, right);
36 }
37
38 public static IntArithmeticBinaryTerm div(Term<Integer> left, Term<Integer> right) {
39 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.DIV, left, right);
40 }
41
42 public static IntArithmeticBinaryTerm pow(Term<Integer> left, Term<Integer> right) {
43 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.POW, left, right);
44 }
45
46 public static IntArithmeticBinaryTerm min(Term<Integer> left, Term<Integer> right) {
47 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MIN, left, right);
48 }
49
50 public static IntArithmeticBinaryTerm max(Term<Integer> left, Term<Integer> right) {
51 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MAX, left, right);
52 }
53
54 public static IntComparisonTerm eq(Term<Integer> left, Term<Integer> right) {
55 return new IntComparisonTerm(ComparisonOperator.EQ, left, right);
56 }
57
58 public static IntComparisonTerm notEq(Term<Integer> left, Term<Integer> right) {
59 return new IntComparisonTerm(ComparisonOperator.NOT_EQ, left, right);
60 }
61
62 public static IntComparisonTerm less(Term<Integer> left, Term<Integer> right) {
63 return new IntComparisonTerm(ComparisonOperator.LESS, left, right);
64 }
65
66 public static IntComparisonTerm lessEq(Term<Integer> left, Term<Integer> right) {
67 return new IntComparisonTerm(ComparisonOperator.LESS_EQ, left, right);
68 }
69
70 public static IntComparisonTerm greater(Term<Integer> left, Term<Integer> right) {
71 return new IntComparisonTerm(ComparisonOperator.GREATER, left, right);
72 }
73
74 public static IntComparisonTerm greaterEq(Term<Integer> left, Term<Integer> right) {
75 return new IntComparisonTerm(ComparisonOperator.GREATER_EQ, left, right);
76 }
77
78 public static RealToIntTerm asInt(Term<Double> body) {
79 return new RealToIntTerm(body);
80 }
81}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java
new file mode 100644
index 00000000..53875ddc
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class RealToIntTerm extends UnaryTerm<Integer, Double> {
8 protected RealToIntTerm(Term<Double> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Integer> getType() {
14 return Integer.class;
15 }
16
17 @Override
18 public Class<Double> getBodyType() {
19 return Double.class;
20 }
21
22 @Override
23 protected Integer doEvaluate(Double bodyValue) {
24 return bodyValue.intValue();
25 }
26
27 @Override
28 protected Term<Integer> doSubstitute(Substitution substitution, Term<Double> substitutedBody) {
29 return new RealToIntTerm(substitutedBody);
30 }
31
32 @Override
33 public String toString() {
34 return "(%s) as int".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java
new file mode 100644
index 00000000..55590824
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class IntToRealTerm extends UnaryTerm<Double, Integer> {
8 protected IntToRealTerm(Term<Integer> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Double> getType() {
14 return Double.class;
15 }
16
17 @Override
18 public Class<Integer> getBodyType() {
19 return Integer.class;
20 }
21
22 @Override
23 protected Term<Double> doSubstitute(Substitution substitution, Term<Integer> substitutedBody) {
24 return new IntToRealTerm(substitutedBody);
25 }
26
27 @Override
28 protected Double doEvaluate(Integer bodyValue) {
29 return bodyValue.doubleValue();
30 }
31
32 @Override
33 public String toString() {
34 return "(%s) as real".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java
new file mode 100644
index 00000000..57bcbe5e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticBinaryOperator;
5import tools.refinery.store.query.term.ArithmeticBinaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealArithmeticBinaryTerm extends ArithmeticBinaryTerm<Double> {
9 public RealArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<Double> left, Term<Double> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Double> getType() {
15 return Double.class;
16 }
17
18 @Override
19 public Term<Double> doSubstitute(Substitution substitution, Term<Double> substitutedLeft,
20 Term<Double> substitutedRight) {
21 return new RealArithmeticBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Double doEvaluate(Double leftValue, Double rightValue) {
26 return switch (getOperator()) {
27 case ADD -> leftValue + rightValue;
28 case SUB -> leftValue - rightValue;
29 case MUL -> leftValue * rightValue;
30 case DIV -> leftValue / rightValue;
31 case POW -> Math.pow(leftValue, rightValue);
32 case MIN -> Math.min(leftValue, rightValue);
33 case MAX -> Math.max(leftValue, rightValue);
34 };
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java
new file mode 100644
index 00000000..632e68bf
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticUnaryOperator;
5import tools.refinery.store.query.term.ArithmeticUnaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealArithmeticUnaryTerm extends ArithmeticUnaryTerm<Double> {
9 public RealArithmeticUnaryTerm(ArithmeticUnaryOperator operation, Term<Double> body) {
10 super(operation, body);
11 }
12
13 @Override
14 public Class<Double> getType() {
15 return Double.class;
16 }
17
18 @Override
19 protected Term<Double> doSubstitute(Substitution substitution, Term<Double> substitutedBody) {
20 return new RealArithmeticUnaryTerm(getOperator(), substitutedBody);
21 }
22
23 @Override
24 protected Double doEvaluate(Double bodyValue) {
25 return switch(getOperator()) {
26 case PLUS -> bodyValue;
27 case MINUS -> -bodyValue;
28 };
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java
new file mode 100644
index 00000000..75d97adb
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java
@@ -0,0 +1,35 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ComparisonOperator;
5import tools.refinery.store.query.term.ComparisonTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealComparisonTerm extends ComparisonTerm<Double> {
9 public RealComparisonTerm(ComparisonOperator operator, Term<Double> left, Term<Double> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Double> getOperandType() {
15 return Double.class;
16 }
17
18 @Override
19 public Term<Boolean> doSubstitute(Substitution substitution, Term<Double> substitutedLeft,
20 Term<Double> substitutedRight) {
21 return new RealComparisonTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Boolean doEvaluate(Double leftValue, Double rightValue) {
26 return switch (getOperator()) {
27 case EQ -> leftValue.equals(rightValue);
28 case NOT_EQ -> !leftValue.equals(rightValue);
29 case LESS -> leftValue < rightValue;
30 case LESS_EQ -> leftValue <= rightValue;
31 case GREATER -> leftValue > rightValue;
32 case GREATER_EQ -> leftValue >= rightValue;
33 };
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java
new file mode 100644
index 00000000..23384530
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.ExtremeValueAggregator;
4
5import java.util.Comparator;
6
7public final class RealExtremeValueAggregator {
8 public static final ExtremeValueAggregator<Double> MINIMUM = new ExtremeValueAggregator<>(Double.class,
9 Double.POSITIVE_INFINITY);
10
11 public static final ExtremeValueAggregator<Double> MAXIMUM = new ExtremeValueAggregator<>(Double.class,
12 Double.NEGATIVE_INFINITY, Comparator.reverseOrder());
13
14 private RealExtremeValueAggregator() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java
new file mode 100644
index 00000000..d5888664
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java
@@ -0,0 +1,85 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.StatefulAggregate;
4import tools.refinery.store.query.term.StatefulAggregator;
5
6import java.util.Map;
7import java.util.TreeMap;
8
9public final class RealSumAggregator implements StatefulAggregator<Double, Double> {
10 public static final RealSumAggregator INSTANCE = new RealSumAggregator();
11
12 private RealSumAggregator() {
13 }
14
15 @Override
16 public Class<Double> getResultType() {
17 return null;
18 }
19
20 @Override
21 public Class<Double> getInputType() {
22 return null;
23 }
24
25 @Override
26 public StatefulAggregate<Double, Double> createEmptyAggregate() {
27 return new Aggregate();
28 }
29
30 @Override
31 public Double getEmptyResult() {
32 return 0d;
33 }
34
35 private static class Aggregate implements StatefulAggregate<Double, Double> {
36 private final Map<Double, Integer> values;
37
38 public Aggregate() {
39 values = new TreeMap<>();
40 }
41
42 private Aggregate(Aggregate other) {
43 values = new TreeMap<>(other.values);
44 }
45
46 @Override
47 public void add(Double value) {
48 values.compute(value, (ignoredValue, currentCount) -> currentCount == null ? 1 : currentCount + 1);
49 }
50
51 @Override
52 public void remove(Double value) {
53 values.compute(value, (theValue, currentCount) -> {
54 if (currentCount == null || currentCount <= 0) {
55 throw new IllegalStateException("Invalid count %d for value %f".formatted(currentCount, theValue));
56 }
57 return currentCount.equals(1) ? null : currentCount - 1;
58 });
59 }
60
61 @Override
62 public Double getResult() {
63 return values.entrySet()
64 .stream()
65 .mapToDouble(entry -> entry.getKey() * entry.getValue())
66 .reduce(Double::sum)
67 .orElse(0d);
68 }
69
70 @Override
71 public boolean isEmpty() {
72 return values.isEmpty();
73 }
74
75 @Override
76 public StatefulAggregate<Double, Double> deepCopy() {
77 return new Aggregate(this);
78 }
79
80 @Override
81 public boolean contains(Double value) {
82 return values.containsKey(value);
83 }
84 }
85}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java
new file mode 100644
index 00000000..a8117842
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java
@@ -0,0 +1,81 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.*;
4
5public final class RealTerms {
6 public static final Aggregator<Double, Double> REAL_SUM = RealSumAggregator.INSTANCE;
7 public static final Aggregator<Double, Double> REAL_MIN = RealExtremeValueAggregator.MINIMUM;
8 public static final Aggregator<Double, Double> REAL_MAX = RealExtremeValueAggregator.MAXIMUM;
9
10 private RealTerms() {
11 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static ConstantTerm<Double> constant(double value) {
15 return new ConstantTerm<>(Double.class, value);
16 }
17
18 public static RealArithmeticUnaryTerm plus(Term<Double> body) {
19 return new RealArithmeticUnaryTerm(ArithmeticUnaryOperator.PLUS, body);
20 }
21
22 public static RealArithmeticUnaryTerm minus(Term<Double> body) {
23 return new RealArithmeticUnaryTerm(ArithmeticUnaryOperator.MINUS, body);
24 }
25
26 public static RealArithmeticBinaryTerm add(Term<Double> left, Term<Double> right) {
27 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.ADD, left, right);
28 }
29
30 public static RealArithmeticBinaryTerm sub(Term<Double> left, Term<Double> right) {
31 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.SUB, left, right);
32 }
33
34 public static RealArithmeticBinaryTerm mul(Term<Double> left, Term<Double> right) {
35 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MUL, left, right);
36 }
37
38 public static RealArithmeticBinaryTerm div(Term<Double> left, Term<Double> right) {
39 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.DIV, left, right);
40 }
41
42 public static RealArithmeticBinaryTerm pow(Term<Double> left, Term<Double> right) {
43 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.POW, left, right);
44 }
45
46 public static RealArithmeticBinaryTerm min(Term<Double> left, Term<Double> right) {
47 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MIN, left, right);
48 }
49
50 public static RealArithmeticBinaryTerm max(Term<Double> left, Term<Double> right) {
51 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MAX, left, right);
52 }
53
54 public static RealComparisonTerm eq(Term<Double> left, Term<Double> right) {
55 return new RealComparisonTerm(ComparisonOperator.EQ, left, right);
56 }
57
58 public static RealComparisonTerm notEq(Term<Double> left, Term<Double> right) {
59 return new RealComparisonTerm(ComparisonOperator.NOT_EQ, left, right);
60 }
61
62 public static RealComparisonTerm less(Term<Double> left, Term<Double> right) {
63 return new RealComparisonTerm(ComparisonOperator.LESS, left, right);
64 }
65
66 public static RealComparisonTerm lessEq(Term<Double> left, Term<Double> right) {
67 return new RealComparisonTerm(ComparisonOperator.LESS_EQ, left, right);
68 }
69
70 public static RealComparisonTerm greater(Term<Double> left, Term<Double> right) {
71 return new RealComparisonTerm(ComparisonOperator.GREATER, left, right);
72 }
73
74 public static RealComparisonTerm greaterEq(Term<Double> left, Term<Double> right) {
75 return new RealComparisonTerm(ComparisonOperator.GREATER_EQ, left, right);
76 }
77
78 public static IntToRealTerm asReal(Term<Integer> body) {
79 return new IntToRealTerm(body);
80 }
81}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java
new file mode 100644
index 00000000..fb512d88
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.valuation;
2
3import tools.refinery.store.query.term.AnyDataVariable;
4import tools.refinery.store.query.term.DataVariable;
5
6import java.util.Set;
7
8public record RestrictedValuation(Valuation valuation, Set<AnyDataVariable> allowedVariables) implements Valuation {
9 @Override
10 public <T> T getValue(DataVariable<T> variable) {
11 if (!allowedVariables.contains(variable)) {
12 throw new IllegalArgumentException("Variable %s is not in scope".formatted(variable));
13 }
14 return valuation.getValue(variable);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java
new file mode 100644
index 00000000..8e79663c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.valuation;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.DataVariable;
5
6public record SubstitutedValuation(Valuation originalValuation, Substitution substitution) implements Valuation {
7 @Override
8 public <T> T getValue(DataVariable<T> variable) {
9 return originalValuation.getValue(substitution.getTypeSafeSubstitute(variable));
10 }
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java
new file mode 100644
index 00000000..3ba9a6b8
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.valuation;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.AnyDataVariable;
6import tools.refinery.store.query.term.DataVariable;
7
8import java.util.Set;
9
10public interface Valuation {
11 <T> T getValue(DataVariable<T> variable);
12
13 default Valuation substitute(@Nullable Substitution substitution) {
14 if (substitution == null) {
15 return this;
16 }
17 return new SubstitutedValuation(this, substitution);
18 }
19
20 default Valuation restrict(Set<? extends AnyDataVariable> allowedVariables) {
21 return new RestrictedValuation(this, Set.copyOf(allowedVariables));
22 }
23}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
index bc3ac1ea..6ae410f2 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
@@ -1,13 +1,13 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.FunctionalDependency; 4import tools.refinery.store.query.dnf.FunctionalDependency;
5import tools.refinery.store.representation.AnySymbol; 5import tools.refinery.store.representation.AnySymbol;
6import tools.refinery.store.query.RelationLike; 6import tools.refinery.store.query.Constraint;
7 7
8import java.util.Set; 8import java.util.Set;
9 9
10public sealed interface AnyRelationView extends RelationLike permits RelationView { 10public sealed interface AnyRelationView extends Constraint permits RelationView {
11 AnySymbol getSymbol(); 11 AnySymbol getSymbol();
12 12
13 String getViewName(); 13 String getViewName();
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java
new file mode 100644
index 00000000..050b9496
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class ForbiddenRelationView extends TuplePreservingRelationView<TruthValue> {
8 public ForbiddenRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "forbidden");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return !value.may();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
index 3d278a8b..7ec9e7ac 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
@@ -1,22 +1,31 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.FunctionalDependency; 4import tools.refinery.store.query.dnf.FunctionalDependency;
5import tools.refinery.store.query.term.DataSort;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.Sort;
5import tools.refinery.store.representation.Symbol; 8import tools.refinery.store.representation.Symbol;
6import tools.refinery.store.tuple.Tuple; 9import tools.refinery.store.tuple.Tuple;
7import tools.refinery.store.tuple.Tuple1; 10import tools.refinery.store.tuple.Tuple1;
8 11
12import java.util.List;
13import java.util.Objects;
9import java.util.Set; 14import java.util.Set;
10import java.util.stream.Collectors; 15import java.util.stream.Collectors;
11import java.util.stream.IntStream; 16import java.util.stream.IntStream;
12 17
13public final class FunctionalRelationView<T> extends RelationView<T> { 18public final class FunctionalRelationView<T> extends RelationView<T> {
19 private final T defaultValue;
20
14 public FunctionalRelationView(Symbol<T> symbol, String name) { 21 public FunctionalRelationView(Symbol<T> symbol, String name) {
15 super(symbol, name); 22 super(symbol, name);
23 defaultValue = symbol.defaultValue();
16 } 24 }
17 25
18 public FunctionalRelationView(Symbol<T> symbol) { 26 public FunctionalRelationView(Symbol<T> symbol) {
19 super(symbol); 27 super(symbol);
28 defaultValue = symbol.defaultValue();
20 } 29 }
21 30
22 @Override 31 @Override
@@ -37,7 +46,7 @@ public final class FunctionalRelationView<T> extends RelationView<T> {
37 46
38 @Override 47 @Override
39 public boolean filter(Tuple key, T value) { 48 public boolean filter(Tuple key, T value) {
40 return true; 49 return !Objects.equals(defaultValue, value);
41 } 50 }
42 51
43 @Override 52 @Override
@@ -68,4 +77,29 @@ public final class FunctionalRelationView<T> extends RelationView<T> {
68 public int arity() { 77 public int arity() {
69 return getSymbol().arity() + 1; 78 return getSymbol().arity() + 1;
70 } 79 }
80
81 @Override
82 public List<Sort> getSorts() {
83 var sorts = new Sort[arity()];
84 int valueIndex = sorts.length - 1;
85 for (int i = 0; i < valueIndex; i++) {
86 sorts[i] = NodeSort.INSTANCE;
87 }
88 sorts[valueIndex] = new DataSort<>(getSymbol().valueType());
89 return List.of(sorts);
90 }
91
92 @Override
93 public boolean equals(Object o) {
94 if (this == o) return true;
95 if (o == null || getClass() != o.getClass()) return false;
96 if (!super.equals(o)) return false;
97 FunctionalRelationView<?> that = (FunctionalRelationView<?>) o;
98 return Objects.equals(defaultValue, that.defaultValue);
99 }
100
101 @Override
102 public int hashCode() {
103 return Objects.hash(super.hashCode(), defaultValue);
104 }
71} 105}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java
new file mode 100644
index 00000000..a2a84b3c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class MayRelationView extends TuplePreservingRelationView<TruthValue> {
8 public MayRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "may");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return value.may();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java
new file mode 100644
index 00000000..72ac0ca3
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class MustRelationView extends TuplePreservingRelationView<TruthValue> {
8 public MustRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "must");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return value.must();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
index ea9fd5e2..d7164b3b 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
@@ -1,14 +1,10 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.map.CursorAsIterator; 3import tools.refinery.store.map.CursorAsIterator;
5import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
6import tools.refinery.store.query.literal.CallPolarity;
7import tools.refinery.store.query.literal.RelationViewLiteral;
8import tools.refinery.store.representation.Symbol; 5import tools.refinery.store.representation.Symbol;
9import tools.refinery.store.tuple.Tuple; 6import tools.refinery.store.tuple.Tuple;
10 7
11import java.util.List;
12import java.util.Objects; 8import java.util.Objects;
13import java.util.UUID; 9import java.util.UUID;
14 10
@@ -56,20 +52,14 @@ public abstract non-sealed class RelationView<T> implements AnyRelationView {
56 return (() -> new CursorAsIterator<>(model.getInterpretation(symbol).getAll(), this::forwardMap, this::filter)); 52 return (() -> new CursorAsIterator<>(model.getInterpretation(symbol).getAll(), this::forwardMap, this::filter));
57 } 53 }
58 54
59 public RelationViewLiteral call(CallPolarity polarity, List<Variable> arguments) { 55 @Override
60 return new RelationViewLiteral(polarity, this, arguments); 56 public String toString() {
61 } 57 return name();
62
63 public RelationViewLiteral call(CallPolarity polarity, Variable... arguments) {
64 return call(polarity, List.of(arguments));
65 }
66
67 public RelationViewLiteral call(Variable... arguments) {
68 return call(CallPolarity.POSITIVE, arguments);
69 } 58 }
70 59
71 public RelationViewLiteral callTransitive(Variable left, Variable right) { 60 @Override
72 return call(CallPolarity.TRANSITIVE, List.of(left, right)); 61 public String toReferenceString() {
62 return "@RelationView(\"%s\") %s".formatted(viewName, symbol.name());
73 } 63 }
74 64
75 @Override 65 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
index 8cc4986e..234b3a9a 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
@@ -1,10 +1,15 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.term.NodeSort;
5import tools.refinery.store.query.term.Sort;
4import tools.refinery.store.tuple.Tuple; 6import tools.refinery.store.tuple.Tuple;
5import tools.refinery.store.tuple.Tuple1; 7import tools.refinery.store.tuple.Tuple1;
6import tools.refinery.store.representation.Symbol; 8import tools.refinery.store.representation.Symbol;
7 9
10import java.util.Arrays;
11import java.util.List;
12
8public abstract class TuplePreservingRelationView<T> extends RelationView<T> { 13public abstract class TuplePreservingRelationView<T> extends RelationView<T> {
9 protected TuplePreservingRelationView(Symbol<T> symbol, String name) { 14 protected TuplePreservingRelationView(Symbol<T> symbol, String name) {
10 super(symbol, name); 15 super(symbol, name);
@@ -38,7 +43,15 @@ public abstract class TuplePreservingRelationView<T> extends RelationView<T> {
38 return filter(key, value); 43 return filter(key, value);
39 } 44 }
40 45
46 @Override
41 public int arity() { 47 public int arity() {
42 return this.getSymbol().arity(); 48 return this.getSymbol().arity();
43 } 49 }
50
51 @Override
52 public List<Sort> getSorts() {
53 var sorts = new Sort[arity()];
54 Arrays.fill(sorts, NodeSort.INSTANCE);
55 return List.of(sorts);
56 }
44} 57}