From 3c1e93b7cb50d20abe08f1b95b8dc8d62ed4c38c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 27 Nov 2022 21:11:39 +0100 Subject: feat: add cardinality interval abstraction --- .../cardinality/CardinalityInterval.java | 21 ++++ .../cardinality/CardinalityIntervals.java | 46 ++++++++ .../cardinality/EmptyCardinalityInterval.java | 59 ++++++++++ .../cardinality/FiniteUpperCardinality.java | 55 ++++++++++ .../cardinality/NonEmptyCardinalityInterval.java | 74 +++++++++++++ .../cardinality/UnboundedUpperCardinality.java | 42 +++++++ .../cardinality/UpperCardinalities.java | 33 ++++++ .../cardinality/UpperCardinality.java | 22 ++++ .../cardinality/CardinalityIntervalTest.java | 122 +++++++++++++++++++++ .../cardinality/CardinalityIntervalsTest.java | 21 ++++ .../cardinality/EmptyCardinalityIntervalTest.java | 14 +++ .../cardinality/FiniteCardinalityIntervalTest.java | 20 ++++ .../cardinality/FiniteUpperCardinalityTest.java | 12 ++ .../cardinality/UpperCardinalitiesTest.java | 25 +++++ .../cardinality/UpperCardinalityTest.java | 110 +++++++++++++++++++ 15 files changed, 676 insertions(+) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityInterval.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervals.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityInterval.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinality.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/NonEmptyCardinalityInterval.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UnboundedUpperCardinality.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinalities.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/representation/cardinality/UpperCardinality.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/CardinalityIntervalsTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/EmptyCardinalityIntervalTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteCardinalityIntervalTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/FiniteUpperCardinalityTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalitiesTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/model/representation/cardinality/UpperCardinalityTest.java (limited to 'subprojects/store/src') 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 @@ +package tools.refinery.store.model.representation.cardinality; + +public sealed interface CardinalityInterval permits NonEmptyCardinalityInterval, EmptyCardinalityInterval { + int lowerBound(); + + UpperCardinality upperBound(); + + boolean isEmpty(); + + CardinalityInterval min(CardinalityInterval other); + + CardinalityInterval max(CardinalityInterval other); + + CardinalityInterval add(CardinalityInterval other); + + CardinalityInterval multiply(CardinalityInterval other); + + CardinalityInterval meet(CardinalityInterval other); + + CardinalityInterval join(CardinalityInterval other); +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +public final class CardinalityIntervals { + public static final CardinalityInterval NONE = exactly(0); + + public static final CardinalityInterval ONE = exactly(1); + + public static final CardinalityInterval LONE = atMost(1); + + public static final CardinalityInterval SET = atLeast(0); + + public static final CardinalityInterval SOME = atLeast(1); + + public static final CardinalityInterval ERROR = EmptyCardinalityInterval.INSTANCE; + + private CardinalityIntervals() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static CardinalityInterval between(int lowerBound, UpperCardinality upperBound) { + if (upperBound.compareToInt(lowerBound) < 0) { + return ERROR; + } + return new NonEmptyCardinalityInterval(lowerBound, upperBound); + } + + public static CardinalityInterval between(int lowerBound, int upperBound) { + return between(lowerBound, UpperCardinalities.valueOf(upperBound)); + } + + public static CardinalityInterval atMost(UpperCardinality upperBound) { + return new NonEmptyCardinalityInterval(0, upperBound); + } + + public static CardinalityInterval atMost(int upperBound) { + return atMost(UpperCardinalities.valueOf(upperBound)); + } + + public static CardinalityInterval atLeast(int lowerBound) { + return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.UNBOUNDED); + } + + public static CardinalityInterval exactly(int lowerBound) { + return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.valueOf(lowerBound)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +public final class EmptyCardinalityInterval implements CardinalityInterval { + static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval(); + + private EmptyCardinalityInterval() { + // Singleton constructor. + } + + @Override + public int lowerBound() { + return 1; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public UpperCardinality upperBound() { + return UpperCardinalities.ZERO; + } + + @Override + public CardinalityInterval min(CardinalityInterval other) { + return this; + } + + @Override + public CardinalityInterval max(CardinalityInterval other) { + return this; + } + + @Override + public CardinalityInterval add(CardinalityInterval other) { + return this; + } + + @Override + public CardinalityInterval multiply(CardinalityInterval other) { + return this; + } + + @Override + public CardinalityInterval meet(CardinalityInterval other) { + return this; + } + + @Override + public CardinalityInterval join(CardinalityInterval other) { + return other; + } + + @Override + public String toString() { + return "error"; + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.IntBinaryOperator; + +public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardinality { + public FiniteUpperCardinality { + if (finiteUpperBound < 0) { + throw new IllegalArgumentException("finiteUpperBound must not be negative"); + } + } + + @Override + public UpperCardinality add(UpperCardinality other) { + return lift(other, Integer::sum); + } + + @Override + public UpperCardinality multiply(UpperCardinality other) { + return lift(other, (a, b) -> a * b); + } + + @Override + public int compareTo(@NotNull UpperCardinality upperCardinality) { + if (upperCardinality instanceof FiniteUpperCardinality finiteUpperCardinality) { + return compareToInt(finiteUpperCardinality.finiteUpperBound); + } + if (upperCardinality instanceof UnboundedUpperCardinality) { + return -1; + } + throw new IllegalArgumentException("Unknown UpperCardinality: " + upperCardinality); + } + + @Override + public int compareToInt(int value) { + return Integer.compare(finiteUpperBound, value); + } + + @Override + public String toString() { + return Integer.toString(finiteUpperBound); + } + + private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) { + if (other instanceof FiniteUpperCardinality finiteUpperCardinality) { + return UpperCardinalities.valueOf(operator.applyAsInt(finiteUpperBound, + finiteUpperCardinality.finiteUpperBound)); + } + if (other instanceof UnboundedUpperCardinality) { + return UpperCardinalities.UNBOUNDED; + } + throw new IllegalArgumentException("Unknown UpperCardinality: " + other); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import java.util.function.BinaryOperator; +import java.util.function.IntBinaryOperator; + +public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upperBound) implements CardinalityInterval { + public NonEmptyCardinalityInterval { + if (lowerBound < 0) { + throw new IllegalArgumentException("lowerBound must not be negative"); + } + if (upperBound.compareToInt(lowerBound) < 0) { + throw new IllegalArgumentException("lowerBound must not be larger than upperBound"); + } + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public CardinalityInterval min(CardinalityInterval other) { + return lift(other, Math::min, UpperCardinality::min); + } + + @Override + public CardinalityInterval max(CardinalityInterval other) { + return lift(other, Math::max, UpperCardinality::max); + } + + @Override + public CardinalityInterval add(CardinalityInterval other) { + return lift(other, Integer::sum, UpperCardinality::add); + } + + @Override + public CardinalityInterval multiply(CardinalityInterval other) { + return lift(other, (a, b) -> a * b, UpperCardinality::multiply); + } + + @Override + public CardinalityInterval meet(CardinalityInterval other) { + return lift(other, Math::max, UpperCardinality::min); + } + + @Override + public CardinalityInterval join(CardinalityInterval other) { + return lift(other, Math::min, UpperCardinality::max, this); + } + + private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, + BinaryOperator upperOperator, + CardinalityInterval whenEmpty) { + if (other instanceof NonEmptyCardinalityInterval nonEmptyOther) { + return CardinalityIntervals.between(lowerOperator.applyAsInt(lowerBound, nonEmptyOther.lowerBound), + upperOperator.apply(upperBound, nonEmptyOther.upperBound)); + } + if (other instanceof EmptyCardinalityInterval) { + return whenEmpty; + } + throw new IllegalArgumentException("Unknown CardinalityInterval: " + other); + } + + private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, + BinaryOperator upperOperator) { + return lift(other, lowerOperator, upperOperator, CardinalityIntervals.ERROR); + } + + @Override + public String toString() { + var closeBracket = upperBound instanceof UnboundedUpperCardinality ? ")" : "]"; + return "[%d..%s%s".formatted(lowerBound, upperBound, closeBracket); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.jetbrains.annotations.NotNull; + +public final class UnboundedUpperCardinality implements UpperCardinality { + static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality(); + + private UnboundedUpperCardinality() { + // Singleton constructor. + } + + @Override + public UpperCardinality add(UpperCardinality other) { + return this; + } + + @Override + public UpperCardinality multiply(UpperCardinality other) { + return this; + } + + @Override + public int compareTo(@NotNull UpperCardinality upperCardinality) { + if (upperCardinality instanceof FiniteUpperCardinality) { + return 1; + } + if (upperCardinality instanceof UnboundedUpperCardinality) { + return 0; + } + throw new IllegalArgumentException("Unknown UpperCardinality: " + upperCardinality); + } + + @Override + public int compareToInt(int value) { + return 1; + } + + @Override + public String toString() { + return "*"; + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +public final class UpperCardinalities { + public static final UpperCardinality UNBOUNDED = UnboundedUpperCardinality.INSTANCE; + + public static final UpperCardinality ZERO; + + public static final UpperCardinality ONE; + + private static final FiniteUpperCardinality[] cache = new FiniteUpperCardinality[256]; + + static { + for (int i = 0; i < cache.length; i++) { + cache[i] = new FiniteUpperCardinality(i); + } + ZERO = cache[0]; + ONE = cache[1]; + } + + private UpperCardinalities() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static UpperCardinality valueOf(int upperBound) { + if (upperBound < 0) { + return UNBOUNDED; + } + if (upperBound < cache.length) { + return cache[upperBound]; + } + return new FiniteUpperCardinality(upperBound); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +public sealed interface UpperCardinality extends Comparable permits FiniteUpperCardinality, + UnboundedUpperCardinality { + default UpperCardinality min(UpperCardinality other) { + return this.compareTo(other) <= 0 ? this : other; + } + + default UpperCardinality max(UpperCardinality other) { + return this.compareTo(other) >= 0 ? this : other; + } + + UpperCardinality add(UpperCardinality other); + + UpperCardinality multiply(UpperCardinality other); + + int compareToInt(int value); + + static UpperCardinality of(int upperBound) { + return UpperCardinalities.valueOf(upperBound); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static tools.refinery.store.model.representation.cardinality.CardinalityIntervals.*; + +class CardinalityIntervalTest { + @ParameterizedTest(name = "min({0}, {1}) == {2}") + @MethodSource + void minTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.min(b), equalTo(expected)); + } + + static Stream minTest() { + return Stream.of( + Arguments.of(atMost(1), atMost(1), atMost(1)), + Arguments.of(atMost(1), between(2, 3), atMost(1)), + Arguments.of(atMost(1), atLeast(2), atMost(1)), + Arguments.of(atMost(1), ERROR, ERROR), + Arguments.of(atLeast(1), atLeast(2), atLeast(1)), + Arguments.of(atLeast(1), ERROR, ERROR), + Arguments.of(ERROR, atLeast(2), ERROR), + Arguments.of(ERROR, ERROR, ERROR) + ); + } + + @ParameterizedTest(name = "max({0}, {1}) == {2}") + @MethodSource + void maxTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.max(b), equalTo(expected)); + } + + static Stream maxTest() { + return Stream.of( + Arguments.of(atMost(1), atMost(1), atMost(1)), + Arguments.of(atMost(1), between(2, 3), between(2, 3)), + Arguments.of(atMost(1), atLeast(2), atLeast(2)), + Arguments.of(atMost(1), ERROR, ERROR), + Arguments.of(atLeast(1), atLeast(2), atLeast(2)), + Arguments.of(atLeast(1), ERROR, ERROR), + Arguments.of(ERROR, atLeast(2), ERROR), + Arguments.of(ERROR, ERROR, ERROR) + ); + } + + @ParameterizedTest(name = "{0} + {1} == {2}") + @MethodSource + void addTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.add(b), equalTo(expected)); + } + + static Stream addTest() { + return Stream.of( + Arguments.of(atMost(1), atMost(1), atMost(2)), + Arguments.of(atMost(1), between(2, 3), between(2, 4)), + Arguments.of(atMost(1), atLeast(2), atLeast(2)), + Arguments.of(atMost(1), ERROR, ERROR), + Arguments.of(atLeast(1), atLeast(2), atLeast(3)), + Arguments.of(atLeast(1), ERROR, ERROR), + Arguments.of(ERROR, atLeast(2), ERROR), + Arguments.of(ERROR, ERROR, ERROR) + ); + } + + @ParameterizedTest(name = "{0} * {1} == {2}") + @MethodSource + void multiplyTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.multiply(b), equalTo(expected)); + } + + static Stream multiplyTest() { + return Stream.of( + Arguments.of(between(2, 3), between(4, 5), between(8, 15)), + Arguments.of(atLeast(2), between(4, 5), atLeast(8)), + Arguments.of(between(2, 3), atLeast(4), atLeast(8)), + Arguments.of(between(2, 3), ERROR, ERROR), + Arguments.of(ERROR, between(4, 5), ERROR), + Arguments.of(ERROR, ERROR, ERROR) + ); + } + + @ParameterizedTest(name = "{0} /\\ {1} == {2}") + @MethodSource + void meetTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.meet(b), equalTo(expected)); + } + + static Stream meetTest() { + return Stream.of( + Arguments.of(atMost(1), atMost(2), atMost(1)), + Arguments.of(atMost(2), between(1, 3), between(1, 2)), + Arguments.of(atMost(1), between(1, 3), exactly(1)), + Arguments.of(atMost(1), between(2, 3), ERROR), + Arguments.of(atMost(1), ERROR, ERROR), + Arguments.of(ERROR, atMost(1), ERROR), + Arguments.of(ERROR, ERROR, ERROR) + ); + } + + @ParameterizedTest(name = "{0} \\/ {1} == {2}") + @MethodSource + void joinTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { + assertThat(a.join(b), equalTo(expected)); + } + + static Stream joinTest() { + return Stream.of( + Arguments.of(atMost(1), atMost(2), atMost(2)), + Arguments.of(atMost(2), between(1, 3), atMost(3)), + Arguments.of(atMost(1), between(2, 3), atMost(3)), + Arguments.of(atMost(1), ERROR, atMost(1)), + Arguments.of(ERROR, atMost(1), atMost(1)), + Arguments.of(ERROR, ERROR, ERROR) + ); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +class CardinalityIntervalsTest { + @Test + void betweenEmptyTest() { + var interval = CardinalityIntervals.between(2, 1); + assertThat(interval.isEmpty(), equalTo(true)); + } + + @Test + void betweenNegativeUpperBoundTest() { + var interval = CardinalityIntervals.between(0, -1); + assertThat(interval.upperBound(), equalTo(UpperCardinalities.UNBOUNDED)); + assertThat(interval.isEmpty(), equalTo(false)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.lessThan; + +class EmptyCardinalityIntervalTest { + @Test + void inconsistentBoundsTest() { + assertThat(CardinalityIntervals.ERROR.upperBound().compareToInt(CardinalityIntervals.ERROR.lowerBound()), + lessThan(0)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FiniteCardinalityIntervalTest { + @Test + void invalidLowerBoundConstructorTest() { + assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(-1, + UpperCardinalities.UNBOUNDED)); + } + + @Test + void invalidUpperBoundConstructorTest() { + var upperCardinality = UpperCardinality.of(1); + assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(2, + upperCardinality)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FiniteUpperCardinalityTest { + @Test + void invalidConstructorTest() { + assertThrows(IllegalArgumentException.class, () -> new FiniteUpperCardinality(-1)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +class UpperCardinalitiesTest { + @ParameterizedTest + @ValueSource(ints = {0, 1, 255, 256, 1000, Integer.MAX_VALUE}) + void valueOfBoundedTest(int value) { + var upperCardinality = UpperCardinalities.valueOf(value); + assertThat(upperCardinality, instanceOf(FiniteUpperCardinality.class)); + assertThat(((FiniteUpperCardinality) upperCardinality).finiteUpperBound(), equalTo(value)); + } + + @Test + void valueOfUnboundedTest() { + var upperCardinality = UpperCardinalities.valueOf(-1); + assertThat(upperCardinality, instanceOf(UnboundedUpperCardinality.class)); + } +} 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 @@ +package tools.refinery.store.model.representation.cardinality; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +class UpperCardinalityTest { + @ParameterizedTest(name = "min({0}, {1}) == {2}") + @MethodSource + void minTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { + assertThat(a.min(b), equalTo(expected)); + } + + static Stream minTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(0)), + Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(0)), + Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinality.of(0)), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinality.of(0)), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) + ); + } + + @ParameterizedTest(name = "max({0}, {1}) == {2}") + @MethodSource + void maxTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { + assertThat(a.max(b), equalTo(expected)); + } + + static Stream maxTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(1)), + Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(1)), + Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) + ); + } + + @ParameterizedTest(name = "{0} + {1} == {2}") + @MethodSource + void addTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { + assertThat(a.add(b), equalTo(expected)); + } + + static Stream addTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(5)), + Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) + ); + } + + @ParameterizedTest(name = "{0} * {1} == {2}") + @MethodSource + void multiplyTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { + assertThat(a.multiply(b), equalTo(expected)); + } + + static Stream multiplyTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(6)), + Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) + ); + } + + @ParameterizedTest(name = "{0}.compareTo({1}) == {2}") + @MethodSource + void compareToTest(UpperCardinality a, UpperCardinality b, int expected) { + assertThat(a.compareTo(b), equalTo(expected)); + } + + static Stream compareToTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), 0), + Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), -1), + Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), 1), + Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, -1), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), 1), + Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, 0) + ); + } + + @ParameterizedTest(name = "{0}.compareToInt({1}) == {2}") + @MethodSource + void compareToIntTest(UpperCardinality a, int b, int expected) { + assertThat(a.compareToInt(b), equalTo(expected)); + } + + static Stream compareToIntTest() { + return Stream.of( + Arguments.of(UpperCardinality.of(3), -1, 1), + Arguments.of(UpperCardinality.of(3), 2, 1), + Arguments.of(UpperCardinality.of(3), 3, 0), + Arguments.of(UpperCardinality.of(3), 4, -1), + Arguments.of(UpperCardinalities.UNBOUNDED, -1, 1), + Arguments.of(UpperCardinalities.UNBOUNDED, 3, 1) + ); + } +} -- cgit v1.2.3-70-g09d2