1
0
mirror of https://github.com/DanilaFe/abacus synced 2026-01-28 09:35:19 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
0be67410ec Add a binary number type based on the naive number. 2017-08-01 15:00:52 -07:00
14 changed files with 354 additions and 351 deletions

View File

@@ -5,10 +5,10 @@ Summer project for NWAPW.
Created by Arthur Drobot, Danila Fedorin and Riley Jones.
## Project Description
Abacus is a calculator built with extensibility and usability in mind. It provides a plugin interface, via Java, as Lua proves too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
Abacus is a calculator built with extensibility and usability in mind. It provides a plugin interface, via Java, as Lua provides too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
## Current State
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in teams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in treams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
- [x] Basic number class
- [x] Implementation of basic functions
- [x] Implementation of `exp`, `ln`, `sqrt` using the basic functions and Taylor Series

View File

@@ -1,6 +1,6 @@
package org.nwapw.abacus;
import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.config.ConfigurationObject;
import org.nwapw.abacus.fx.AbacusApplication;
import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.NumberInterface;
@@ -46,7 +46,7 @@ public class Abacus {
/**
* The configuration loaded from a file.
*/
private Configuration configuration;
private ConfigurationObject configuration;
/**
* The tree builder used to construct a tree
* from a string.
@@ -59,8 +59,8 @@ public class Abacus {
public Abacus() {
pluginManager = new PluginManager();
numberReducer = new NumberReducer(this);
configuration = new Configuration(CONFIG_FILE);
configuration.saveTo(CONFIG_FILE);
configuration = new ConfigurationObject(CONFIG_FILE);
configuration.save(CONFIG_FILE);
LexerTokenizer lexerTokenizer = new LexerTokenizer();
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
@@ -114,7 +114,7 @@ public class Abacus {
*
* @return the configuration object.
*/
public Configuration getConfiguration() {
public ConfigurationObject getConfiguration() {
return configuration;
}

View File

@@ -1,83 +1,14 @@
package org.nwapw.abacus.config;
import com.moandjiezana.toml.Toml;
import com.moandjiezana.toml.TomlWriter;
import java.io.File;
import java.io.IOException;
/**
* The configuration object that stores
* options that the user can change.
* Serializable class that will be used to load TOML
* configurations.
*/
public class Configuration {
/**
* The TOML writer used to write this configuration to a file.
* The type of number this calculator should use.
*/
private static final TomlWriter TOML_WRITER = new TomlWriter();
/**
* The TOML reader used to load this config from a file.
*/
private static final Toml TOML_READER = new Toml();
public String numberType;
/**
* The implementation of the number that should be used.
*/
private String numberImplementation = "naive";
/**
* Creates a new configuration with the given values.
* @param numberImplementation the number implementation, like "naive" or "precise"
*/
public Configuration(String numberImplementation){
this.numberImplementation = numberImplementation;
}
/**
* Loads a configuration from a given file, keeping non-specified fields default.
* @param fromFile the file to load from.
*/
public Configuration(File fromFile){
if(!fromFile.exists()) return;
copyFrom(TOML_READER.read(fromFile).to(Configuration.class));
}
/**
* Copies the values from the given configuration into this one.
* @param otherConfiguration the configuration to copy from.
*/
public void copyFrom(Configuration otherConfiguration){
this.numberImplementation = otherConfiguration.numberImplementation;
}
/**
* Saves this configuration to the given file, creating
* any directories that do not exist.
* @param file the file to save to.
*/
public void saveTo(File file){
if(file.getParentFile() != null) file.getParentFile().mkdirs();
try {
TOML_WRITER.write(this, file);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Gets the number implementation from this configuration.
* @return the number implementation.
*/
public String getNumberImplementation() {
return numberImplementation;
}
/**
* Sets the number implementation for the configuration
* @param numberImplementation the number implementation.
*/
public void setNumberImplementation(String numberImplementation) {
this.numberImplementation = numberImplementation;
}
}

View File

@@ -0,0 +1,111 @@
package org.nwapw.abacus.config;
import com.moandjiezana.toml.Toml;
import com.moandjiezana.toml.TomlWriter;
import java.io.File;
import java.io.IOException;
/**
* A configuration object, which essentially
* manages saving, loading, and getting values
* from the configuration. While Configuration is
* the data model, this is the interface with it.
*/
public class ConfigurationObject {
/**
* The writer used to store the configuration.
*/
private static final TomlWriter TOML_WRITER = new TomlWriter();
/**
* The configuration instance being modeled.
*/
private Configuration configuration;
/**
* Creates a new configuration object with the given config.
*
* @param config the config to use.
*/
public ConfigurationObject(Configuration config) {
setup(config);
}
/**
* Create a configuration object by attempting to
* load a config from the given path, using the
* default configuration otherwise.
*
* @param path the path to attempt to load.
*/
public ConfigurationObject(File path) {
Configuration config;
if (!path.exists()) {
config = getDefaultConfig();
} else {
Toml parse = new Toml();
parse.read(path);
config = parse.to(Configuration.class);
}
setup(config);
}
/**
* Creates a new configuration object with the
* default configuration.
*/
public ConfigurationObject() {
setup(getDefaultConfig());
}
/**
* Sets up the ConfigurationObject.
* different constructors do different things,
* but they all lead here.
*
* @param configuration the configuration to set up with.
*/
private void setup(Configuration configuration) {
this.configuration = configuration;
}
/**
* Creates a default configuration.
*
* @return the newly created default configuration.
*/
private Configuration getDefaultConfig() {
configuration = new Configuration();
configuration.numberType = "naive";
return configuration;
}
/**
* Returns the implementation the user has requested to
* represent their numbers.
*
* @return the implementation name.
*/
public String getNumberImplementation() {
return configuration.numberType;
}
/**
* Saves the ConfigurationObject to the given file.
*
* @param toFile the file to save ot.
* @return true if the save succeed, false if otherwise.
*/
public boolean save(File toFile) {
if (toFile.getParentFile() != null) toFile.getParentFile().mkdirs();
try {
TOML_WRITER.write(configuration, toFile);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -4,5 +4,5 @@ package org.nwapw.abacus.function;
* The type of an operator, describing how it should behave.
*/
public enum OperatorType {
BINARY_INFIX, UNARY_POSTFIX, UNARY_PREFIX
BINARY_INFIX, UNARY_POSTFIX
}

View File

@@ -1,6 +1,5 @@
package org.nwapw.abacus.fx;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
@@ -26,10 +25,7 @@ public class AbacusController {
* Constant string that is displayed if the tree could not be reduced.
*/
private static final String ERR_EVAL = "Evaluation Error";
/**
* Constant string that is displayed if the calculations are stopped before they are done.
*/
private static final String ERR_STOP = "Stopped";
@FXML
private TableView<HistoryModel> historyTable;
@FXML
@@ -44,8 +40,6 @@ public class AbacusController {
private TextField inputField;
@FXML
private Button inputButton;
@FXML
private ComboBox<String> numberImplementationBox;
/**
* The list of history entries, created by the users.
@@ -56,22 +50,6 @@ public class AbacusController {
* The abacus instance used for calculations and all
* other main processing code.
*/
private ObservableList<String> numberImplementationOptions;
/**
* Thread used for calculating.
*/
private Thread calcThread;
/**
* Checks whether the calculator is calculating.
*/
private boolean calculating;
/**
* Seconds delayed for timer;
*/
private double delay = 0;
private Abacus abacus;
@FXML
@@ -79,15 +57,9 @@ public class AbacusController {
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
param -> new CopyableCell<>();
abacus = new Abacus();
historyData = FXCollections.observableArrayList();
historyTable.setItems(historyData);
numberImplementationOptions = FXCollections.observableArrayList();
numberImplementationBox.setItems(numberImplementationOptions);
numberImplementationBox.valueProperty().addListener((observable, oldValue, newValue)
-> {
abacus.getConfiguration().setNumberImplementation(newValue);
abacus.getConfiguration().saveTo(Abacus.CONFIG_FILE);
});
historyTable.getSelectionModel().setCellSelectionEnabled(true);
inputColumn.setCellFactory(cellFactory);
inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
@@ -95,73 +67,28 @@ public class AbacusController {
parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
outputColumn.setCellFactory(cellFactory);
outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
abacus = new Abacus();
numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumbers());
String actualImplementation = abacus.getConfiguration().getNumberImplementation();
String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "naive";
numberImplementationBox.getSelectionModel().select(toSelect);
}
@FXML
private void performCalculation(){
Runnable calculator = new Runnable(){
public void run() {
if(delay>0) {
Runnable timer = new Runnable() {
public void run() {
long gap = (long) (delay * 1000);
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime <= gap) {
}
stopCalculation();
}
};
Thread maxTime = new Thread(timer);
maxTime.setName("maxTime");
maxTime.start();
}
calculating = true;
Platform.runLater(() -> inputButton.setDisable(true));
inputButton.setDisable(true);
TreeNode constructedTree = abacus.parseString(inputField.getText());
if (constructedTree == null) {
Platform.runLater(() ->outputText.setText(ERR_SYNTAX));
Platform.runLater(() -> inputButton.setDisable(false));
//return;
}else {
NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
if (evaluatedNumber == null) {
if(Thread.currentThread().isInterrupted()){
Platform.runLater(() -> outputText.setText(ERR_STOP));
Platform.runLater(() -> inputButton.setDisable(false));
}else {
Platform.runLater(() -> outputText.setText(ERR_EVAL));
Platform.runLater(() -> inputButton.setDisable(false));
//return;
if(constructedTree == null){
outputText.setText(ERR_SYNTAX);
inputButton.setDisable(false);
return;
}
} else {
Platform.runLater(() -> outputText.setText(evaluatedNumber.toString()));
NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
if(evaluatedNumber == null){
outputText.setText(ERR_EVAL);
inputButton.setDisable(false);
return;
}
outputText.setText(evaluatedNumber.toString());
historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), evaluatedNumber.toString()));
Platform.runLater(() -> inputButton.setDisable(false));
Platform.runLater(() -> inputField.setText(""));
}
}
calculating = false;
}
};
if(!calculating) {
calcThread = new Thread(calculator);
calcThread.setName("calcThread");
calcThread.start();
}
}
@FXML
private void stopCalculation(){
calcThread.interrupt();
calculating = false;
//Platform.runLater(() ->inputButton.setDisable(false));
inputButton.setDisable(false);
inputField.setText("");
}
}

View File

@@ -0,0 +1,162 @@
package org.nwapw.abacus.number;
public class BinaryNumber implements NumberInterface{
/**
* The number zero.
*/
public static final BinaryNumber ZERO = new BinaryNumber(0);
/**
* The number one.
*/
public static final BinaryNumber ONE = new BinaryNumber(1);
/**
* The value of this number.
*/
private double value;
/**
* Creates a new BinaryNumber with the given string.
*
* @param value the value, which will be parsed as a double.
*/
public BinaryNumber(String value) {
toStandard(value);
}
/**
*
*/
private void toStandard(String value) {
String before;
String after = "";
if(value.indexOf(".")==-1){
before=value;
}else{
before = value.substring(0,value.indexOf("."));
after = value.substring(value.indexOf(".")+1);
}
double sum = 0;
for(int it=0;before.length()>0;it++){
if(before.charAt(before.length()-1)=='1'){
sum+=Math.pow(2,it);
}
if(before.length()>1) {
before = before.substring(0, before.length()-1);
}else{
before = "";
}
}
for(int it=-1;after.length()>0;it--) {
if (after.charAt(0) == '1') {
sum += Math.pow(2, it);
}
if (after.length() > 1) {
after = after.substring(1);
} else {
after = "";
}
}
this.value = sum;
}
/**
* Creates a new BinaryNumber with the given value.
*
* @param value the value to use.
*/
public BinaryNumber(double value) {
toStandard(""+value);
}
@Override
public int getMaxPrecision() {
return 18;
}
@Override
public NumberInterface multiply(NumberInterface multiplier) {
return new BinaryNumber(value * ((BinaryNumber) multiplier).value);
}
@Override
public NumberInterface divide(NumberInterface divisor) {
return new BinaryNumber(value / ((BinaryNumber) divisor).value);
}
@Override
public NumberInterface add(NumberInterface summand) {
return new BinaryNumber(value + ((BinaryNumber) summand).value);
}
@Override
public NumberInterface subtract(NumberInterface subtrahend) {
return new BinaryNumber(value - ((BinaryNumber) subtrahend).value);
}
@Override
public NumberInterface negate() {
return new BinaryNumber(-value);
}
@Override
public NumberInterface intPow(int exponent) {
if (exponent == 0) {
return BinaryNumber.ONE;
}
boolean takeReciprocal = exponent < 0;
exponent = Math.abs(exponent);
NumberInterface power = this;
for (int currentExponent = 1; currentExponent < exponent; currentExponent++) {
power = power.multiply(this);
}
if (takeReciprocal) {
power = BinaryNumber.ONE.divide(power);
}
return power;
}
@Override
public int compareTo(NumberInterface number) {
BinaryNumber num = (BinaryNumber) number;
return Double.compare(value, num.value);
}
@Override
public int signum() {
return this.compareTo(ZERO);
}
@Override
public NumberInterface promoteTo(Class<? extends NumberInterface> toClass) {
if (toClass == this.getClass()) return this;
else if (toClass == PreciseNumber.class) {
return new PreciseNumber(Double.toString(value));
}
return null;
}
public String toString() {
double sum = 0;
double tempValue = Math.floor(value);
double fraction = value-tempValue;
for (int it=0;tempValue > 0;it++) {
if (tempValue % 2 == 1) {
sum+=Math.pow(10,it);
tempValue-=1;
}
tempValue=tempValue/2;
}
for (int it=0;fraction > 0;it--) {
if (fraction % 2 >= 1) {
sum+=Math.pow(10,it);
fraction-=1;
}
fraction=fraction*2;
}
double shiftBy = Math.pow(10, 10);
return Double.toString(Math.round(sum * shiftBy) / shiftBy);
}
}

View File

@@ -55,12 +55,9 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
public List<Match<TokenType>> intoPostfix(List<Match<TokenType>> from) {
ArrayList<Match<TokenType>> output = new ArrayList<>();
Stack<Match<TokenType>> tokenStack = new Stack<>();
TokenType previousType;
TokenType matchType = null;
while (!from.isEmpty()) {
Match<TokenType> match = from.remove(0);
previousType = matchType;
matchType = match.getType();
TokenType matchType = match.getType();
if (matchType == TokenType.NUM) {
output.add(match);
} else if (matchType == TokenType.FUNCTION) {
@@ -77,13 +74,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
continue;
}
if(tokenString.equals("-") && (previousType == null || previousType == TokenType.OP ||
previousType == TokenType.OPEN_PARENTH)){
from.add(0, new Match<>("`", TokenType.OP));
continue;
}
while (!tokenStack.empty() && type == OperatorType.BINARY_INFIX) {
while (!tokenStack.empty()) {
Match<TokenType> otherMatch = tokenStack.peek();
TokenType otherMatchType = otherMatch.getType();
if (!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break;
@@ -112,8 +103,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
}
while (!tokenStack.empty()) {
Match<TokenType> match = tokenStack.peek();
TokenType newMatchType = match.getType();
if (!(newMatchType == TokenType.OP || newMatchType == TokenType.FUNCTION)) return null;
TokenType matchType = match.getType();
if (!(matchType == TokenType.OP || matchType == TokenType.FUNCTION)) return null;
output.add(tokenStack.pop());
}
return output;
@@ -136,11 +127,11 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
TreeNode right = constructRecursive(matches);
TreeNode left = constructRecursive(matches);
if (left == null || right == null) return null;
else return new BinaryNode(operator, left, right);
else return new BinaryInfixNode(operator, left, right);
} else {
TreeNode applyTo = constructRecursive(matches);
if (applyTo == null) return null;
else return new UnaryNode(operator, applyTo);
else return new UnaryPrefixNode(operator, applyTo);
}
} else if (matchType == TokenType.NUM) {
return new NumberNode(abacus.numberFromString(match.getContent()));

View File

@@ -31,13 +31,9 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface sum = params[0];
for (int i = 1; i < params.length; i++) {
sum = sum.add(params[i]);
if(Thread.currentThread().isInterrupted())
return null;
}
return sum;
}
@@ -53,26 +49,7 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
return params[0].subtract(params[1]);
}
});
/**
* The negation operator, -
*/
public static final Operator OP_NEGATE = new Operator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0, new Function() {
@Override
protected boolean matchesParams(NumberInterface[] params) {
return params.length == 1;
}
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
return params[0].negate();
}
});
/**
@@ -86,13 +63,9 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface product = params[0];
for (int i = 1; i < params.length; i++) {
product = product.multiply(params[i]);
if(Thread.currentThread().isInterrupted())
return null;
}
return product;
}
@@ -103,14 +76,16 @@ public class StandardPlugin extends Plugin {
public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
@Override
protected boolean matchesParams(NumberInterface[] params) {
return params.length == 2;
return params.length >= 1;
}
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
return params[0].divide(params[1]);
NumberInterface product = params[0];
for (int i = 1; i < params.length; i++) {
product = product.multiply(params[i]);
}
return product;
}
});
/**
@@ -125,19 +100,15 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
if (params[0].signum() == 0) {
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
}
NumberInterface factorial = params[0];
NumberInterface multiplier = params[0];
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
while (!Thread.currentThread().isInterrupted()&&(multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass())))!=null&&multiplier.signum() == 1) {
while ((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1) {
factorial = factorial.multiply(multiplier);
}
if(Thread.currentThread().isInterrupted())
return null;
return factorial;
/*if(!storedList.containsKey(params[0].getClass())){
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
@@ -157,12 +128,7 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface check;
if((check = FUNCTION_EXP.apply(FUNCTION_LN.apply(params[0])))!=null&&(check = check.multiply(params[1]))!=null)
return check;
return null;
return FUNCTION_EXP.apply(FUNCTION_LN.apply(params[0]).multiply(params[1]));
}
});
/**
@@ -176,8 +142,6 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
return params[0].multiply((new NaiveNumber(params[0].signum())).promoteTo(params[0].getClass()));
}
};
@@ -192,17 +156,14 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface maxError = getMaxError(params[0]);
int n = 0;
if(params[0].signum() <= 0){
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
NumberInterface check;
while((check = FUNCTION_ABS.apply(currentTerm))!=null && (check.compareTo(maxError) > 0)){
while(FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0){
n++;
if(Thread.currentThread().isInterrupted()||(currentTerm = currentTerm.multiply(params[0]))==null||(currentTerm = currentTerm.divide((new NaiveNumber(n)).promoteTo(params[0].getClass())))==null||(sum = (sum.add(currentTerm)))==null)
return null;
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
sum = sum.add(currentTerm);
}
return sum;
}
@@ -211,28 +172,18 @@ 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())), right = maxError;
NumberInterface check;
if((check =intPow(new NaiveNumber(3).promoteTo(params[0].getClass()),params[0].getClass(),(new NaiveNumber(params[0].ceiling())).promoteTo(params[0].getClass())))==null)
return null;
NumberInterface left = params[0].multiply(check), right = maxError;
NumberInterface left = params[0].multiply((new NaiveNumber(3)).promoteTo(params[0].getClass()).intPow(params[0].ceiling())), right = maxError;
do{
if((check = factorial(params[0].getClass(),n+1))==null||(check = nextNumerator.divide(check))==null||(sum = sum.add(check))==null)
return null;
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n+1)));
n++;
if((nextNumerator = nextNumerator.multiply(params[0]))==null)
return null;
if((left = left.multiply(params[0]))==null)
return null;
nextNumerator = nextNumerator.multiply(params[0]);
left = left.multiply(params[0]);
NumberInterface nextN = (new NaiveNumber(n+1)).promoteTo(params[0].getClass());
if((right = right.multiply(nextN))==null)
return null;
right = right.multiply(nextN);
//System.out.println(left + ", " + right);
}
while(!Thread.currentThread().isInterrupted()&&left.compareTo(right) > 0);
while(left.compareTo(right) > 0);
//System.out.println(n+1);
if(Thread.currentThread().isInterrupted())
return null;
return sum;
}
}
@@ -248,32 +199,26 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface param = params[0];
int powersOf2 = 0;
NumberInterface check;
while (!Thread.currentThread().isInterrupted()&&(check = FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))))!=null&&(check.compareTo((new NaiveNumber(0.1)).promoteTo(param.getClass()))) >= 0) {
if ((check = param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())))!=null&&check.signum() == 1) {
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()));
powersOf2++;
if ((check = param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())))==null||check.signum() != 1) {
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()));
powersOf2--;
if ((check = param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())))==null||check.signum() != 1) {
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
break;
//No infinite loop for you.
}
}
}
NumberInterface check2;
if(!Thread.currentThread().isInterrupted()&&(check = getLog2(param))!=null&&(check = check.multiply((new NaiveNumber(powersOf2).promoteTo(param.getClass()))))!=null&&(check2 = getLogPartialSum(param))!=null&&(check = check.add(check2))!=null)
return check;
return null;
return getLog2(param).multiply((new NaiveNumber(powersOf2)).promoteTo(param.getClass())).add(getLogPartialSum(param));
}
/**
@@ -283,23 +228,16 @@ public class StandardPlugin extends Plugin {
* @return the partial sum.
*/
private NumberInterface getLogPartialSum(NumberInterface x) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface maxError = getMaxError(x);
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
int n = 1;
NumberInterface check;
while (!Thread.currentThread().isInterrupted()&&(check = FUNCTION_ABS.apply(currentTerm))!=null&&check.compareTo(maxError) > 0) {
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
n++;
if((currentNumerator = currentNumerator.multiply(x))==null||(currentNumerator = currentNumerator.negate())==null)
return null;
currentNumerator = currentNumerator.multiply(x).negate();
currentTerm = currentNumerator.divide(new NaiveNumber(n).promoteTo(x.getClass()));
sum = sum.add(currentTerm);
}
if(Thread.currentThread().isInterrupted())
return null;
return sum;
}
@@ -309,8 +247,6 @@ public class StandardPlugin extends Plugin {
* @return the value of log(2) with the appropriate precision.
*/
private NumberInterface getLog2(NumberInterface number) {
if(Thread.currentThread().isInterrupted())
return null;
NumberInterface maxError = getMaxError(number);
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
@@ -319,17 +255,13 @@ public class StandardPlugin extends Plugin {
NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
int n = 0;
while (!Thread.currentThread().isInterrupted()&&a.compareTo(maxError) >= 1) {
while (a.compareTo(maxError) >= 1) {
n++;
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
b = b.divide((new NaiveNumber(4)).promoteTo(number.getClass()));
c = NaiveNumber.ONE.promoteTo(number.getClass()).divide((new NaiveNumber(n)).promoteTo(number.getClass()));
NumberInterface check;
if(a==null||(check = a.add(b))==null||(check = check.multiply(c))==null||(sum = sum.add(check))==null)
return null;
sum = sum.add(a.add(b).multiply(c));
}
if(Thread.currentThread().isInterrupted())
return null;
return sum;
}
};
@@ -385,7 +317,6 @@ public class StandardPlugin extends Plugin {
registerOperator("+", OP_ADD);
registerOperator("-", OP_SUBTRACT);
registerOperator("`", OP_NEGATE);
registerOperator("*", OP_MULTIPLY);
registerOperator("/", OP_DIVIDE);
registerOperator("^", OP_CARET);
@@ -403,43 +334,18 @@ public class StandardPlugin extends Plugin {
}
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n){
if(Thread.currentThread().isInterrupted())
return null;
if(!factorialLists.containsKey(numberClass)){
factorialLists.put(numberClass, new ArrayList<>());
factorialLists.put(numberClass, new ArrayList<NumberInterface>());
factorialLists.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
factorialLists.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
}
ArrayList<NumberInterface> list = factorialLists.get(numberClass);
if(n >= list.size()){
while(!Thread.currentThread().isInterrupted()&&list.size() < n + 16){
while(list.size() < n + 16){
list.add(list.get(list.size()-1).multiply(new NaiveNumber(list.size()).promoteTo(numberClass)));
}
}
if(Thread.currentThread().isInterrupted())
return null;
return list.get(n);
}
public static NumberInterface intPow(NumberInterface number, Class<? extends NumberInterface> numberClass,NumberInterface exponent) {
if(Thread.currentThread().isInterrupted())
return null;
if (exponent.compareTo((new NaiveNumber(0)).promoteTo(numberClass))==0) {
return (new NaiveNumber(1)).promoteTo(numberClass);
}
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(Thread.currentThread().isInterrupted())
return null;
}
if (takeReciprocal) {
power = (new NaiveNumber(1)).promoteTo(numberClass).divide(power);
}
if(Thread.currentThread().isInterrupted())
return null;
return power;
}
}

View File

@@ -3,7 +3,7 @@ package org.nwapw.abacus.tree;
/**
* A tree node that represents an operation being applied to two operands.
*/
public class BinaryNode extends TreeNode {
public class BinaryInfixNode extends TreeNode {
/**
* The operation being applied.
@@ -18,7 +18,7 @@ public class BinaryNode extends TreeNode {
*/
private TreeNode right;
private BinaryNode() {
private BinaryInfixNode() {
}
/**
@@ -27,7 +27,7 @@ public class BinaryNode extends TreeNode {
*
* @param operation the operation.
*/
public BinaryNode(String operation) {
public BinaryInfixNode(String operation) {
this(operation, null, null);
}
@@ -39,7 +39,7 @@ public class BinaryNode extends TreeNode {
* @param left the left node of the expression.
* @param right the right node of the expression.
*/
public BinaryNode(String operation, TreeNode left, TreeNode right) {
public BinaryInfixNode(String operation, TreeNode left, TreeNode right) {
this.operation = operation;
this.left = left;
this.right = right;
@@ -92,15 +92,10 @@ public class BinaryNode extends TreeNode {
@Override
public <T> T reduce(Reducer<T> reducer) {
if(Thread.currentThread().isInterrupted())
return null;
T leftReduce = left.reduce(reducer);
T rightReduce = right.reduce(reducer);
if (leftReduce == null || rightReduce == null) return null;
T a = reducer.reduceNode(this, leftReduce, rightReduce);
if(Thread.currentThread().isInterrupted())
return null;
return a;
return reducer.reduceNode(this, leftReduce, rightReduce);
}
@Override

View File

@@ -62,17 +62,12 @@ public class FunctionNode extends TreeNode {
@Override
public <T> T reduce(Reducer<T> reducer) {
if(Thread.currentThread().isInterrupted())
return null;
Object[] reducedChildren = new Object[children.size()];
for (int i = 0; i < reducedChildren.length; i++) {
reducedChildren[i] = children.get(i).reduce(reducer);
if (Thread.currentThread().isInterrupted()||reducedChildren[i] == null) return null;
if (reducedChildren[i] == null) return null;
}
T a = reducer.reduceNode(this, reducedChildren);
if(Thread.currentThread().isInterrupted())
return null;
return a;
return reducer.reduceNode(this, reducedChildren);
}
@Override

View File

@@ -28,15 +28,15 @@ public class NumberReducer implements Reducer<NumberInterface> {
public NumberInterface reduceNode(TreeNode node, Object... children) {
if (node instanceof NumberNode) {
return ((NumberNode) node).getNumber();
} else if (node instanceof BinaryNode) {
} else if (node instanceof BinaryInfixNode) {
NumberInterface left = (NumberInterface) children[0];
NumberInterface right = (NumberInterface) children[1];
Function function = abacus.getPluginManager().operatorFor(((BinaryNode) node).getOperation()).getFunction();
Function function = abacus.getPluginManager().operatorFor(((BinaryInfixNode) node).getOperation()).getFunction();
if (function == null) return null;
return function.apply(left, right);
} else if (node instanceof UnaryNode) {
} else if (node instanceof UnaryPrefixNode) {
NumberInterface child = (NumberInterface) children[0];
Function functionn = abacus.getPluginManager().operatorFor(((UnaryNode) node).getOperation()).getFunction();
Function functionn = abacus.getPluginManager().operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction();
if (functionn == null) return null;
return functionn.apply(child);
} else if (node instanceof FunctionNode) {

View File

@@ -1,6 +1,6 @@
package org.nwapw.abacus.tree;
public class UnaryNode extends TreeNode {
public class UnaryPrefixNode extends TreeNode {
/**
* The operation this node will apply.
@@ -16,7 +16,7 @@ public class UnaryNode extends TreeNode {
*
* @param operation the operation for this node.
*/
public UnaryNode(String operation) {
public UnaryPrefixNode(String operation) {
this(operation, null);
}
@@ -26,21 +26,16 @@ public class UnaryNode extends TreeNode {
* @param operation the operation for this node.
* @param applyTo the node to apply the function to.
*/
public UnaryNode(String operation, TreeNode applyTo) {
public UnaryPrefixNode(String operation, TreeNode applyTo) {
this.operation = operation;
this.applyTo = applyTo;
}
@Override
public <T> T reduce(Reducer<T> reducer) {
if(Thread.currentThread().isInterrupted())
return null;
Object reducedChild = applyTo.reduce(reducer);
if (reducedChild == null) return null;
T a = reducer.reduceNode(this, reducedChild);
if(Thread.currentThread().isInterrupted())
return null;
return a;
return reducer.reduceNode(this, reducedChild);
}
/**

View File

@@ -5,8 +5,6 @@
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.GridPane?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.nwapw.abacus.fx.AbacusController">
@@ -37,19 +35,11 @@
<TextField fx:id="inputField" onAction="#performCalculation"/>
<Button fx:id="inputButton" text="Calculate" maxWidth="Infinity"
onAction="#performCalculation"/>
<Button fx:id="stopButton" text="Stop" maxWidth="Infinity"
onAction="#stopCalculation"/>
</VBox>
</bottom>
</BorderPane>
</Tab>
<Tab text="Settings" closable="false">
<GridPane hgap="10" vgap="10">
<padding><Insets left="10" right="10" top="10" bottom="10"/></padding>
<Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
</GridPane>
</Tab>
<Tab text="Settings" closable="false"/>
</TabPane>
</center>