From ba63dd787441c1949b17ff0484fa98ddfc2b94d6 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 20 Sep 2017 13:04:20 -0700 Subject: [PATCH 1/4] Implement a range that works for NumberInterfaces. --- .../org/nwapw/abacus/number/NumberRange.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt diff --git a/core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt b/core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt new file mode 100644 index 0000000..6adfc65 --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt @@ -0,0 +1,28 @@ +package org.nwapw.abacus.number + +import org.nwapw.abacus.Abacus + +/** + * A closed range designed specifically for [NumberInterface] + * + * Besides providing the usual functionality of a [ClosedRange], this range + * also handles promotion - that is, it's safe to use it with numbers of different + * implementations, even as starting and ending points. + * + * @property abacus the abacus instance used for promotion. + * @property start the starting point of the range. + * @property endInclusive the ending point of the range. + */ +class NumberRange(val abacus: Abacus, + override val start: NumberInterface, + override val endInclusive: NumberInterface): ClosedRange { + + override operator fun contains(value: NumberInterface): Boolean { + val promotionResult = abacus.promotionManager.promote(start, endInclusive, value) + val newStart = promotionResult.items[0] + val newEnd = promotionResult.items[1] + val newValue = promotionResult.items[2] + return newValue >= newStart && newValue <= newEnd + } + +} \ No newline at end of file From 4c94abb18b169175c9b85112a5b09edc756fc71e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 20 Sep 2017 13:19:55 -0700 Subject: [PATCH 2/4] Create the NumberRangeBuilder utility class. --- .../nwapw/abacus/number/NumberRangeBuilder.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 core/src/main/kotlin/org/nwapw/abacus/number/NumberRangeBuilder.kt diff --git a/core/src/main/kotlin/org/nwapw/abacus/number/NumberRangeBuilder.kt b/core/src/main/kotlin/org/nwapw/abacus/number/NumberRangeBuilder.kt new file mode 100644 index 0000000..c0ed09c --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/number/NumberRangeBuilder.kt @@ -0,0 +1,24 @@ +package org.nwapw.abacus.number + +import org.nwapw.abacus.Abacus + +/** + * A utility class for creating [NumberRange] instances. + * + * Unlike a regular [ClosedRange], a NumberRange must have a third parameter, + * which is the [Abacus] instance that is used for promotion. However, the ".." operator + * is infix, and can only take two parameters. The solution is, instead of returning instances + * of NumberRange directly, to return a builder, which then provides a [with] infix function + * to attach it to an instance of Abacus. + * @property start the beginning of the range. + * @property endInclusive the end of the range. + */ +class NumberRangeBuilder(private val start: NumberInterface, private val endInclusive: NumberInterface) { + + /** + * Generate a [NumberRange] with the given instance of [abacus]. + * @return a new range with the given instance of Abacus. + */ + infix fun with(abacus: Abacus) = NumberRange(abacus, start, endInclusive) + +} From e364f4e94b3afff4a874479c0b416e504b7df2ca Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 20 Sep 2017 13:22:18 -0700 Subject: [PATCH 3/4] Have the NumberInterface provide the Kotlin rangeTo method. --- .../org/nwapw/abacus/number/NumberInterface.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java b/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java index e37c4fb..bcccddb 100755 --- a/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java +++ b/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java @@ -238,4 +238,16 @@ public abstract class NumberInterface implements Comparable { */ public abstract NumberInterface getMaxError(); + /** + * Returns a NumberRangeBuilder object, which is used to create a range. + * The reason that this returns a builder and not an actual range is that + * the NumberRange needs to promote values passed to it, which + * requires an abacus instance. + * @param other the value at the bottom of the range. + * @return the resulting range builder. + */ + public NumberRangeBuilder rangeTo(NumberInterface other){ + return new NumberRangeBuilder(this, other); + } + } From fd3f56aa8f4c70c878f44c72245fc9fe7941db7a Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 20 Sep 2017 15:23:17 -0700 Subject: [PATCH 4/4] Write some tests for the Ranges. --- .../org/nwapw/abacus/tests/RangeTests.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 core/src/test/java/org/nwapw/abacus/tests/RangeTests.java diff --git a/core/src/test/java/org/nwapw/abacus/tests/RangeTests.java b/core/src/test/java/org/nwapw/abacus/tests/RangeTests.java new file mode 100644 index 0000000..85f3bc0 --- /dev/null +++ b/core/src/test/java/org/nwapw/abacus/tests/RangeTests.java @@ -0,0 +1,97 @@ +package org.nwapw.abacus.tests; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nwapw.abacus.Abacus; +import org.nwapw.abacus.config.Configuration; +import org.nwapw.abacus.number.NaiveNumber; +import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.number.NumberRange; +import org.nwapw.abacus.number.PreciseNumber; +import org.nwapw.abacus.plugin.StandardPlugin; + +import java.util.function.Function; + +public class RangeTests { + + private static Abacus abacus = new Abacus(new Configuration( "precise", new String[]{})); + private static Function naivePromotion = i -> new NaiveNumber((i.toString())); + private static Function precisePromotion = i -> new PreciseNumber((i.toString())); + + @BeforeClass + public static void prepareTests() { + abacus.getPluginManager().addInstantiated(new StandardPlugin(abacus.getPluginManager())); + abacus.reload(); + } + + public static NumberRange naiveRange(String bottom, String top) { + return new NaiveNumber(bottom).rangeTo(new NaiveNumber(top)).with(abacus); + } + + @Test + public void testNaiveRange(){ + NumberRange range = naiveRange("0", "10"); + Assert.assertTrue(range.getStart().toString().startsWith("0")); + Assert.assertTrue(range.getEndInclusive().toString().startsWith("10")); + } + + @Test + public void testNaiveRangeBelow() { + NumberRange range = naiveRange("0", "10"); + Assert.assertFalse(range.contains(new NaiveNumber("-10"))); + } + + @Test + public void testNaiveRangeAbove() { + NumberRange range = naiveRange("0", "10"); + Assert.assertFalse(range.contains(new NaiveNumber("20"))); + } + + @Test + public void testNaiveRangeJustWithinBottom() { + NumberRange range = naiveRange("0", "10"); + Assert.assertTrue(range.contains(new NaiveNumber("0"))); + } + + @Test + public void testNaiveRangeJustWithinTop() { + NumberRange range = naiveRange("0", "10"); + Assert.assertTrue(range.contains(new NaiveNumber("10"))); + } + + @Test + public void testNaiveRangeWithin() { + NumberRange range = naiveRange("0", "10"); + Assert.assertTrue(range.contains(new NaiveNumber("5"))); + } + + public static void addTestPromotionPaths() { + StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().put("precise", precisePromotion); + StandardPlugin.IMPLEMENTATION_PRECISE.getPromotionPaths().put("naive", naivePromotion); + abacus.reload(); + } + + public static void removeTestPromotionPaths() { + StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().remove("precise"); + StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().remove("naive"); + abacus.reload(); + } + + @Test + public void testPromotionWithin() { + addTestPromotionPaths(); + NumberRange range = naiveRange("0", "10"); + Assert.assertTrue(range.contains(new PreciseNumber("5"))); + removeTestPromotionPaths(); + } + + @Test + public void testPromotionOutside(){ + addTestPromotionPaths(); + NumberRange range = naiveRange("0","10"); + Assert.assertFalse(range.contains(new PreciseNumber("20"))); + removeTestPromotionPaths(); + } + +}