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
Arthur Drobot
7d5efa1fe6 Add fromInt function into StandardPlugin and replace existing instantiations and promotions of NaiveNumbers with integer values with fromInt calls. 2017-08-07 10:33:16 -07:00
arthur326
6a15c266c4 Fix typo in cot function. 2017-08-06 18:13:01 -07:00
9f61fc5dbe Remove the correct unused intPow function. 2017-08-05 18:23:24 -07:00
bae6ee5526 Revert "Remove the NumberInterface::intPow method."
0c16bb4e9b
2017-08-05 18:22:43 -07:00
4f94700aef Remove the NumberInterface::intPow method. 2017-08-05 18:11:16 -07:00
b7152da58d Merge branch 'stoppable-alternate' 2017-08-05 17:58:26 -07:00
d17a8a9fa7 Add missing javadoc. 2017-08-05 17:29:35 -07:00
71c9f0d141 Merge branch 'documentation' 2017-08-05 17:28:08 -07:00
fb02984e60 Decrease test intensity to prevent travis CI from killing gradle. 2017-08-05 17:24:16 -07:00
a9ac4681f0 Use a specific configuration instead of chancing it with a local file. 2017-08-05 17:13:03 -07:00
62d7053441 Get rid of unnecessary supplier. 2017-08-05 17:09:12 -07:00
f3cbb600ac Remove the default load-from-file behavior from the Abacus core. 2017-08-05 17:04:07 -07:00
abc0e2d59f Add tests for more complex functions. 2017-08-05 16:57:52 -07:00
f7d1be086b Add tests for basic operations. 2017-08-05 16:57:22 -07:00
21a925d6d2 Write two functions to help test the code. 2017-08-05 16:56:50 -07:00
0d21898f20 Make loading plugins a non-core part of Abacus, avoiding desktop APIs. 2017-08-05 16:21:02 -07:00
3e39087fde Add numerous documentation fixes. 2017-08-05 16:15:30 -07:00
11 changed files with 186 additions and 55 deletions

View File

