aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-11-27 21:11:39 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-12-09 00:07:38 +0100
commit3c1e93b7cb50d20abe08f1b95b8dc8d62ed4c38c (patch)
tree21f4c04e750614d9f60484a08ee5402452acaf01 /subprojects/store/src
parentrefactor: be more conservative w/preview features (diff)
downloadrefinery-3c1e93b7cb50d20abe08f1b95b8dc8d62ed4c38c.tar.gz
refinery-3c1e93b7cb50d20abe08f1b95b8dc8d62ed4c38c.tar.zst
refinery-3c1e93b7cb50d20abe08f1b95b8dc8d62ed4c38c.zip
feat: add cardinality interval abstraction
Diffstat (limited to 'subprojects/store/src')
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityInterval.java21
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervals.java46
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityInterval.java59
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinality.java55
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/NonEmptyCardinalityInterval.java74
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UnboundedUpperCardinality.java42
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinalities.java33
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinality.java22
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalTest.java122
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalsTest.java21
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityIntervalTest.java14
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteCardinalityIntervalTest.java20
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinalityTest.java12
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalitiesTest.java25
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalityTest.java110
15 files changed, 676 insertions, 0 deletions
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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.jetbrains.annotations.NotNull;
4
5import java.util.function.IntBinaryOperator;
6
7public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import java.util.function.BinaryOperator;
4import java.util.function.IntBinaryOperator;
5
6public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.jetbrains.annotations.NotNull;
4
5public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3public 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.params.ParameterizedTest;
4import org.junit.jupiter.params.provider.Arguments;
5import org.junit.jupiter.params.provider.MethodSource;
6
7import java.util.stream.Stream;
8
9import static org.hamcrest.MatcherAssert.assertThat;
10import static org.hamcrest.Matchers.equalTo;
11import static tools.refinery.store.model.representation.cardinality.CardinalityIntervals.*;
12
13class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.api.Test;
4
5import static org.hamcrest.MatcherAssert.assertThat;
6import static org.hamcrest.Matchers.*;
7
8class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.api.Test;
4
5import static org.hamcrest.MatcherAssert.assertThat;
6import static org.hamcrest.Matchers.lessThan;
7
8class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.api.Test;
4
5import static org.junit.jupiter.api.Assertions.assertThrows;
6
7class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.api.Test;
4
5import static org.junit.jupiter.api.Assertions.assertThrows;
6
7class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.api.Test;
4import org.junit.jupiter.params.ParameterizedTest;
5import org.junit.jupiter.params.provider.ValueSource;
6
7import static org.hamcrest.MatcherAssert.assertThat;
8import static org.hamcrest.Matchers.equalTo;
9import static org.hamcrest.Matchers.instanceOf;
10
11class 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 @@
1package tools.refinery.store.model.representation.cardinality;
2
3import org.junit.jupiter.params.ParameterizedTest;
4import org.junit.jupiter.params.provider.Arguments;
5import org.junit.jupiter.params.provider.MethodSource;
6
7import java.util.stream.Stream;
8
9import static org.hamcrest.MatcherAssert.assertThat;
10import static org.hamcrest.Matchers.equalTo;
11
12class 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}