mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-25 16:15:19 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb02984e60 | |||
| a9ac4681f0 | |||
| 62d7053441 | |||
| f3cbb600ac | |||
| abc0e2d59f | |||
| f7d1be086b | |||
| 21a925d6d2 | |||
| 0d21898f20 | |||
| 508e98413d | |||
| d06f611a2e | |||
| c541eaab97 | |||
| 0058ec9c71 | |||
|
|
f8bf60f383 | ||
|
|
4369eba107 | ||
| 385a0c960d | |||
|
|
d7ae1a80f1 | ||
|
|
e4a45c0ec4 | ||
| 22cf99d23d | |||
| 39b36f84e0 | |||
| b036b6c242 | |||
| eb3410f854 | |||
| f967053e3d | |||
| 75824a2a77 | |||
| da602876e7 | |||
|
|
8df468e04a | ||
|
|
e029ab1fea | ||
| af56d31723 | |||
|
|
eff7be0204 | ||
| 34ae4b42c6 | |||
| b680215f57 | |||
| e6cc08043e | |||
| 3e10ea223f | |||
| 44c52b412c | |||
| eb51d5d3e4 | |||
| 8ae28f2dab | |||
| 0bade4a7df | |||
|
|
9d5f9d901c | ||
| 0f02867a4e | |||
|
|
dad546c5b5 | ||
| f0e1b85dcf | |||
| 37261c2f58 | |||
| 691118c206 | |||
|
|
95845a1585 | ||
| 819fff6391 | |||
|
|
8cf0c94947 | ||
| 20f6e0b0b2 | |||
| 4056013d1f | |||
| c7b5d4c4fc | |||
| be28e26607 | |||
| 2f1ed5f0d1 | |||
| 2615273d28 | |||
| 6e1d2ce629 | |||
| 44b8efd9bc | |||
| 2502c90837 | |||
| e49f28a850 | |||
| 88e4a87d81 | |||
| cda09518c3 | |||
| 56510d97de | |||
|
|
86533d53c9 | ||
|
|
9b71f9aaf4 | ||
|
|
cf953da40a | ||
|
|
27ad10c0f1 | ||
|
|
601c4fea55 | ||
| c2ae0b4138 | |||
| 16938b4e06 | |||
|
|
52fbfd5134 | ||
|
|
b31151384d | ||
| 21d88fe256 | |||
| 3d61ead0f6 |
@@ -5,10 +5,10 @@ Summer project for NWAPW.
|
|||||||
Created by Arthur Drobot, Danila Fedorin and Riley Jones.
|
Created by Arthur Drobot, Danila Fedorin and Riley Jones.
|
||||||
|
|
||||||
## Project Description
|
## Project Description
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
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.
|
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.
|
||||||
- [x] Basic number class
|
- [x] Basic number class
|
||||||
- [x] Implementation of basic functions
|
- [x] Implementation of basic functions
|
||||||
- [x] Implementation of `exp`, `ln`, `sqrt` using the basic functions and Taylor Series
|
- [x] Implementation of `exp`, `ln`, `sqrt` using the basic functions and Taylor Series
|
||||||
|
|||||||
@@ -2,21 +2,16 @@ package org.nwapw.abacus;
|
|||||||
|
|
||||||
import org.nwapw.abacus.config.Configuration;
|
import org.nwapw.abacus.config.Configuration;
|
||||||
import org.nwapw.abacus.fx.AbacusApplication;
|
import org.nwapw.abacus.fx.AbacusApplication;
|
||||||
import org.nwapw.abacus.number.NaiveNumber;
|
|
||||||
import org.nwapw.abacus.number.NumberInterface;
|
import org.nwapw.abacus.number.NumberInterface;
|
||||||
import org.nwapw.abacus.parsing.LexerTokenizer;
|
import org.nwapw.abacus.parsing.LexerTokenizer;
|
||||||
import org.nwapw.abacus.parsing.ShuntingYardParser;
|
import org.nwapw.abacus.parsing.ShuntingYardParser;
|
||||||
import org.nwapw.abacus.parsing.TreeBuilder;
|
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.PluginManager;
|
||||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||||
import org.nwapw.abacus.tree.NumberReducer;
|
import org.nwapw.abacus.tree.NumberReducer;
|
||||||
import org.nwapw.abacus.tree.TreeNode;
|
import org.nwapw.abacus.tree.TreeNode;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main calculator class. This is responsible
|
* The main calculator class. This is responsible
|
||||||
* for piecing together all of the components, allowing
|
* for piecing together all of the components, allowing
|
||||||
@@ -25,13 +20,9 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
public class Abacus {
|
public class Abacus {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation to use for the number representation.
|
* The default number implementation to be used if no other one is available / selected.
|
||||||
*/
|
*/
|
||||||
public static final Class<? extends NumberInterface> DEFAULT_NUMBER = NaiveNumber.class;
|
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
|
* The plugin manager responsible for
|
||||||
@@ -56,25 +47,16 @@ public class Abacus {
|
|||||||
/**
|
/**
|
||||||
* Creates a new instance of the Abacus calculator.
|
* Creates a new instance of the Abacus calculator.
|
||||||
*/
|
*/
|
||||||
public Abacus() {
|
public Abacus(Configuration configuration) {
|
||||||
pluginManager = new PluginManager();
|
pluginManager = new PluginManager(this);
|
||||||
numberReducer = new NumberReducer(this);
|
numberReducer = new NumberReducer(this);
|
||||||
configuration = new Configuration(CONFIG_FILE);
|
this.configuration = new Configuration(configuration);
|
||||||
configuration.saveTo(CONFIG_FILE);
|
|
||||||
LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||||
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
|
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
|
||||||
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
|
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
|
||||||
|
|
||||||
pluginManager.addListener(lexerTokenizer);
|
|
||||||
pluginManager.addListener(shuntingYardParser);
|
pluginManager.addListener(shuntingYardParser);
|
||||||
pluginManager.addInstantiated(new StandardPlugin(pluginManager));
|
pluginManager.addListener(lexerTokenizer);
|
||||||
try {
|
|
||||||
ClassFinder.loadJars("plugins")
|
|
||||||
.forEach(plugin -> pluginManager.addClass(plugin));
|
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
pluginManager.load();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -147,15 +129,10 @@ public class Abacus {
|
|||||||
* @return the resulting number.
|
* @return the resulting number.
|
||||||
*/
|
*/
|
||||||
public NumberInterface numberFromString(String numberString) {
|
public NumberInterface numberFromString(String numberString) {
|
||||||
Class<? extends NumberInterface> toInstantiate =
|
NumberImplementation toInstantiate =
|
||||||
pluginManager.numberFor(configuration.getNumberImplementation());
|
pluginManager.numberImplementationFor(configuration.getNumberImplementation());
|
||||||
if (toInstantiate == null) toInstantiate = DEFAULT_NUMBER;
|
if (toInstantiate == null) toInstantiate = DEFAULT_IMPLEMENTATION;
|
||||||
|
|
||||||
try {
|
return toInstantiate.instanceForString(numberString);
|
||||||
return toInstantiate.getConstructor(String.class).newInstance(numberString);
|
|
||||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import com.moandjiezana.toml.TomlWriter;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration object that stores
|
* The configuration object that stores
|
||||||
@@ -12,52 +15,78 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public class Configuration {
|
public class Configuration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The defaults TOML string.
|
||||||
|
*/
|
||||||
|
private static final String DEFAULT_CONFIG =
|
||||||
|
"numberImplementation = \"naive\"\n" +
|
||||||
|
"disabledPlugins = []";
|
||||||
|
/**
|
||||||
|
* The defaults TOML object, parsed from the string.
|
||||||
|
*/
|
||||||
|
private static final Toml DEFAULT_TOML = new Toml().read(DEFAULT_CONFIG);
|
||||||
/**
|
/**
|
||||||
* The TOML writer used to write this configuration to a file.
|
* The TOML writer used to write this configuration to a file.
|
||||||
*/
|
*/
|
||||||
private static final TomlWriter TOML_WRITER = new TomlWriter();
|
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();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of the number that should be used.
|
* The implementation of the number that should be used.
|
||||||
*/
|
*/
|
||||||
private String numberImplementation = "naive";
|
private String numberImplementation = "<default>";
|
||||||
|
/**
|
||||||
|
* The list of disabled plugins in this 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.
|
* Creates a new configuration with the given values.
|
||||||
|
*
|
||||||
* @param numberImplementation the number implementation, like "naive" or "precise"
|
* @param numberImplementation the number implementation, like "naive" or "precise"
|
||||||
|
* @param disabledPlugins the list of disabled plugins.
|
||||||
*/
|
*/
|
||||||
public Configuration(String numberImplementation){
|
public Configuration(String numberImplementation, String[] disabledPlugins) {
|
||||||
this.numberImplementation = numberImplementation;
|
this.numberImplementation = numberImplementation;
|
||||||
|
this.disabledPlugins.addAll(Arrays.asList(disabledPlugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a configuration from a given file, keeping non-specified fields default.
|
* Loads a configuration from a given file, keeping non-specified fields default.
|
||||||
|
*
|
||||||
* @param fromFile the file to load from.
|
* @param fromFile the file to load from.
|
||||||
*/
|
*/
|
||||||
public Configuration(File fromFile){
|
public Configuration(File fromFile) {
|
||||||
if(!fromFile.exists()) return;
|
if (!fromFile.exists()) return;
|
||||||
copyFrom(TOML_READER.read(fromFile).to(Configuration.class));
|
copyFrom(new Toml(DEFAULT_TOML).read(fromFile).to(Configuration.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the values from the given configuration into this one.
|
* Copies the values from the given configuration into this one.
|
||||||
|
*
|
||||||
* @param otherConfiguration the configuration to copy from.
|
* @param otherConfiguration the configuration to copy from.
|
||||||
*/
|
*/
|
||||||
public void copyFrom(Configuration otherConfiguration){
|
public void copyFrom(Configuration otherConfiguration) {
|
||||||
this.numberImplementation = otherConfiguration.numberImplementation;
|
this.numberImplementation = otherConfiguration.numberImplementation;
|
||||||
|
this.disabledPlugins.addAll(otherConfiguration.disabledPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves this configuration to the given file, creating
|
* Saves this configuration to the given file, creating
|
||||||
* any directories that do not exist.
|
* any directories that do not exist.
|
||||||
|
*
|
||||||
* @param file the file to save to.
|
* @param file the file to save to.
|
||||||
*/
|
*/
|
||||||
public void saveTo(File file){
|
public void saveTo(File file) {
|
||||||
if(file.getParentFile() != null) file.getParentFile().mkdirs();
|
if (file.getParentFile() != null) file.getParentFile().mkdirs();
|
||||||
try {
|
try {
|
||||||
TOML_WRITER.write(this, file);
|
TOML_WRITER.write(this, file);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -65,8 +94,18 @@ 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.
|
* Gets the number implementation from this configuration.
|
||||||
|
*
|
||||||
* @return the number implementation.
|
* @return the number implementation.
|
||||||
*/
|
*/
|
||||||
public String getNumberImplementation() {
|
public String getNumberImplementation() {
|
||||||
@@ -75,9 +114,20 @@ public class Configuration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the number implementation for the configuration
|
* Sets the number implementation for the configuration
|
||||||
|
*
|
||||||
* @param numberImplementation the number implementation.
|
* @param numberImplementation the number implementation.
|
||||||
*/
|
*/
|
||||||
public void setNumberImplementation(String numberImplementation) {
|
public void setNumberImplementation(String numberImplementation) {
|
||||||
this.numberImplementation = numberImplementation;
|
this.numberImplementation = numberImplementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of disabled plugins.
|
||||||
|
*
|
||||||
|
* @return the list of disabled plugins.
|
||||||
|
*/
|
||||||
|
public Set<String> getDisabledPlugins() {
|
||||||
|
return disabledPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package org.nwapw.abacus.function;
|
package org.nwapw.abacus.function;
|
||||||
|
|
||||||
import org.nwapw.abacus.number.NaiveNumber;
|
|
||||||
import org.nwapw.abacus.number.NumberInterface;
|
import org.nwapw.abacus.number.NumberInterface;
|
||||||
import org.nwapw.abacus.number.PreciseNumber;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that operates on one or more
|
* A function that operates on one or more
|
||||||
@@ -12,15 +8,6 @@ import java.util.HashMap;
|
|||||||
*/
|
*/
|
||||||
public abstract class Function {
|
public abstract class Function {
|
||||||
|
|
||||||
/**
|
|
||||||
* A map to correctly promote different number implementations to each other.
|
|
||||||
*/
|
|
||||||
private static final HashMap<Class<? extends NumberInterface>, Integer> priorityMap =
|
|
||||||
new HashMap<Class<? extends NumberInterface>, Integer>() {{
|
|
||||||
put(NaiveNumber.class, 0);
|
|
||||||
put(PreciseNumber.class, 1);
|
|
||||||
}};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given params will work for the given function.
|
* Checks whether the given params will work for the given function.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,22 +1,51 @@
|
|||||||
package org.nwapw.abacus.fx;
|
package org.nwapw.abacus.fx;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.control.cell.CheckBoxListCell;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
import org.nwapw.abacus.Abacus;
|
import org.nwapw.abacus.Abacus;
|
||||||
|
import org.nwapw.abacus.config.Configuration;
|
||||||
import org.nwapw.abacus.number.NumberInterface;
|
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 org.nwapw.abacus.tree.TreeNode;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The controller for the abacus FX UI, responsible
|
* The controller for the abacus FX UI, responsible
|
||||||
* for all the user interaction.
|
* for all the user interaction.
|
||||||
*/
|
*/
|
||||||
public class AbacusController {
|
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.
|
||||||
|
*/
|
||||||
|
private static final String APPLY_MSG_TITLE = "\"Apply\" Needed";
|
||||||
|
/**
|
||||||
|
* The text for the header of the apply alert dialog.
|
||||||
|
*/
|
||||||
|
private static final String APPLY_MSG_HEADER = "The settings have not been applied.";
|
||||||
|
/**
|
||||||
|
* The text for the dialog that is shown if settings haven't been applied.
|
||||||
|
*/
|
||||||
|
private static final String APPLY_MSG_TEXT = "You have made changes to the configuration, however, you haven't pressed \"Apply\". " +
|
||||||
|
"The changes to the configuration will not be present in the calculator until \"Apply\" is pressed.";
|
||||||
/**
|
/**
|
||||||
* Constant string that is displayed if the text could not be lexed or parsed.
|
* Constant string that is displayed if the text could not be lexed or parsed.
|
||||||
*/
|
*/
|
||||||
@@ -25,7 +54,16 @@ public class AbacusController {
|
|||||||
* Constant string that is displayed if the tree could not be reduced.
|
* Constant string that is displayed if the tree could not be reduced.
|
||||||
*/
|
*/
|
||||||
private static final String ERR_EVAL = "Evaluation Error";
|
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 TabPane coreTabPane;
|
||||||
|
@FXML
|
||||||
|
private Tab calculateTab;
|
||||||
|
@FXML
|
||||||
|
private Tab settingsTab;
|
||||||
@FXML
|
@FXML
|
||||||
private TableView<HistoryModel> historyTable;
|
private TableView<HistoryModel> historyTable;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -42,6 +80,8 @@ public class AbacusController {
|
|||||||
private Button inputButton;
|
private Button inputButton;
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<String> numberImplementationBox;
|
private ComboBox<String> numberImplementationBox;
|
||||||
|
@FXML
|
||||||
|
private ListView<ToggleablePlugin> enabledPluginView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of history entries, created by the users.
|
* The list of history entries, created by the users.
|
||||||
@@ -54,57 +94,220 @@ public class AbacusController {
|
|||||||
*/
|
*/
|
||||||
private ObservableList<String> numberImplementationOptions;
|
private ObservableList<String> numberImplementationOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <<<<<<< HEAD
|
||||||
|
* The list of plugin objects that can be toggled on and off,
|
||||||
|
* and, when reloaded, get added to the plugin manager's black list.
|
||||||
|
*/
|
||||||
|
private ObservableList<ToggleablePlugin> enabledPlugins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abacus instance used for changing the plugin configuration.
|
||||||
|
*/
|
||||||
private Abacus abacus;
|
private Abacus abacus;
|
||||||
|
/**
|
||||||
|
* Thread used for calculating.
|
||||||
|
*/
|
||||||
|
private Thread calcThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the calculator is calculating.
|
||||||
|
*/
|
||||||
|
private boolean calculating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seconds delayed for timer;
|
||||||
|
*/
|
||||||
|
private double delay = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean which represents whether changes were made to the configuration.
|
||||||
|
*/
|
||||||
|
private boolean changesMade;
|
||||||
|
/**
|
||||||
|
* Whether an alert about changes to the configuration was already shown.
|
||||||
|
*/
|
||||||
|
private boolean reloadAlertShown;
|
||||||
|
/**
|
||||||
|
* The alert shown when a press to "apply" is needed.
|
||||||
|
*/
|
||||||
|
private Alert reloadAlert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alerts the user if the changes they made
|
||||||
|
* have not yet been applied.
|
||||||
|
*/
|
||||||
|
private void alertIfApplyNeeded(boolean ignorePrevious) {
|
||||||
|
if (changesMade && (!reloadAlertShown || ignorePrevious)) {
|
||||||
|
reloadAlertShown = true;
|
||||||
|
reloadAlert.showAndWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize(){
|
public void initialize() {
|
||||||
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
|
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
|
||||||
param -> new CopyableCell<>();
|
param -> new CopyableCell<>();
|
||||||
|
Callback<ListView<ToggleablePlugin>, ListCell<ToggleablePlugin>> pluginCellFactory =
|
||||||
|
param -> new CheckBoxListCell<>(ToggleablePlugin::enabledProperty, new StringConverter<ToggleablePlugin>() {
|
||||||
|
@Override
|
||||||
|
public String toString(ToggleablePlugin object) {
|
||||||
|
return object.getClassName().substring(object.getClassName().lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ToggleablePlugin fromString(String string) {
|
||||||
|
return new ToggleablePlugin(true, string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
historyData = FXCollections.observableArrayList();
|
historyData = FXCollections.observableArrayList();
|
||||||
historyTable.setItems(historyData);
|
historyTable.setItems(historyData);
|
||||||
numberImplementationOptions = FXCollections.observableArrayList();
|
numberImplementationOptions = FXCollections.observableArrayList();
|
||||||
numberImplementationBox.setItems(numberImplementationOptions);
|
numberImplementationBox.setItems(numberImplementationOptions);
|
||||||
numberImplementationBox.valueProperty().addListener((observable, oldValue, newValue)
|
numberImplementationBox.getSelectionModel().selectedIndexProperty().addListener(e -> changesMade = true);
|
||||||
-> {
|
|
||||||
abacus.getConfiguration().setNumberImplementation(newValue);
|
|
||||||
abacus.getConfiguration().saveTo(Abacus.CONFIG_FILE);
|
|
||||||
});
|
|
||||||
historyTable.getSelectionModel().setCellSelectionEnabled(true);
|
historyTable.getSelectionModel().setCellSelectionEnabled(true);
|
||||||
|
enabledPlugins = FXCollections.observableArrayList();
|
||||||
|
enabledPluginView.setItems(enabledPlugins);
|
||||||
|
enabledPluginView.setCellFactory(pluginCellFactory);
|
||||||
inputColumn.setCellFactory(cellFactory);
|
inputColumn.setCellFactory(cellFactory);
|
||||||
inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
|
inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
|
||||||
parsedColumn.setCellFactory(cellFactory);
|
parsedColumn.setCellFactory(cellFactory);
|
||||||
parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
|
parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
|
||||||
outputColumn.setCellFactory(cellFactory);
|
outputColumn.setCellFactory(cellFactory);
|
||||||
outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
|
outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
|
||||||
|
coreTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
|
||||||
|
});
|
||||||
|
|
||||||
abacus = new Abacus();
|
abacus = new Abacus(new Configuration(CONFIG_FILE));
|
||||||
numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumbers());
|
PluginManager abacusPluginManager = abacus.getPluginManager();
|
||||||
String actualImplementation = abacus.getConfiguration().getNumberImplementation();
|
abacusPluginManager.addListener(this);
|
||||||
String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "naive";
|
abacusPluginManager.addInstantiated(new StandardPlugin(abacus.getPluginManager()));
|
||||||
numberImplementationBox.getSelectionModel().select(toSelect);
|
try {
|
||||||
|
ClassFinder.loadJars("plugins").forEach(abacusPluginManager::addClass);
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
abacusPluginManager.reload();
|
||||||
|
|
||||||
|
changesMade = false;
|
||||||
|
reloadAlertShown = false;
|
||||||
|
|
||||||
|
reloadAlert = new Alert(Alert.AlertType.WARNING);
|
||||||
|
reloadAlert.setTitle(APPLY_MSG_TITLE);
|
||||||
|
reloadAlert.setHeaderText(APPLY_MSG_HEADER);
|
||||||
|
reloadAlert.setContentText(APPLY_MSG_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void performCalculation(){
|
private void performCalculation() {
|
||||||
inputButton.setDisable(true);
|
Runnable calculator = new Runnable() {
|
||||||
TreeNode constructedTree = abacus.parseString(inputField.getText());
|
public void run() {
|
||||||
if(constructedTree == null){
|
if (delay > 0) {
|
||||||
outputText.setText(ERR_SYNTAX);
|
Runnable timer = new Runnable() {
|
||||||
inputButton.setDisable(false);
|
public void run() {
|
||||||
return;
|
long gap = (long) (delay * 1000);
|
||||||
}
|
long startTime = System.currentTimeMillis();
|
||||||
NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
|
while (System.currentTimeMillis() - startTime <= gap) {
|
||||||
if(evaluatedNumber == null){
|
}
|
||||||
outputText.setText(ERR_EVAL);
|
stopCalculation();
|
||||||
inputButton.setDisable(false);
|
}
|
||||||
return;
|
};
|
||||||
}
|
Thread maxTime = new Thread(timer);
|
||||||
outputText.setText(evaluatedNumber.toString());
|
maxTime.setName("maxTime");
|
||||||
historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), evaluatedNumber.toString()));
|
maxTime.start();
|
||||||
|
}
|
||||||
|
calculating = true;
|
||||||
|
Platform.runLater(() -> 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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> outputText.setText(evaluatedNumber.toString()));
|
||||||
|
historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), evaluatedNumber.toString()));
|
||||||
|
|
||||||
inputButton.setDisable(false);
|
Platform.runLater(() -> inputButton.setDisable(false));
|
||||||
inputField.setText("");
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performSaveAndReload() {
|
||||||
|
performSave();
|
||||||
|
performReload();
|
||||||
|
changesMade = false;
|
||||||
|
reloadAlertShown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performReload() {
|
||||||
|
alertIfApplyNeeded(true);
|
||||||
|
abacus.getPluginManager().reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performSave() {
|
||||||
|
Configuration configuration = abacus.getConfiguration();
|
||||||
|
configuration.setNumberImplementation(numberImplementationBox.getSelectionModel().getSelectedItem());
|
||||||
|
Set<String> disabledPlugins = configuration.getDisabledPlugins();
|
||||||
|
disabledPlugins.clear();
|
||||||
|
for (ToggleablePlugin pluginEntry : enabledPlugins) {
|
||||||
|
if (!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
|
||||||
|
}
|
||||||
|
configuration.saveTo(CONFIG_FILE);
|
||||||
|
changesMade = false;
|
||||||
|
reloadAlertShown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad(PluginManager manager) {
|
||||||
|
Configuration configuration = abacus.getConfiguration();
|
||||||
|
Set<String> disabledPlugins = configuration.getDisabledPlugins();
|
||||||
|
numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumberImplementations());
|
||||||
|
String actualImplementation = configuration.getNumberImplementation();
|
||||||
|
String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "<default>";
|
||||||
|
numberImplementationBox.getSelectionModel().select(toSelect);
|
||||||
|
for (Class<?> pluginClass : abacus.getPluginManager().getLoadedPluginClasses()) {
|
||||||
|
String fullName = pluginClass.getName();
|
||||||
|
ToggleablePlugin plugin = new ToggleablePlugin(!disabledPlugins.contains(fullName), fullName);
|
||||||
|
plugin.enabledProperty().addListener(e -> changesMade = true);
|
||||||
|
enabledPlugins.add(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnload(PluginManager manager) {
|
||||||
|
enabledPlugins.clear();
|
||||||
|
numberImplementationOptions.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.awt.datatransfer.StringSelection;
|
|||||||
/**
|
/**
|
||||||
* A cell that copies its value to the clipboard
|
* A cell that copies its value to the clipboard
|
||||||
* when double clicked.
|
* when double clicked.
|
||||||
|
*
|
||||||
* @param <S> The type of the table view generic type.
|
* @param <S> The type of the table view generic type.
|
||||||
* @param <T> The type of the value contained in the cell.
|
* @param <T> The type of the value contained in the cell.
|
||||||
*/
|
*/
|
||||||
@@ -17,9 +18,9 @@ public class CopyableCell<S, T> extends TableCell<S, T> {
|
|||||||
/**
|
/**
|
||||||
* Creates a new copyable cell.
|
* Creates a new copyable cell.
|
||||||
*/
|
*/
|
||||||
public CopyableCell(){
|
public CopyableCell() {
|
||||||
addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
||||||
if(event.getClickCount() == 2){
|
if (event.getClickCount() == 2) {
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard()
|
Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
.setContents(new StringSelection(getText()), null);
|
.setContents(new StringSelection(getText()), null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ public class HistoryModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new history model with the given variables.
|
* Creates a new history model with the given variables.
|
||||||
* @param input the user input
|
*
|
||||||
|
* @param input the user input
|
||||||
* @param parsed the parsed input
|
* @param parsed the parsed input
|
||||||
* @param output the program output.
|
* @param output the program output.
|
||||||
*/
|
*/
|
||||||
public HistoryModel(String input, String parsed, String output){
|
public HistoryModel(String input, String parsed, String output) {
|
||||||
this.input = new SimpleStringProperty();
|
this.input = new SimpleStringProperty();
|
||||||
this.parsed = new SimpleStringProperty();
|
this.parsed = new SimpleStringProperty();
|
||||||
this.output = new SimpleStringProperty();
|
this.output = new SimpleStringProperty();
|
||||||
@@ -41,13 +42,16 @@ public class HistoryModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the input property.
|
* Gets the input property.
|
||||||
|
*
|
||||||
* @return the input property.
|
* @return the input property.
|
||||||
*/
|
*/
|
||||||
public StringProperty inputProperty() {
|
public StringProperty inputProperty() {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the input.
|
* Gets the input.
|
||||||
|
*
|
||||||
* @return the input.
|
* @return the input.
|
||||||
*/
|
*/
|
||||||
public String getInput() {
|
public String getInput() {
|
||||||
@@ -56,13 +60,16 @@ public class HistoryModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the parsed input property.
|
* Gets the parsed input property.
|
||||||
|
*
|
||||||
* @return the parsed input property.
|
* @return the parsed input property.
|
||||||
*/
|
*/
|
||||||
public StringProperty parsedProperty() {
|
public StringProperty parsedProperty() {
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the parsed input.
|
* Gets the parsed input.
|
||||||
|
*
|
||||||
* @return the parsed input.
|
* @return the parsed input.
|
||||||
*/
|
*/
|
||||||
public String getParsed() {
|
public String getParsed() {
|
||||||
@@ -71,13 +78,16 @@ public class HistoryModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the output property.
|
* Gets the output property.
|
||||||
|
*
|
||||||
* @return the output property.
|
* @return the output property.
|
||||||
*/
|
*/
|
||||||
public StringProperty outputProperty() {
|
public StringProperty outputProperty() {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the program output.
|
* Gets the program output.
|
||||||
|
*
|
||||||
* @return the output.
|
* @return the output.
|
||||||
*/
|
*/
|
||||||
public String getOutput() {
|
public String getOutput() {
|
||||||
|
|||||||
60
src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
Normal file
60
src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package org.nwapw.abacus.fx;
|
||||||
|
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents an entry in the plugin check box list.
|
||||||
|
* The changes from this property are written to the config on application.
|
||||||
|
*/
|
||||||
|
public class ToggleablePlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property that determines whether the plugin will be enabled.
|
||||||
|
*/
|
||||||
|
private final BooleanProperty enabled;
|
||||||
|
/**
|
||||||
|
* The name of the class this entry toggles.
|
||||||
|
*/
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new toggleable plugin with the given properties.
|
||||||
|
*
|
||||||
|
* @param enabled the enabled / disabled state at the beginning.
|
||||||
|
* @param className the name of the class this plugin toggles.
|
||||||
|
*/
|
||||||
|
public ToggleablePlugin(boolean enabled, String className) {
|
||||||
|
this.enabled = new SimpleBooleanProperty();
|
||||||
|
this.enabled.setValue(enabled);
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the enabled property of this plugin.
|
||||||
|
*
|
||||||
|
* @return the enabled property.
|
||||||
|
*/
|
||||||
|
public BooleanProperty enabledProperty() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this plugin entry should be enabled.
|
||||||
|
*
|
||||||
|
* @return whether this plugin will be enabled.
|
||||||
|
*/
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class name this plugin toggles.
|
||||||
|
*
|
||||||
|
* @return the class name that should be disabled.
|
||||||
|
*/
|
||||||
|
public String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ public class NaiveNumber implements NumberInterface {
|
|||||||
public NaiveNumber(String value) {
|
public NaiveNumber(String value) {
|
||||||
this(Double.parseDouble(value));
|
this(Double.parseDouble(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new NaiveNumber with the given value.
|
* Creates a new NaiveNumber with the given value.
|
||||||
*
|
*
|
||||||
@@ -94,8 +95,23 @@ public class NaiveNumber implements NumberInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int ceiling() {
|
public NumberInterface ceiling() {
|
||||||
return (int) Math.ceil(value);
|
return new NaiveNumber(Math.ceil(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface floor() {
|
||||||
|
return new NaiveNumber(Math.floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface fractionalPart() {
|
||||||
|
return new NaiveNumber(value - Math.floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return (int) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -81,9 +81,32 @@ public interface NumberInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the least integer greater than or equal to the number.
|
* Returns the least integer greater than or equal to the number.
|
||||||
|
*
|
||||||
* @return the least integer >= the number, if int can hold the value.
|
* @return the least integer >= the number, if int can hold the value.
|
||||||
*/
|
*/
|
||||||
int ceiling();
|
NumberInterface ceiling();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the greatest integer less than or equal to the number.
|
||||||
|
*
|
||||||
|
* @return the greatest int >= the number, if int can hold the value.
|
||||||
|
*/
|
||||||
|
NumberInterface floor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fractional part of the number.
|
||||||
|
*
|
||||||
|
* @return the fractional part of the number.
|
||||||
|
*/
|
||||||
|
NumberInterface fractionalPart();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the integer representation of this number, discarding any fractional part,
|
||||||
|
* if int can hold the value.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int intValue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promotes this class to another number class.
|
* Promotes this class to another number class.
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ public class PreciseNumber implements NumberInterface {
|
|||||||
/**
|
/**
|
||||||
* The number one.
|
* The number one.
|
||||||
*/
|
*/
|
||||||
static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
public static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
||||||
/**
|
/**
|
||||||
* The number zero.
|
* The number zero.
|
||||||
*/
|
*/
|
||||||
static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
public static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
||||||
/**
|
/**
|
||||||
* The number ten.
|
* The number ten.
|
||||||
*/
|
*/
|
||||||
static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value of the PreciseNumber.
|
* The value of the PreciseNumber.
|
||||||
@@ -99,8 +99,38 @@ public class PreciseNumber implements NumberInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int ceiling() {
|
public NumberInterface ceiling() {
|
||||||
return (int) Math.ceil(value.doubleValue());
|
String str = value.toPlainString();
|
||||||
|
int decimalIndex = str.indexOf('.');
|
||||||
|
if (decimalIndex != -1) {
|
||||||
|
return this.floor().add(ONE);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface floor() {
|
||||||
|
String str = value.toPlainString();
|
||||||
|
int decimalIndex = str.indexOf('.');
|
||||||
|
if (decimalIndex != -1) {
|
||||||
|
return new PreciseNumber(str.substring(0, decimalIndex));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface fractionalPart() {
|
||||||
|
String str = value.toPlainString();
|
||||||
|
int decimalIndex = str.indexOf('.');
|
||||||
|
if (decimalIndex != -1) {
|
||||||
|
return new PreciseNumber(str.substring(decimalIndex + 1));
|
||||||
|
}
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return value.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tokenString.equals("-") && (previousType == null || previousType == TokenType.OP ||
|
if (tokenString.equals("-") && (previousType == null || previousType == TokenType.OP ||
|
||||||
previousType == TokenType.OPEN_PARENTH)){
|
previousType == TokenType.OPEN_PARENTH)) {
|
||||||
from.add(0, new Match<>("`", TokenType.OP));
|
from.add(0, new Match<>("`", TokenType.OP));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
|||||||
if (!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break;
|
if (!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break;
|
||||||
|
|
||||||
if (otherMatchType == TokenType.OP) {
|
if (otherMatchType == TokenType.OP) {
|
||||||
int otherPrecedence = precedenceMap.get(match.getContent());
|
int otherPrecedence = precedenceMap.get(otherMatch.getContent());
|
||||||
if (otherPrecedence < precedence ||
|
if (otherPrecedence < precedence ||
|
||||||
(associativity == OperatorAssociativity.RIGHT && otherPrecedence == precedence)) {
|
(associativity == OperatorAssociativity.RIGHT && otherPrecedence == precedence)) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package org.nwapw.abacus.plugin;
|
||||||
|
|
||||||
|
import org.nwapw.abacus.number.NumberInterface;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that holds data about a number implementation.
|
||||||
|
*/
|
||||||
|
public abstract class NumberImplementation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of paths through which this implementation can be promoted.
|
||||||
|
*/
|
||||||
|
protected Map<Class<? extends NumberInterface>, Function<NumberInterface, NumberInterface>> promotionPaths;
|
||||||
|
/**
|
||||||
|
* The implementation class for this implementation.
|
||||||
|
*/
|
||||||
|
private Class<? extends NumberInterface> implementation;
|
||||||
|
/**
|
||||||
|
* The priority of converting into this number implementation.
|
||||||
|
*/
|
||||||
|
private int priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public NumberImplementation(Class<? extends NumberInterface> implementation, int priority) {
|
||||||
|
this.implementation = implementation;
|
||||||
|
this.priority = priority;
|
||||||
|
promotionPaths = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of all promotion paths this implementation can take.
|
||||||
|
*
|
||||||
|
* @return the map of documentation paths.
|
||||||
|
*/
|
||||||
|
public final Map<Class<? extends NumberInterface>, Function<NumberInterface, NumberInterface>> getPromotionPaths() {
|
||||||
|
return promotionPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the implementation class used by this implementation.
|
||||||
|
*
|
||||||
|
* @return the implementation class.
|
||||||
|
*/
|
||||||
|
public final Class<? extends NumberInterface> getImplementation() {
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the priority of this number implementation.
|
||||||
|
*
|
||||||
|
* @return the priority.
|
||||||
|
*/
|
||||||
|
public final int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract function to create a new instance from a string.
|
||||||
|
*
|
||||||
|
* @param string the string to create a number from.
|
||||||
|
* @return the resulting number.
|
||||||
|
*/
|
||||||
|
public abstract NumberInterface instanceForString(String string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instance of pi with the given implementation.
|
||||||
|
*
|
||||||
|
* @return pi
|
||||||
|
*/
|
||||||
|
public abstract NumberInterface instanceForPi();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,9 +26,9 @@ public abstract class Plugin {
|
|||||||
*/
|
*/
|
||||||
private Map<String, Operator> operators;
|
private Map<String, Operator> operators;
|
||||||
/**
|
/**
|
||||||
* A hash map of operators mapped to their string names.
|
* The map of the number implementations this plugin provides.
|
||||||
*/
|
*/
|
||||||
private Map<String, Class<? extends NumberInterface>> numbers;
|
private Map<String, NumberImplementation> numberImplementations;
|
||||||
/**
|
/**
|
||||||
* The plugin manager in which to search for functions
|
* The plugin manager in which to search for functions
|
||||||
* not inside this package,
|
* not inside this package,
|
||||||
@@ -51,7 +51,7 @@ public abstract class Plugin {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
functions = new HashMap<>();
|
functions = new HashMap<>();
|
||||||
operators = new HashMap<>();
|
operators = new HashMap<>();
|
||||||
numbers = new HashMap<>();
|
numberImplementations = new HashMap<>();
|
||||||
enabled = false;
|
enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,12 +74,12 @@ public abstract class Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the list of all numbers provided by this plugin.
|
* Gets the list of number implementations provided by this plugin.
|
||||||
*
|
*
|
||||||
* @return the list of registered numbers.
|
* @return the list of registered number implementations.
|
||||||
*/
|
*/
|
||||||
public final Set<String> providedNumbers() {
|
public final Set<String> providedNumberImplementations() {
|
||||||
return numbers.keySet();
|
return numberImplementations.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,13 +103,13 @@ public abstract class Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the class under the given name.
|
* Gets the number implementation under the given name.
|
||||||
*
|
*
|
||||||
* @param numberName the name of the class.
|
* @param name the name of the number implementation to look up.
|
||||||
* @return the class, or null if the plugin doesn't provide it.
|
* @return the number implementation associated with that name, or null if the plugin doesn't provide it.
|
||||||
*/
|
*/
|
||||||
public final Class<? extends NumberInterface> getNumber(String numberName) {
|
public final NumberImplementation getNumberImplementation(String name) {
|
||||||
return numbers.get(numberName);
|
return numberImplementations.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,16 +158,14 @@ public abstract class Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be used in load(). Registers a number class
|
* To be used in load(). Registers a new number implementation with the plugin.
|
||||||
* with the plugin internally, which makes it possible
|
* This makes it accessible to the plugin manager.
|
||||||
* for the user to select it as an "implementation" for the
|
|
||||||
* number that they would like to use.
|
|
||||||
*
|
*
|
||||||
* @param name the name to register it under.
|
* @param name the name of the implementation.
|
||||||
* @param toRegister the class to register.
|
* @param implementation the actual implementation class to register.
|
||||||
*/
|
*/
|
||||||
protected final void registerNumber(String name, Class<? extends NumberInterface> toRegister) {
|
protected final void registerNumberImplementation(String name, NumberImplementation implementation) {
|
||||||
numbers.put(name, toRegister);
|
numberImplementations.put(name, implementation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,6 +192,31 @@ public abstract class Plugin {
|
|||||||
return manager.operatorFor(name);
|
return manager.operatorFor(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches the PluginManager for the given number implementation
|
||||||
|
* name. This can be used by the plugins internally in order to find
|
||||||
|
* implementations that they do not provide.
|
||||||
|
*
|
||||||
|
* @param name the name for which to search.
|
||||||
|
* @return the resulting number implementation, or null if none was found.
|
||||||
|
*/
|
||||||
|
protected final NumberImplementation numberImplementationFor(String name) {
|
||||||
|
return manager.numberImplementationFor(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches the plugin manager for a Pi value for the given number implementation.
|
||||||
|
* This is done so that number implementations with various degrees of precision
|
||||||
|
* can provide their own pi values, without losing said precision by
|
||||||
|
* promoting NaiveNumbers.
|
||||||
|
*
|
||||||
|
* @param forClass the class to which to find the pi instance.
|
||||||
|
* @return the pi value for the given class.
|
||||||
|
*/
|
||||||
|
protected final NumberInterface getPi(Class<? extends NumberInterface> forClass) {
|
||||||
|
return manager.piFor(forClass);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method to be overridden by plugin implementation, in which the plugins
|
* Abstract method to be overridden by plugin implementation, in which the plugins
|
||||||
* are supposed to register the functions they provide and do any other
|
* are supposed to register the functions they provide and do any other
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.nwapw.abacus.plugin;
|
package org.nwapw.abacus.plugin;
|
||||||
|
|
||||||
|
import org.nwapw.abacus.Abacus;
|
||||||
import org.nwapw.abacus.function.Function;
|
import org.nwapw.abacus.function.Function;
|
||||||
import org.nwapw.abacus.function.Operator;
|
import org.nwapw.abacus.function.Operator;
|
||||||
import org.nwapw.abacus.number.NumberInterface;
|
import org.nwapw.abacus.number.NumberInterface;
|
||||||
@@ -32,10 +33,19 @@ public class PluginManager {
|
|||||||
*/
|
*/
|
||||||
private Map<String, Operator> cachedOperators;
|
private Map<String, Operator> cachedOperators;
|
||||||
/**
|
/**
|
||||||
* List of registered number implementations that have
|
* The list of number implementations that have
|
||||||
* been cached, that is, found in a plugin and returned.
|
* been cached, that is, found in a plugin and returned.
|
||||||
*/
|
*/
|
||||||
private Map<String, Class<? extends NumberInterface>> cachedNumbers;
|
private Map<String, NumberImplementation> cachedNumberImplementations;
|
||||||
|
/**
|
||||||
|
* The list of number implementations that have been
|
||||||
|
* found by their implementation class.
|
||||||
|
*/
|
||||||
|
private Map<Class<? extends NumberInterface>, NumberImplementation> cachedInterfaceImplementations;
|
||||||
|
/**
|
||||||
|
* The pi values for each implementation class that have already been computer.
|
||||||
|
*/
|
||||||
|
private Map<Class<? extends NumberInterface>, NumberInterface> cachedPi;
|
||||||
/**
|
/**
|
||||||
* List of all functions loaded by the plugins.
|
* List of all functions loaded by the plugins.
|
||||||
*/
|
*/
|
||||||
@@ -45,26 +55,34 @@ public class PluginManager {
|
|||||||
*/
|
*/
|
||||||
private Set<String> allOperators;
|
private Set<String> allOperators;
|
||||||
/**
|
/**
|
||||||
* List of all numbers loaded by the plugins.
|
* List of all the number implementations loaded by the plugins.
|
||||||
*/
|
*/
|
||||||
private Set<String> allNumbers;
|
private Set<String> allNumberImplementations;
|
||||||
/**
|
/**
|
||||||
* The list of plugin listeners attached to this instance.
|
* The list of plugin listeners attached to this instance.
|
||||||
*/
|
*/
|
||||||
private Set<PluginListener> listeners;
|
private Set<PluginListener> listeners;
|
||||||
|
/**
|
||||||
|
* The abacus instance used to access other
|
||||||
|
* components of the application.
|
||||||
|
*/
|
||||||
|
private Abacus abacus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new plugin manager.
|
* Creates a new plugin manager.
|
||||||
*/
|
*/
|
||||||
public PluginManager() {
|
public PluginManager(Abacus abacus) {
|
||||||
|
this.abacus = abacus;
|
||||||
loadedPluginClasses = new HashSet<>();
|
loadedPluginClasses = new HashSet<>();
|
||||||
plugins = new HashSet<>();
|
plugins = new HashSet<>();
|
||||||
cachedFunctions = new HashMap<>();
|
cachedFunctions = new HashMap<>();
|
||||||
cachedOperators = new HashMap<>();
|
cachedOperators = new HashMap<>();
|
||||||
cachedNumbers = new HashMap<>();
|
cachedNumberImplementations = new HashMap<>();
|
||||||
|
cachedInterfaceImplementations = new HashMap<>();
|
||||||
|
cachedPi = new HashMap<>();
|
||||||
allFunctions = new HashSet<>();
|
allFunctions = new HashSet<>();
|
||||||
allOperators = new HashSet<>();
|
allOperators = new HashSet<>();
|
||||||
allNumbers = new HashSet<>();
|
allNumberImplementations = new HashSet<>();
|
||||||
listeners = new HashSet<>();
|
listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +98,13 @@ public class PluginManager {
|
|||||||
* @param getFunction the function to get the T value under the given name
|
* @param getFunction the function to get the T value under the given name
|
||||||
* @param name the name to search for
|
* @param name the name to search for
|
||||||
* @param <T> the type of element being search
|
* @param <T> the type of element being search
|
||||||
|
* @param <K> the type of key that the cache is indexed by.
|
||||||
* @return the retrieved element, or null if it was not found.
|
* @return the retrieved element, or null if it was not found.
|
||||||
*/
|
*/
|
||||||
private static <T> T searchCached(Collection<Plugin> plugins, Map<String, T> cache,
|
private static <T, K> T searchCached(Collection<Plugin> plugins, Map<K, T> cache,
|
||||||
java.util.function.Function<Plugin, Set<String>> setFunction,
|
java.util.function.Function<Plugin, Set<K>> setFunction,
|
||||||
java.util.function.BiFunction<Plugin, String, T> getFunction,
|
java.util.function.BiFunction<Plugin, K, T> getFunction,
|
||||||
String name) {
|
K name) {
|
||||||
if (cache.containsKey(name)) return cache.get(name);
|
if (cache.containsKey(name)) return cache.get(name);
|
||||||
|
|
||||||
T loadedValue = null;
|
T loadedValue = null;
|
||||||
@@ -121,13 +140,54 @@ public class PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a numer implementation under the given name.
|
* Gets the number implementation under the given name.
|
||||||
*
|
*
|
||||||
* @param name the name of the implementation.
|
* @param name the name of the implementation.
|
||||||
* @return the implementation class
|
* @return the implementation.
|
||||||
*/
|
*/
|
||||||
public Class<? extends NumberInterface> numberFor(String name) {
|
public NumberImplementation numberImplementationFor(String name) {
|
||||||
return searchCached(plugins, cachedNumbers, Plugin::providedNumbers, Plugin::getNumber, name);
|
return searchCached(plugins, cachedNumberImplementations, Plugin::providedNumberImplementations,
|
||||||
|
Plugin::getNumberImplementation, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number implementation for the given implementation class.
|
||||||
|
*
|
||||||
|
* @param name the class for which to find the implementation.
|
||||||
|
* @return the implementation.
|
||||||
|
*/
|
||||||
|
public NumberImplementation interfaceImplementationFor(Class<? extends NumberInterface> name) {
|
||||||
|
if (cachedInterfaceImplementations.containsKey(name)) return cachedInterfaceImplementations.get(name);
|
||||||
|
NumberImplementation toReturn = null;
|
||||||
|
outside:
|
||||||
|
for (Plugin plugin : plugins) {
|
||||||
|
for (String implementationName : plugin.providedNumberImplementations()) {
|
||||||
|
NumberImplementation implementation = plugin.getNumberImplementation(implementationName);
|
||||||
|
if (implementation.getImplementation().equals(name)) {
|
||||||
|
toReturn = implementation;
|
||||||
|
break outside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedInterfaceImplementations.put(name, toReturn);
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mathematical constant pi for the given implementation class.
|
||||||
|
*
|
||||||
|
* @param forClass the class for which to find pi.
|
||||||
|
* @return pi
|
||||||
|
*/
|
||||||
|
public NumberInterface piFor(Class<? extends NumberInterface> forClass) {
|
||||||
|
if (cachedPi.containsKey(forClass)) return cachedPi.get(forClass);
|
||||||
|
NumberImplementation implementation = interfaceImplementationFor(forClass);
|
||||||
|
NumberInterface generatedPi = null;
|
||||||
|
if (implementation != null) {
|
||||||
|
generatedPi = implementation.instanceForPi();
|
||||||
|
}
|
||||||
|
cachedPi.put(forClass, generatedPi);
|
||||||
|
return generatedPi;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,11 +220,16 @@ public class PluginManager {
|
|||||||
* Loads all the plugins in the PluginManager.
|
* Loads all the plugins in the PluginManager.
|
||||||
*/
|
*/
|
||||||
public void load() {
|
public void load() {
|
||||||
for (Plugin plugin : plugins) plugin.enable();
|
Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
|
||||||
for (Plugin plugin : plugins) {
|
for (Plugin plugin : plugins) {
|
||||||
|
if (disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||||
|
plugin.enable();
|
||||||
|
}
|
||||||
|
for (Plugin plugin : plugins) {
|
||||||
|
if (disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||||
allFunctions.addAll(plugin.providedFunctions());
|
allFunctions.addAll(plugin.providedFunctions());
|
||||||
allOperators.addAll(plugin.providedOperators());
|
allOperators.addAll(plugin.providedOperators());
|
||||||
allNumbers.addAll(plugin.providedNumbers());
|
allNumberImplementations.addAll(plugin.providedNumberImplementations());
|
||||||
}
|
}
|
||||||
listeners.forEach(e -> e.onLoad(this));
|
listeners.forEach(e -> e.onLoad(this));
|
||||||
}
|
}
|
||||||
@@ -173,10 +238,20 @@ public class PluginManager {
|
|||||||
* Unloads all the plugins in the PluginManager.
|
* Unloads all the plugins in the PluginManager.
|
||||||
*/
|
*/
|
||||||
public void unload() {
|
public void unload() {
|
||||||
for (Plugin plugin : plugins) plugin.disable();
|
listeners.forEach(e -> e.onUnload(this));
|
||||||
|
Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
|
||||||
|
for (Plugin plugin : plugins) {
|
||||||
|
if (disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||||
|
plugin.disable();
|
||||||
|
}
|
||||||
|
cachedFunctions.clear();
|
||||||
|
cachedOperators.clear();
|
||||||
|
cachedNumberImplementations.clear();
|
||||||
|
cachedInterfaceImplementations.clear();
|
||||||
|
cachedPi.clear();
|
||||||
allFunctions.clear();
|
allFunctions.clear();
|
||||||
allOperators.clear();
|
allOperators.clear();
|
||||||
allNumbers.clear();
|
allNumberImplementations.clear();
|
||||||
listeners.forEach(e -> e.onUnload(this));
|
listeners.forEach(e -> e.onUnload(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +260,7 @@ public class PluginManager {
|
|||||||
*/
|
*/
|
||||||
public void reload() {
|
public void reload() {
|
||||||
unload();
|
unload();
|
||||||
reload();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,12 +282,12 @@ public class PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all the number implementations loaded by the Plugin Manager
|
* Gets all the number implementations loaded by the Plugin Manager.
|
||||||
*
|
*
|
||||||
* @return the set of all implementations that were loaded
|
* @return the set of all implementations that were loaded.
|
||||||
*/
|
*/
|
||||||
public Set<String> getAllNumbers() {
|
public Set<String> getAllNumberImplementations() {
|
||||||
return allNumbers;
|
return allNumberImplementations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -233,4 +308,13 @@ public class PluginManager {
|
|||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of all the plugin class files that have been
|
||||||
|
* added to the plugin manager.
|
||||||
|
*
|
||||||
|
* @return the list of all the added plugin classes.
|
||||||
|
*/
|
||||||
|
public Set<Class<?>> getLoadedPluginClasses() {
|
||||||
|
return loadedPluginClasses;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import java.util.function.BiFunction;
|
|||||||
*/
|
*/
|
||||||
public class StandardPlugin extends Plugin {
|
public class StandardPlugin extends Plugin {
|
||||||
|
|
||||||
private static HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> factorialLists = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The addition operator, +
|
* The addition operator, +
|
||||||
*/
|
*/
|
||||||
@@ -31,9 +29,13 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
NumberInterface sum = params[0];
|
NumberInterface sum = params[0];
|
||||||
for (int i = 1; i < params.length; i++) {
|
for (int i = 1; i < params.length; i++) {
|
||||||
sum = sum.add(params[i]);
|
sum = sum.add(params[i]);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
@@ -49,7 +51,10 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return params[0].subtract(params[1]);
|
return params[0].subtract(params[1]);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/**
|
/**
|
||||||
@@ -63,6 +68,8 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return params[0].negate();
|
return params[0].negate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -77,9 +84,13 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
NumberInterface product = params[0];
|
NumberInterface product = params[0];
|
||||||
for (int i = 1; i < params.length; i++) {
|
for (int i = 1; i < params.length; i++) {
|
||||||
product = product.multiply(params[i]);
|
product = product.multiply(params[i]);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
@@ -90,16 +101,14 @@ public class StandardPlugin extends Plugin {
|
|||||||
public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
|
public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesParams(NumberInterface[] params) {
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
return params.length >= 1;
|
return params.length == 2 && params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
NumberInterface product = params[0];
|
if (Thread.currentThread().isInterrupted())
|
||||||
for (int i = 1; i < params.length; i++) {
|
return null;
|
||||||
product = product.multiply(params[i]);
|
return params[0].divide(params[1]);
|
||||||
}
|
|
||||||
return product;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/**
|
/**
|
||||||
@@ -109,20 +118,26 @@ public class StandardPlugin extends Plugin {
|
|||||||
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesParams(NumberInterface[] params) {
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
return params.length == 1;
|
return params.length == 1
|
||||||
|
&& params[0].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
||||||
|
&& params[0].signum() >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
if (params[0].signum() == 0) {
|
if (params[0].signum() == 0) {
|
||||||
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
|
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
|
||||||
}
|
}
|
||||||
NumberInterface factorial = params[0];
|
NumberInterface factorial = params[0];
|
||||||
NumberInterface multiplier = params[0];
|
NumberInterface multiplier = params[0];
|
||||||
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
|
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
|
||||||
while ((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1) {
|
while (!Thread.currentThread().isInterrupted() && (multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))) != null && multiplier.signum() == 1) {
|
||||||
factorial = factorial.multiply(multiplier);
|
factorial = factorial.multiply(multiplier);
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return factorial;
|
return factorial;
|
||||||
/*if(!storedList.containsKey(params[0].getClass())){
|
/*if(!storedList.containsKey(params[0].getClass())){
|
||||||
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
|
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
|
||||||
@@ -131,20 +146,6 @@ public class StandardPlugin extends Plugin {
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/**
|
|
||||||
* The caret / pow operator, ^
|
|
||||||
*/
|
|
||||||
public static final Operator OP_CARET = new Operator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2, new Function() {
|
|
||||||
@Override
|
|
||||||
protected boolean matchesParams(NumberInterface[] params) {
|
|
||||||
return params.length == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
||||||
return FUNCTION_EXP.apply(FUNCTION_LN.apply(params[0]).multiply(params[1]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
* The absolute value function, abs(-3) = 3
|
* The absolute value function, abs(-3) = 3
|
||||||
*/
|
*/
|
||||||
@@ -156,83 +157,48 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return params[0].multiply((new NaiveNumber(params[0].signum())).promoteTo(params[0].getClass()));
|
return params[0].multiply((new NaiveNumber(params[0].signum())).promoteTo(params[0].getClass()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* The exponential function, exp(1) = e^1 = 2.71...
|
|
||||||
*/
|
|
||||||
public static final Function FUNCTION_EXP = new Function() {
|
|
||||||
@Override
|
|
||||||
protected boolean matchesParams(NumberInterface[] params) {
|
|
||||||
return params.length == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
||||||
NumberInterface maxError = getMaxError(params[0]);
|
|
||||||
int n = 0;
|
|
||||||
if(params[0].signum() <= 0){
|
|
||||||
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
|
|
||||||
while(FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0){
|
|
||||||
n++;
|
|
||||||
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
|
|
||||||
sum = sum.add(currentTerm);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
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 = 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;
|
|
||||||
do{
|
|
||||||
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n+1)));
|
|
||||||
n++;
|
|
||||||
nextNumerator = nextNumerator.multiply(params[0]);
|
|
||||||
left = left.multiply(params[0]);
|
|
||||||
NumberInterface nextN = (new NaiveNumber(n+1)).promoteTo(params[0].getClass());
|
|
||||||
right = right.multiply(nextN);
|
|
||||||
//System.out.println(left + ", " + right);
|
|
||||||
}
|
|
||||||
while(left.compareTo(right) > 0);
|
|
||||||
//System.out.println(n+1);
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* The natural log function.
|
* The natural log function.
|
||||||
*/
|
*/
|
||||||
public static final Function FUNCTION_LN = new Function() {
|
public static final Function FUNCTION_LN = new Function() {
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesParams(NumberInterface[] params) {
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
return params.length == 1;
|
return params.length == 1 && params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
NumberInterface param = params[0];
|
NumberInterface param = params[0];
|
||||||
int powersOf2 = 0;
|
int powersOf2 = 0;
|
||||||
while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo((new NaiveNumber(0.1)).promoteTo(param.getClass())) >= 0) {
|
NumberInterface check;
|
||||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) {
|
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) {
|
||||||
param = param.divide(new NaiveNumber(2).promoteTo(param.getClass()));
|
param = param.divide(new NaiveNumber(2).promoteTo(param.getClass()));
|
||||||
powersOf2++;
|
powersOf2++;
|
||||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
|
if ((check = param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))) == null || check.signum() != 1) {
|
||||||
break;
|
break;
|
||||||
//No infinite loop for you.
|
//No infinite loop for you.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass()));
|
param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass()));
|
||||||
powersOf2--;
|
powersOf2--;
|
||||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
|
if ((check = param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))) == null || check.signum() != 1) {
|
||||||
break;
|
break;
|
||||||
//No infinite loop for you.
|
//No infinite loop for you.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getLog2(param).multiply((new NaiveNumber(powersOf2)).promoteTo(param.getClass())).add(getLogPartialSum(param));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,16 +208,23 @@ public class StandardPlugin extends Plugin {
|
|||||||
* @return the partial sum.
|
* @return the partial sum.
|
||||||
*/
|
*/
|
||||||
private NumberInterface getLogPartialSum(NumberInterface x) {
|
private NumberInterface getLogPartialSum(NumberInterface x) {
|
||||||
|
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
NumberInterface maxError = getMaxError(x);
|
NumberInterface maxError = getMaxError(x);
|
||||||
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
||||||
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
||||||
int n = 1;
|
int n = 1;
|
||||||
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
|
NumberInterface check;
|
||||||
|
while (!Thread.currentThread().isInterrupted() && (check = FUNCTION_ABS.apply(currentTerm)) != null && check.compareTo(maxError) > 0) {
|
||||||
n++;
|
n++;
|
||||||
currentNumerator = currentNumerator.multiply(x).negate();
|
if ((currentNumerator = currentNumerator.multiply(x)) == null || (currentNumerator = currentNumerator.negate()) == null)
|
||||||
|
return null;
|
||||||
currentTerm = currentNumerator.divide(new NaiveNumber(n).promoteTo(x.getClass()));
|
currentTerm = currentNumerator.divide(new NaiveNumber(n).promoteTo(x.getClass()));
|
||||||
sum = sum.add(currentTerm);
|
sum = sum.add(currentTerm);
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +234,8 @@ public class StandardPlugin extends Plugin {
|
|||||||
* @return the value of log(2) with the appropriate precision.
|
* @return the value of log(2) with the appropriate precision.
|
||||||
*/
|
*/
|
||||||
private NumberInterface getLog2(NumberInterface number) {
|
private NumberInterface getLog2(NumberInterface number) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
NumberInterface maxError = getMaxError(number);
|
NumberInterface maxError = getMaxError(number);
|
||||||
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
|
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
|
||||||
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
||||||
@@ -269,13 +244,17 @@ public class StandardPlugin extends Plugin {
|
|||||||
NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
|
NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
|
||||||
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
|
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
|
||||||
int n = 0;
|
int n = 0;
|
||||||
while (a.compareTo(maxError) >= 1) {
|
while (!Thread.currentThread().isInterrupted() && a.compareTo(maxError) >= 1) {
|
||||||
n++;
|
n++;
|
||||||
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
|
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
|
||||||
b = b.divide((new NaiveNumber(4)).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()));
|
c = NaiveNumber.ONE.promoteTo(number.getClass()).divide((new NaiveNumber(n)).promoteTo(number.getClass()));
|
||||||
sum = sum.add(a.add(b).multiply(c));
|
NumberInterface check;
|
||||||
|
if (a == null || (check = a.add(b)) == null || (check = check.multiply(c)) == null || (sum = sum.add(check)) == null)
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -293,6 +272,218 @@ public class StandardPlugin extends Plugin {
|
|||||||
return OP_CARET.getFunction().apply(params[0], ((new NaiveNumber(0.5)).promoteTo(params[0].getClass())));
|
return OP_CARET.getFunction().apply(params[0], ((new NaiveNumber(0.5)).promoteTo(params[0].getClass())));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* The implementation for double-based naive numbers.
|
||||||
|
*/
|
||||||
|
public static final NumberImplementation IMPLEMENTATION_NAIVE = new NumberImplementation(NaiveNumber.class, 0) {
|
||||||
|
@Override
|
||||||
|
public NumberInterface instanceForString(String string) {
|
||||||
|
return new NaiveNumber(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface instanceForPi() {
|
||||||
|
return new NaiveNumber(Math.PI);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The implementation for the infinite-precision BigDecimal.
|
||||||
|
*/
|
||||||
|
public static final NumberImplementation IMPLEMENTATION_PRECISE = new NumberImplementation(PreciseNumber.class, 0) {
|
||||||
|
@Override
|
||||||
|
public NumberInterface instanceForString(String string) {
|
||||||
|
return new PreciseNumber(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface instanceForPi() {
|
||||||
|
NumberInterface C = FUNCTION_SQRT.apply(new PreciseNumber("10005")).multiply(new PreciseNumber("426880"));
|
||||||
|
NumberInterface M = PreciseNumber.ONE;
|
||||||
|
NumberInterface L = new PreciseNumber("13591409");
|
||||||
|
NumberInterface X = M;
|
||||||
|
NumberInterface sum = L;
|
||||||
|
int termsNeeded = C.getMaxPrecision() / 13 + 1;
|
||||||
|
|
||||||
|
NumberInterface lSummand = new PreciseNumber("545140134");
|
||||||
|
NumberInterface xMultiplier = new PreciseNumber("262537412")
|
||||||
|
.multiply(new PreciseNumber("1000000000"))
|
||||||
|
.add(new PreciseNumber("640768000"))
|
||||||
|
.negate();
|
||||||
|
for (int i = 0; i < termsNeeded; i++) {
|
||||||
|
M = M
|
||||||
|
.multiply(new NaiveNumber(12 * i + 2).promoteTo(PreciseNumber.class))
|
||||||
|
.multiply(new NaiveNumber(12 * i + 6).promoteTo(PreciseNumber.class))
|
||||||
|
.multiply(new NaiveNumber(12 * i + 10).promoteTo(PreciseNumber.class))
|
||||||
|
.divide(new NaiveNumber(Math.pow(i + 1, 3)).promoteTo(PreciseNumber.class));
|
||||||
|
L = L.add(lSummand);
|
||||||
|
X = X.multiply(xMultiplier);
|
||||||
|
sum = sum.add(M.multiply(L).divide(X));
|
||||||
|
}
|
||||||
|
return C.divide(sum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static final HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> FACTORIAL_LISTS = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* The exponential function, exp(1) = e^1 = 2.71...
|
||||||
|
*/
|
||||||
|
public static final Function FUNCTION_EXP = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
NumberInterface maxError = getMaxError(params[0]);
|
||||||
|
int n = 0;
|
||||||
|
if (params[0].signum() <= 0) {
|
||||||
|
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
|
||||||
|
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
|
||||||
|
n++;
|
||||||
|
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
|
||||||
|
sum = sum.add(currentTerm);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
} 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 = 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;
|
||||||
|
do {
|
||||||
|
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n + 1)));
|
||||||
|
n++;
|
||||||
|
nextNumerator = nextNumerator.multiply(params[0]);
|
||||||
|
left = left.multiply(params[0]);
|
||||||
|
NumberInterface nextN = (new NaiveNumber(n + 1)).promoteTo(params[0].getClass());
|
||||||
|
right = right.multiply(nextN);
|
||||||
|
//System.out.println(left + ", " + right);
|
||||||
|
}
|
||||||
|
while (left.compareTo(right) > 0);
|
||||||
|
//System.out.println(n+1);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The caret / pow operator, ^
|
||||||
|
*/
|
||||||
|
public static final Operator OP_CARET = new Operator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2, new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 2
|
||||||
|
&& !(params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
||||||
|
&& params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if (Thread.currentThread().isInterrupted()) return null;
|
||||||
|
else if (params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0)
|
||||||
|
return NaiveNumber.ZERO.promoteTo(params[0].getClass());
|
||||||
|
else if (params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0)
|
||||||
|
return NaiveNumber.ONE.promoteTo(params[1].getClass());
|
||||||
|
FUNCTION_EXP.apply(FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0])).multiply(params[1]));
|
||||||
|
NumberInterface check = FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0]));
|
||||||
|
if(check == null) return null;
|
||||||
|
return FUNCTION_EXP.apply(check.multiply(params[1]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* The sine function (the argument is interpreted in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionSin = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
NumberInterface pi = getPi(params[0].getClass());
|
||||||
|
NumberInterface twoPi = pi.multiply(new NaiveNumber(2).promoteTo(pi.getClass()));
|
||||||
|
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) {
|
||||||
|
theta = pi.subtract(theta);
|
||||||
|
}
|
||||||
|
//System.out.println(theta);
|
||||||
|
return sinTaylor(theta);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The cosine function (the argument is in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionCos = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return functionSin.apply(getPi(params[0].getClass()).divide(new NaiveNumber(2).promoteTo(params[0].getClass()))
|
||||||
|
.subtract(params[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The tangent function (the argument is in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionTan = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return functionSin.apply(params[0]).divide(functionCos.apply(params[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The secant function (the argument is in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionSec = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionCos.apply(params[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The cosecant function (the argument is in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionCsc = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionSin.apply(params[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The cotangent function (the argument is in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionCot = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return functionCos.apply(params[0]).divide(functionCos.apply(params[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public StandardPlugin(PluginManager manager) {
|
public StandardPlugin(PluginManager manager) {
|
||||||
super(manager);
|
super(manager);
|
||||||
@@ -324,10 +515,94 @@ public class StandardPlugin extends Plugin {
|
|||||||
return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision());
|
return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @return a number of numClass with value n factorial.
|
||||||
|
*/
|
||||||
|
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
|
if (!FACTORIAL_LISTS.containsKey(numberClass)) {
|
||||||
|
FACTORIAL_LISTS.put(numberClass, new ArrayList<>());
|
||||||
|
FACTORIAL_LISTS.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
||||||
|
FACTORIAL_LISTS.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
||||||
|
}
|
||||||
|
ArrayList<NumberInterface> list = FACTORIAL_LISTS.get(numberClass);
|
||||||
|
if (n >= list.size()) {
|
||||||
|
while (!Thread.currentThread().isInterrupted() && 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the Taylor series for sin (centered at 0) at x.
|
||||||
|
*
|
||||||
|
* @param x where the series is evaluated.
|
||||||
|
* @return the value of the series
|
||||||
|
*/
|
||||||
|
private static NumberInterface sinTaylor(NumberInterface x) {
|
||||||
|
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
|
||||||
|
NumberInterface maxError = getMaxError(x);
|
||||||
|
int n = 1;
|
||||||
|
do {
|
||||||
|
n += 2;
|
||||||
|
power = power.multiply(multiplier);
|
||||||
|
currentTerm = power.divide(factorial(x.getClass(), n));
|
||||||
|
sum = sum.add(currentTerm);
|
||||||
|
} while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an equivalent angle in the interval [0, 2pi)
|
||||||
|
*
|
||||||
|
* @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(new NaiveNumber("2").promoteTo(phi.getClass()));
|
||||||
|
NumberInterface theta = FUNCTION_ABS.apply(phi).subtract(twoPi
|
||||||
|
.multiply(FUNCTION_ABS.apply(phi).divide(twoPi).floor())); //Now theta is in [0, 2pi).
|
||||||
|
if (phi.signum() < 0) {
|
||||||
|
theta = twoPi.subtract(theta);
|
||||||
|
}
|
||||||
|
return theta;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
registerNumber("naive", NaiveNumber.class);
|
registerNumberImplementation("naive", IMPLEMENTATION_NAIVE);
|
||||||
registerNumber("precise", PreciseNumber.class);
|
registerNumberImplementation("precise", IMPLEMENTATION_PRECISE);
|
||||||
|
|
||||||
registerOperator("+", OP_ADD);
|
registerOperator("+", OP_ADD);
|
||||||
registerOperator("-", OP_SUBTRACT);
|
registerOperator("-", OP_SUBTRACT);
|
||||||
@@ -341,26 +616,16 @@ public class StandardPlugin extends Plugin {
|
|||||||
registerFunction("exp", FUNCTION_EXP);
|
registerFunction("exp", FUNCTION_EXP);
|
||||||
registerFunction("ln", FUNCTION_LN);
|
registerFunction("ln", FUNCTION_LN);
|
||||||
registerFunction("sqrt", FUNCTION_SQRT);
|
registerFunction("sqrt", FUNCTION_SQRT);
|
||||||
|
registerFunction("sin", functionSin);
|
||||||
|
registerFunction("cos", functionCos);
|
||||||
|
registerFunction("tan", functionTan);
|
||||||
|
registerFunction("sec", functionSec);
|
||||||
|
registerFunction("csc", functionCsc);
|
||||||
|
registerFunction("cot", functionCot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n){
|
|
||||||
if(!factorialLists.containsKey(numberClass)){
|
|
||||||
factorialLists.put(numberClass, new ArrayList<>());
|
|
||||||
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(list.size() < n + 16){
|
|
||||||
list.add(list.get(list.size()-1).multiply(new NaiveNumber(list.size()).promoteTo(numberClass)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list.get(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,10 +92,15 @@ public class BinaryNode extends TreeNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T reduce(Reducer<T> reducer) {
|
public <T> T reduce(Reducer<T> reducer) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
T leftReduce = left.reduce(reducer);
|
T leftReduce = left.reduce(reducer);
|
||||||
T rightReduce = right.reduce(reducer);
|
T rightReduce = right.reduce(reducer);
|
||||||
if (leftReduce == null || rightReduce == null) return null;
|
if (leftReduce == null || rightReduce == null) return null;
|
||||||
return reducer.reduceNode(this, leftReduce, rightReduce);
|
T a = reducer.reduceNode(this, leftReduce, rightReduce);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -62,12 +62,17 @@ public class FunctionNode extends TreeNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T reduce(Reducer<T> reducer) {
|
public <T> T reduce(Reducer<T> reducer) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
Object[] reducedChildren = new Object[children.size()];
|
Object[] reducedChildren = new Object[children.size()];
|
||||||
for (int i = 0; i < reducedChildren.length; i++) {
|
for (int i = 0; i < reducedChildren.length; i++) {
|
||||||
reducedChildren[i] = children.get(i).reduce(reducer);
|
reducedChildren[i] = children.get(i).reduce(reducer);
|
||||||
if (reducedChildren[i] == null) return null;
|
if (Thread.currentThread().isInterrupted() || reducedChildren[i] == null) return null;
|
||||||
}
|
}
|
||||||
return reducer.reduceNode(this, reducedChildren);
|
T a = reducer.reduceNode(this, reducedChildren);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -33,9 +33,14 @@ public class UnaryNode extends TreeNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T reduce(Reducer<T> reducer) {
|
public <T> T reduce(Reducer<T> reducer) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
Object reducedChild = applyTo.reduce(reducer);
|
Object reducedChild = applyTo.reduce(reducer);
|
||||||
if (reducedChild == null) return null;
|
if (reducedChild == null) return null;
|
||||||
return reducer.reduceNode(this, reducedChild);
|
T a = reducer.reduceNode(this, reducedChild);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return null;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -250,6 +250,6 @@ public class Window extends JFrame {
|
|||||||
@Override
|
@Override
|
||||||
public void setVisible(boolean b) {
|
public void setVisible(boolean b) {
|
||||||
super.setVisible(b);
|
super.setVisible(b);
|
||||||
if(b) inputField.requestFocusInWindow();
|
if (b) inputField.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,14 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.BorderPane?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
|
||||||
<?import javafx.scene.text.Text?>
|
<?import javafx.scene.text.Text?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<BorderPane xmlns:fx="http://javafx.com/fxml"
|
||||||
<?import javafx.scene.layout.GridPane?>
|
xmlns="http://javafx.com/javafx"
|
||||||
<BorderPane xmlns="http://javafx.com/javafx"
|
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
|
||||||
fx:controller="org.nwapw.abacus.fx.AbacusController">
|
fx:controller="org.nwapw.abacus.fx.AbacusController">
|
||||||
<center>
|
<center>
|
||||||
<TabPane>
|
<TabPane fx:id="coreTabPane">
|
||||||
<Tab text="Calculator" closable="false">
|
<Tab fx:id="calculateTab" text="Calculator" closable="false">
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<center>
|
<center>
|
||||||
<TableView fx:id="historyTable">
|
<TableView fx:id="historyTable">
|
||||||
@@ -37,15 +34,28 @@
|
|||||||
<TextField fx:id="inputField" onAction="#performCalculation"/>
|
<TextField fx:id="inputField" onAction="#performCalculation"/>
|
||||||
<Button fx:id="inputButton" text="Calculate" maxWidth="Infinity"
|
<Button fx:id="inputButton" text="Calculate" maxWidth="Infinity"
|
||||||
onAction="#performCalculation"/>
|
onAction="#performCalculation"/>
|
||||||
|
<Button fx:id="stopButton" text="Stop" maxWidth="Infinity"
|
||||||
|
onAction="#stopCalculation"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
</bottom>
|
</bottom>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab text="Settings" closable="false">
|
<Tab fx:id="settingsTab" text="Settings" closable="false">
|
||||||
<GridPane hgap="10" vgap="10">
|
<GridPane hgap="10" vgap="10">
|
||||||
<padding><Insets left="10" right="10" top="10" bottom="10"/></padding>
|
<padding>
|
||||||
|
<Insets left="10" right="10" top="10" bottom="10"/>
|
||||||
|
</padding>
|
||||||
<Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
|
<Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
|
||||||
<ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
|
<ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
|
||||||
|
<ListView fx:id="enabledPluginView"
|
||||||
|
GridPane.rowIndex="1" GridPane.columnIndex="0"
|
||||||
|
GridPane.columnSpan="2" maxHeight="100"/>
|
||||||
|
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2" hgap="10"
|
||||||
|
vgap="10">
|
||||||
|
<Button text="Apply" onAction="#performSave"/>
|
||||||
|
<Button text="Reload Plugins" onAction="#performReload"/>
|
||||||
|
<Button text="Apply and Reload" onAction="#performSaveAndReload"/>
|
||||||
|
</FlowPane>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
</Tab>
|
</Tab>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|||||||
104
src/test/java/org/nwapw/abacus/tests/CalculationTests.java
Normal file
104
src/test/java/org/nwapw/abacus/tests/CalculationTests.java
Normal 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import org.junit.Assert;
|
|||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.nwapw.abacus.Abacus;
|
import org.nwapw.abacus.Abacus;
|
||||||
|
import org.nwapw.abacus.config.Configuration;
|
||||||
import org.nwapw.abacus.function.Function;
|
import org.nwapw.abacus.function.Function;
|
||||||
import org.nwapw.abacus.function.Operator;
|
import org.nwapw.abacus.function.Operator;
|
||||||
import org.nwapw.abacus.function.OperatorAssociativity;
|
import org.nwapw.abacus.function.OperatorAssociativity;
|
||||||
@@ -18,7 +19,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class TokenizerTests {
|
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 LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||||
private static Function subtractFunction = new Function() {
|
private static Function subtractFunction = new Function() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user