diff options
20 files changed, 912 insertions, 2 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java new file mode 100644 index 00000000..ffd5f6de --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java | |||
@@ -0,0 +1,97 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.cardinality; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
5 | import tools.refinery.store.model.representation.cardinality.FiniteUpperCardinality; | ||
6 | import tools.refinery.store.model.representation.cardinality.UnboundedUpperCardinality; | ||
7 | import tools.refinery.store.model.representation.cardinality.UpperCardinalities; | ||
8 | import tools.refinery.store.model.representation.cardinality.UpperCardinality; | ||
9 | |||
10 | import java.util.stream.Stream; | ||
11 | |||
12 | public class UpperCardinalitySumAggregationOperator implements IMultisetAggregationOperator<UpperCardinality, | ||
13 | UpperCardinalitySumAggregationOperator.Accumulator, UpperCardinality> { | ||
14 | public static final UpperCardinalitySumAggregationOperator INSTANCE = new UpperCardinalitySumAggregationOperator(); | ||
15 | |||
16 | public static final BoundAggregator BOUND_AGGREGATOR = new BoundAggregator(INSTANCE, UpperCardinality.class, | ||
17 | UpperCardinality.class); | ||
18 | |||
19 | private UpperCardinalitySumAggregationOperator() { | ||
20 | // Singleton constructor. | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public String getName() { | ||
25 | return "sum<UpperCardinality>"; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "%s computes the sum of finite or unbounded upper cardinalities".formatted(getName()); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public Accumulator createNeutral() { | ||
35 | return new Accumulator(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean isNeutral(Accumulator result) { | ||
40 | return result.sumFiniteUpperBounds == 0 && result.countUnbounded == 0; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Accumulator update(Accumulator oldResult, UpperCardinality updateValue, boolean isInsertion) { | ||
45 | if (updateValue instanceof FiniteUpperCardinality finiteUpperCardinality) { | ||
46 | int finiteUpperBound = finiteUpperCardinality.finiteUpperBound(); | ||
47 | if (isInsertion) { | ||
48 | oldResult.sumFiniteUpperBounds += finiteUpperBound; | ||
49 | } else { | ||
50 | oldResult.sumFiniteUpperBounds -= finiteUpperBound; | ||
51 | } | ||
52 | } else if (updateValue instanceof UnboundedUpperCardinality) { | ||
53 | if (isInsertion) { | ||
54 | oldResult.countUnbounded += 1; | ||
55 | } else { | ||
56 | oldResult.countUnbounded -= 1; | ||
57 | } | ||
58 | } else { | ||
59 | throw new IllegalArgumentException("Unknown UpperCardinality: " + updateValue); | ||
60 | } | ||
61 | return oldResult; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public UpperCardinality getAggregate(Accumulator result) { | ||
66 | return result.countUnbounded > 0 ? UpperCardinalities.UNBOUNDED : | ||
67 | UpperCardinalities.valueOf(result.sumFiniteUpperBounds); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public UpperCardinality aggregateStream(Stream<UpperCardinality> stream) { | ||
72 | var result = stream.collect(this::createNeutral, (accumulator, value) -> update(accumulator, value, true), | ||
73 | (left, right) -> new Accumulator(left.sumFiniteUpperBounds + right.sumFiniteUpperBounds, | ||
74 | left.countUnbounded + right.countUnbounded)); | ||
75 | return getAggregate(result); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public Accumulator clone(Accumulator original) { | ||
80 | return new Accumulator(original.sumFiniteUpperBounds, original.countUnbounded); | ||
81 | } | ||
82 | |||
83 | public static class Accumulator { | ||
84 | private int sumFiniteUpperBounds; | ||
85 | |||
86 | private int countUnbounded; | ||
87 | |||
88 | private Accumulator(int sumFiniteUpperBounds, int countUnbounded) { | ||
89 | this.sumFiniteUpperBounds = sumFiniteUpperBounds; | ||
90 | this.countUnbounded = countUnbounded; | ||
91 | } | ||
92 | |||
93 | private Accumulator() { | ||
94 | this(0, 0); | ||
95 | } | ||
96 | } | ||
97 | } | ||
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java index a7e09023..d18187e5 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.store.query.viatra.tests; | 1 | package tools.refinery.store.query.viatra; |
2 | 2 | ||
3 | import org.junit.jupiter.api.Test; | 3 | import org.junit.jupiter.api.Test; |
4 | import tools.refinery.store.model.representation.Relation; | 4 | import tools.refinery.store.model.representation.Relation; |
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java index 117edd3e..e8fa6ed1 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.store.query.viatra.tests; | 1 | package tools.refinery.store.query.viatra; |
2 | 2 | ||
3 | import org.junit.jupiter.api.Test; | 3 | import org.junit.jupiter.api.Test; |
4 | import tools.refinery.store.model.representation.Relation; | 4 | import tools.refinery.store.model.representation.Relation; |
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java new file mode 100644 index 00000000..07869050 --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java | |||
@@ -0,0 +1,50 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.params.ParameterizedTest; | ||
4 | import org.junit.jupiter.params.provider.Arguments; | ||
5 | import org.junit.jupiter.params.provider.MethodSource; | ||
6 | import tools.refinery.store.model.representation.cardinality.UpperCardinalities; | ||
7 | import tools.refinery.store.model.representation.cardinality.UpperCardinality; | ||
8 | |||
9 | import java.util.stream.Stream; | ||
10 | |||
11 | import static org.hamcrest.MatcherAssert.assertThat; | ||
12 | import static org.hamcrest.Matchers.equalTo; | ||
13 | |||
14 | class UpperCardinalitySumAggregationOperatorStreamTest { | ||
15 | @ParameterizedTest | ||
16 | @MethodSource | ||
17 | void testStream(Stream<UpperCardinality> stream, UpperCardinality expected) { | ||
18 | var result = UpperCardinalitySumAggregationOperator.INSTANCE.aggregateStream(stream); | ||
19 | assertThat(result, equalTo(expected)); | ||
20 | } | ||
21 | |||
22 | static Stream<Arguments> testStream() { | ||
23 | return Stream.of( | ||
24 | Arguments.of(Stream.of(), UpperCardinalities.ZERO), | ||
25 | Arguments.of(Stream.of(UpperCardinality.of(3)), UpperCardinality.of(3)), | ||
26 | Arguments.of( | ||
27 | Stream.of( | ||
28 | UpperCardinality.of(2), | ||
29 | UpperCardinality.of(3) | ||
30 | ), | ||
31 | UpperCardinality.of(5) | ||
32 | ), | ||
33 | Arguments.of(Stream.of(UpperCardinalities.UNBOUNDED), UpperCardinalities.UNBOUNDED), | ||
34 | Arguments.of( | ||
35 | Stream.of( | ||
36 | UpperCardinalities.UNBOUNDED, | ||
37 | UpperCardinalities.UNBOUNDED | ||
38 | ), | ||
39 | UpperCardinalities.UNBOUNDED | ||
40 | ), | ||
41 | Arguments.of( | ||
42 | Stream.of( | ||
43 | UpperCardinalities.UNBOUNDED, | ||
44 | UpperCardinality.of(3) | ||
45 | ), | ||
46 | UpperCardinalities.UNBOUNDED | ||
47 | ) | ||
48 | ); | ||
49 | } | ||
50 | } | ||
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java new file mode 100644 index 00000000..afc4a2f3 --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java | |||
@@ -0,0 +1,87 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.BeforeEach; | ||
4 | import org.junit.jupiter.api.Test; | ||
5 | import tools.refinery.store.model.representation.cardinality.UpperCardinalities; | ||
6 | import tools.refinery.store.model.representation.cardinality.UpperCardinality; | ||
7 | |||
8 | import static org.hamcrest.MatcherAssert.assertThat; | ||
9 | import static org.hamcrest.Matchers.equalTo; | ||
10 | |||
11 | class UpperCardinalitySumAggregationOperatorTest { | ||
12 | private UpperCardinalitySumAggregationOperator.Accumulator accumulator; | ||
13 | |||
14 | @BeforeEach | ||
15 | void beforeEach() { | ||
16 | accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.createNeutral(); | ||
17 | } | ||
18 | |||
19 | @Test | ||
20 | void emptyAggregationTest() { | ||
21 | assertResult(UpperCardinality.of(0)); | ||
22 | } | ||
23 | |||
24 | @Test | ||
25 | void singleBoundedTest() { | ||
26 | insert(UpperCardinality.of(3)); | ||
27 | assertResult(UpperCardinality.of(3)); | ||
28 | } | ||
29 | |||
30 | @Test | ||
31 | void multipleBoundedTest() { | ||
32 | insert(UpperCardinality.of(2)); | ||
33 | insert(UpperCardinality.of(3)); | ||
34 | assertResult(UpperCardinality.of(5)); | ||
35 | } | ||
36 | |||
37 | @Test | ||
38 | void singleUnboundedTest() { | ||
39 | insert(UpperCardinalities.UNBOUNDED); | ||
40 | assertResult(UpperCardinalities.UNBOUNDED); | ||
41 | } | ||
42 | |||
43 | @Test | ||
44 | void multipleUnboundedTest() { | ||
45 | insert(UpperCardinalities.UNBOUNDED); | ||
46 | insert(UpperCardinalities.UNBOUNDED); | ||
47 | assertResult(UpperCardinalities.UNBOUNDED); | ||
48 | } | ||
49 | |||
50 | @Test | ||
51 | void removeBoundedTest() { | ||
52 | insert(UpperCardinality.of(2)); | ||
53 | insert(UpperCardinality.of(3)); | ||
54 | remove(UpperCardinality.of(2)); | ||
55 | assertResult(UpperCardinality.of(3)); | ||
56 | } | ||
57 | |||
58 | @Test | ||
59 | void removeAllUnboundedTest() { | ||
60 | insert(UpperCardinalities.UNBOUNDED); | ||
61 | insert(UpperCardinality.of(3)); | ||
62 | remove(UpperCardinalities.UNBOUNDED); | ||
63 | assertResult(UpperCardinality.of(3)); | ||
64 | } | ||
65 | |||
66 | @Test | ||
67 | void removeSomeUnboundedTest() { | ||
68 | insert(UpperCardinalities.UNBOUNDED); | ||
69 | insert(UpperCardinalities.UNBOUNDED); | ||
70 | insert(UpperCardinality.of(3)); | ||
71 | remove(UpperCardinalities.UNBOUNDED); | ||
72 | assertResult(UpperCardinalities.UNBOUNDED); | ||
73 | } | ||
74 | |||
75 | private void insert(UpperCardinality value) { | ||
76 | accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.update(accumulator, value, true); | ||
77 | } | ||
78 | |||
79 | private void remove(UpperCardinality value) { | ||
80 | accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.update(accumulator, value, false); | ||
81 | } | ||
82 | |||
83 | private void assertResult(UpperCardinality expected) { | ||
84 | var result = UpperCardinalitySumAggregationOperator.INSTANCE.getAggregate(accumulator); | ||
85 | assertThat(result, equalTo(expected)); | ||
86 | } | ||
87 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityInterval.java new file mode 100644 index 00000000..1f252b4c --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityInterval.java | |||
@@ -0,0 +1,21 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | public sealed interface CardinalityInterval permits NonEmptyCardinalityInterval, EmptyCardinalityInterval { | ||
4 | int lowerBound(); | ||
5 | |||
6 | UpperCardinality upperBound(); | ||
7 | |||
8 | boolean isEmpty(); | ||
9 | |||
10 | CardinalityInterval min(CardinalityInterval other); | ||
11 | |||
12 | CardinalityInterval max(CardinalityInterval other); | ||
13 | |||
14 | CardinalityInterval add(CardinalityInterval other); | ||
15 | |||
16 | CardinalityInterval multiply(CardinalityInterval other); | ||
17 | |||
18 | CardinalityInterval meet(CardinalityInterval other); | ||
19 | |||
20 | CardinalityInterval join(CardinalityInterval other); | ||
21 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervals.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervals.java new file mode 100644 index 00000000..b7c52bd1 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervals.java | |||
@@ -0,0 +1,46 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | public final class CardinalityIntervals { | ||
4 | public static final CardinalityInterval NONE = exactly(0); | ||
5 | |||
6 | public static final CardinalityInterval ONE = exactly(1); | ||
7 | |||
8 | public static final CardinalityInterval LONE = atMost(1); | ||
9 | |||
10 | public static final CardinalityInterval SET = atLeast(0); | ||
11 | |||
12 | public static final CardinalityInterval SOME = atLeast(1); | ||
13 | |||
14 | public static final CardinalityInterval ERROR = EmptyCardinalityInterval.INSTANCE; | ||
15 | |||
16 | private CardinalityIntervals() { | ||
17 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | ||
18 | } | ||
19 | |||
20 | public static CardinalityInterval between(int lowerBound, UpperCardinality upperBound) { | ||
21 | if (upperBound.compareToInt(lowerBound) < 0) { | ||
22 | return ERROR; | ||
23 | } | ||
24 | return new NonEmptyCardinalityInterval(lowerBound, upperBound); | ||
25 | } | ||
26 | |||
27 | public static CardinalityInterval between(int lowerBound, int upperBound) { | ||
28 | return between(lowerBound, UpperCardinalities.valueOf(upperBound)); | ||
29 | } | ||
30 | |||
31 | public static CardinalityInterval atMost(UpperCardinality upperBound) { | ||
32 | return new NonEmptyCardinalityInterval(0, upperBound); | ||
33 | } | ||
34 | |||
35 | public static CardinalityInterval atMost(int upperBound) { | ||
36 | return atMost(UpperCardinalities.valueOf(upperBound)); | ||
37 | } | ||
38 | |||
39 | public static CardinalityInterval atLeast(int lowerBound) { | ||
40 | return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.UNBOUNDED); | ||
41 | } | ||
42 | |||
43 | public static CardinalityInterval exactly(int lowerBound) { | ||
44 | return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.valueOf(lowerBound)); | ||
45 | } | ||
46 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityInterval.java new file mode 100644 index 00000000..127651df --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityInterval.java | |||
@@ -0,0 +1,59 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | public final class EmptyCardinalityInterval implements CardinalityInterval { | ||
4 | static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval(); | ||
5 | |||
6 | private EmptyCardinalityInterval() { | ||
7 | // Singleton constructor. | ||
8 | } | ||
9 | |||
10 | @Override | ||
11 | public int lowerBound() { | ||
12 | return 1; | ||
13 | } | ||
14 | |||
15 | @Override | ||
16 | public boolean isEmpty() { | ||
17 | return true; | ||
18 | } | ||
19 | |||
20 | @Override | ||
21 | public UpperCardinality upperBound() { | ||
22 | return UpperCardinalities.ZERO; | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public CardinalityInterval min(CardinalityInterval other) { | ||
27 | return this; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public CardinalityInterval max(CardinalityInterval other) { | ||
32 | return this; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public CardinalityInterval add(CardinalityInterval other) { | ||
37 | return this; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public CardinalityInterval multiply(CardinalityInterval other) { | ||
42 | return this; | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public CardinalityInterval meet(CardinalityInterval other) { | ||
47 | return this; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public CardinalityInterval join(CardinalityInterval other) { | ||
52 | return other; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public String toString() { | ||
57 | return "error"; | ||
58 | } | ||
59 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinality.java new file mode 100644 index 00000000..ec643a97 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinality.java | |||
@@ -0,0 +1,55 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.jetbrains.annotations.NotNull; | ||
4 | |||
5 | import java.util.function.IntBinaryOperator; | ||
6 | |||
7 | public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardinality { | ||
8 | public FiniteUpperCardinality { | ||
9 | if (finiteUpperBound < 0) { | ||
10 | throw new IllegalArgumentException("finiteUpperBound must not be negative"); | ||
11 | } | ||
12 | } | ||
13 | |||
14 | @Override | ||
15 | public UpperCardinality add(UpperCardinality other) { | ||
16 | return lift(other, Integer::sum); | ||
17 | } | ||
18 | |||
19 | @Override | ||
20 | public UpperCardinality multiply(UpperCardinality other) { | ||
21 | return lift(other, (a, b) -> a * b); | ||
22 | } | ||
23 | |||
24 | @Override | ||
25 | public int compareTo(@NotNull UpperCardinality upperCardinality) { | ||
26 | if (upperCardinality instanceof FiniteUpperCardinality finiteUpperCardinality) { | ||
27 | return compareToInt(finiteUpperCardinality.finiteUpperBound); | ||
28 | } | ||
29 | if (upperCardinality instanceof UnboundedUpperCardinality) { | ||
30 | return -1; | ||
31 | } | ||
32 | throw new IllegalArgumentException("Unknown UpperCardinality: " + upperCardinality); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public int compareToInt(int value) { | ||
37 | return Integer.compare(finiteUpperBound, value); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public String toString() { | ||
42 | return Integer.toString(finiteUpperBound); | ||
43 | } | ||
44 | |||
45 | private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) { | ||
46 | if (other instanceof FiniteUpperCardinality finiteUpperCardinality) { | ||
47 | return UpperCardinalities.valueOf(operator.applyAsInt(finiteUpperBound, | ||
48 | finiteUpperCardinality.finiteUpperBound)); | ||
49 | } | ||
50 | if (other instanceof UnboundedUpperCardinality) { | ||
51 | return UpperCardinalities.UNBOUNDED; | ||
52 | } | ||
53 | throw new IllegalArgumentException("Unknown UpperCardinality: " + other); | ||
54 | } | ||
55 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/NonEmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/NonEmptyCardinalityInterval.java new file mode 100644 index 00000000..b534f2e3 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/NonEmptyCardinalityInterval.java | |||
@@ -0,0 +1,74 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import java.util.function.BinaryOperator; | ||
4 | import java.util.function.IntBinaryOperator; | ||
5 | |||
6 | public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upperBound) implements CardinalityInterval { | ||
7 | public NonEmptyCardinalityInterval { | ||
8 | if (lowerBound < 0) { | ||
9 | throw new IllegalArgumentException("lowerBound must not be negative"); | ||
10 | } | ||
11 | if (upperBound.compareToInt(lowerBound) < 0) { | ||
12 | throw new IllegalArgumentException("lowerBound must not be larger than upperBound"); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | @Override | ||
17 | public boolean isEmpty() { | ||
18 | return false; | ||
19 | } | ||
20 | |||
21 | @Override | ||
22 | public CardinalityInterval min(CardinalityInterval other) { | ||
23 | return lift(other, Math::min, UpperCardinality::min); | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public CardinalityInterval max(CardinalityInterval other) { | ||
28 | return lift(other, Math::max, UpperCardinality::max); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public CardinalityInterval add(CardinalityInterval other) { | ||
33 | return lift(other, Integer::sum, UpperCardinality::add); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public CardinalityInterval multiply(CardinalityInterval other) { | ||
38 | return lift(other, (a, b) -> a * b, UpperCardinality::multiply); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public CardinalityInterval meet(CardinalityInterval other) { | ||
43 | return lift(other, Math::max, UpperCardinality::min); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public CardinalityInterval join(CardinalityInterval other) { | ||
48 | return lift(other, Math::min, UpperCardinality::max, this); | ||
49 | } | ||
50 | |||
51 | private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, | ||
52 | BinaryOperator<UpperCardinality> upperOperator, | ||
53 | CardinalityInterval whenEmpty) { | ||
54 | if (other instanceof NonEmptyCardinalityInterval nonEmptyOther) { | ||
55 | return CardinalityIntervals.between(lowerOperator.applyAsInt(lowerBound, nonEmptyOther.lowerBound), | ||
56 | upperOperator.apply(upperBound, nonEmptyOther.upperBound)); | ||
57 | } | ||
58 | if (other instanceof EmptyCardinalityInterval) { | ||
59 | return whenEmpty; | ||
60 | } | ||
61 | throw new IllegalArgumentException("Unknown CardinalityInterval: " + other); | ||
62 | } | ||
63 | |||
64 | private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, | ||
65 | BinaryOperator<UpperCardinality> upperOperator) { | ||
66 | return lift(other, lowerOperator, upperOperator, CardinalityIntervals.ERROR); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public String toString() { | ||
71 | var closeBracket = upperBound instanceof UnboundedUpperCardinality ? ")" : "]"; | ||
72 | return "[%d..%s%s".formatted(lowerBound, upperBound, closeBracket); | ||
73 | } | ||
74 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UnboundedUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UnboundedUpperCardinality.java new file mode 100644 index 00000000..4199d44f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UnboundedUpperCardinality.java | |||
@@ -0,0 +1,42 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.jetbrains.annotations.NotNull; | ||
4 | |||
5 | public final class UnboundedUpperCardinality implements UpperCardinality { | ||
6 | static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality(); | ||
7 | |||
8 | private UnboundedUpperCardinality() { | ||
9 | // Singleton constructor. | ||
10 | } | ||
11 | |||
12 | @Override | ||
13 | public UpperCardinality add(UpperCardinality other) { | ||
14 | return this; | ||
15 | } | ||
16 | |||
17 | @Override | ||
18 | public UpperCardinality multiply(UpperCardinality other) { | ||
19 | return this; | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | public int compareTo(@NotNull UpperCardinality upperCardinality) { | ||
24 | if (upperCardinality instanceof FiniteUpperCardinality) { | ||
25 | return 1; | ||
26 | } | ||
27 | if (upperCardinality instanceof UnboundedUpperCardinality) { | ||
28 | return 0; | ||
29 | } | ||
30 | throw new IllegalArgumentException("Unknown UpperCardinality: " + upperCardinality); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int compareToInt(int value) { | ||
35 | return 1; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public String toString() { | ||
40 | return "*"; | ||
41 | } | ||
42 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinalities.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinalities.java new file mode 100644 index 00000000..b433cda2 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinalities.java | |||
@@ -0,0 +1,33 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | public final class UpperCardinalities { | ||
4 | public static final UpperCardinality UNBOUNDED = UnboundedUpperCardinality.INSTANCE; | ||
5 | |||
6 | public static final UpperCardinality ZERO; | ||
7 | |||
8 | public static final UpperCardinality ONE; | ||
9 | |||
10 | private static final FiniteUpperCardinality[] cache = new FiniteUpperCardinality[256]; | ||
11 | |||
12 | static { | ||
13 | for (int i = 0; i < cache.length; i++) { | ||
14 | cache[i] = new FiniteUpperCardinality(i); | ||
15 | } | ||
16 | ZERO = cache[0]; | ||
17 | ONE = cache[1]; | ||
18 | } | ||
19 | |||
20 | private UpperCardinalities() { | ||
21 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | ||
22 | } | ||
23 | |||
24 | public static UpperCardinality valueOf(int upperBound) { | ||
25 | if (upperBound < 0) { | ||
26 | return UNBOUNDED; | ||
27 | } | ||
28 | if (upperBound < cache.length) { | ||
29 | return cache[upperBound]; | ||
30 | } | ||
31 | return new FiniteUpperCardinality(upperBound); | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinality.java new file mode 100644 index 00000000..91dd40b0 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinality.java | |||
@@ -0,0 +1,22 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | public sealed interface UpperCardinality extends Comparable<UpperCardinality> permits FiniteUpperCardinality, | ||
4 | UnboundedUpperCardinality { | ||
5 | default UpperCardinality min(UpperCardinality other) { | ||
6 | return this.compareTo(other) <= 0 ? this : other; | ||
7 | } | ||
8 | |||
9 | default UpperCardinality max(UpperCardinality other) { | ||
10 | return this.compareTo(other) >= 0 ? this : other; | ||
11 | } | ||
12 | |||
13 | UpperCardinality add(UpperCardinality other); | ||
14 | |||
15 | UpperCardinality multiply(UpperCardinality other); | ||
16 | |||
17 | int compareToInt(int value); | ||
18 | |||
19 | static UpperCardinality of(int upperBound) { | ||
20 | return UpperCardinalities.valueOf(upperBound); | ||
21 | } | ||
22 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalTest.java new file mode 100644 index 00000000..8a39b9b5 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalTest.java | |||
@@ -0,0 +1,122 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.params.ParameterizedTest; | ||
4 | import org.junit.jupiter.params.provider.Arguments; | ||
5 | import org.junit.jupiter.params.provider.MethodSource; | ||
6 | |||
7 | import java.util.stream.Stream; | ||
8 | |||
9 | import static org.hamcrest.MatcherAssert.assertThat; | ||
10 | import static org.hamcrest.Matchers.equalTo; | ||
11 | import static tools.refinery.store.model.representation.cardinality.CardinalityIntervals.*; | ||
12 | |||
13 | class CardinalityIntervalTest { | ||
14 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
15 | @MethodSource | ||
16 | void minTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
17 | assertThat(a.min(b), equalTo(expected)); | ||
18 | } | ||
19 | |||
20 | static Stream<Arguments> minTest() { | ||
21 | return Stream.of( | ||
22 | Arguments.of(atMost(1), atMost(1), atMost(1)), | ||
23 | Arguments.of(atMost(1), between(2, 3), atMost(1)), | ||
24 | Arguments.of(atMost(1), atLeast(2), atMost(1)), | ||
25 | Arguments.of(atMost(1), ERROR, ERROR), | ||
26 | Arguments.of(atLeast(1), atLeast(2), atLeast(1)), | ||
27 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
28 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
29 | Arguments.of(ERROR, ERROR, ERROR) | ||
30 | ); | ||
31 | } | ||
32 | |||
33 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
34 | @MethodSource | ||
35 | void maxTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
36 | assertThat(a.max(b), equalTo(expected)); | ||
37 | } | ||
38 | |||
39 | static Stream<Arguments> maxTest() { | ||
40 | return Stream.of( | ||
41 | Arguments.of(atMost(1), atMost(1), atMost(1)), | ||
42 | Arguments.of(atMost(1), between(2, 3), between(2, 3)), | ||
43 | Arguments.of(atMost(1), atLeast(2), atLeast(2)), | ||
44 | Arguments.of(atMost(1), ERROR, ERROR), | ||
45 | Arguments.of(atLeast(1), atLeast(2), atLeast(2)), | ||
46 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
47 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
48 | Arguments.of(ERROR, ERROR, ERROR) | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
53 | @MethodSource | ||
54 | void addTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
55 | assertThat(a.add(b), equalTo(expected)); | ||
56 | } | ||
57 | |||
58 | static Stream<Arguments> addTest() { | ||
59 | return Stream.of( | ||
60 | Arguments.of(atMost(1), atMost(1), atMost(2)), | ||
61 | Arguments.of(atMost(1), between(2, 3), between(2, 4)), | ||
62 | Arguments.of(atMost(1), atLeast(2), atLeast(2)), | ||
63 | Arguments.of(atMost(1), ERROR, ERROR), | ||
64 | Arguments.of(atLeast(1), atLeast(2), atLeast(3)), | ||
65 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
66 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
67 | Arguments.of(ERROR, ERROR, ERROR) | ||
68 | ); | ||
69 | } | ||
70 | |||
71 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
72 | @MethodSource | ||
73 | void multiplyTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
74 | assertThat(a.multiply(b), equalTo(expected)); | ||
75 | } | ||
76 | |||
77 | static Stream<Arguments> multiplyTest() { | ||
78 | return Stream.of( | ||
79 | Arguments.of(between(2, 3), between(4, 5), between(8, 15)), | ||
80 | Arguments.of(atLeast(2), between(4, 5), atLeast(8)), | ||
81 | Arguments.of(between(2, 3), atLeast(4), atLeast(8)), | ||
82 | Arguments.of(between(2, 3), ERROR, ERROR), | ||
83 | Arguments.of(ERROR, between(4, 5), ERROR), | ||
84 | Arguments.of(ERROR, ERROR, ERROR) | ||
85 | ); | ||
86 | } | ||
87 | |||
88 | @ParameterizedTest(name = "{0} /\\ {1} == {2}") | ||
89 | @MethodSource | ||
90 | void meetTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
91 | assertThat(a.meet(b), equalTo(expected)); | ||
92 | } | ||
93 | |||
94 | static Stream<Arguments> meetTest() { | ||
95 | return Stream.of( | ||
96 | Arguments.of(atMost(1), atMost(2), atMost(1)), | ||
97 | Arguments.of(atMost(2), between(1, 3), between(1, 2)), | ||
98 | Arguments.of(atMost(1), between(1, 3), exactly(1)), | ||
99 | Arguments.of(atMost(1), between(2, 3), ERROR), | ||
100 | Arguments.of(atMost(1), ERROR, ERROR), | ||
101 | Arguments.of(ERROR, atMost(1), ERROR), | ||
102 | Arguments.of(ERROR, ERROR, ERROR) | ||
103 | ); | ||
104 | } | ||
105 | |||
106 | @ParameterizedTest(name = "{0} \\/ {1} == {2}") | ||
107 | @MethodSource | ||
108 | void joinTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
109 | assertThat(a.join(b), equalTo(expected)); | ||
110 | } | ||
111 | |||
112 | static Stream<Arguments> joinTest() { | ||
113 | return Stream.of( | ||
114 | Arguments.of(atMost(1), atMost(2), atMost(2)), | ||
115 | Arguments.of(atMost(2), between(1, 3), atMost(3)), | ||
116 | Arguments.of(atMost(1), between(2, 3), atMost(3)), | ||
117 | Arguments.of(atMost(1), ERROR, atMost(1)), | ||
118 | Arguments.of(ERROR, atMost(1), atMost(1)), | ||
119 | Arguments.of(ERROR, ERROR, ERROR) | ||
120 | ); | ||
121 | } | ||
122 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalsTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalsTest.java new file mode 100644 index 00000000..ef68a607 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalsTest.java | |||
@@ -0,0 +1,21 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.Test; | ||
4 | |||
5 | import static org.hamcrest.MatcherAssert.assertThat; | ||
6 | import static org.hamcrest.Matchers.*; | ||
7 | |||
8 | class CardinalityIntervalsTest { | ||
9 | @Test | ||
10 | void betweenEmptyTest() { | ||
11 | var interval = CardinalityIntervals.between(2, 1); | ||
12 | assertThat(interval.isEmpty(), equalTo(true)); | ||
13 | } | ||
14 | |||
15 | @Test | ||
16 | void betweenNegativeUpperBoundTest() { | ||
17 | var interval = CardinalityIntervals.between(0, -1); | ||
18 | assertThat(interval.upperBound(), equalTo(UpperCardinalities.UNBOUNDED)); | ||
19 | assertThat(interval.isEmpty(), equalTo(false)); | ||
20 | } | ||
21 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityIntervalTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityIntervalTest.java new file mode 100644 index 00000000..41c884ec --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityIntervalTest.java | |||
@@ -0,0 +1,14 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.Test; | ||
4 | |||
5 | import static org.hamcrest.MatcherAssert.assertThat; | ||
6 | import static org.hamcrest.Matchers.lessThan; | ||
7 | |||
8 | class EmptyCardinalityIntervalTest { | ||
9 | @Test | ||
10 | void inconsistentBoundsTest() { | ||
11 | assertThat(CardinalityIntervals.ERROR.upperBound().compareToInt(CardinalityIntervals.ERROR.lowerBound()), | ||
12 | lessThan(0)); | ||
13 | } | ||
14 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteCardinalityIntervalTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteCardinalityIntervalTest.java new file mode 100644 index 00000000..dfb37786 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteCardinalityIntervalTest.java | |||
@@ -0,0 +1,20 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.Test; | ||
4 | |||
5 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
6 | |||
7 | class FiniteCardinalityIntervalTest { | ||
8 | @Test | ||
9 | void invalidLowerBoundConstructorTest() { | ||
10 | assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(-1, | ||
11 | UpperCardinalities.UNBOUNDED)); | ||
12 | } | ||
13 | |||
14 | @Test | ||
15 | void invalidUpperBoundConstructorTest() { | ||
16 | var upperCardinality = UpperCardinality.of(1); | ||
17 | assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(2, | ||
18 | upperCardinality)); | ||
19 | } | ||
20 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinalityTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinalityTest.java new file mode 100644 index 00000000..3f0f7a4a --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinalityTest.java | |||
@@ -0,0 +1,12 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.Test; | ||
4 | |||
5 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
6 | |||
7 | class FiniteUpperCardinalityTest { | ||
8 | @Test | ||
9 | void invalidConstructorTest() { | ||
10 | assertThrows(IllegalArgumentException.class, () -> new FiniteUpperCardinality(-1)); | ||
11 | } | ||
12 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalitiesTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalitiesTest.java new file mode 100644 index 00000000..13171ae5 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalitiesTest.java | |||
@@ -0,0 +1,25 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.api.Test; | ||
4 | import org.junit.jupiter.params.ParameterizedTest; | ||
5 | import org.junit.jupiter.params.provider.ValueSource; | ||
6 | |||
7 | import static org.hamcrest.MatcherAssert.assertThat; | ||
8 | import static org.hamcrest.Matchers.equalTo; | ||
9 | import static org.hamcrest.Matchers.instanceOf; | ||
10 | |||
11 | class UpperCardinalitiesTest { | ||
12 | @ParameterizedTest | ||
13 | @ValueSource(ints = {0, 1, 255, 256, 1000, Integer.MAX_VALUE}) | ||
14 | void valueOfBoundedTest(int value) { | ||
15 | var upperCardinality = UpperCardinalities.valueOf(value); | ||
16 | assertThat(upperCardinality, instanceOf(FiniteUpperCardinality.class)); | ||
17 | assertThat(((FiniteUpperCardinality) upperCardinality).finiteUpperBound(), equalTo(value)); | ||
18 | } | ||
19 | |||
20 | @Test | ||
21 | void valueOfUnboundedTest() { | ||
22 | var upperCardinality = UpperCardinalities.valueOf(-1); | ||
23 | assertThat(upperCardinality, instanceOf(UnboundedUpperCardinality.class)); | ||
24 | } | ||
25 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalityTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalityTest.java new file mode 100644 index 00000000..f5763c2d --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalityTest.java | |||
@@ -0,0 +1,110 @@ | |||
1 | package tools.refinery.store.model.representation.cardinality; | ||
2 | |||
3 | import org.junit.jupiter.params.ParameterizedTest; | ||
4 | import org.junit.jupiter.params.provider.Arguments; | ||
5 | import org.junit.jupiter.params.provider.MethodSource; | ||
6 | |||
7 | import java.util.stream.Stream; | ||
8 | |||
9 | import static org.hamcrest.MatcherAssert.assertThat; | ||
10 | import static org.hamcrest.Matchers.equalTo; | ||
11 | |||
12 | class UpperCardinalityTest { | ||
13 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
14 | @MethodSource | ||
15 | void minTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
16 | assertThat(a.min(b), equalTo(expected)); | ||
17 | } | ||
18 | |||
19 | static Stream<Arguments> minTest() { | ||
20 | return Stream.of( | ||
21 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
22 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(0)), | ||
23 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
24 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinality.of(0)), | ||
25 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinality.of(0)), | ||
26 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
27 | ); | ||
28 | } | ||
29 | |||
30 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
31 | @MethodSource | ||
32 | void maxTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
33 | assertThat(a.max(b), equalTo(expected)); | ||
34 | } | ||
35 | |||
36 | static Stream<Arguments> maxTest() { | ||
37 | return Stream.of( | ||
38 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
39 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(1)), | ||
40 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(1)), | ||
41 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
42 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinalities.UNBOUNDED), | ||
43 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
44 | ); | ||
45 | } | ||
46 | |||
47 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
48 | @MethodSource | ||
49 | void addTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
50 | assertThat(a.add(b), equalTo(expected)); | ||
51 | } | ||
52 | |||
53 | static Stream<Arguments> addTest() { | ||
54 | return Stream.of( | ||
55 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(5)), | ||
56 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
57 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
58 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
59 | ); | ||
60 | } | ||
61 | |||
62 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
63 | @MethodSource | ||
64 | void multiplyTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
65 | assertThat(a.multiply(b), equalTo(expected)); | ||
66 | } | ||
67 | |||
68 | static Stream<Arguments> multiplyTest() { | ||
69 | return Stream.of( | ||
70 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(6)), | ||
71 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
72 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
73 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
74 | ); | ||
75 | } | ||
76 | |||
77 | @ParameterizedTest(name = "{0}.compareTo({1}) == {2}") | ||
78 | @MethodSource | ||
79 | void compareToTest(UpperCardinality a, UpperCardinality b, int expected) { | ||
80 | assertThat(a.compareTo(b), equalTo(expected)); | ||
81 | } | ||
82 | |||
83 | static Stream<Arguments> compareToTest() { | ||
84 | return Stream.of( | ||
85 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), 0), | ||
86 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), -1), | ||
87 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), 1), | ||
88 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, -1), | ||
89 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), 1), | ||
90 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, 0) | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | @ParameterizedTest(name = "{0}.compareToInt({1}) == {2}") | ||
95 | @MethodSource | ||
96 | void compareToIntTest(UpperCardinality a, int b, int expected) { | ||
97 | assertThat(a.compareToInt(b), equalTo(expected)); | ||
98 | } | ||
99 | |||
100 | static Stream<Arguments> compareToIntTest() { | ||
101 | return Stream.of( | ||
102 | Arguments.of(UpperCardinality.of(3), -1, 1), | ||
103 | Arguments.of(UpperCardinality.of(3), 2, 1), | ||
104 | Arguments.of(UpperCardinality.of(3), 3, 0), | ||
105 | Arguments.of(UpperCardinality.of(3), 4, -1), | ||
106 | Arguments.of(UpperCardinalities.UNBOUNDED, -1, 1), | ||
107 | Arguments.of(UpperCardinalities.UNBOUNDED, 3, 1) | ||
108 | ); | ||
109 | } | ||
110 | } | ||