@@ -6,16 +6,12 @@ import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.parsing.LexerTokenizer;
import org.nwapw.abacus.parsing.ShuntingYardParser;
import org.nwapw.abacus.parsing.TreeBuilder;
import org.nwapw.abacus.plugin.ClassFinder;
import org.nwapw.abacus.plugin.NumberImplementation;
import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.StandardPlugin;
import org.nwapw.abacus.tree.NumberReducer;
import org.nwapw.abacus.tree.TreeNode;
import java.io.File;
import java.io.IOException;
/**
* The main calculator class. This is responsible
* for piecing together all of the components, allowing
@@ -27,10 +23,6 @@ public class Abacus {
* The default number implementation to be used if no other one is available / selected.
*/
public static final NumberImplementation DEFAULT_IMPLEMENTATION = StandardPlugin.IMPLEMENTATION_NAIVE;
/**
* The file used for saving and loading configuration.
*/
public static final File CONFIG_FILE = new File("config.toml");
/**
* The plugin manager responsible for
@@ -54,26 +46,19 @@ public class Abacus {
/**
* Creates a new instance of the Abacus calculator.
*
* @param configuration the configuration object for this Abacus instance.
*/
public Abacus() {
public Abacus(Configuration configuration) {
pluginManager = new PluginManager(this);
numberReducer = new NumberReducer(this);
configuration = new Configuration(CONFIG_FILE);
configuration.saveTo(CONFIG_FILE);
this.configuration = new Configuration(configuration);
LexerTokenizer lexerTokenizer = new LexerTokenizer();
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
pluginManager.addListener(lexerTokenizer);
pluginManager.addListener(shuntingYardParser);
pluginManager.addInstantiated(new StandardPlugin(pluginManager));
try {
ClassFinder.loadJars("plugins")
.forEach(plugin -> pluginManager.addClass(plugin));
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
pluginManager.load();
pluginManager.addListener(lexerTokenizer);
}
public static void main(String[] args) {

View File

@@ -39,6 +39,15 @@ public class Configuration {
*/
private Set<String> disabledPlugins = new HashSet<>();
/**
* Creates a new configuration form the given configuration.
*
* @param copyFrom the configuration to copy.
*/
public Configuration(Configuration copyFrom){
copyFrom(copyFrom);
}
/**
* Creates a new configuration with the given values.
*
@@ -85,6 +94,15 @@ public class Configuration {
}
}
/**
* Gets the value of this configuration as a string.
*
* @return the string that represents this configuration.
*/
public String asTomlString(){
return TOML_WRITER.write(this);
}
/**
* Gets the number implementation from this configuration.
*

View File

@@ -26,6 +26,7 @@ public class Operator {
* Creates a new operator with the given parameters.
*
* @param associativity the associativity of the operator.
* @param operatorType the type of this operator, like binary infix or unary postfix.
* @param precedence the precedence of the operator.
* @param function the function that the operator calls.
*/

View File

@@ -13,10 +13,14 @@ import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.number.ComputationInterruptedException;
import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.plugin.ClassFinder;
import org.nwapw.abacus.plugin.PluginListener;
import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.StandardPlugin;
import org.nwapw.abacus.tree.TreeNode;
import java.io.File;
import java.io.IOException;
import java.util.Set;
@@ -26,6 +30,10 @@ import java.util.Set;
*/
public class AbacusController implements PluginListener {
/**
* The file used for saving and loading configuration.
*/
public static final File CONFIG_FILE = new File("config.toml");
/**
* The title for the apply alert dialog.
*/
@@ -205,9 +213,16 @@ public class AbacusController implements PluginListener {
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
});
abacus = new Abacus();
abacus.getPluginManager().addListener(this);
abacus.getPluginManager().reload();
abacus = new Abacus(new Configuration(CONFIG_FILE));
PluginManager abacusPluginManager = abacus.getPluginManager();
abacusPluginManager.addListener(this);
abacusPluginManager.addInstantiated(new StandardPlugin(abacus.getPluginManager()));
try {
ClassFinder.loadJars("plugins").forEach(abacusPluginManager::addClass);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
abacusPluginManager.reload();
changesMade = false;
reloadAlertShown = false;
@@ -255,7 +270,7 @@ public class AbacusController implements PluginListener {
for (ToggleablePlugin pluginEntry : enabledPlugins) {
if (!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
}
configuration.saveTo(Abacus.CONFIG_FILE);
configuration.saveTo(CONFIG_FILE);
changesMade = false;
reloadAlertShown = false;
}

View File

@@ -15,7 +15,7 @@ public class ValueNode<T> extends PatternNode<T> {
/**
* Creates a new node that matches the given character.
*
* @param value
* @param value the character value of the node.
*/
public ValueNode(char value) {
this.value = value;

View File

@@ -182,7 +182,7 @@ public abstract class NumberInterface {
* Also, checks if the thread has been interrupted, and if so, throws
* an exception.
*
* @return the least integer >= the number, if int can hold the value.
* @return the least integer bigger or equal to the number, if int can hold the value.
*/
public final NumberInterface ceiling(){
checkInterrupted();
@@ -192,7 +192,7 @@ public abstract class NumberInterface {
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest int >= the number, if int can hold the value.
* @return the greatest integer smaller or equal the number, if int can hold the value.
*/
protected abstract NumberInterface floorInternal();
@@ -230,7 +230,7 @@ public abstract class NumberInterface {
* Returns the integer representation of this number, discarding any fractional part,
* if int can hold the value.
*
* @return
* @return the integer value of this number.
*/
public abstract int intValue();

View File

@@ -28,7 +28,7 @@ public abstract class NumberImplementation {
* Creates a new number implementation with the given data.
*
* @param implementation the implementation class.
* @param priority the priority, higher -> more likely to be converted into.
* @param priority the priority, higher means more likely to be converted into.
*/
public NumberImplementation(Class<? extends NumberInterface> implementation, int priority) {
this.implementation = implementation;

View File

@@ -70,6 +70,8 @@ public class PluginManager {
/**
* Creates a new plugin manager.
*
* @param abacus the abacus instance.
*/
public PluginManager(Abacus abacus) {
this.abacus = abacus;

View File

@@ -18,6 +18,11 @@ import java.util.function.BiFunction;
*/
public class StandardPlugin extends Plugin {
/**
* Stores objects of NumberInterface with integer values for reuse.
*/
private final static HashMap<Class<? extends NumberInterface>, HashMap<Integer, NumberInterface>> integerValues = new HashMap<>();
/**
* The addition operator, +
*/
@@ -112,7 +117,7 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if (params[0].signum() == 0) {
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
return fromInt(params[0].getClass(), 1);
}
NumberInterface factorial = params[0];
NumberInterface multiplier = params[0];
@@ -157,14 +162,14 @@ public class StandardPlugin extends Plugin {
int powersOf2 = 0;
while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo(new NaiveNumber(0.1).promoteTo(param.getClass())) >= 0) {
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) {
param = param.divide(new NaiveNumber(2).promoteTo(param.getClass()));
param = param.divide(fromInt(param.getClass(), 2));
powersOf2++;
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
break;
//No infinite loop for you.
}
} else {
param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass()));
param = param.multiply(fromInt(param.getClass(), 2));
powersOf2--;
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != -1) {
break;
@@ -203,17 +208,17 @@ public class StandardPlugin extends Plugin {
*/
private NumberInterface getLog2(NumberInterface number) {
NumberInterface maxError = getMaxError(number);
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
//NumberInterface errorBound = fromInt(number.getClass(), 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 = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
NumberInterface a = fromInt(number.getClass(), 1), b = a, c = a;
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
int n = 0;
while (a.compareTo(maxError) >= 1) {
n++;
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
b = b.divide((new NaiveNumber(4)).promoteTo(number.getClass()));
a = a.divide(fromInt(number.getClass(), 3));
b = b.divide(fromInt(number.getClass(), 4));
c = NaiveNumber.ONE.promoteTo(number.getClass()).divide((new NaiveNumber(n)).promoteTo(number.getClass()));
sum = sum.add(a.add(b).multiply(c));
}
@@ -311,7 +316,7 @@ public class StandardPlugin extends Plugin {
//right and left refer to lhs and rhs in the above inequality.
NumberInterface sum = NaiveNumber.ONE.promoteTo(params[0].getClass());
NumberInterface nextNumerator = params[0];
NumberInterface left = params[0].multiply((new NaiveNumber(3)).promoteTo(params[0].getClass()).intPow(params[0].ceiling().intValue())), right = maxError;
NumberInterface left = params[0].multiply(fromInt(params[0].getClass(), 3).intPow(params[0].ceiling().intValue())), right = maxError;
do {
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n + 1)));
n++;
@@ -359,12 +364,12 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
NumberInterface pi = getPi(params[0].getClass());
NumberInterface twoPi = pi.multiply(new NaiveNumber(2).promoteTo(pi.getClass()));
NumberInterface twoPi = pi.multiply(fromInt(pi.getClass(), 2));
NumberInterface theta = getSmallAngle(params[0], pi);
//System.out.println(theta);
if (theta.compareTo(pi.multiply(new NaiveNumber(1.5).promoteTo(twoPi.getClass()))) >= 0) {
theta = theta.subtract(twoPi);
} else if (theta.compareTo(pi.divide(new NaiveNumber(2).promoteTo(pi.getClass()))) > 0) {
} else if (theta.compareTo(pi.divide(fromInt(pi.getClass(), 2))) > 0) {
theta = pi.subtract(theta);
}
//System.out.println(theta);
@@ -382,7 +387,7 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
return functionSin.apply(getPi(params[0].getClass()).divide(new NaiveNumber(2).promoteTo(params[0].getClass()))
return functionSin.apply(getPi(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
.subtract(params[0]));
}
};
@@ -439,7 +444,7 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
return functionCos.apply(params[0]).divide(functionCos.apply(params[0]));
return functionCos.apply(params[0]).divide(functionSin.apply(params[0]));
}
};
@@ -470,7 +475,7 @@ public class StandardPlugin extends Plugin {
* @return the maximum error.
*/
private static NumberInterface getMaxError(NumberInterface number) {
return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision());
return fromInt(number.getClass(), 10).intPow(-number.getMaxPrecision());
}
/**
@@ -531,20 +536,20 @@ public class StandardPlugin extends Plugin {
return theta;
}
public static NumberInterface intPow(NumberInterface number, Class<? extends NumberInterface> numberClass, NumberInterface exponent) {
if (exponent.compareTo((new NaiveNumber(0)).promoteTo(numberClass)) == 0) {
return (new NaiveNumber(1)).promoteTo(numberClass);
/**
* 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<? extends NumberInterface> numType, int n){
if(!integerValues.containsKey(numType)){
integerValues.put(numType, new HashMap<>());
}
boolean takeReciprocal = exponent.compareTo((new NaiveNumber(0)).promoteTo(numberClass)) < 0;
exponent = FUNCTION_ABS.apply(exponent);
NumberInterface power = number;
for (NumberInterface currentExponent = (new NaiveNumber(1)).promoteTo(numberClass); currentExponent.compareTo(exponent) < 0; currentExponent = currentExponent.add((new NaiveNumber(1)).promoteTo(numberClass))) {
power = power.multiply(number);
if(!integerValues.get(numType).containsKey(n)){
integerValues.get(numType).put(n, new NaiveNumber(n).promoteTo(numType));
}
if (takeReciprocal) {
power = (new NaiveNumber(1)).promoteTo(numberClass).divide(power);
}
return power;
return integerValues.get(numType).get(n);
}
@Override

View File

@@ -0,0 +1,104 @@
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.NumberInterface;
import org.nwapw.abacus.plugin.StandardPlugin;
import org.nwapw.abacus.tree.TreeNode;
public class CalculationTests {
private static Abacus abacus = new Abacus(new Configuration("precise", new String[]{}));
@BeforeClass
public static void prepareTests(){
abacus.getPluginManager().addInstantiated(new StandardPlugin(abacus.getPluginManager()));
abacus.getPluginManager().load();
}
private void testOutput(String input, String parseOutput, String output){
TreeNode parsedTree = abacus.parseString(input);
Assert.assertNotNull(parsedTree);
Assert.assertEquals(parsedTree.toString(), parseOutput);
NumberInterface result = abacus.evaluateTree(parsedTree);
Assert.assertNotNull(result);
Assert.assertTrue(result.toString().startsWith(output));
}
private void testEvalError(String input, String parseOutput){
TreeNode parsedTree = abacus.parseString(input);
Assert.assertNotNull(parsedTree);
Assert.assertEquals(parsedTree.toString(), parseOutput);
Assert.assertNull(abacus.evaluateTree(parsedTree));
}
@Test
public void testAddition(){
testOutput("9.5+10", "(9.5+10)", "19.5");
}
@Test
public void testSubtraction(){
testOutput("9.5-10", "(9.5-10)", "-0.5");
}
@Test
public void testMultiplication(){
testOutput("9.5*10", "(9.5*10)", "95");
}
@Test
public void testDivision(){
testOutput("9.5/2", "(9.5/2)", "4.75");
}
@Test
public void testNegation(){
testOutput("-9.5", "(9.5)`", "-9.5");
}
@Test
public void testFactorial(){
testOutput("7!", "(7)!", "5040");
}
@Test
public void testAbs(){
testOutput("abs(-1)", "abs((1)`)", "1");
testOutput("abs(1)", "abs(1)", "1");
}
@Test
public void testLn(){
testEvalError("ln(-1)", "ln((1)`)");
testOutput("ln2", "ln(2)", "0.6931471805599453094172321214581765680755");
}
@Test
public void testSqrt(){
testOutput("sqrt0", "sqrt(0)", "0");
testOutput("sqrt4", "sqrt(4)", "2");
testOutput("sqrt2", "sqrt(2)", "1.4142135623730950488016887242096980785696");
}
@Test
public void testExp(){
testOutput("exp0", "exp(0)", "1");
testOutput("exp1", "exp(1)", "2.718281828459045235360287471352662497757247");
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
}
@Test
public void testPow(){
testOutput("0^2", "(0^2)", "0");
testOutput("2^0", "(2^0)", "1");
testOutput("2^1", "(2^1)", "2");
testOutput("2^-1", "(2^(1)`)", "0.5");
testOutput("2^50", "(2^50)", "112589990684262");
}
}

View File

@@ -4,6 +4,7 @@ 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.function.Function;
import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.function.OperatorAssociativity;
@@ -18,7 +19,7 @@ import java.util.List;
public class TokenizerTests {
private static Abacus abacus = new Abacus();
private static Abacus abacus = new Abacus(new Configuration("precise", new String[]{}));
private static LexerTokenizer lexerTokenizer = new LexerTokenizer();
private static Function subtractFunction = new Function() {
@Override