1
0
mirror of https://github.com/DanilaFe/abacus synced 2026-01-25 16:15:19 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
fd3f56aa8f Write some tests for the Ranges. 2017-09-20 15:23:17 -07:00
e364f4e94b Have the NumberInterface provide the Kotlin rangeTo method. 2017-09-20 13:22:18 -07:00
4c94abb18b Create the NumberRangeBuilder utility class. 2017-09-20 13:19:55 -07:00
ba63dd7874 Implement a range that works for NumberInterfaces. 2017-09-20 13:04:20 -07:00
566598b702 Merge pull request #33 from DanilaFe/promotion-fix
Fix a bug that made some same-priority implementations not convert.
2017-09-20 12:56:12 -07:00
eb91a5b875 Fix a bug that made some same-priority implementations not convert. 2017-09-20 12:47:30 -07:00
fcd4694203 Merge pull request #32 from DanilaFe/promotion-exception
Add an exception thrown when promotion fails.
2017-09-20 12:16:00 -07:00
566831246c Add an exception thrown when promotion fails. 2017-09-20 12:06:06 -07:00
ad8a0a9b2a Merge pull request #29 from DanilaFe/fixes
Revert "Remove unnecessary nullability from parseString."
2017-09-16 03:05:20 -07:00
e430e738cf Merge branch 'master' into fixes 2017-09-16 03:03:52 -07:00
f6e326e0f1 Revert "Remove unnecessary nullability from parseString."
88e3bb7109
2017-09-16 03:02:35 -07:00
07581557c7 Merge pull request #27 from DanilaFe/fixes
Fix a number of small issues not worthy of their own branches.
2017-09-16 01:26:40 -07:00
14ac9c67f4 Implement the comparable interface. 2017-09-16 00:18:43 -07:00
0ff071e212 Add a more complete .gitignore 2017-09-16 00:17:03 -07:00
88e3bb7109 Remove unnecessary nullability from parseString. 2017-09-16 00:16:48 -07:00
540e5d6099 Load default implementation if one is not found. 2017-09-16 00:16:32 -07:00
c9e93d87a2 Merge pull request #23 from DanilaFe/thread-safety
Make some parts of the code more thread safe.
2017-09-15 22:59:42 -07:00
9 changed files with 200 additions and 23 deletions

4
.gitignore vendored
View File

@@ -24,7 +24,9 @@ hs_err_pid*
# Custom Stuff
# Gradle
.gradle/*
build/*
**/build/*
**/out/**
**/.DS_Store
# IntelliJ
.idea/*

View File

@@ -0,0 +1,24 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown when a promotion fails.
*/
public class PromotionException extends AbacusException {
/**
* Creates a new PromotionException with the default message
* and no additional information.
*/
public PromotionException() {
this("");
}
/**
* Creates a new PromotionException with the given additional message.
* @param message the additional message to include with the error.
*/
public PromotionException(String message) {
super("Failed to promote number instances", message);
}
}

View File

@@ -5,7 +5,7 @@ import org.nwapw.abacus.exception.ComputationInterruptedException;
/**
* An interface used to represent a number.
*/
public abstract class NumberInterface {
public abstract class NumberInterface implements Comparable<NumberInterface> {
/**
* Check if the thread was interrupted and
@@ -158,14 +158,6 @@ public abstract class NumberInterface {
return intPowInternal(exponent);
}
/**
* Compares this number to another.
*
* @param number the number to compare to.
* @return same as Integer.compare();
*/
public abstract int compareTo(NumberInterface number);
/**
* Same as Math.signum().
*
@@ -246,4 +238,16 @@ public abstract class NumberInterface {
*/
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);
}
}

View File

@@ -68,6 +68,7 @@ class Abacus(val configuration: Configuration) {
pluginManager.reload()
with(mutableContext) {
numberImplementation = pluginManager.numberImplementationFor(configuration.numberImplementation)
?: StandardPlugin.IMPLEMENTATION_NAIVE
clearVariables()
clearDefinitions()
}

View File

@@ -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<NumberInterface> {
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
}
}

View File

@@ -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)
}

View File

@@ -1,6 +1,7 @@
package org.nwapw.abacus.number
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.exception.PromotionException
import org.nwapw.abacus.plugin.NumberImplementation
import org.nwapw.abacus.plugin.PluginListener
import org.nwapw.abacus.plugin.PluginManager
@@ -45,27 +46,25 @@ class PromotionManager(val abacus: Abacus) : PluginListener {
* @param numbers the numbers to promote.
* @return the resulting promotion result.
*/
fun promote(vararg numbers: NumberInterface): PromotionResult? {
fun promote(vararg numbers: NumberInterface): PromotionResult {
val pluginManager = abacus.pluginManager
val implementations = numbers.map { pluginManager.interfaceImplementationFor(it.javaClass) }
val highestPriority = implementations.sortedBy { it.priority }.last()
return PromotionResult(items = numbers.map {
if(it.javaClass == highestPriority.implementation) it
else computePaths[pluginManager.interfaceImplementationFor(it.javaClass) to highestPriority]
?.promote(it) ?: return null
?.promote(it) ?: throw PromotionException()
}.toTypedArray(), promotedTo = highestPriority)
}
override fun onLoad(manager: PluginManager) {
val implementations = manager.allNumberImplementations.map { manager.numberImplementationFor(it) }
for((index, value) in implementations.withIndex()){
for(i in index until implementations.size){
val other = implementations[i]
val promoteFrom = if(other.priority > value.priority) value else other
val promoteTo = if(other.priority > value.priority) other else value
for(first in implementations) {
for(second in implementations) {
val promoteFrom = if(second.priority > first.priority) first else second
val promoteTo = if(second.priority > first.priority) second else first
val path = computePathBetween(promoteFrom, promoteTo)
computePaths.put(promoteFrom to promoteTo, path)
computePaths[promoteFrom to promoteTo] = path
}
}
}

View File

@@ -36,15 +36,13 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<Nu
is NumberBinaryNode -> {
val left = children[0] as NumberInterface
val right = children[1] as NumberInterface
val promotionResult = promotionManager.promote(left, right) ?:
throw EvaluationException("promotion failed.")
val promotionResult = promotionManager.promote(left, right)
context.numberImplementation = promotionResult.promotedTo
abacus.pluginManager.operatorFor(treeNode.operation).apply(context, *promotionResult.items)
}
is FunctionNode -> {
val promotionResult = promotionManager
.promote(*children.map { it as NumberInterface }.toTypedArray()) ?:
throw EvaluationException("promotion failed.")
.promote(*children.map { it as NumberInterface }.toTypedArray())
context.numberImplementation = promotionResult.promotedTo
abacus.pluginManager.functionFor(treeNode.callTo).apply(context, *promotionResult.items)
}

View File

@@ -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<NumberInterface, NumberInterface> naivePromotion = i -> new NaiveNumber((i.toString()));
private static Function<NumberInterface, NumberInterface> 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();
}
}