mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-26 08:35:20 +00:00
Compare commits
23 Commits
less-null
...
number-ran
| Author | SHA1 | Date | |
|---|---|---|---|
| fd3f56aa8f | |||
| e364f4e94b | |||
| 4c94abb18b | |||
| ba63dd7874 | |||
| 566598b702 | |||
| eb91a5b875 | |||
| fcd4694203 | |||
| 566831246c | |||
| ad8a0a9b2a | |||
| e430e738cf | |||
| f6e326e0f1 | |||
| 07581557c7 | |||
| 14ac9c67f4 | |||
| 0ff071e212 | |||
| 88e3bb7109 | |||
| 540e5d6099 | |||
| c9e93d87a2 | |||
| 337edd68fa | |||
| 08967fbb8f | |||
| 46f78bb2ed | |||
| 87529da15f | |||
| 7cd117dac1 | |||
| 8975bfdb99 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -24,7 +24,9 @@ hs_err_pid*
|
|||||||
# Custom Stuff
|
# Custom Stuff
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle/*
|
.gradle/*
|
||||||
build/*
|
**/build/*
|
||||||
|
**/out/**
|
||||||
|
**/.DS_Store
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
.idea/*
|
.idea/*
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import org.nwapw.abacus.exception.ComputationInterruptedException;
|
|||||||
/**
|
/**
|
||||||
* An interface used to represent a number.
|
* 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
|
* Check if the thread was interrupted and
|
||||||
@@ -158,14 +158,6 @@ public abstract class NumberInterface {
|
|||||||
return intPowInternal(exponent);
|
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().
|
* Same as Math.signum().
|
||||||
*
|
*
|
||||||
@@ -246,4 +238,16 @@ public abstract class NumberInterface {
|
|||||||
*/
|
*/
|
||||||
public abstract NumberInterface getMaxError();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ public class PluginManager {
|
|||||||
registeredNumberImplementations.put(name, implementation);
|
registeredNumberImplementations.put(name, implementation);
|
||||||
interfaceImplementationNames.put(implementation.getImplementation(), name);
|
interfaceImplementationNames.put(implementation.getImplementation(), name);
|
||||||
interfaceImplementations.put(implementation.getImplementation(), implementation);
|
interfaceImplementations.put(implementation.getImplementation(), implementation);
|
||||||
|
cachedPi.put(implementation.getImplementation(), implementation.instanceForPi());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,10 +219,6 @@ public class PluginManager {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toReturn == null) {
|
|
||||||
toReturn = new Documentation(name, "", "", "", type);
|
|
||||||
registerDocumentation(toReturn);
|
|
||||||
}
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,14 +249,7 @@ public class PluginManager {
|
|||||||
* @return pi
|
* @return pi
|
||||||
*/
|
*/
|
||||||
public NumberInterface piFor(Class<? extends NumberInterface> forClass) {
|
public NumberInterface piFor(Class<? extends NumberInterface> forClass) {
|
||||||
if (cachedPi.containsKey(forClass)) return cachedPi.get(forClass);
|
return cachedPi.get(forClass);
|
||||||
NumberImplementation implementation = interfaceImplementationFor(forClass);
|
|
||||||
NumberInterface generatedPi = null;
|
|
||||||
if (implementation != null) {
|
|
||||||
generatedPi = implementation.instanceForPi();
|
|
||||||
}
|
|
||||||
cachedPi.put(forClass, generatedPi);
|
|
||||||
return generatedPi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -693,8 +693,7 @@ public class StandardPlugin extends Plugin {
|
|||||||
* @param n non-negative integer.
|
* @param n non-negative integer.
|
||||||
* @return a number of numClass with value n factorial.
|
* @return a number of numClass with value n factorial.
|
||||||
*/
|
*/
|
||||||
public static NumberInterface factorial(NumberImplementation implementation, int n) {
|
synchronized public static NumberInterface factorial(NumberImplementation implementation, int n) {
|
||||||
|
|
||||||
if (!FACTORIAL_LISTS.containsKey(implementation)) {
|
if (!FACTORIAL_LISTS.containsKey(implementation)) {
|
||||||
FACTORIAL_LISTS.put(implementation, new ArrayList<>());
|
FACTORIAL_LISTS.put(implementation, new ArrayList<>());
|
||||||
FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1"));
|
FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1"));
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class Abacus(val configuration: Configuration) {
|
|||||||
pluginManager.reload()
|
pluginManager.reload()
|
||||||
with(mutableContext) {
|
with(mutableContext) {
|
||||||
numberImplementation = pluginManager.numberImplementationFor(configuration.numberImplementation)
|
numberImplementation = pluginManager.numberImplementationFor(configuration.numberImplementation)
|
||||||
|
?: StandardPlugin.IMPLEMENTATION_NAIVE
|
||||||
clearVariables()
|
clearVariables()
|
||||||
clearDefinitions()
|
clearDefinitions()
|
||||||
}
|
}
|
||||||
|
|||||||
28
core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt
Normal file
28
core/src/main/kotlin/org/nwapw/abacus/number/NumberRange.kt
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.nwapw.abacus.number
|
package org.nwapw.abacus.number
|
||||||
|
|
||||||
import org.nwapw.abacus.Abacus
|
import org.nwapw.abacus.Abacus
|
||||||
|
import org.nwapw.abacus.exception.PromotionException
|
||||||
import org.nwapw.abacus.plugin.NumberImplementation
|
import org.nwapw.abacus.plugin.NumberImplementation
|
||||||
import org.nwapw.abacus.plugin.PluginListener
|
import org.nwapw.abacus.plugin.PluginListener
|
||||||
import org.nwapw.abacus.plugin.PluginManager
|
import org.nwapw.abacus.plugin.PluginManager
|
||||||
@@ -37,21 +38,6 @@ class PromotionManager(val abacus: Abacus) : PluginListener {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If a path between the given implementations has already been computed, uses
|
|
||||||
* the already calculated path. Otherwise, calls [computePathBetween] to compute a new
|
|
||||||
* path.
|
|
||||||
*
|
|
||||||
* @param from the implementation to start from.
|
|
||||||
* @param to the implementation to get to.
|
|
||||||
* @return the resulting promotion path, or null if it is not found
|
|
||||||
*/
|
|
||||||
fun getPathBetween(from: NumberImplementation, to: NumberImplementation): PromotionPath? {
|
|
||||||
return computePaths.computeIfAbsent(from to to, {
|
|
||||||
computePathBetween(it.first, it.second)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promote all the numbers in the list to the same number implementation, to ensure
|
* Promote all the numbers in the list to the same number implementation, to ensure
|
||||||
* they can be used with each other. Finds the highest priority implementation
|
* they can be used with each other. Finds the highest priority implementation
|
||||||
@@ -60,22 +46,30 @@ class PromotionManager(val abacus: Abacus) : PluginListener {
|
|||||||
* @param numbers the numbers to promote.
|
* @param numbers the numbers to promote.
|
||||||
* @return the resulting promotion result.
|
* @return the resulting promotion result.
|
||||||
*/
|
*/
|
||||||
fun promote(vararg numbers: NumberInterface): PromotionResult? {
|
fun promote(vararg numbers: NumberInterface): PromotionResult {
|
||||||
val pluginManager = abacus.pluginManager
|
val pluginManager = abacus.pluginManager
|
||||||
val implementations = numbers.map { pluginManager.interfaceImplementationFor(it.javaClass) }
|
val implementations = numbers.map { pluginManager.interfaceImplementationFor(it.javaClass) }
|
||||||
val highestPriority = implementations.sortedBy { it.priority }.last()
|
val highestPriority = implementations.sortedBy { it.priority }.last()
|
||||||
return PromotionResult(items = numbers.map {
|
return PromotionResult(items = numbers.map {
|
||||||
if(it.javaClass == highestPriority.implementation) it
|
if(it.javaClass == highestPriority.implementation) it
|
||||||
else getPathBetween(pluginManager.interfaceImplementationFor(it.javaClass), highestPriority)
|
else computePaths[pluginManager.interfaceImplementationFor(it.javaClass) to highestPriority]
|
||||||
?.promote(it) ?: return null
|
?.promote(it) ?: throw PromotionException()
|
||||||
}.toTypedArray(), promotedTo = highestPriority)
|
}.toTypedArray(), promotedTo = highestPriority)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoad(manager: PluginManager?) {
|
override fun onLoad(manager: PluginManager) {
|
||||||
|
val implementations = manager.allNumberImplementations.map { manager.numberImplementationFor(it) }
|
||||||
|
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[promoteFrom to promoteTo] = path
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnload(manager: PluginManager?) {
|
override fun onUnload(manager: PluginManager) {
|
||||||
computePaths.clear()
|
computePaths.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,15 +36,13 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<Nu
|
|||||||
is NumberBinaryNode -> {
|
is NumberBinaryNode -> {
|
||||||
val left = children[0] as NumberInterface
|
val left = children[0] as NumberInterface
|
||||||
val right = children[1] as NumberInterface
|
val right = children[1] as NumberInterface
|
||||||
val promotionResult = promotionManager.promote(left, right) ?:
|
val promotionResult = promotionManager.promote(left, right)
|
||||||
throw EvaluationException("promotion failed.")
|
|
||||||
context.numberImplementation = promotionResult.promotedTo
|
context.numberImplementation = promotionResult.promotedTo
|
||||||
abacus.pluginManager.operatorFor(treeNode.operation).apply(context, *promotionResult.items)
|
abacus.pluginManager.operatorFor(treeNode.operation).apply(context, *promotionResult.items)
|
||||||
}
|
}
|
||||||
is FunctionNode -> {
|
is FunctionNode -> {
|
||||||
val promotionResult = promotionManager
|
val promotionResult = promotionManager
|
||||||
.promote(*children.map { it as NumberInterface }.toTypedArray()) ?:
|
.promote(*children.map { it as NumberInterface }.toTypedArray())
|
||||||
throw EvaluationException("promotion failed.")
|
|
||||||
context.numberImplementation = promotionResult.promotedTo
|
context.numberImplementation = promotionResult.promotedTo
|
||||||
abacus.pluginManager.functionFor(treeNode.callTo).apply(context, *promotionResult.items)
|
abacus.pluginManager.functionFor(treeNode.callTo).apply(context, *promotionResult.items)
|
||||||
}
|
}
|
||||||
|
|||||||
97
core/src/test/java/org/nwapw/abacus/tests/RangeTests.java
Normal file
97
core/src/test/java/org/nwapw/abacus/tests/RangeTests.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -361,7 +361,12 @@ public class AbacusController implements PluginListener {
|
|||||||
enabledPlugins.add(plugin);
|
enabledPlugins.add(plugin);
|
||||||
}
|
}
|
||||||
PluginManager pluginManager = abacus.getPluginManager();
|
PluginManager pluginManager = abacus.getPluginManager();
|
||||||
functionList.addAll(manager.getAllFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.FUNCTION))
|
functionList.addAll(manager.getAllFunctions().stream().map(name -> {
|
||||||
|
Documentation documentationInstance = pluginManager.documentationFor(name, DocumentationType.FUNCTION);
|
||||||
|
if(documentationInstance == null)
|
||||||
|
documentationInstance = new Documentation(name, "", "", "", DocumentationType.FUNCTION);
|
||||||
|
return documentationInstance;
|
||||||
|
})
|
||||||
.collect(Collectors.toCollection(ArrayList::new)));
|
.collect(Collectors.toCollection(ArrayList::new)));
|
||||||
functionList.addAll(manager.getAllTreeValueFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.TREE_VALUE_FUNCTION))
|
functionList.addAll(manager.getAllTreeValueFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.TREE_VALUE_FUNCTION))
|
||||||
.collect(Collectors.toCollection(ArrayList::new)));
|
.collect(Collectors.toCollection(ArrayList::new)));
|
||||||
|
|||||||
Reference in New Issue
Block a user