diff --git a/core/src/main/java/org/nwapw/abacus/Abacus.java b/core/src/main/java/org/nwapw/abacus/Abacus.java index 113bda8..1b1dc49 100644 --- a/core/src/main/java/org/nwapw/abacus/Abacus.java +++ b/core/src/main/java/org/nwapw/abacus/Abacus.java @@ -2,6 +2,7 @@ package org.nwapw.abacus; import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.number.PromotionManager; import org.nwapw.abacus.parsing.LexerTokenizer; import org.nwapw.abacus.parsing.ShuntingYardParser; import org.nwapw.abacus.parsing.TreeBuilder; @@ -42,6 +43,10 @@ public class Abacus { * from a string. */ private TreeBuilder treeBuilder; + /** + * Promotion manager responsible for the promotion system. + */ + private PromotionManager promotionManager; /** * Creates a new instance of the Abacus calculator. @@ -55,9 +60,20 @@ public class Abacus { LexerTokenizer lexerTokenizer = new LexerTokenizer(); ShuntingYardParser shuntingYardParser = new ShuntingYardParser(); treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser); + promotionManager = new PromotionManager(this); pluginManager.addListener(shuntingYardParser); pluginManager.addListener(lexerTokenizer); + pluginManager.addListener(promotionManager); + } + + /** + * Gets the promotion manager. + * + * @return the promotion manager. + */ + public PromotionManager getPromotionManager() { + return promotionManager; } /** @@ -120,16 +136,15 @@ public class Abacus { } /** - * Creates a number from a string. + * Gets the number implementation. * - * @param numberString the string to create the number from. - * @return the resulting number. + * @return the number implementation to use for creating numbers. */ - public NumberInterface numberFromString(String numberString) { - NumberImplementation toInstantiate = + public NumberImplementation getNumberImplementation() { + NumberImplementation selectedImplementation = pluginManager.numberImplementationFor(configuration.getNumberImplementation()); - if (toInstantiate == null) toInstantiate = DEFAULT_IMPLEMENTATION; - - return toInstantiate.instanceForString(numberString); + if (selectedImplementation != null) return selectedImplementation; + return DEFAULT_IMPLEMENTATION; } + } diff --git a/core/src/main/java/org/nwapw/abacus/number/NaiveNumber.java b/core/src/main/java/org/nwapw/abacus/number/NaiveNumber.java index cd17c89..4f03243 100755 --- a/core/src/main/java/org/nwapw/abacus/number/NaiveNumber.java +++ b/core/src/main/java/org/nwapw/abacus/number/NaiveNumber.java @@ -114,15 +114,6 @@ public class NaiveNumber extends NumberInterface { return (int) value; } - @Override - public NumberInterface promoteToInternal(Class toClass) { - if (toClass == this.getClass()) return this; - else if (toClass == PreciseNumber.class) { - return new PreciseNumber(Double.toString(value)); - } - return null; - } - public String toString() { double shiftBy = Math.pow(10, 10); return Double.toString(Math.round(value * shiftBy) / shiftBy); 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 adf401d..4a3fe5c 100755 --- a/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java +++ b/core/src/main/java/org/nwapw/abacus/number/NumberInterface.java @@ -236,29 +236,6 @@ public abstract class NumberInterface { */ public abstract int intValue(); - /** - * Promotes this class to another number class. - * - * @param toClass the class to promote to. - * @return the resulting new instance. - */ - @Deprecated - protected abstract NumberInterface promoteToInternal(Class toClass); - - /** - * Promotes this class to another number class. Also, checks if the - * thread has been interrupted, and if so, throws - * an exception. - * - * @param toClass the class to promote to. - * @return the resulting new instance. - */ - @Deprecated - public final NumberInterface promoteTo(Class toClass) { - checkInterrupted(); - return promoteToInternal(toClass); - } - /** * Returns the smallest error this instance can tolerate depending * on its precision and value. diff --git a/core/src/main/java/org/nwapw/abacus/number/PreciseNumber.java b/core/src/main/java/org/nwapw/abacus/number/PreciseNumber.java index f6f5afe..41e9080 100755 --- a/core/src/main/java/org/nwapw/abacus/number/PreciseNumber.java +++ b/core/src/main/java/org/nwapw/abacus/number/PreciseNumber.java @@ -152,14 +152,6 @@ public class PreciseNumber extends NumberInterface { return new PreciseNumber(value.negate()); } - @Override - public NumberInterface promoteToInternal(Class toClass) { - if (toClass == this.getClass()) { - return this; - } - return null; - } - @Override public String toString() { return value.round(outputContext).toString(); diff --git a/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java b/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java index 2d7b16b..be57744 100644 --- a/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java +++ b/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java @@ -50,13 +50,13 @@ public class LexerTokenizer implements Tokenizer>, PluginListen for (String operator : manager.getAllOperators()) { lexer.register(Pattern.sanitize(operator), TokenType.OP); } - for (String operator : manager.getAllTreeValueOperators()){ + for (String operator : manager.getAllTreeValueOperators()) { lexer.register(Pattern.sanitize(operator), TokenType.TREE_VALUE_OP); } for (String function : manager.getAllFunctions()) { lexer.register(Pattern.sanitize(function), TokenType.FUNCTION); } - for (String function : manager.getAllTreeValueFunctions()){ + for (String function : manager.getAllTreeValueFunctions()) { lexer.register(Pattern.sanitize(function), TokenType.TREE_VALUE_FUNCTION); } } @@ -66,13 +66,13 @@ public class LexerTokenizer implements Tokenizer>, PluginListen for (String operator : manager.getAllOperators()) { lexer.unregister(Pattern.sanitize(operator), TokenType.OP); } - for (String operator : manager.getAllTreeValueOperators()){ + for (String operator : manager.getAllTreeValueOperators()) { lexer.unregister(Pattern.sanitize(operator), TokenType.TREE_VALUE_OP); } for (String function : manager.getAllFunctions()) { lexer.unregister(Pattern.sanitize(function), TokenType.FUNCTION); } - for (String function : manager.getAllTreeValueFunctions()){ + for (String function : manager.getAllTreeValueFunctions()) { lexer.unregister(Pattern.sanitize(function), TokenType.TREE_VALUE_FUNCTION); } } diff --git a/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java b/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java index 7490d96..9d326a8 100644 --- a/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java +++ b/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java @@ -134,7 +134,7 @@ public class ShuntingYardParser implements Parser>, PluginListe TreeNode right = constructRecursive(matches); TreeNode left = constructRecursive(matches); if (left == null || right == null) return null; - if(matchType == TokenType.OP) { + if (matchType == TokenType.OP) { return new NumberBinaryNode(operator, left, right); } else { return new TreeValueBinaryNode(operator, left, right); @@ -142,7 +142,7 @@ public class ShuntingYardParser implements Parser>, PluginListe } else { TreeNode applyTo = constructRecursive(matches); if (applyTo == null) return null; - if(matchType == TokenType.OP){ + if (matchType == TokenType.OP) { return new NumberUnaryNode(operator, applyTo); } else { return new TreeValueUnaryNode(operator, applyTo); @@ -155,7 +155,7 @@ public class ShuntingYardParser implements Parser>, PluginListe } else if (matchType == TokenType.FUNCTION || matchType == TokenType.TREE_VALUE_FUNCTION) { String functionName = match.getContent(); CallNode node; - if(matchType == TokenType.FUNCTION){ + if (matchType == TokenType.FUNCTION) { node = new FunctionNode(functionName); } else { node = new TreeValueFunctionNode(functionName); diff --git a/core/src/main/java/org/nwapw/abacus/plugin/NumberImplementation.java b/core/src/main/java/org/nwapw/abacus/plugin/NumberImplementation.java index 70e085e..ad5d1d1 100644 --- a/core/src/main/java/org/nwapw/abacus/plugin/NumberImplementation.java +++ b/core/src/main/java/org/nwapw/abacus/plugin/NumberImplementation.java @@ -14,7 +14,7 @@ public abstract class NumberImplementation { /** * The list of paths through which this implementation can be promoted. */ - private Map, Function> promotionPaths; + private Map> promotionPaths; /** * The implementation class for this implementation. */ @@ -41,7 +41,7 @@ public abstract class NumberImplementation { * * @return the map of documentation paths. */ - public final Map, Function> getPromotionPaths() { + public final Map> getPromotionPaths() { return promotionPaths; } diff --git a/core/src/main/java/org/nwapw/abacus/plugin/PluginManager.java b/core/src/main/java/org/nwapw/abacus/plugin/PluginManager.java index 64b3eb3..0c99c58 100644 --- a/core/src/main/java/org/nwapw/abacus/plugin/PluginManager.java +++ b/core/src/main/java/org/nwapw/abacus/plugin/PluginManager.java @@ -49,10 +49,13 @@ public class PluginManager { */ private Set registeredDocumentation; /** - * The list of number implementations that have been - * found by their implementation class. + * The list of number implementation names. */ - private Map, NumberImplementation> cachedInterfaceImplementations; + private Map, String> interfaceImplementationNames; + /** + * The list of number implementations. + */ + private Map, NumberImplementation> interfaceImplementations; /** * The pi values for each implementation class that have already been computer. */ @@ -82,7 +85,8 @@ public class PluginManager { registeredTreeValueOperators = new HashMap<>(); registeredNumberImplementations = new HashMap<>(); registeredDocumentation = new HashSet<>(); - cachedInterfaceImplementations = new HashMap<>(); + interfaceImplementations = new HashMap<>(); + interfaceImplementationNames = new HashMap<>(); cachedPi = new HashMap<>(); listeners = new HashSet<>(); } @@ -135,6 +139,8 @@ public class PluginManager { */ public void registerNumberImplementation(String name, NumberImplementation implementation) { registeredNumberImplementations.put(name, implementation); + interfaceImplementationNames.put(implementation.getImplementation(), name); + interfaceImplementations.put(implementation.getImplementation(), implementation); } /** @@ -226,17 +232,17 @@ public class PluginManager { * @return the implementation. */ public NumberImplementation interfaceImplementationFor(Class name) { - if (cachedInterfaceImplementations.containsKey(name)) return cachedInterfaceImplementations.get(name); - NumberImplementation toReturn = null; - for (String key : registeredNumberImplementations.keySet()) { - NumberImplementation implementation = registeredNumberImplementations.get(key); - if (implementation.getImplementation() == name) { - toReturn = implementation; - break; - } - } - cachedInterfaceImplementations.put(name, toReturn); - return toReturn; + return interfaceImplementations.get(name); + } + + /** + * Gets the number implementation name for the given implementation class. + * + * @param name the class for which to find the implementation name. + * @return the implementation name. + */ + public String interfaceImplementationNameFor(Class name) { + return interfaceImplementationNames.get(name); } /** @@ -329,7 +335,8 @@ public class PluginManager { registeredTreeValueOperators.clear(); registeredNumberImplementations.clear(); registeredDocumentation.clear(); - cachedInterfaceImplementations.clear(); + interfaceImplementationNames.clear(); + interfaceImplementations.clear(); cachedPi.clear(); listeners.forEach(e -> e.onUnload(this)); } diff --git a/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java b/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java index 16bbba1..36ac473 100755 --- a/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java @@ -7,7 +7,6 @@ import org.nwapw.abacus.number.PreciseNumber; import java.util.ArrayList; import java.util.HashMap; -import java.util.function.BiFunction; /** * The plugin providing standard functions such as addition and subtraction to @@ -20,12 +19,12 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_ADD = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].add(params[1]); } }; @@ -34,12 +33,12 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_SUBTRACT = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].subtract(params[1]); } @@ -49,12 +48,12 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_NEGATE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].negate(); } }; @@ -63,12 +62,12 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_MULTIPLY = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].multiply(params[1]); } }; @@ -86,20 +85,6 @@ public class StandardPlugin extends Plugin { return new NaiveNumber(Math.PI); } }; - /** - * The square root function. - */ - public static final NumberFunction FUNCTION_SQRT = new NumberFunction() { - @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 1; - } - - @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return OP_CARET.apply(params[0], ((new NaiveNumber(0.5)).promoteTo(params[0].getClass()))); - } - }; /** * The implementation for the infinite-precision BigDecimal. */ @@ -111,7 +96,7 @@ public class StandardPlugin extends Plugin { @Override public NumberInterface instanceForPi() { - NumberInterface C = FUNCTION_SQRT.apply(new PreciseNumber("10005")).multiply(new PreciseNumber("426880")); + NumberInterface C = FUNCTION_SQRT.apply(this, new PreciseNumber("10005")).multiply(new PreciseNumber("426880")); NumberInterface M = PreciseNumber.ONE; NumberInterface L = new PreciseNumber("13591409"); NumberInterface X = M; @@ -136,21 +121,17 @@ public class StandardPlugin extends Plugin { return C.divide(sum); } }; - /** - * Stores objects of NumberInterface with integer values for reuse. - */ - private final static HashMap, HashMap> integerValues = new HashMap<>(); /** * The division operator, / */ public static final NumberOperator OP_DIVIDE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) { @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 2 && params[1].compareTo(fromInt(params[0].getClass(), 0)) != 0; + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 2 && params[1].compareTo(implementation.instanceForString(Integer.toString(0))) != 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].divide(params[1]); } }; @@ -160,18 +141,18 @@ public class StandardPlugin extends Plugin { public static final NumberOperator OP_FACTORIAL = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0) { //private HashMap, ArrayList> storedList = new HashMap, ArrayList>(); @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1 - && params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0 + && params[0].fractionalPart().compareTo(implementation.instanceForString("0")) == 0 && params[0].signum() >= 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { if (params[0].signum() == 0) { - return fromInt(params[0].getClass(), 1); + return implementation.instanceForString("1"); } - NumberInterface one = fromInt(params[0].getClass(), 1); + NumberInterface one = implementation.instanceForString("1"); NumberInterface factorial = params[0]; NumberInterface multiplier = params[0]; //It is necessary to later prevent calls of factorial on anything but non-negative integers. @@ -191,27 +172,27 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_NPR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2 && params[0].fractionalPart().signum() == 0 && params[1].fractionalPart().signum() == 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { if (params[0].compareTo(params[1]) < 0 || params[0].signum() < 0 || - (params[0].signum() == 0 && params[1].signum() != 0)) return fromInt(params[0].getClass(), 0); - NumberInterface total = fromInt(params[0].getClass(), 1); + (params[0].signum() == 0 && params[1].signum() != 0)) return implementation.instanceForString("0"); + NumberInterface total = implementation.instanceForString("1"); NumberInterface multiplyBy = params[0]; NumberInterface remainingMultiplications = params[1]; - NumberInterface halfway = params[0].divide(fromInt(params[0].getClass(), 2)); + NumberInterface halfway = params[0].divide(implementation.instanceForString("2")); if (remainingMultiplications.compareTo(halfway) > 0) { remainingMultiplications = params[0].subtract(remainingMultiplications); } while (remainingMultiplications.signum() > 0) { total = total.multiply(multiplyBy); - remainingMultiplications = remainingMultiplications.subtract(fromInt(params[0].getClass(), 1)); - multiplyBy = multiplyBy.subtract(fromInt(params[0].getClass(), 1)); + remainingMultiplications = remainingMultiplications.subtract(implementation.instanceForString("1")); + multiplyBy = multiplyBy.subtract(implementation.instanceForString("1")); } return total; } @@ -221,14 +202,14 @@ public class StandardPlugin extends Plugin { */ public static final NumberOperator OP_NCR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2 && params[0].fractionalPart().signum() == 0 && params[1].fractionalPart().signum() == 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return OP_NPR.apply(params).divide(OP_FACTORIAL.apply(params[1])); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return OP_NPR.apply(implementation, params).divide(OP_FACTORIAL.apply(implementation, params[1])); } }; /** @@ -236,13 +217,13 @@ public class StandardPlugin extends Plugin { */ public static final NumberFunction FUNCTION_ABS = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return params[0].multiply(fromInt(params[0].getClass(), params[0].signum())); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return params[0].multiply(implementation.instanceForString(Integer.toString(params[0].signum()))); } }; /** @@ -250,25 +231,25 @@ public class StandardPlugin extends Plugin { */ public static final NumberFunction FUNCTION_LN = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 1 && params[0].compareTo(fromInt(params[0].getClass(), 0)) > 0; + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 1 && params[0].compareTo(implementation.instanceForString("0")) > 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { NumberInterface param = params[0]; - NumberInterface one = fromInt(param.getClass(), 1); + NumberInterface one = implementation.instanceForString("1"); int powersOf2 = 0; - while (FUNCTION_ABS.apply(param.subtract(one)).compareTo(new NaiveNumber(0.1).promoteTo(param.getClass())) >= 0) { + while (FUNCTION_ABS.apply(implementation, param.subtract(one)).compareTo(implementation.instanceForString(".1")) >= 0) { if (param.subtract(one).signum() == 1) { - param = param.divide(fromInt(param.getClass(), 2)); + param = param.divide(implementation.instanceForString("2")); powersOf2++; if (param.subtract(one).signum() != 1) { break; //No infinite loop for you. } } else { - param = param.multiply(fromInt(param.getClass(), 2)); + param = param.multiply(implementation.instanceForString("2")); powersOf2--; if (param.subtract(one).signum() != -1) { break; @@ -276,7 +257,7 @@ public class StandardPlugin extends Plugin { } } } - return getLog2(param).multiply(fromInt(param.getClass(), powersOf2)).add(getLogPartialSum(param)); + return getLog2(implementation, param).multiply(implementation.instanceForString(Integer.toString(powersOf2))).add(getLogPartialSum(implementation, param)); } /** @@ -285,16 +266,16 @@ public class StandardPlugin extends Plugin { * @param x value at which the series is evaluated. 0 < x < 2. (x=2 is convergent but impractical.) * @return the partial sum. */ - private NumberInterface getLogPartialSum(NumberInterface x) { + private NumberInterface getLogPartialSum(NumberImplementation implementation, NumberInterface x) { NumberInterface maxError = x.getMaxError(); - x = x.subtract(fromInt(x.getClass(), 1)); //Terms used are for log(x+1). + x = x.subtract(implementation.instanceForString("1")); //Terms used are for log(x+1). NumberInterface currentNumerator = x, currentTerm = x, sum = x; int n = 1; - while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) { + while (FUNCTION_ABS.apply(implementation, currentTerm).compareTo(maxError) > 0) { n++; currentNumerator = currentNumerator.multiply(x).negate(); - currentTerm = currentNumerator.divide(fromInt(x.getClass(), n)); + currentTerm = currentNumerator.divide(implementation.instanceForString(Integer.toString(n))); sum = sum.add(currentTerm); } return sum; @@ -305,21 +286,21 @@ public class StandardPlugin extends Plugin { * @param number a number of the same type as the return type. (Used for precision.) * @return the value of log(2) with the appropriate precision. */ - private NumberInterface getLog2(NumberInterface number) { + private NumberInterface getLog2(NumberImplementation implementation, NumberInterface number) { NumberInterface maxError = number.getMaxError(); - //NumberInterface errorBound = fromInt(number.getClass(), 1); + //NumberInterface errorBound = implementation.instanceForString("1"); //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n) //In the following, a=1/3^n, b=1/4^n, c = 1/n. //a is also an error bound. - NumberInterface a = fromInt(number.getClass(), 1), b = a, c = a; - NumberInterface sum = fromInt(number.getClass(), 0); - NumberInterface one = fromInt(number.getClass(), 1); + NumberInterface a = implementation.instanceForString("1"), b = a, c = a; + NumberInterface sum = implementation.instanceForString("0"); + NumberInterface one = implementation.instanceForString("1"); int n = 0; while (a.compareTo(maxError) >= 1) { n++; - a = a.divide(fromInt(number.getClass(), 3)); - b = b.divide(fromInt(number.getClass(), 4)); - c = one.divide(fromInt(number.getClass(), n)); + a = a.divide(implementation.instanceForString("3")); + b = b.divide(implementation.instanceForString("4")); + c = one.divide(implementation.instanceForString(Integer.toString(n))); sum = sum.add(a.add(b).multiply(c)); } return sum; @@ -330,44 +311,88 @@ public class StandardPlugin extends Plugin { */ public static final NumberFunction FUNCTION_RAND_INT = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return fromInt(params[0].getClass(), (int) Math.round(Math.random() * params[0].floor().intValue())); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return implementation.instanceForString(Long.toString(Math.round(Math.random() * params[0].floor().intValue()))); } }; - private static final HashMap, ArrayList> FACTORIAL_LISTS = new HashMap<>(); + /** + * The caret / pow operator, ^ + */ + public static final NumberOperator OP_CARET = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) { + @Override + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + NumberInterface zero = implementation.instanceForString("0"); + return params.length == 2 + && !(params[0].compareTo(zero) == 0 + && params[1].compareTo(zero) == 0) + && !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(zero) != 0); + } + + @Override + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + NumberInterface zero = implementation.instanceForString("0"); + if (params[0].compareTo(zero) == 0) + return zero; + else if (params[1].compareTo(zero) == 0) + return implementation.instanceForString("1"); + //Detect integer bases: + if (params[0].fractionalPart().compareTo(implementation.instanceForString("0")) == 0 + && FUNCTION_ABS.apply(implementation, params[1]).compareTo(implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))) < 0 + && FUNCTION_ABS.apply(implementation, params[1]).compareTo(implementation.instanceForString("1")) >= 0) { + NumberInterface[] newParams = {params[0], params[1].fractionalPart()}; + return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(implementation, newParams)); + } + return FUNCTION_EXP.apply(implementation, FUNCTION_LN.apply(implementation, FUNCTION_ABS.apply(implementation, params[0])).multiply(params[1])); + } + }; + /** + * The square root function. + */ + public static final NumberFunction FUNCTION_SQRT = new NumberFunction() { + @Override + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 1; + } + + @Override + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return OP_CARET.apply(implementation, params[0], implementation.instanceForString(".5")); + } + }; + private static final HashMap> FACTORIAL_LISTS = new HashMap<>(); /** * The exponential function, exp(1) = e^1 = 2.71... */ public static final NumberFunction FUNCTION_EXP = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { NumberInterface maxError = params[0].getMaxError(); int n = 0; if (params[0].signum() < 0) { NumberInterface[] negatedParams = {params[0].negate()}; - return fromInt(params[0].getClass(), 1).divide(applyInternal(negatedParams)); + return implementation.instanceForString("1").divide(applyInternal(implementation, negatedParams)); } else { //We need n such that x^(n+1) * 3^ceil(x) <= maxError * (n+1)!. //right and left refer to lhs and rhs in the above inequality. - NumberInterface sum = fromInt(params[0].getClass(), 1); + NumberInterface sum = implementation.instanceForString("1"); NumberInterface nextNumerator = params[0]; - NumberInterface left = params[0].multiply(fromInt(params[0].getClass(), 3).intPow(params[0].ceiling().intValue())), right = maxError; + NumberInterface left = params[0].multiply(implementation.instanceForString("3").intPow(params[0].ceiling().intValue())), right = maxError; do { - sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n + 1))); + sum = sum.add(nextNumerator.divide(factorial(implementation, n + 1))); n++; nextNumerator = nextNumerator.multiply(params[0]); left = left.multiply(params[0]); - NumberInterface nextN = fromInt(params[0].getClass(), n + 1); + NumberInterface nextN = implementation.instanceForString(Integer.toString(n + 1)); right = right.multiply(nextN); //System.out.println(left + ", " + right); } @@ -377,58 +402,28 @@ public class StandardPlugin extends Plugin { } } }; - /** - * The caret / pow operator, ^ - */ - public static final NumberOperator OP_CARET = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) { - @Override - public boolean matchesParams(NumberInterface[] params) { - NumberInterface zero = fromInt(params[0].getClass(), 0); - return params.length == 2 - && !(params[0].compareTo(zero) == 0 - && params[1].compareTo(zero) == 0) - && !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(zero) != 0); - } - - @Override - public NumberInterface applyInternal(NumberInterface[] params) { - NumberInterface zero = fromInt(params[0].getClass(), 0); - if (params[0].compareTo(zero) == 0) - return zero; - else if (params[1].compareTo(zero) == 0) - return fromInt(params[0].getClass(), 1); - //Detect integer bases: - if (params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0 - && FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 0 - && FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0) { - NumberInterface[] newParams = {params[0], params[1].fractionalPart()}; - return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams)); - } - return FUNCTION_EXP.apply(FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0])).multiply(params[1])); - } - }; /** * The sine function (the argument is interpreted in radians). */ public final NumberFunction functionSin = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { NumberInterface pi = piFor(params[0].getClass()); - NumberInterface twoPi = pi.multiply(fromInt(pi.getClass(), 2)); - NumberInterface theta = getSmallAngle(params[0], pi); + NumberInterface twoPi = pi.multiply(implementation.instanceForString("2")); + NumberInterface theta = getSmallAngle(implementation, params[0], pi); //System.out.println(theta); - if (theta.compareTo(pi.multiply(new NaiveNumber(1.5).promoteTo(twoPi.getClass()))) >= 0) { + if (theta.compareTo(pi.multiply(implementation.instanceForString("1.5"))) >= 0) { theta = theta.subtract(twoPi); - } else if (theta.compareTo(pi.divide(fromInt(pi.getClass(), 2))) > 0) { + } else if (theta.compareTo(pi.divide(implementation.instanceForString("2"))) > 0) { theta = pi.subtract(theta); } //System.out.println(theta); - return sinTaylor(theta); + return sinTaylor(implementation, theta); } }; /** @@ -436,13 +431,13 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionCos = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return functionSin.apply(piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2)) + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return functionSin.apply(implementation, piFor(params[0].getClass()).divide(implementation.instanceForString("2")) .subtract(params[0])); } }; @@ -451,13 +446,13 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionTan = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return functionSin.apply(params[0]).divide(functionCos.apply(params[0])); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return functionSin.apply(implementation, params[0]).divide(functionCos.apply(implementation, params[0])); } }; /** @@ -465,13 +460,13 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionSec = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return fromInt(params[0].getClass(), 1).divide(functionCos.apply(params[0])); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return implementation.instanceForString("1").divide(functionCos.apply(implementation, params[0])); } }; /** @@ -479,13 +474,13 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionCsc = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return fromInt(params[0].getClass(), 1).divide(functionSin.apply(params[0])); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return implementation.instanceForString("1").divide(functionSin.apply(implementation, params[0])); } }; /** @@ -493,13 +488,13 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionCot = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return functionCos.apply(params[0]).divide(functionSin.apply(params[0])); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return functionCos.apply(implementation, params[0]).divide(functionSin.apply(implementation, params[0])); } }; @@ -508,28 +503,28 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArcsin = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1 - && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) <= 0; + && FUNCTION_ABS.apply(implementation, params[0]).compareTo(implementation.instanceForString("1")) <= 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - if (FUNCTION_ABS.apply(params[0]).compareTo(new NaiveNumber(0.8).promoteTo(params[0].getClass())) >= 0) { - NumberInterface[] newParams = {FUNCTION_SQRT.apply(fromInt(params[0].getClass(), 1).subtract(params[0].multiply(params[0])))}; - return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2)) - .subtract(applyInternal(newParams)).multiply(fromInt(params[0].getClass(), params[0].signum())); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + if (FUNCTION_ABS.apply(implementation, params[0]).compareTo(implementation.instanceForString(".8")) >= 0) { + NumberInterface[] newParams = {FUNCTION_SQRT.apply(implementation, implementation.instanceForString("1").subtract(params[0].multiply(params[0])))}; + return piFor(params[0].getClass()).divide(implementation.instanceForString("2")) + .subtract(applyInternal(implementation, newParams)).multiply(implementation.instanceForString(Integer.toString(params[0].signum()))); } NumberInterface currentTerm = params[0], sum = currentTerm, - multiplier = currentTerm.multiply(currentTerm), summandBound = sum.getMaxError().multiply(fromInt(sum.getClass(), 1).subtract(multiplier)), - power = currentTerm, coefficient = fromInt(params[0].getClass(), 1); + multiplier = currentTerm.multiply(currentTerm), summandBound = sum.getMaxError().multiply(implementation.instanceForString("1").subtract(multiplier)), + power = currentTerm, coefficient = implementation.instanceForString("1"); int exponent = 1; - while (FUNCTION_ABS.apply(currentTerm).compareTo(summandBound) > 0) { + while (FUNCTION_ABS.apply(implementation, currentTerm).compareTo(summandBound) > 0) { exponent += 2; power = power.multiply(multiplier); - coefficient = coefficient.multiply(fromInt(params[0].getClass(), exponent - 2)) - .divide(fromInt(params[0].getClass(), exponent - 1)); - currentTerm = power.multiply(coefficient).divide(fromInt(power.getClass(), exponent)); + coefficient = coefficient.multiply(implementation.instanceForString(Integer.toString(exponent - 2))) + .divide(implementation.instanceForString(Integer.toString(exponent - 1))); + currentTerm = power.multiply(coefficient).divide(implementation.instanceForString(Integer.toString(exponent))); sum = sum.add(currentTerm); } return sum; @@ -541,14 +536,14 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArccos = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) <= 0; + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 1 && FUNCTION_ABS.apply(implementation, params[0]).compareTo(implementation.instanceForString("1")) <= 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2)) - .subtract(functionArcsin.apply(params)); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return piFor(params[0].getClass()).divide(implementation.instanceForString("2")) + .subtract(functionArcsin.apply(implementation, params)); } }; @@ -557,14 +552,14 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArccsc = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) >= 0; + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 1 && FUNCTION_ABS.apply(implementation, params[0]).compareTo(implementation.instanceForString("1")) >= 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - NumberInterface[] reciprocalParamArr = {fromInt(params[0].getClass(), 1).divide(params[0])}; - return functionArcsin.apply(reciprocalParamArr); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + NumberInterface[] reciprocalParamArr = {implementation.instanceForString("1").divide(params[0])}; + return functionArcsin.apply(implementation, reciprocalParamArr); } }; @@ -573,14 +568,14 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArcsec = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { - return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) >= 0; + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { + return params.length == 1 && FUNCTION_ABS.apply(implementation, params[0]).compareTo(implementation.instanceForString("1")) >= 0; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - NumberInterface[] reciprocalParamArr = {fromInt(params[0].getClass(), 1).divide(params[0])}; - return functionArccos.apply(reciprocalParamArr); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + NumberInterface[] reciprocalParamArr = {implementation.instanceForString("1").divide(params[0])}; + return functionArccos.apply(implementation, reciprocalParamArr); } }; @@ -589,36 +584,36 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArctan = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { if (params[0].signum() == -1) { NumberInterface[] negatedParams = {params[0].negate()}; - return applyInternal(negatedParams).negate(); + return applyInternal(implementation, negatedParams).negate(); } - if (params[0].compareTo(fromInt(params[0].getClass(), 1)) > 0) { - NumberInterface[] reciprocalParams = {fromInt(params[0].getClass(), 1).divide(params[0])}; - return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2)) - .subtract(applyInternal(reciprocalParams)); + if (params[0].compareTo(implementation.instanceForString("1")) > 0) { + NumberInterface[] reciprocalParams = {implementation.instanceForString("1").divide(params[0])}; + return piFor(params[0].getClass()).divide(implementation.instanceForString("2")) + .subtract(applyInternal(implementation, reciprocalParams)); } - if (params[0].compareTo(fromInt(params[0].getClass(), 1)) == 0) { - return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 4)); + if (params[0].compareTo(implementation.instanceForString("1")) == 0) { + return piFor(params[0].getClass()).divide(implementation.instanceForString("4")); } - if (params[0].compareTo(new NaiveNumber(0.9).promoteTo(params[0].getClass())) >= 0) { - NumberInterface[] newParams = {params[0].multiply(fromInt(params[0].getClass(), 2)) - .divide(fromInt(params[0].getClass(), 1).subtract(params[0].multiply(params[0])))}; - return applyInternal(newParams).divide(fromInt(params[0].getClass(), 2)); + if (params[0].compareTo(implementation.instanceForString(".9")) >= 0) { + NumberInterface[] newParams = {params[0].multiply(implementation.instanceForString("2")) + .divide(implementation.instanceForString("1").subtract(params[0].multiply(params[0])))}; + return applyInternal(implementation, newParams).divide(implementation.instanceForString("2")); } NumberInterface currentPower = params[0], currentTerm = currentPower, sum = currentTerm, maxError = params[0].getMaxError(), multiplier = currentPower.multiply(currentPower).negate(); int n = 1; - while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) { + while (FUNCTION_ABS.apply(implementation, currentTerm).compareTo(maxError) > 0) { n += 2; currentPower = currentPower.multiply(multiplier); - currentTerm = currentPower.divide(fromInt(currentPower.getClass(), n)); + currentTerm = currentPower.divide(implementation.instanceForString(Integer.toString(n))); sum = sum.add(currentTerm); } return sum; @@ -630,14 +625,14 @@ public class StandardPlugin extends Plugin { */ public final NumberFunction functionArccot = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 1; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2)) - .subtract(functionArctan.apply(params)); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return piFor(params[0].getClass()).divide(implementation.instanceForString("2")) + .subtract(functionArctan.apply(implementation, params)); } }; @@ -645,40 +640,25 @@ public class StandardPlugin extends Plugin { super(manager); } - /** - * Returns a partial sum of a series whose terms are given by the nthTermFunction, evaluated at x. - * - * @param x the value at which the series is evaluated. - * @param nthTermFunction the function that returns the nth term of the series, in the format term(x, n). - * @param n the number of terms in the partial sum. - * @return the value of the partial sum that has the same class as x. - */ - private static NumberInterface sumSeries(NumberInterface x, BiFunction nthTermFunction, int n) { - NumberInterface sum = fromInt(x.getClass(), 0); - for (int i = 0; i <= n; i++) { - sum = sum.add(nthTermFunction.apply(i, x)); - } - return sum; - } - /** * A factorial function that uses memoization for each number class; it efficiently * computes factorials of non-negative integers. * - * @param numberClass type of number to return. - * @param n non-negative integer. + * @param implementation type of number to return. + * @param n non-negative integer. * @return a number of numClass with value n factorial. */ - public static NumberInterface factorial(Class numberClass, int n) { - if (!FACTORIAL_LISTS.containsKey(numberClass)) { - FACTORIAL_LISTS.put(numberClass, new ArrayList<>()); - FACTORIAL_LISTS.get(numberClass).add(fromInt(numberClass, 1)); - FACTORIAL_LISTS.get(numberClass).add(fromInt(numberClass, 1)); + public static NumberInterface factorial(NumberImplementation implementation, int n) { + + if (!FACTORIAL_LISTS.containsKey(implementation)) { + FACTORIAL_LISTS.put(implementation, new ArrayList<>()); + FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1")); + FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1")); } - ArrayList list = FACTORIAL_LISTS.get(numberClass); + ArrayList list = FACTORIAL_LISTS.get(implementation); if (n >= list.size()) { while (list.size() < n + 16) { - list.add(list.get(list.size() - 1).multiply(fromInt(numberClass, list.size()))); + list.add(list.get(list.size() - 1).multiply(implementation.instanceForString(Integer.toString(list.size())))); } } return list.get(n); @@ -690,16 +670,16 @@ public class StandardPlugin extends Plugin { * @param x where the series is evaluated. * @return the value of the series */ - private static NumberInterface sinTaylor(NumberInterface x) { + private static NumberInterface sinTaylor(NumberImplementation implementation, NumberInterface x) { NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x; NumberInterface maxError = x.getMaxError(); int n = 1; do { n += 2; power = power.multiply(multiplier); - currentTerm = power.divide(factorial(x.getClass(), n)); + currentTerm = power.divide(factorial(implementation, n)); sum = sum.add(currentTerm); - } while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0); + } while (FUNCTION_ABS.apply(implementation, currentTerm).compareTo(maxError) > 0); return sum; } @@ -709,33 +689,16 @@ public class StandardPlugin extends Plugin { * @param phi an angle (in radians). * @return theta in [0, 2pi) that differs from phi by a multiple of 2pi. */ - private static NumberInterface getSmallAngle(NumberInterface phi, NumberInterface pi) { - NumberInterface twoPi = pi.multiply(fromInt(pi.getClass(), 2)); - NumberInterface theta = FUNCTION_ABS.apply(phi).subtract(twoPi - .multiply(FUNCTION_ABS.apply(phi).divide(twoPi).floor())); //Now theta is in [0, 2pi). + private static NumberInterface getSmallAngle(NumberImplementation implementation, NumberInterface phi, NumberInterface pi) { + NumberInterface twoPi = pi.multiply(implementation.instanceForString("2")); + NumberInterface theta = FUNCTION_ABS.apply(implementation, phi).subtract(twoPi + .multiply(FUNCTION_ABS.apply(implementation, phi).divide(twoPi).floor())); //Now theta is in [0, 2pi). if (phi.signum() < 0) { theta = twoPi.subtract(theta); } return theta; } - /** - * Returns a number of class numType with value n. - * - * @param numType class of number to return. - * @param n value of returned number. - * @return numClass instance with value n. - */ - private static NumberInterface fromInt(Class numType, int n) { - if (!integerValues.containsKey(numType)) { - integerValues.put(numType, new HashMap<>()); - } - if (!integerValues.get(numType).containsKey(n)) { - integerValues.get(numType).put(n, new NaiveNumber(n).promoteTo(numType)); - } - return integerValues.get(numType).get(n); - } - @Override public void onEnable() { registerNumberImplementation("naive", IMPLEMENTATION_NAIVE); @@ -824,6 +787,6 @@ public class StandardPlugin extends Plugin { @Override public void onDisable() { - + FACTORIAL_LISTS.clear(); } } diff --git a/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java b/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java index fa73958..5d497ec 100644 --- a/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java +++ b/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java @@ -1,8 +1,13 @@ package org.nwapw.abacus.tree; import org.nwapw.abacus.Abacus; -import org.nwapw.abacus.function.*; +import org.nwapw.abacus.function.NumberFunction; +import org.nwapw.abacus.function.NumberOperator; +import org.nwapw.abacus.function.TreeValueFunction; +import org.nwapw.abacus.function.TreeValueOperator; import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.number.PromotionManager; +import org.nwapw.abacus.number.PromotionResult; /** * A reducer implementation that turns a tree into a single number. @@ -26,19 +31,22 @@ public class NumberReducer implements Reducer { @Override public NumberInterface reduceNode(TreeNode node, Object... children) { + PromotionManager manager = abacus.getPromotionManager(); if (node instanceof NumberNode) { - return abacus.numberFromString(((NumberNode) node).getNumber()); - } else if(node instanceof VariableNode) { - return abacus.numberFromString("0"); + return abacus.getNumberImplementation().instanceForString(((NumberNode) node).getNumber()); + } else if (node instanceof VariableNode) { + return abacus.getNumberImplementation().instanceForString("0"); } else if (node instanceof NumberBinaryNode) { NumberInterface left = (NumberInterface) children[0]; NumberInterface right = (NumberInterface) children[1]; NumberOperator operator = abacus.getPluginManager().operatorFor(((BinaryNode) node).getOperation()); - return operator.apply(left, right); + PromotionResult result = manager.promote(left, right); + if (result == null) return null; + return operator.apply(result.getPromotedTo(), result.getItems()); } else if (node instanceof NumberUnaryNode) { NumberInterface child = (NumberInterface) children[0]; NumberOperator operator = abacus.getPluginManager().operatorFor(((UnaryNode) node).getOperation()); - return operator.apply(child); + return operator.apply(abacus.getPluginManager().interfaceImplementationFor(child.getClass()), child); } else if (node instanceof FunctionNode) { NumberInterface[] convertedChildren = new NumberInterface[children.length]; for (int i = 0; i < convertedChildren.length; i++) { @@ -46,29 +54,31 @@ public class NumberReducer implements Reducer { } NumberFunction function = abacus.getPluginManager().functionFor(((FunctionNode) node).getCallTo()); if (function == null) return null; - return function.apply(convertedChildren); - } else if (node instanceof TreeValueFunctionNode){ + PromotionResult result = manager.promote(convertedChildren); + if (result == null) return null; + return function.apply(result.getPromotedTo(), result.getItems()); + } else if (node instanceof TreeValueFunctionNode) { CallNode callNode = (CallNode) node; TreeNode[] realChildren = new TreeNode[callNode.getChildren().size()]; - for(int i = 0; i < realChildren.length; i++){ + for (int i = 0; i < realChildren.length; i++) { realChildren[i] = callNode.getChildren().get(i); } TreeValueFunction function = abacus.getPluginManager().treeValueFunctionFor(callNode.getCallTo()); - if(function == null) return null; - return function.applyWithReducer(this, realChildren); + if (function == null) return null; + return function.applyWithReducer(abacus.getNumberImplementation(), this, realChildren); } else if (node instanceof TreeValueBinaryNode) { BinaryNode binaryNode = (BinaryNode) node; TreeValueOperator operator = abacus.getPluginManager() .treeValueOperatorFor(binaryNode.getOperation()); - if(operator == null) return null; - return operator.applyWithReducer(this, binaryNode.getLeft(), binaryNode.getRight()); - } else if(node instanceof TreeValueUnaryNode) { + if (operator == null) return null; + return operator.applyWithReducer(abacus.getNumberImplementation(), this, binaryNode.getLeft(), binaryNode.getRight()); + } else if (node instanceof TreeValueUnaryNode) { UnaryNode unaryNode = (UnaryNode) node; TreeValueOperator operator = abacus.getPluginManager() .treeValueOperatorFor(unaryNode.getOperation()); - if(operator == null) return null; - return operator.applyWithReducer(this, unaryNode.getApplyTo()); + if (operator == null) return null; + return operator.applyWithReducer(abacus.getNumberImplementation(), this, unaryNode.getApplyTo()); } return null; } diff --git a/core/src/main/kotlin/org/nwapw/abacus/function/applicable/Applicable.kt b/core/src/main/kotlin/org/nwapw/abacus/function/applicable/Applicable.kt index 5e219c1..69f0afc 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/function/applicable/Applicable.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/function/applicable/Applicable.kt @@ -1,5 +1,7 @@ package org.nwapw.abacus.function.applicable +import org.nwapw.abacus.plugin.NumberImplementation + /** * A class that can be applied to arguments. * @@ -15,7 +17,7 @@ interface Applicable { * @param params the parameter array to verify for compatibility. * @return whether the array can be used with applyInternal. */ - fun matchesParams(params: Array): Boolean + fun matchesParams(implementation: NumberImplementation, params: Array): Boolean /** * Applies the applicable object to the given parameters, @@ -23,7 +25,7 @@ interface Applicable { * @param params the parameters to apply to. * @return the result of the application. */ - fun applyInternal(params: Array): O? + fun applyInternal(implementation: NumberImplementation, params: Array): O? /** * If the parameters can be used with this applicable, returns @@ -32,9 +34,9 @@ interface Applicable { * @param params the parameters to apply to. * @return the result of the operation, or null if parameters do not match. */ - fun apply(vararg params: T): O? { - if (!matchesParams(params)) return null - return applyInternal(params) + fun apply(implementation: NumberImplementation, vararg params: T): O? { + if (!matchesParams(implementation, params)) return null + return applyInternal(implementation, params) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/function/applicable/ReducerApplicable.kt b/core/src/main/kotlin/org/nwapw/abacus/function/applicable/ReducerApplicable.kt index 6cef5e1..bdb3276 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/function/applicable/ReducerApplicable.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/function/applicable/ReducerApplicable.kt @@ -1,5 +1,6 @@ package org.nwapw.abacus.function.applicable +import org.nwapw.abacus.plugin.NumberImplementation import org.nwapw.abacus.tree.Reducer /** @@ -18,7 +19,7 @@ interface ReducerApplicable { * given parameters. * @param params the parameters to check. */ - fun matchesParams(params: Array): Boolean + fun matchesParams(implementation: NumberImplementation, params: Array): Boolean /** * Applies this applicable to the given arguments, and reducer. @@ -26,7 +27,7 @@ interface ReducerApplicable { * @param params the arguments to apply to. * @return the result of the application. */ - fun applyWithReducerInternal(reducer: Reducer, params: Array): O? + fun applyWithReducerInternal(implementation: NumberImplementation, reducer: Reducer, params: Array): O? /** * Applies this applicable to the given arguments, and reducer, @@ -35,9 +36,9 @@ interface ReducerApplicable { * @param params the arguments to apply to. * @return the result of the application, or null if the arguments are incompatible. */ - fun applyWithReducer(reducer: Reducer, vararg params: T): O? { - if (!matchesParams(params)) return null - return applyWithReducerInternal(reducer, params) + fun applyWithReducer(implementation: NumberImplementation, reducer: Reducer, vararg params: T): O? { + if (!matchesParams(implementation, params)) return null + return applyWithReducerInternal(implementation, reducer, params) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/number/NumberUtils.kt b/core/src/main/kotlin/org/nwapw/abacus/number/NumberUtils.kt new file mode 100644 index 0000000..03c46fd --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/number/NumberUtils.kt @@ -0,0 +1,16 @@ +@file:JvmName("NumberUtils") +package org.nwapw.abacus.number + +typealias PromotionFunction = java.util.function.Function +typealias PromotionPath = List +typealias NumberClass = Class + +/** + * Promote a number through this path. The functions in this path + * are applied in order to the number, and the final result is returned. + * + * @param from the number to start from. + */ +fun PromotionPath.promote(from: NumberInterface): NumberInterface { + return fold(from, { current, function -> function.apply(current) }) +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/number/PromotionManager.kt b/core/src/main/kotlin/org/nwapw/abacus/number/PromotionManager.kt new file mode 100644 index 0000000..421d642 --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/number/PromotionManager.kt @@ -0,0 +1,83 @@ +package org.nwapw.abacus.number + +import org.nwapw.abacus.Abacus +import org.nwapw.abacus.plugin.NumberImplementation +import org.nwapw.abacus.plugin.PluginListener +import org.nwapw.abacus.plugin.PluginManager +import java.util.function.Function + +/** + * A class that handles promotions based on priority and the + * transition paths each implementation provides. + * + * @property abacus the Abacus instance to use to access other components. + */ +class PromotionManager(val abacus: Abacus) : PluginListener { + + /** + * The already computed paths + */ + val computePaths = mutableMapOf, PromotionPath?>() + + /** + * Computes a path between a starting and an ending implementation. + * + * @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 computePathBetween(from: NumberImplementation, to: NumberImplementation): PromotionPath? { + val fromName = abacus.pluginManager.interfaceImplementationNameFor(from.implementation) + val toName = abacus.pluginManager.interfaceImplementationNameFor(to.implementation) + + if(fromName == toName) return listOf(Function { it }) + + if(from.promotionPaths.containsKey(toName)) + return listOf(from.promotionPaths[toName] ?: 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 + * they can be used with each other. Finds the highest priority implementation + * in the list, and promotes all other numbers to it. + * + * @param numbers the numbers to promote. + * @return the resulting promotion result. + */ + 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 getPathBetween(pluginManager.interfaceImplementationFor(it.javaClass), highestPriority) + ?.promote(it) ?: return null + }.toTypedArray(), promotedTo = highestPriority) + } + + override fun onLoad(manager: PluginManager?) { + + } + + override fun onUnload(manager: PluginManager?) { + computePaths.clear() + } + + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/number/PromotionResult.kt b/core/src/main/kotlin/org/nwapw/abacus/number/PromotionResult.kt new file mode 100644 index 0000000..51623fb --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/number/PromotionResult.kt @@ -0,0 +1,11 @@ +package org.nwapw.abacus.number + +import org.nwapw.abacus.plugin.NumberImplementation + +/** + * The result of promoting an array of NumberInterfaces. + * + * @param promotedTo the implementation to which the numbers were promoted. + * @param items the items the items resulting from the promotion. + */ +data class PromotionResult(val promotedTo: NumberImplementation, val items: Array) \ No newline at end of file diff --git a/core/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java b/core/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java index 79a7cd2..660be2f 100644 --- a/core/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java +++ b/core/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java @@ -9,6 +9,7 @@ import org.nwapw.abacus.function.*; import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.parsing.LexerTokenizer; +import org.nwapw.abacus.plugin.NumberImplementation; import org.nwapw.abacus.plugin.Plugin; import org.nwapw.abacus.tree.TokenType; @@ -20,12 +21,12 @@ public class TokenizerTests { private static LexerTokenizer lexerTokenizer = new LexerTokenizer(); private static NumberFunction subtractFunction = new NumberFunction() { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return params.length == 2; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { return params[0].subtract(params[1]); } }; @@ -36,26 +37,26 @@ public class TokenizerTests { 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return true; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return subtractFunction.apply(params); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return subtractFunction.apply(implementation, params); } }); registerOperator("-", new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { @Override - public boolean matchesParams(NumberInterface[] params) { + public boolean matchesParams(NumberImplementation implementation, NumberInterface[] params) { return true; } @Override - public NumberInterface applyInternal(NumberInterface[] params) { - return subtractFunction.apply(params); + public NumberInterface applyInternal(NumberImplementation implementation, NumberInterface[] params) { + return subtractFunction.apply(implementation, params); } }); registerFunction("subtract", subtractFunction); diff --git a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java index e8096fd..4f62247 100644 --- a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java +++ b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java @@ -14,8 +14,7 @@ import org.nwapw.abacus.Abacus; import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.function.Documentation; import org.nwapw.abacus.function.DocumentationType; -import org.nwapw.abacus.number.ComputationInterruptedException; -import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.number.*; import org.nwapw.abacus.plugin.ClassFinder; import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginManager;