mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-25 16:15:19 +00:00
Compare commits
1 Commits
sig-fig
...
binary-num
| Author | SHA1 | Date | |
|---|---|---|---|
| 0be67410ec |
@@ -5,10 +5,10 @@ Summer project for NWAPW.
|
||||
Created by Arthur Drobot, Danila Fedorin and Riley Jones.
|
||||
|
||||
## Project Description
|
||||
Abacus is a calculator built with extensibility and usability in mind. It provides a plugin interface, via Java, as Lua proves too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
|
||||
Abacus is a calculator built with extensibility and usability in mind. It provides a plugin interface, via Java, as Lua provides too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
|
||||
|
||||
## Current State
|
||||
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in teams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
|
||||
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in treams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
|
||||
- [x] Basic number class
|
||||
- [x] Implementation of basic functions
|
||||
- [x] Implementation of `exp`, `ln`, `sqrt` using the basic functions and Taylor Series
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
package org.nwapw.abacus;
|
||||
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.config.ConfigurationObject;
|
||||
import org.nwapw.abacus.fx.AbacusApplication;
|
||||
import org.nwapw.abacus.number.NaiveNumber;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.parsing.LexerTokenizer;
|
||||
import org.nwapw.abacus.parsing.ShuntingYardParser;
|
||||
import org.nwapw.abacus.parsing.TreeBuilder;
|
||||
import org.nwapw.abacus.plugin.NumberImplementation;
|
||||
import org.nwapw.abacus.plugin.ClassFinder;
|
||||
import org.nwapw.abacus.plugin.PluginManager;
|
||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||
import org.nwapw.abacus.tree.NumberReducer;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* The main calculator class. This is responsible
|
||||
* for piecing together all of the components, allowing
|
||||
@@ -20,9 +25,13 @@ import org.nwapw.abacus.tree.TreeNode;
|
||||
public class Abacus {
|
||||
|
||||
/**
|
||||
* The default number implementation to be used if no other one is available / selected.
|
||||
* The default implementation to use for the number representation.
|
||||
*/
|
||||
public static final NumberImplementation DEFAULT_IMPLEMENTATION = StandardPlugin.IMPLEMENTATION_NAIVE;
|
||||
public static final Class<? extends NumberInterface> DEFAULT_NUMBER = NaiveNumber.class;
|
||||
/**
|
||||
* The file used for saving and loading configuration.
|
||||
*/
|
||||
public static final File CONFIG_FILE = new File("config.toml");
|
||||
|
||||
/**
|
||||
* The plugin manager responsible for
|
||||
@@ -37,7 +46,7 @@ public class Abacus {
|
||||
/**
|
||||
* The configuration loaded from a file.
|
||||
*/
|
||||
private Configuration configuration;
|
||||
private ConfigurationObject configuration;
|
||||
/**
|
||||
* The tree builder used to construct a tree
|
||||
* from a string.
|
||||
@@ -46,19 +55,26 @@ public class Abacus {
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Abacus calculator.
|
||||
*
|
||||
* @param configuration the configuration object for this Abacus instance.
|
||||
*/
|
||||
public Abacus(Configuration configuration) {
|
||||
pluginManager = new PluginManager(this);
|
||||
public Abacus() {
|
||||
pluginManager = new PluginManager();
|
||||
numberReducer = new NumberReducer(this);
|
||||
this.configuration = new Configuration(configuration);
|
||||
configuration = new ConfigurationObject(CONFIG_FILE);
|
||||
configuration.save(CONFIG_FILE);
|
||||
LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
|
||||
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
|
||||
|
||||
pluginManager.addListener(shuntingYardParser);
|
||||
pluginManager.addListener(lexerTokenizer);
|
||||
pluginManager.addListener(shuntingYardParser);
|
||||
pluginManager.addInstantiated(new StandardPlugin(pluginManager));
|
||||
try {
|
||||
ClassFinder.loadJars("plugins")
|
||||
.forEach(plugin -> pluginManager.addClass(plugin));
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
pluginManager.load();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -98,7 +114,7 @@ public class Abacus {
|
||||
*
|
||||
* @return the configuration object.
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
public ConfigurationObject getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@@ -131,10 +147,15 @@ public class Abacus {
|
||||
* @return the resulting number.
|
||||
*/
|
||||
public NumberInterface numberFromString(String numberString) {
|
||||
NumberImplementation toInstantiate =
|
||||
pluginManager.numberImplementationFor(configuration.getNumberImplementation());
|
||||
if (toInstantiate == null) toInstantiate = DEFAULT_IMPLEMENTATION;
|
||||
Class<? extends NumberInterface> toInstantiate =
|
||||
pluginManager.numberFor(configuration.getNumberImplementation());
|
||||
if (toInstantiate == null) toInstantiate = DEFAULT_NUMBER;
|
||||
|
||||
return toInstantiate.instanceForString(numberString);
|
||||
try {
|
||||
return toInstantiate.getConstructor(String.class).newInstance(numberString);
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,159 +1,14 @@
|
||||
package org.nwapw.abacus.config;
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import com.moandjiezana.toml.TomlWriter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The configuration object that stores
|
||||
* options that the user can change.
|
||||
* Serializable class that will be used to load TOML
|
||||
* configurations.
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
/**
|
||||
* The defaults TOML string.
|
||||
* The type of number this calculator should use.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
private static final TomlWriter TOML_WRITER = new TomlWriter();
|
||||
|
||||
/**
|
||||
* The computation delay for which the thread can run without interruption.
|
||||
*/
|
||||
private double computationDelay = 0;
|
||||
/**
|
||||
* The implementation of the number that should be used.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param computationDelay the delay before the computation gets killed.
|
||||
* @param numberImplementation the number implementation, like "naive" or "precise"
|
||||
* @param disabledPlugins the list of disabled plugins.
|
||||
*/
|
||||
public Configuration(double computationDelay, String numberImplementation, String[] disabledPlugins) {
|
||||
this.computationDelay = computationDelay;
|
||||
this.numberImplementation = numberImplementation;
|
||||
this.disabledPlugins.addAll(Arrays.asList(disabledPlugins));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a configuration from a given file, keeping non-specified fields default.
|
||||
*
|
||||
* @param fromFile the file to load from.
|
||||
*/
|
||||
public Configuration(File fromFile) {
|
||||
if (!fromFile.exists()) return;
|
||||
copyFrom(new Toml(DEFAULT_TOML).read(fromFile).to(Configuration.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the values from the given configuration into this one.
|
||||
*
|
||||
* @param otherConfiguration the configuration to copy from.
|
||||
*/
|
||||
public void copyFrom(Configuration otherConfiguration) {
|
||||
this.computationDelay = otherConfiguration.computationDelay;
|
||||
this.numberImplementation = otherConfiguration.numberImplementation;
|
||||
this.disabledPlugins.addAll(otherConfiguration.disabledPlugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this configuration to the given file, creating
|
||||
* any directories that do not exist.
|
||||
*
|
||||
* @param file the file to save to.
|
||||
*/
|
||||
public void saveTo(File file) {
|
||||
if (file.getParentFile() != null) file.getParentFile().mkdirs();
|
||||
try {
|
||||
TOML_WRITER.write(this, file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 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.
|
||||
*
|
||||
* @return the number implementation.
|
||||
*/
|
||||
public String getNumberImplementation() {
|
||||
return numberImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number implementation for the configuration
|
||||
*
|
||||
* @param numberImplementation the number implementation.
|
||||
*/
|
||||
public void setNumberImplementation(String numberImplementation) {
|
||||
this.numberImplementation = numberImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of disabled plugins.
|
||||
*
|
||||
* @return the list of disabled plugins.
|
||||
*/
|
||||
public Set<String> getDisabledPlugins() {
|
||||
return disabledPlugins;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the computation delay specified in the configuration.
|
||||
*
|
||||
* @return the computaton delay.
|
||||
*/
|
||||
public double getComputationDelay() {
|
||||
return computationDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the computation delay.
|
||||
*
|
||||
* @param computationDelay the new computation delay.
|
||||
*/
|
||||
public void setComputationDelay(double computationDelay) {
|
||||
this.computationDelay = computationDelay;
|
||||
}
|
||||
public String numberType;
|
||||
|
||||
}
|
||||
|
||||
111
src/main/java/org/nwapw/abacus/config/ConfigurationObject.java
Normal file
111
src/main/java/org/nwapw/abacus/config/ConfigurationObject.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package org.nwapw.abacus.config;
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import com.moandjiezana.toml.TomlWriter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A configuration object, which essentially
|
||||
* manages saving, loading, and getting values
|
||||
* from the configuration. While Configuration is
|
||||
* the data model, this is the interface with it.
|
||||
*/
|
||||
public class ConfigurationObject {
|
||||
|
||||
/**
|
||||
* The writer used to store the configuration.
|
||||
*/
|
||||
private static final TomlWriter TOML_WRITER = new TomlWriter();
|
||||
/**
|
||||
* The configuration instance being modeled.
|
||||
*/
|
||||
private Configuration configuration;
|
||||
|
||||
/**
|
||||
* Creates a new configuration object with the given config.
|
||||
*
|
||||
* @param config the config to use.
|
||||
*/
|
||||
public ConfigurationObject(Configuration config) {
|
||||
setup(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration object by attempting to
|
||||
* load a config from the given path, using the
|
||||
* default configuration otherwise.
|
||||
*
|
||||
* @param path the path to attempt to load.
|
||||
*/
|
||||
public ConfigurationObject(File path) {
|
||||
Configuration config;
|
||||
if (!path.exists()) {
|
||||
config = getDefaultConfig();
|
||||
} else {
|
||||
Toml parse = new Toml();
|
||||
parse.read(path);
|
||||
config = parse.to(Configuration.class);
|
||||
}
|
||||
setup(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new configuration object with the
|
||||
* default configuration.
|
||||
*/
|
||||
public ConfigurationObject() {
|
||||
setup(getDefaultConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the ConfigurationObject.
|
||||
* different constructors do different things,
|
||||
* but they all lead here.
|
||||
*
|
||||
* @param configuration the configuration to set up with.
|
||||
*/
|
||||
private void setup(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default configuration.
|
||||
*
|
||||
* @return the newly created default configuration.
|
||||
*/
|
||||
private Configuration getDefaultConfig() {
|
||||
configuration = new Configuration();
|
||||
configuration.numberType = "naive";
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the implementation the user has requested to
|
||||
* represent their numbers.
|
||||
*
|
||||
* @return the implementation name.
|
||||
*/
|
||||
public String getNumberImplementation() {
|
||||
return configuration.numberType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the ConfigurationObject to the given file.
|
||||
*
|
||||
* @param toFile the file to save ot.
|
||||
* @return true if the save succeed, false if otherwise.
|
||||
*/
|
||||
public boolean save(File toFile) {
|
||||
if (toFile.getParentFile() != null) toFile.getParentFile().mkdirs();
|
||||
try {
|
||||
TOML_WRITER.write(configuration, toFile);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.nwapw.abacus.function;
|
||||
|
||||
import org.nwapw.abacus.number.NaiveNumber;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.number.PreciseNumber;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A function that operates on one or more
|
||||
@@ -8,6 +12,15 @@ import org.nwapw.abacus.number.NumberInterface;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -26,7 +26,6 @@ public class Operator {
|
||||
* Creates a new operator with the given parameters.
|
||||
*
|
||||
* @param associativity the associativity of the operator.
|
||||
* @param operatorType the type of this operator, like binary infix or unary postfix.
|
||||
* @param precedence the precedence of the operator.
|
||||
* @param function the function that the operator calls.
|
||||
*/
|
||||
|
||||
@@ -4,5 +4,5 @@ package org.nwapw.abacus.function;
|
||||
* The type of an operator, describing how it should behave.
|
||||
*/
|
||||
public enum OperatorType {
|
||||
BINARY_INFIX, UNARY_POSTFIX, UNARY_PREFIX
|
||||
BINARY_INFIX, UNARY_POSTFIX
|
||||
}
|
||||
|
||||
@@ -12,25 +12,13 @@ import javafx.stage.Stage;
|
||||
*/
|
||||
public class AbacusApplication extends Application {
|
||||
|
||||
/**
|
||||
* The controller currently managing the application.
|
||||
*/
|
||||
private AbacusController controller;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/abacus.fxml"));
|
||||
Parent parent = loader.load();
|
||||
controller = loader.getController();
|
||||
Parent parent = FXMLLoader.load(getClass().getResource("/abacus.fxml"));
|
||||
Scene mainScene = new Scene(parent, 320, 480);
|
||||
primaryStage.setScene(mainScene);
|
||||
primaryStage.setTitle("Abacus");
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
super.stop();
|
||||
controller.performStop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,22 @@
|
||||
package org.nwapw.abacus.fx;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.CheckBoxListCell;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.number.ComputationInterruptedException;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.plugin.ClassFinder;
|
||||
import org.nwapw.abacus.plugin.PluginListener;
|
||||
import org.nwapw.abacus.plugin.PluginManager;
|
||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The controller for the abacus FX UI, responsible
|
||||
* for all the user interaction.
|
||||
*/
|
||||
public class AbacusController implements PluginListener {
|
||||
public class AbacusController {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -57,20 +25,7 @@ public class AbacusController implements PluginListener {
|
||||
* Constant string that is displayed if the tree could not be reduced.
|
||||
*/
|
||||
private static final String ERR_EVAL = "Evaluation Error";
|
||||
/**
|
||||
* Constant string that is displayed if the calculations are stopped before they are done.
|
||||
*/
|
||||
private static final String ERR_STOP = "Stopped";
|
||||
/**
|
||||
* Constant string that is displayed if the calculations are interrupted by an exception.
|
||||
*/
|
||||
private static final String ERR_EXCEPTION = "Exception Thrown";
|
||||
@FXML
|
||||
private TabPane coreTabPane;
|
||||
@FXML
|
||||
private Tab calculateTab;
|
||||
@FXML
|
||||
private Tab settingsTab;
|
||||
|
||||
@FXML
|
||||
private TableView<HistoryModel> historyTable;
|
||||
@FXML
|
||||
@@ -85,14 +40,6 @@ public class AbacusController implements PluginListener {
|
||||
private TextField inputField;
|
||||
@FXML
|
||||
private Button inputButton;
|
||||
@FXML
|
||||
private Button stopButton;
|
||||
@FXML
|
||||
private ComboBox<String> numberImplementationBox;
|
||||
@FXML
|
||||
private ListView<ToggleablePlugin> enabledPluginView;
|
||||
@FXML
|
||||
private TextField computationLimitField;
|
||||
|
||||
/**
|
||||
* The list of history entries, created by the users.
|
||||
@@ -103,236 +50,45 @@ public class AbacusController implements PluginListener {
|
||||
* The abacus instance used for calculations and all
|
||||
* other main processing code.
|
||||
*/
|
||||
private ObservableList<String> numberImplementationOptions;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* The runnable that takes care of killing computations that take too long.
|
||||
*/
|
||||
private final Runnable TIMER_RUNNABLE = () -> {
|
||||
try {
|
||||
Configuration abacusConfig = abacus.getConfiguration();
|
||||
if(abacusConfig.getComputationDelay() == 0) return;
|
||||
Thread.sleep((long) (abacusConfig.getComputationDelay() * 1000));
|
||||
performStop();
|
||||
} catch (InterruptedException e) { }
|
||||
};
|
||||
/**
|
||||
* The runnable used to perform the calculation.
|
||||
*/
|
||||
private final Runnable CALCULATION_RUNNABLE = new Runnable() {
|
||||
|
||||
private String attemptCalculation(){
|
||||
try {
|
||||
TreeNode constructedTree = abacus.parseString(inputField.getText());
|
||||
if (constructedTree == null) {
|
||||
return ERR_SYNTAX;
|
||||
}
|
||||
NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
|
||||
if (evaluatedNumber == null) {
|
||||
return ERR_EVAL;
|
||||
}
|
||||
String resultingString = evaluatedNumber.toString();
|
||||
historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), resultingString));
|
||||
return resultingString;
|
||||
} catch (ComputationInterruptedException exception) {
|
||||
return ERR_STOP;
|
||||
} catch (RuntimeException exception){
|
||||
exception.printStackTrace();
|
||||
return ERR_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String calculation = attemptCalculation();
|
||||
Platform.runLater(() -> {
|
||||
outputText.setText(calculation);
|
||||
inputField.setText("");
|
||||
inputButton.setDisable(false);
|
||||
stopButton.setDisable(true);
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The thread that is waiting to pause the calculation.
|
||||
*/
|
||||
private Thread computationLimitThread;
|
||||
/**
|
||||
* The thread in which the computation runs.
|
||||
*/
|
||||
private Thread calculationThread;
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void initialize() {
|
||||
public void initialize(){
|
||||
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
abacus = new Abacus();
|
||||
historyData = FXCollections.observableArrayList();
|
||||
historyTable.setItems(historyData);
|
||||
numberImplementationOptions = FXCollections.observableArrayList();
|
||||
numberImplementationBox.setItems(numberImplementationOptions);
|
||||
numberImplementationBox.getSelectionModel().selectedIndexProperty().addListener(e -> changesMade = true);
|
||||
historyTable.getSelectionModel().setCellSelectionEnabled(true);
|
||||
enabledPlugins = FXCollections.observableArrayList();
|
||||
enabledPluginView.setItems(enabledPlugins);
|
||||
enabledPluginView.setCellFactory(pluginCellFactory);
|
||||
inputColumn.setCellFactory(cellFactory);
|
||||
inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
|
||||
parsedColumn.setCellFactory(cellFactory);
|
||||
parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
|
||||
outputColumn.setCellFactory(cellFactory);
|
||||
outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
|
||||
coreTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
|
||||
});
|
||||
|
||||
abacus = new Abacus(new Configuration(CONFIG_FILE));
|
||||
PluginManager abacusPluginManager = abacus.getPluginManager();
|
||||
abacusPluginManager.addListener(this);
|
||||
abacusPluginManager.addInstantiated(new StandardPlugin(abacus.getPluginManager()));
|
||||
try {
|
||||
ClassFinder.loadJars("plugins").forEach(abacusPluginManager::addClass);
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
abacusPluginManager.reload();
|
||||
|
||||
computationLimitField.setText(Double.toString(abacus.getConfiguration().getComputationDelay()));
|
||||
computationLimitField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(!newValue.matches("(\\d+(\\.\\d*)?)?")) {
|
||||
computationLimitField.setText(oldValue);
|
||||
} else {
|
||||
changesMade = true;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
public void performCalculation() {
|
||||
private void performCalculation(){
|
||||
inputButton.setDisable(true);
|
||||
stopButton.setDisable(false);
|
||||
calculationThread = new Thread(CALCULATION_RUNNABLE);
|
||||
calculationThread.start();
|
||||
computationLimitThread = new Thread(TIMER_RUNNABLE);
|
||||
computationLimitThread.start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void performStop(){
|
||||
if(calculationThread != null) {
|
||||
calculationThread.interrupt();
|
||||
calculationThread = null;
|
||||
TreeNode constructedTree = abacus.parseString(inputField.getText());
|
||||
if(constructedTree == null){
|
||||
outputText.setText(ERR_SYNTAX);
|
||||
inputButton.setDisable(false);
|
||||
return;
|
||||
}
|
||||
if(computationLimitThread != null){
|
||||
computationLimitThread.interrupt();
|
||||
computationLimitThread = null;
|
||||
NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
|
||||
if(evaluatedNumber == null){
|
||||
outputText.setText(ERR_EVAL);
|
||||
inputButton.setDisable(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
outputText.setText(evaluatedNumber.toString());
|
||||
historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), evaluatedNumber.toString()));
|
||||
|
||||
@FXML
|
||||
public void performSaveAndReload() {
|
||||
performSave();
|
||||
performReload();
|
||||
changesMade = false;
|
||||
reloadAlertShown = false;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void performReload() {
|
||||
alertIfApplyNeeded(true);
|
||||
abacus.getPluginManager().reload();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public 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());
|
||||
}
|
||||
if(computationLimitField.getText().matches("\\d*(\\.\\d+)?") && computationLimitField.getText().length() != 0)
|
||||
configuration.setComputationDelay(Double.parseDouble(computationLimitField.getText()));
|
||||
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();
|
||||
inputButton.setDisable(false);
|
||||
inputField.setText("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.awt.datatransfer.StringSelection;
|
||||
/**
|
||||
* A cell that copies its value to the clipboard
|
||||
* when double clicked.
|
||||
*
|
||||
* @param <S> The type of the table view generic type.
|
||||
* @param <T> The type of the value contained in the cell.
|
||||
*/
|
||||
@@ -18,9 +17,9 @@ public class CopyableCell<S, T> extends TableCell<S, T> {
|
||||
/**
|
||||
* Creates a new copyable cell.
|
||||
*/
|
||||
public CopyableCell() {
|
||||
public CopyableCell(){
|
||||
addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if (event.getClickCount() == 2) {
|
||||
if(event.getClickCount() == 2){
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||
.setContents(new StringSelection(getText()), null);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,11 @@ public class HistoryModel {
|
||||
|
||||
/**
|
||||
* 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 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.parsed = new SimpleStringProperty();
|
||||
this.output = new SimpleStringProperty();
|
||||
@@ -42,16 +41,13 @@ public class HistoryModel {
|
||||
|
||||
/**
|
||||
* Gets the input property.
|
||||
*
|
||||
* @return the input property.
|
||||
*/
|
||||
public StringProperty inputProperty() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input.
|
||||
*
|
||||
* @return the input.
|
||||
*/
|
||||
public String getInput() {
|
||||
@@ -60,16 +56,13 @@ public class HistoryModel {
|
||||
|
||||
/**
|
||||
* Gets the parsed input property.
|
||||
*
|
||||
* @return the parsed input property.
|
||||
*/
|
||||
public StringProperty parsedProperty() {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parsed input.
|
||||
*
|
||||
* @return the parsed input.
|
||||
*/
|
||||
public String getParsed() {
|
||||
@@ -78,16 +71,13 @@ public class HistoryModel {
|
||||
|
||||
/**
|
||||
* Gets the output property.
|
||||
*
|
||||
* @return the output property.
|
||||
*/
|
||||
public StringProperty outputProperty() {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the program output.
|
||||
*
|
||||
* @return the output.
|
||||
*/
|
||||
public String getOutput() {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class ValueNode<T> extends PatternNode<T> {
|
||||
/**
|
||||
* Creates a new node that matches the given character.
|
||||
*
|
||||
* @param value the character value of the node.
|
||||
* @param value
|
||||
*/
|
||||
public ValueNode(char value) {
|
||||
this.value = value;
|
||||
|
||||
162
src/main/java/org/nwapw/abacus/number/BinaryNumber.java
Normal file
162
src/main/java/org/nwapw/abacus/number/BinaryNumber.java
Normal file
@@ -0,0 +1,162 @@
|
||||
package org.nwapw.abacus.number;
|
||||
|
||||
public class BinaryNumber implements NumberInterface{
|
||||
|
||||
/**
|
||||
* The number zero.
|
||||
*/
|
||||
public static final BinaryNumber ZERO = new BinaryNumber(0);
|
||||
/**
|
||||
* The number one.
|
||||
*/
|
||||
public static final BinaryNumber ONE = new BinaryNumber(1);
|
||||
/**
|
||||
* The value of this number.
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* Creates a new BinaryNumber with the given string.
|
||||
*
|
||||
* @param value the value, which will be parsed as a double.
|
||||
*/
|
||||
public BinaryNumber(String value) {
|
||||
toStandard(value);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void toStandard(String value) {
|
||||
String before;
|
||||
String after = "";
|
||||
if(value.indexOf(".")==-1){
|
||||
before=value;
|
||||
}else{
|
||||
before = value.substring(0,value.indexOf("."));
|
||||
after = value.substring(value.indexOf(".")+1);
|
||||
}
|
||||
double sum = 0;
|
||||
for(int it=0;before.length()>0;it++){
|
||||
if(before.charAt(before.length()-1)=='1'){
|
||||
sum+=Math.pow(2,it);
|
||||
}
|
||||
if(before.length()>1) {
|
||||
before = before.substring(0, before.length()-1);
|
||||
}else{
|
||||
before = "";
|
||||
}
|
||||
}
|
||||
for(int it=-1;after.length()>0;it--) {
|
||||
if (after.charAt(0) == '1') {
|
||||
sum += Math.pow(2, it);
|
||||
}
|
||||
if (after.length() > 1) {
|
||||
after = after.substring(1);
|
||||
} else {
|
||||
after = "";
|
||||
}
|
||||
}
|
||||
|
||||
this.value = sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BinaryNumber with the given value.
|
||||
*
|
||||
* @param value the value to use.
|
||||
*/
|
||||
public BinaryNumber(double value) {
|
||||
toStandard(""+value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPrecision() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface multiply(NumberInterface multiplier) {
|
||||
return new BinaryNumber(value * ((BinaryNumber) multiplier).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface divide(NumberInterface divisor) {
|
||||
return new BinaryNumber(value / ((BinaryNumber) divisor).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface add(NumberInterface summand) {
|
||||
return new BinaryNumber(value + ((BinaryNumber) summand).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface subtract(NumberInterface subtrahend) {
|
||||
return new BinaryNumber(value - ((BinaryNumber) subtrahend).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface negate() {
|
||||
return new BinaryNumber(-value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface intPow(int exponent) {
|
||||
if (exponent == 0) {
|
||||
return BinaryNumber.ONE;
|
||||
}
|
||||
boolean takeReciprocal = exponent < 0;
|
||||
exponent = Math.abs(exponent);
|
||||
NumberInterface power = this;
|
||||
for (int currentExponent = 1; currentExponent < exponent; currentExponent++) {
|
||||
power = power.multiply(this);
|
||||
}
|
||||
if (takeReciprocal) {
|
||||
power = BinaryNumber.ONE.divide(power);
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(NumberInterface number) {
|
||||
BinaryNumber num = (BinaryNumber) number;
|
||||
return Double.compare(value, num.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int signum() {
|
||||
return this.compareTo(ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface promoteTo(Class<? extends NumberInterface> toClass) {
|
||||
if (toClass == this.getClass()) return this;
|
||||
else if (toClass == PreciseNumber.class) {
|
||||
return new PreciseNumber(Double.toString(value));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
double sum = 0;
|
||||
double tempValue = Math.floor(value);
|
||||
double fraction = value-tempValue;
|
||||
for (int it=0;tempValue > 0;it++) {
|
||||
|
||||
if (tempValue % 2 == 1) {
|
||||
sum+=Math.pow(10,it);
|
||||
tempValue-=1;
|
||||
}
|
||||
tempValue=tempValue/2;
|
||||
}
|
||||
for (int it=0;fraction > 0;it--) {
|
||||
|
||||
if (fraction % 2 >= 1) {
|
||||
sum+=Math.pow(10,it);
|
||||
fraction-=1;
|
||||
}
|
||||
fraction=fraction*2;
|
||||
}
|
||||
double shiftBy = Math.pow(10, 10);
|
||||
return Double.toString(Math.round(sum * shiftBy) / shiftBy);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.nwapw.abacus.number;
|
||||
|
||||
/**
|
||||
* Exception thrown when the computation is interrupted by
|
||||
* the user.
|
||||
*/
|
||||
public class ComputationInterruptedException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates a new exception of this type.
|
||||
*/
|
||||
public ComputationInterruptedException(){
|
||||
super("Computation interrupted by user.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package org.nwapw.abacus.number;
|
||||
/**
|
||||
* An implementation of NumberInterface using a double.
|
||||
*/
|
||||
public class NaiveNumber extends NumberInterface {
|
||||
public class NaiveNumber implements NumberInterface {
|
||||
|
||||
/**
|
||||
* The number zero.
|
||||
@@ -26,7 +26,6 @@ public class NaiveNumber extends NumberInterface {
|
||||
public NaiveNumber(String value) {
|
||||
this(Double.parseDouble(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NaiveNumber with the given value.
|
||||
*
|
||||
@@ -42,32 +41,32 @@ public class NaiveNumber extends NumberInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface multiplyInternal(NumberInterface multiplier) {
|
||||
public NumberInterface multiply(NumberInterface multiplier) {
|
||||
return new NaiveNumber(value * ((NaiveNumber) multiplier).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface divideInternal(NumberInterface divisor) {
|
||||
public NumberInterface divide(NumberInterface divisor) {
|
||||
return new NaiveNumber(value / ((NaiveNumber) divisor).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface addInternal(NumberInterface summand) {
|
||||
public NumberInterface add(NumberInterface summand) {
|
||||
return new NaiveNumber(value + ((NaiveNumber) summand).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface subtractInternal(NumberInterface subtrahend) {
|
||||
public NumberInterface subtract(NumberInterface subtrahend) {
|
||||
return new NaiveNumber(value - ((NaiveNumber) subtrahend).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface negateInternal() {
|
||||
public NumberInterface negate() {
|
||||
return new NaiveNumber(-value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface intPowInternal(int exponent) {
|
||||
public NumberInterface intPow(int exponent) {
|
||||
if (exponent == 0) {
|
||||
return NaiveNumber.ONE;
|
||||
}
|
||||
@@ -95,27 +94,12 @@ public class NaiveNumber extends NumberInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface ceilingInternal() {
|
||||
return new NaiveNumber(Math.ceil(value));
|
||||
public int ceiling() {
|
||||
return (int) Math.ceil(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface floorInternal() {
|
||||
return new NaiveNumber(Math.floor(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface fractionalPartInternal() {
|
||||
return new NaiveNumber(value - Math.floor(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface promoteToInternal(Class<? extends NumberInterface> toClass) {
|
||||
public NumberInterface promoteTo(Class<? extends NumberInterface> toClass) {
|
||||
if (toClass == this.getClass()) return this;
|
||||
else if (toClass == PreciseNumber.class) {
|
||||
return new PreciseNumber(Double.toString(value));
|
||||
@@ -128,9 +112,5 @@ public class NaiveNumber extends NumberInterface {
|
||||
return Double.toString(Math.round(value * shiftBy) / shiftBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface getMaxError(){
|
||||
return new NaiveNumber(Math.pow(10, -18));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,14 @@ package org.nwapw.abacus.number;
|
||||
/**
|
||||
* An interface used to represent a number.
|
||||
*/
|
||||
public abstract class NumberInterface {
|
||||
public interface NumberInterface {
|
||||
|
||||
/**
|
||||
* Check if the thread was interrupted and
|
||||
* throw an exception to end the computation.
|
||||
*/
|
||||
private static void checkInterrupted(){
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
throw new ComputationInterruptedException();
|
||||
}
|
||||
/**
|
||||
* The maximum precision to which this number operates.
|
||||
*
|
||||
* @return the precision.
|
||||
*/
|
||||
public abstract int getMaxPrecision();
|
||||
int getMaxPrecision();
|
||||
|
||||
/**
|
||||
* Multiplies this number by another, returning
|
||||
@@ -27,21 +19,7 @@ public abstract class NumberInterface {
|
||||
* @param multiplier the multiplier
|
||||
* @return the result of the multiplication.
|
||||
*/
|
||||
protected abstract NumberInterface multiplyInternal(NumberInterface multiplier);
|
||||
|
||||
/**
|
||||
* Multiplies this number by another, returning
|
||||
* a new number instance. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param multiplier the multiplier
|
||||
* @return the result of the multiplication.
|
||||
*/
|
||||
public final NumberInterface multiply(NumberInterface multiplier){
|
||||
checkInterrupted();
|
||||
return multiplyInternal(multiplier);
|
||||
}
|
||||
NumberInterface multiply(NumberInterface multiplier);
|
||||
|
||||
/**
|
||||
* Divides this number by another, returning
|
||||
@@ -50,21 +28,7 @@ public abstract class NumberInterface {
|
||||
* @param divisor the divisor
|
||||
* @return the result of the division.
|
||||
*/
|
||||
protected abstract NumberInterface divideInternal(NumberInterface divisor);
|
||||
|
||||
/**
|
||||
* Divides this number by another, returning
|
||||
* a new number instance. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param divisor the divisor
|
||||
* @return the result of the division.
|
||||
*/
|
||||
public final NumberInterface divide(NumberInterface divisor){
|
||||
checkInterrupted();
|
||||
return divideInternal(divisor);
|
||||
}
|
||||
NumberInterface divide(NumberInterface divisor);
|
||||
|
||||
/**
|
||||
* Adds this number to another, returning
|
||||
@@ -73,21 +37,7 @@ public abstract class NumberInterface {
|
||||
* @param summand the summand
|
||||
* @return the result of the summation.
|
||||
*/
|
||||
protected abstract NumberInterface addInternal(NumberInterface summand);
|
||||
|
||||
/**
|
||||
* Adds this number to another, returning
|
||||
* a new number instance. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param summand the summand
|
||||
* @return the result of the summation.
|
||||
*/
|
||||
public final NumberInterface add(NumberInterface summand){
|
||||
checkInterrupted();
|
||||
return addInternal(summand);
|
||||
}
|
||||
NumberInterface add(NumberInterface summand);
|
||||
|
||||
/**
|
||||
* Subtracts another number from this number,
|
||||
@@ -96,21 +46,7 @@ public abstract class NumberInterface {
|
||||
* @param subtrahend the subtrahend.
|
||||
* @return the result of the subtraction.
|
||||
*/
|
||||
protected abstract NumberInterface subtractInternal(NumberInterface subtrahend);
|
||||
|
||||
/**
|
||||
* Subtracts another number from this number,
|
||||
* a new number instance. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param subtrahend the subtrahend.
|
||||
* @return the result of the subtraction.
|
||||
*/
|
||||
public final NumberInterface subtract(NumberInterface subtrahend){
|
||||
checkInterrupted();
|
||||
return subtractInternal(subtrahend);
|
||||
}
|
||||
NumberInterface subtract(NumberInterface subtrahend);
|
||||
|
||||
/**
|
||||
* Returns a new instance of this number with
|
||||
@@ -118,21 +54,7 @@ public abstract class NumberInterface {
|
||||
*
|
||||
* @return the new instance.
|
||||
*/
|
||||
protected abstract NumberInterface negateInternal();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new instance of this number with
|
||||
* the sign flipped. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @return the new instance.
|
||||
*/
|
||||
public final NumberInterface negate(){
|
||||
checkInterrupted();
|
||||
return negateInternal();
|
||||
}
|
||||
NumberInterface negate();
|
||||
|
||||
/**
|
||||
* Raises this number to an integer power.
|
||||
@@ -140,20 +62,7 @@ public abstract class NumberInterface {
|
||||
* @param exponent the exponent to which to take the number.
|
||||
* @return the resulting value.
|
||||
*/
|
||||
protected abstract NumberInterface intPowInternal(int exponent);
|
||||
|
||||
/**
|
||||
* Raises this number to an integer power. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param exponent the exponent to which to take the number.
|
||||
* @return the resulting value.
|
||||
*/
|
||||
public final NumberInterface intPow(int exponent){
|
||||
checkInterrupted();
|
||||
return intPowInternal(exponent);
|
||||
}
|
||||
NumberInterface intPow(int exponent);
|
||||
|
||||
/**
|
||||
* Compares this number to another.
|
||||
@@ -161,78 +70,20 @@ public abstract class NumberInterface {
|
||||
* @param number the number to compare to.
|
||||
* @return same as Integer.compare();
|
||||
*/
|
||||
public abstract int compareTo(NumberInterface number);
|
||||
int compareTo(NumberInterface number);
|
||||
|
||||
/**
|
||||
* Same as Math.signum().
|
||||
*
|
||||
* @return 1 if this number is positive, -1 if this number is negative, 0 if this number is 0.
|
||||
*/
|
||||
public abstract int signum();
|
||||
int signum();
|
||||
|
||||
/**
|
||||
* Returns the least integer greater than or equal to the number.
|
||||
*
|
||||
* @return the least integer greater or equal to the number, if int can hold the value.
|
||||
* @return the least integer >= the number, if int can hold the value.
|
||||
*/
|
||||
protected abstract NumberInterface ceilingInternal();
|
||||
|
||||
/**
|
||||
* Returns the least integer greater than or equal to the number.
|
||||
* Also, checks if the thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @return the least integer bigger or equal to the number.
|
||||
*/
|
||||
public final NumberInterface ceiling(){
|
||||
checkInterrupted();
|
||||
return ceilingInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the greatest integer less than or equal to the number.
|
||||
*
|
||||
* @return the greatest integer smaller or equal the number.
|
||||
*/
|
||||
protected abstract NumberInterface floorInternal();
|
||||
|
||||
/**
|
||||
* Return the greatest integer less than or equal to the number.
|
||||
* Also, checks if the thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @return the greatest int smaller than or equal to the number.
|
||||
*/
|
||||
public final NumberInterface floor(){
|
||||
checkInterrupted();
|
||||
return floorInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fractional part of the number.
|
||||
*
|
||||
* @return the fractional part of the number.
|
||||
*/
|
||||
protected abstract NumberInterface fractionalPartInternal();
|
||||
|
||||
/**
|
||||
* Returns the fractional part of the number, specifically x - floor(x).
|
||||
* Also, checks if the thread has been interrupted,
|
||||
* and if so, throws an exception.
|
||||
* @return the fractional part of the number.
|
||||
*/
|
||||
public final NumberInterface fractionalPart(){
|
||||
checkInterrupted();
|
||||
return fractionalPartInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer representation of this number, discarding any fractional part,
|
||||
* if int can hold the value.
|
||||
*
|
||||
* @return the integer value of this number.
|
||||
*/
|
||||
public abstract int intValue();
|
||||
int ceiling();
|
||||
|
||||
/**
|
||||
* Promotes this class to another number class.
|
||||
@@ -240,26 +91,6 @@ public abstract class NumberInterface {
|
||||
* @param toClass the class to promote to.
|
||||
* @return the resulting new instance.
|
||||
*/
|
||||
protected abstract NumberInterface promoteToInternal(Class<? extends NumberInterface> toClass);
|
||||
|
||||
/**
|
||||
* Promotes this class to another number class. Also, checks if the
|
||||
* thread has been interrupted, and if so, throws
|
||||
* an exception.
|
||||
*
|
||||
* @param toClass the class to promote to.
|
||||
* @return the resulting new instance.
|
||||
*/
|
||||
public final NumberInterface promoteTo(Class<? extends NumberInterface> toClass) {
|
||||
checkInterrupted();
|
||||
return promoteToInternal(toClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smallest error this instance can tolerate depending
|
||||
* on its precision and value.
|
||||
* @return the smallest error that should be permitted in calculations.
|
||||
*/
|
||||
public abstract NumberInterface getMaxError();
|
||||
NumberInterface promoteTo(Class<? extends NumberInterface> toClass);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +1,26 @@
|
||||
package org.nwapw.abacus.number;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* A number that uses a BigDecimal to store its value,
|
||||
* leading to infinite possible precision.
|
||||
*/
|
||||
public class PreciseNumber extends NumberInterface {
|
||||
public class PreciseNumber implements NumberInterface {
|
||||
|
||||
/**
|
||||
* The number one.
|
||||
*/
|
||||
public static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
||||
static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
||||
/**
|
||||
* The number zero.
|
||||
*/
|
||||
public static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
||||
static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
||||
/**
|
||||
* The number ten.
|
||||
*/
|
||||
public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
||||
|
||||
/**
|
||||
* The number of extra significant figures kept in calculations before rounding for output.
|
||||
*/
|
||||
private static int numExtraInternalSigFigs = 15;
|
||||
|
||||
/**
|
||||
* MathContext that is used when rounding a number prior to output.
|
||||
*/
|
||||
private static MathContext outputContext = new MathContext(50);
|
||||
|
||||
/**
|
||||
* MathContext that is actually used in calculations.
|
||||
*/
|
||||
private static MathContext internalContext = new MathContext(outputContext.getPrecision()+numExtraInternalSigFigs);
|
||||
static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
||||
|
||||
/**
|
||||
* The value of the PreciseNumber.
|
||||
@@ -63,31 +48,31 @@ public class PreciseNumber extends NumberInterface {
|
||||
|
||||
@Override
|
||||
public int getMaxPrecision() {
|
||||
return internalContext.getPrecision();
|
||||
return 65;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface multiplyInternal(NumberInterface multiplier) {
|
||||
public NumberInterface multiply(NumberInterface multiplier) {
|
||||
return new PreciseNumber(this.value.multiply(((PreciseNumber) multiplier).value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface divideInternal(NumberInterface divisor) {
|
||||
return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, internalContext));
|
||||
public NumberInterface divide(NumberInterface divisor) {
|
||||
return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.getMaxPrecision(), RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface addInternal(NumberInterface summand) {
|
||||
public NumberInterface add(NumberInterface summand) {
|
||||
return new PreciseNumber(value.add(((PreciseNumber) summand).value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface subtractInternal(NumberInterface subtrahend) {
|
||||
public NumberInterface subtract(NumberInterface subtrahend) {
|
||||
return new PreciseNumber(value.subtract(((PreciseNumber) subtrahend).value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface intPowInternal(int exponent) {
|
||||
public NumberInterface intPow(int exponent) {
|
||||
if (exponent == 0) {
|
||||
return PreciseNumber.ONE;
|
||||
}
|
||||
@@ -114,46 +99,17 @@ public class PreciseNumber extends NumberInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface ceilingInternal() {
|
||||
String str = value.toPlainString();
|
||||
int decimalIndex = str.indexOf('.');
|
||||
if (decimalIndex != -1) {
|
||||
return this.floor().add(ONE);
|
||||
}
|
||||
return this;
|
||||
public int ceiling() {
|
||||
return (int) Math.ceil(value.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface floorInternal() {
|
||||
String str = value.toPlainString();
|
||||
int decimalIndex = str.indexOf('.');
|
||||
if (decimalIndex != -1) {
|
||||
NumberInterface floor = new PreciseNumber(str.substring(0, decimalIndex));
|
||||
if(signum() == -1){
|
||||
floor = floor.subtract(ONE);
|
||||
}
|
||||
return floor;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface fractionalPartInternal() {
|
||||
return this.subtractInternal(floorInternal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface negateInternal() {
|
||||
public NumberInterface negate() {
|
||||
return new PreciseNumber(value.negate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface promoteToInternal(Class<? extends NumberInterface> toClass) {
|
||||
public NumberInterface promoteTo(Class<? extends NumberInterface> toClass) {
|
||||
if (toClass == this.getClass()) {
|
||||
return this;
|
||||
}
|
||||
@@ -162,11 +118,7 @@ public class PreciseNumber extends NumberInterface {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.round(outputContext).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface getMaxError(){
|
||||
return new PreciseNumber(value.ulp()).multiplyInternal(TEN.intPowInternal(value.precision()-internalContext.getPrecision()));
|
||||
BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP);
|
||||
return rounded.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,12 +55,9 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
public List<Match<TokenType>> intoPostfix(List<Match<TokenType>> from) {
|
||||
ArrayList<Match<TokenType>> output = new ArrayList<>();
|
||||
Stack<Match<TokenType>> tokenStack = new Stack<>();
|
||||
TokenType previousType;
|
||||
TokenType matchType = null;
|
||||
while (!from.isEmpty()) {
|
||||
Match<TokenType> match = from.remove(0);
|
||||
previousType = matchType;
|
||||
matchType = match.getType();
|
||||
TokenType matchType = match.getType();
|
||||
if (matchType == TokenType.NUM) {
|
||||
output.add(match);
|
||||
} else if (matchType == TokenType.FUNCTION) {
|
||||
@@ -77,19 +74,13 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokenString.equals("-") && (previousType == null || previousType == TokenType.OP ||
|
||||
previousType == TokenType.OPEN_PARENTH)) {
|
||||
from.add(0, new Match<>("`", TokenType.OP));
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!tokenStack.empty() && type == OperatorType.BINARY_INFIX) {
|
||||
while (!tokenStack.empty()) {
|
||||
Match<TokenType> otherMatch = tokenStack.peek();
|
||||
TokenType otherMatchType = otherMatch.getType();
|
||||
if (!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break;
|
||||
|
||||
if (otherMatchType == TokenType.OP) {
|
||||
int otherPrecedence = precedenceMap.get(otherMatch.getContent());
|
||||
int otherPrecedence = precedenceMap.get(match.getContent());
|
||||
if (otherPrecedence < precedence ||
|
||||
(associativity == OperatorAssociativity.RIGHT && otherPrecedence == precedence)) {
|
||||
break;
|
||||
@@ -112,8 +103,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
}
|
||||
while (!tokenStack.empty()) {
|
||||
Match<TokenType> match = tokenStack.peek();
|
||||
TokenType newMatchType = match.getType();
|
||||
if (!(newMatchType == TokenType.OP || newMatchType == TokenType.FUNCTION)) return null;
|
||||
TokenType matchType = match.getType();
|
||||
if (!(matchType == TokenType.OP || matchType == TokenType.FUNCTION)) return null;
|
||||
output.add(tokenStack.pop());
|
||||
}
|
||||
return output;
|
||||
@@ -136,11 +127,11 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
TreeNode right = constructRecursive(matches);
|
||||
TreeNode left = constructRecursive(matches);
|
||||
if (left == null || right == null) return null;
|
||||
else return new BinaryNode(operator, left, right);
|
||||
else return new BinaryInfixNode(operator, left, right);
|
||||
} else {
|
||||
TreeNode applyTo = constructRecursive(matches);
|
||||
if (applyTo == null) return null;
|
||||
else return new UnaryNode(operator, applyTo);
|
||||
else return new UnaryPrefixNode(operator, applyTo);
|
||||
}
|
||||
} else if (matchType == TokenType.NUM) {
|
||||
return new NumberNode(abacus.numberFromString(match.getContent()));
|
||||
@@ -162,10 +153,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
@Override
|
||||
public TreeNode constructTree(List<Match<TokenType>> tokens) {
|
||||
tokens = intoPostfix(new ArrayList<>(tokens));
|
||||
if(tokens == null) return null;
|
||||
Collections.reverse(tokens);
|
||||
TreeNode constructedTree = constructRecursive(tokens);
|
||||
return tokens.size() == 0 ? constructedTree : null;
|
||||
return constructRecursive(tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
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 means 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();
|
||||
|
||||
}
|
||||
@@ -17,6 +17,18 @@ import java.util.Set;
|
||||
*/
|
||||
public abstract class Plugin {
|
||||
|
||||
/**
|
||||
* A hash map of functions mapped to their string names.
|
||||
*/
|
||||
private Map<String, Function> functions;
|
||||
/**
|
||||
* A hash map of operators mapped to their string names.
|
||||
*/
|
||||
private Map<String, Operator> operators;
|
||||
/**
|
||||
* A hash map of operators mapped to their string names.
|
||||
*/
|
||||
private Map<String, Class<? extends NumberInterface>> numbers;
|
||||
/**
|
||||
* The plugin manager in which to search for functions
|
||||
* not inside this package,
|
||||
@@ -37,9 +49,69 @@ public abstract class Plugin {
|
||||
*/
|
||||
public Plugin(PluginManager manager) {
|
||||
this.manager = manager;
|
||||
functions = new HashMap<>();
|
||||
operators = new HashMap<>();
|
||||
numbers = new HashMap<>();
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of functions provided by this plugin.
|
||||
*
|
||||
* @return the list of registered functions.
|
||||
*/
|
||||
public final Set<String> providedFunctions() {
|
||||
return functions.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of functions provided by this plugin.
|
||||
*
|
||||
* @return the list of registered functions.
|
||||
*/
|
||||
public final Set<String> providedOperators() {
|
||||
return operators.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all numbers provided by this plugin.
|
||||
*
|
||||
* @return the list of registered numbers.
|
||||
*/
|
||||
public final Set<String> providedNumbers() {
|
||||
return numbers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function under the given function name.
|
||||
*
|
||||
* @param functionName the name of the function to get
|
||||
* @return the function, or null if this plugin doesn't provide it.
|
||||
*/
|
||||
public final Function getFunction(String functionName) {
|
||||
return functions.get(functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operator under the given operator name.
|
||||
*
|
||||
* @param operatorName the name of the operator to get.
|
||||
* @return the operator, or null if this plugin doesn't provide it.
|
||||
*/
|
||||
public final Operator getOperator(String operatorName) {
|
||||
return operators.get(operatorName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class under the given name.
|
||||
*
|
||||
* @param numberName the name of the class.
|
||||
* @return the class, or null if the plugin doesn't provide it.
|
||||
*/
|
||||
public final Class<? extends NumberInterface> getNumber(String numberName) {
|
||||
return numbers.get(numberName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the function, loading the necessary instances
|
||||
* of functions.
|
||||
@@ -57,6 +129,8 @@ public abstract class Plugin {
|
||||
public final void disable() {
|
||||
if (!enabled) return;
|
||||
onDisable();
|
||||
functions.clear();
|
||||
operators.clear();
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
@@ -68,7 +142,7 @@ public abstract class Plugin {
|
||||
* @param toRegister the function implementation.
|
||||
*/
|
||||
protected final void registerFunction(String name, Function toRegister) {
|
||||
manager.registerFunction(name, toRegister);
|
||||
functions.put(name, toRegister);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,18 +154,20 @@ public abstract class Plugin {
|
||||
* @param operator the operator to register.
|
||||
*/
|
||||
protected final void registerOperator(String name, Operator operator) {
|
||||
manager.registerOperator(name, operator);
|
||||
operators.put(name, operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in load(). Registers a new number implementation with the plugin.
|
||||
* This makes it accessible to the plugin manager.
|
||||
* To be used in load(). Registers a number class
|
||||
* with the plugin internally, which makes it possible
|
||||
* for the user to select it as an "implementation" for the
|
||||
* number that they would like to use.
|
||||
*
|
||||
* @param name the name of the implementation.
|
||||
* @param implementation the actual implementation class to register.
|
||||
* @param name the name to register it under.
|
||||
* @param toRegister the class to register.
|
||||
*/
|
||||
protected final void registerNumberImplementation(String name, NumberImplementation implementation) {
|
||||
manager.registerNumberImplementation(name, implementation);
|
||||
protected final void registerNumber(String name, Class<? extends NumberInterface> toRegister) {
|
||||
numbers.put(name, toRegister);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,31 +194,6 @@ public abstract class Plugin {
|
||||
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 piFor(Class<? extends NumberInterface> forClass) {
|
||||
return manager.piFor(forClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to be overridden by plugin implementation, in which the plugins
|
||||
* are supposed to register the functions they provide and do any other
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.nwapw.abacus.plugin;
|
||||
|
||||
import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.function.Function;
|
||||
import org.nwapw.abacus.function.Operator;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
@@ -23,142 +22,112 @@ public class PluginManager {
|
||||
*/
|
||||
private Set<Plugin> plugins;
|
||||
/**
|
||||
* The map of functions registered by the plugins.
|
||||
* List of functions that have been cached,
|
||||
* that is, found in a plugin and returned.
|
||||
*/
|
||||
private Map<String, Function> registeredFunctions;
|
||||
private Map<String, Function> cachedFunctions;
|
||||
/**
|
||||
* The map of operators registered by the plugins
|
||||
* List of operators that have been cached,
|
||||
* that is, found in a plugin and returned.
|
||||
*/
|
||||
private Map<String, Operator> registeredOperators;
|
||||
private Map<String, Operator> cachedOperators;
|
||||
/**
|
||||
* The map of number implementations registered by the plugins.
|
||||
* List of registered number implementations that have
|
||||
* been cached, that is, found in a plugin and returned.
|
||||
*/
|
||||
private Map<String, NumberImplementation> registeredNumberImplementations;
|
||||
private Map<String, Class<? extends NumberInterface>> cachedNumbers;
|
||||
/**
|
||||
* The list of number implementations that have been
|
||||
* found by their implementation class.
|
||||
* List of all functions loaded by the plugins.
|
||||
*/
|
||||
private Map<Class<? extends NumberInterface>, NumberImplementation> cachedInterfaceImplementations;
|
||||
private Set<String> allFunctions;
|
||||
/**
|
||||
* The pi values for each implementation class that have already been computer.
|
||||
* List of all operators loaded by the plugins.
|
||||
*/
|
||||
private Map<Class<? extends NumberInterface>, NumberInterface> cachedPi;
|
||||
private Set<String> allOperators;
|
||||
/**
|
||||
* List of all numbers loaded by the plugins.
|
||||
*/
|
||||
private Set<String> allNumbers;
|
||||
/**
|
||||
* The list of plugin listeners attached to this instance.
|
||||
*/
|
||||
private Set<PluginListener> listeners;
|
||||
/**
|
||||
* The abacus instance used to access other
|
||||
* components of the application.
|
||||
*/
|
||||
private Abacus abacus;
|
||||
|
||||
/**
|
||||
* Creates a new plugin manager.
|
||||
*
|
||||
* @param abacus the abacus instance.
|
||||
*/
|
||||
public PluginManager(Abacus abacus) {
|
||||
this.abacus = abacus;
|
||||
public PluginManager() {
|
||||
loadedPluginClasses = new HashSet<>();
|
||||
plugins = new HashSet<>();
|
||||
registeredFunctions = new HashMap<>();
|
||||
registeredOperators = new HashMap<>();
|
||||
registeredNumberImplementations = new HashMap<>();
|
||||
cachedInterfaceImplementations = new HashMap<>();
|
||||
cachedPi = new HashMap<>();
|
||||
cachedFunctions = new HashMap<>();
|
||||
cachedOperators = new HashMap<>();
|
||||
cachedNumbers = new HashMap<>();
|
||||
allFunctions = new HashSet<>();
|
||||
allOperators = new HashSet<>();
|
||||
allNumbers = new HashSet<>();
|
||||
listeners = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a function under the given name.
|
||||
* @param name the name of the function.
|
||||
* @param function the function to register.
|
||||
*/
|
||||
public void registerFunction(String name, Function function){
|
||||
registeredFunctions.put(name, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an operator under the given name.
|
||||
* @param name the name of the operator.
|
||||
* @param operator the operator to register.
|
||||
*/
|
||||
public void registerOperator(String name, Operator operator){
|
||||
registeredOperators.put(name, operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a number implementation under the given name.
|
||||
* @param name the name of the number implementation.
|
||||
* @param implementation the number implementation to register.
|
||||
*/
|
||||
public void registerNumberImplementation(String name, NumberImplementation implementation){
|
||||
registeredNumberImplementations.put(name, implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function registered under the given name.
|
||||
* @param name the name of the function.
|
||||
* @return the function, or null if it was not found.
|
||||
*/
|
||||
public Function functionFor(String name){
|
||||
return registeredFunctions.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operator registered under the given name.
|
||||
* @param name the name of the operator.
|
||||
* @return the operator, or null if it was not found.
|
||||
*/
|
||||
public Operator operatorFor(String name){
|
||||
return registeredOperators.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number implementation registered under the given name.
|
||||
* @param name the name of the number implementation.
|
||||
* @return the number implementation, or null if it was not found.
|
||||
*/
|
||||
public NumberImplementation numberImplementationFor(String name){
|
||||
return registeredNumberImplementations.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number implementation for the given implementation class.
|
||||
* Searches the plugin list for a certain value, retrieving the Plugin's
|
||||
* list of items of the type using the setFunction and getting the value
|
||||
* of it is available via getFunction. If the value is contained
|
||||
* in the cache, it returns the cached value instead.
|
||||
*
|
||||
* @param name the class for which to find the implementation.
|
||||
* @return the implementation.
|
||||
* @param plugins the plugin list to search.
|
||||
* @param cache the cache to use
|
||||
* @param setFunction the function to retrieve a set of available T's from the plugin
|
||||
* @param getFunction the function to get the T value under the given name
|
||||
* @param name the name to search for
|
||||
* @param <T> the type of element being search
|
||||
* @return the retrieved element, or null if it was not found.
|
||||
*/
|
||||
public NumberImplementation interfaceImplementationFor(Class<? extends NumberInterface> name) {
|
||||
if (cachedInterfaceImplementations.containsKey(name)) return cachedInterfaceImplementations.get(name);
|
||||
NumberImplementation toReturn = null;
|
||||
for(String key : registeredNumberImplementations.keySet()){
|
||||
NumberImplementation implementation = registeredNumberImplementations.get(key);
|
||||
if(implementation.getImplementation() == name) {
|
||||
toReturn = implementation;
|
||||
private static <T> T searchCached(Collection<Plugin> plugins, Map<String, T> cache,
|
||||
java.util.function.Function<Plugin, Set<String>> setFunction,
|
||||
java.util.function.BiFunction<Plugin, String, T> getFunction,
|
||||
String name) {
|
||||
if (cache.containsKey(name)) return cache.get(name);
|
||||
|
||||
T loadedValue = null;
|
||||
for (Plugin plugin : plugins) {
|
||||
if (setFunction.apply(plugin).contains(name)) {
|
||||
loadedValue = getFunction.apply(plugin, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
cachedInterfaceImplementations.put(name, toReturn);
|
||||
return toReturn;
|
||||
|
||||
cache.put(name, loadedValue);
|
||||
return loadedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mathematical constant pi for the given implementation class.
|
||||
* Gets a function under the given name.
|
||||
*
|
||||
* @param forClass the class for which to find pi.
|
||||
* @return pi
|
||||
* @param name the name of the function
|
||||
* @return the function under the given name.
|
||||
*/
|
||||
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;
|
||||
public Function functionFor(String name) {
|
||||
return searchCached(plugins, cachedFunctions, Plugin::providedFunctions, Plugin::getFunction, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operator under the given name.
|
||||
*
|
||||
* @param name the name of the operator.
|
||||
* @return the operator under the given name.
|
||||
*/
|
||||
public Operator operatorFor(String name) {
|
||||
return searchCached(plugins, cachedOperators, Plugin::providedOperators, Plugin::getOperator, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a numer implementation under the given name.
|
||||
*
|
||||
* @param name the name of the implementation.
|
||||
* @return the implementation class
|
||||
*/
|
||||
public Class<? extends NumberInterface> numberFor(String name) {
|
||||
return searchCached(plugins, cachedNumbers, Plugin::providedNumbers, Plugin::getNumber, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,10 +160,11 @@ public class PluginManager {
|
||||
* Loads all the plugins in the PluginManager.
|
||||
*/
|
||||
public void load() {
|
||||
Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
|
||||
for (Plugin plugin : plugins) plugin.enable();
|
||||
for (Plugin plugin : plugins) {
|
||||
if (disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||
plugin.enable();
|
||||
allFunctions.addAll(plugin.providedFunctions());
|
||||
allOperators.addAll(plugin.providedOperators());
|
||||
allNumbers.addAll(plugin.providedNumbers());
|
||||
}
|
||||
listeners.forEach(e -> e.onLoad(this));
|
||||
}
|
||||
@@ -203,14 +173,10 @@ public class PluginManager {
|
||||
* Unloads all the plugins in the PluginManager.
|
||||
*/
|
||||
public void unload() {
|
||||
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();
|
||||
}
|
||||
cachedInterfaceImplementations.clear();
|
||||
cachedPi.clear();
|
||||
for (Plugin plugin : plugins) plugin.disable();
|
||||
allFunctions.clear();
|
||||
allOperators.clear();
|
||||
allNumbers.clear();
|
||||
listeners.forEach(e -> e.onUnload(this));
|
||||
}
|
||||
|
||||
@@ -219,7 +185,7 @@ public class PluginManager {
|
||||
*/
|
||||
public void reload() {
|
||||
unload();
|
||||
load();
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +194,7 @@ public class PluginManager {
|
||||
* @return the set of all functions that were loaded.
|
||||
*/
|
||||
public Set<String> getAllFunctions() {
|
||||
return registeredFunctions.keySet();
|
||||
return allFunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,16 +203,16 @@ public class PluginManager {
|
||||
* @return the set of all operators that were loaded.
|
||||
*/
|
||||
public Set<String> getAllOperators() {
|
||||
return registeredOperators.keySet();
|
||||
return allOperators;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> getAllNumberImplementations() {
|
||||
return registeredNumberImplementations.keySet();
|
||||
public Set<String> getAllNumbers() {
|
||||
return allNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,13 +233,4 @@ public class PluginManager {
|
||||
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,10 +18,7 @@ import java.util.function.BiFunction;
|
||||
*/
|
||||
public class StandardPlugin extends Plugin {
|
||||
|
||||
/**
|
||||
* Stores objects of NumberInterface with integer values for reuse.
|
||||
*/
|
||||
private final static HashMap<Class<? extends NumberInterface>, HashMap<Integer, NumberInterface>> integerValues = new HashMap<>();
|
||||
private static HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> factorialLists = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
||||
|
||||
/**
|
||||
* The addition operator, +
|
||||
@@ -53,21 +50,6 @@ public class StandardPlugin extends Plugin {
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return params[0].subtract(params[1]);
|
||||
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The negation operator, -
|
||||
*/
|
||||
public static final Operator OP_NEGATE = new Operator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0, new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return params[0].negate();
|
||||
}
|
||||
});
|
||||
/**
|
||||
@@ -94,12 +76,16 @@ public class StandardPlugin extends Plugin {
|
||||
public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 2 && params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0;
|
||||
return params.length >= 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return params[0].divide(params[1]);
|
||||
NumberInterface product = params[0];
|
||||
for (int i = 1; i < params.length; i++) {
|
||||
product = product.multiply(params[i]);
|
||||
}
|
||||
return product;
|
||||
}
|
||||
});
|
||||
/**
|
||||
@@ -109,15 +95,13 @@ public class StandardPlugin extends Plugin {
|
||||
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1
|
||||
&& params[0].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
||||
&& params[0].signum() >= 0;
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
if (params[0].signum() == 0) {
|
||||
return fromInt(params[0].getClass(), 1);
|
||||
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
|
||||
}
|
||||
NumberInterface factorial = params[0];
|
||||
NumberInterface multiplier = params[0];
|
||||
@@ -133,6 +117,20 @@ 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
|
||||
*/
|
||||
@@ -147,31 +145,74 @@ public class StandardPlugin extends Plugin {
|
||||
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.
|
||||
*/
|
||||
public static final Function FUNCTION_LN = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1 && params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) > 0;
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
NumberInterface param = params[0];
|
||||
int powersOf2 = 0;
|
||||
while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo(new NaiveNumber(0.1).promoteTo(param.getClass())) >= 0) {
|
||||
while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo((new NaiveNumber(0.1)).promoteTo(param.getClass())) >= 0) {
|
||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) {
|
||||
param = param.divide(fromInt(param.getClass(), 2));
|
||||
param = param.divide(new NaiveNumber(2).promoteTo(param.getClass()));
|
||||
powersOf2++;
|
||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
|
||||
break;
|
||||
//No infinite loop for you.
|
||||
}
|
||||
} else {
|
||||
param = param.multiply(fromInt(param.getClass(), 2));
|
||||
param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass()));
|
||||
powersOf2--;
|
||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != -1) {
|
||||
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
|
||||
break;
|
||||
//No infinite loop for you.
|
||||
}
|
||||
@@ -187,8 +228,7 @@ public class StandardPlugin extends Plugin {
|
||||
* @return the partial sum.
|
||||
*/
|
||||
private NumberInterface getLogPartialSum(NumberInterface x) {
|
||||
|
||||
NumberInterface maxError = x.getMaxError();
|
||||
NumberInterface maxError = getMaxError(x);
|
||||
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
||||
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
||||
int n = 1;
|
||||
@@ -207,18 +247,18 @@ public class StandardPlugin extends Plugin {
|
||||
* @return the value of log(2) with the appropriate precision.
|
||||
*/
|
||||
private NumberInterface getLog2(NumberInterface number) {
|
||||
NumberInterface maxError = number.getMaxError();
|
||||
//NumberInterface errorBound = fromInt(number.getClass(), 1);
|
||||
NumberInterface maxError = getMaxError(number);
|
||||
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
|
||||
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
||||
//In the following, a=1/3^n, b=1/4^n, c = 1/n.
|
||||
//a is also an error bound.
|
||||
NumberInterface a = fromInt(number.getClass(), 1), b = a, c = a;
|
||||
NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
|
||||
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
|
||||
int n = 0;
|
||||
while (a.compareTo(maxError) >= 1) {
|
||||
n++;
|
||||
a = a.divide(fromInt(number.getClass(), 3));
|
||||
b = b.divide(fromInt(number.getClass(), 4));
|
||||
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
|
||||
b = b.divide((new NaiveNumber(4)).promoteTo(number.getClass()));
|
||||
c = NaiveNumber.ONE.promoteTo(number.getClass()).divide((new NaiveNumber(n)).promoteTo(number.getClass()));
|
||||
sum = sum.add(a.add(b).multiply(c));
|
||||
}
|
||||
@@ -239,217 +279,6 @@ public class StandardPlugin extends Plugin {
|
||||
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 = params[0].getMaxError();
|
||||
int n = 0;
|
||||
if (params[0].signum() < 0) {
|
||||
NumberInterface[] negatedParams = {params[0].negate()};
|
||||
return fromInt(params[0].getClass(), 1).divide(applyInternal(negatedParams));
|
||||
} 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(fromInt(params[0].getClass(), 3).intPow(params[0].ceiling().intValue())), right = maxError;
|
||||
do {
|
||||
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n + 1)));
|
||||
n++;
|
||||
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)
|
||||
&& !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
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());
|
||||
//Detect integer bases:
|
||||
if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0
|
||||
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 0
|
||||
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0){
|
||||
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
|
||||
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams));
|
||||
}
|
||||
return FUNCTION_EXP.apply(FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0])).multiply(params[1]));
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The sine function (the argument is interpreted in radians).
|
||||
*/
|
||||
public final Function functionSin = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
NumberInterface pi = piFor(params[0].getClass());
|
||||
NumberInterface twoPi = pi.multiply(fromInt(pi.getClass(), 2));
|
||||
NumberInterface theta = getSmallAngle(params[0], pi);
|
||||
//System.out.println(theta);
|
||||
if (theta.compareTo(pi.multiply(new NaiveNumber(1.5).promoteTo(twoPi.getClass()))) >= 0) {
|
||||
theta = theta.subtract(twoPi);
|
||||
} else if (theta.compareTo(pi.divide(fromInt(pi.getClass(), 2))) > 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(piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
|
||||
.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(functionSin.apply(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
public StandardPlugin(PluginManager manager) {
|
||||
super(manager);
|
||||
@@ -472,87 +301,22 @@ public class StandardPlugin extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* A factorial function that uses memoization for each number class; it efficiently
|
||||
* computes factorials of non-negative integers.
|
||||
* Returns the maximum error based on the precision of the class of number.
|
||||
*
|
||||
* @param numberClass type of number to return.
|
||||
* @param n non-negative integer.
|
||||
* @return a number of numClass with value n factorial.
|
||||
* @param number Any instance of the NumberInterface in question (should return an appropriate precision).
|
||||
* @return the maximum error.
|
||||
*/
|
||||
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n) {
|
||||
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 (list.size() < n + 16) {
|
||||
list.add(list.get(list.size() - 1).multiply(new NaiveNumber(list.size()).promoteTo(numberClass)));
|
||||
}
|
||||
}
|
||||
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 = x.getMaxError();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number of class numType with value n.
|
||||
* @param numType class of number to return.
|
||||
* @param n value of returned number.
|
||||
* @return numClass instance with value n.
|
||||
*/
|
||||
private static NumberInterface fromInt(Class<? extends NumberInterface> numType, int n){
|
||||
if(!integerValues.containsKey(numType)){
|
||||
integerValues.put(numType, new HashMap<>());
|
||||
}
|
||||
if(!integerValues.get(numType).containsKey(n)){
|
||||
integerValues.get(numType).put(n, new NaiveNumber(n).promoteTo(numType));
|
||||
}
|
||||
return integerValues.get(numType).get(n);
|
||||
private static NumberInterface getMaxError(NumberInterface number) {
|
||||
return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
registerNumberImplementation("naive", IMPLEMENTATION_NAIVE);
|
||||
registerNumberImplementation("precise", IMPLEMENTATION_PRECISE);
|
||||
registerNumber("naive", NaiveNumber.class);
|
||||
registerNumber("precise", PreciseNumber.class);
|
||||
|
||||
registerOperator("+", OP_ADD);
|
||||
registerOperator("-", OP_SUBTRACT);
|
||||
registerOperator("`", OP_NEGATE);
|
||||
registerOperator("*", OP_MULTIPLY);
|
||||
registerOperator("/", OP_DIVIDE);
|
||||
registerOperator("^", OP_CARET);
|
||||
@@ -562,16 +326,26 @@ public class StandardPlugin extends Plugin {
|
||||
registerFunction("exp", FUNCTION_EXP);
|
||||
registerFunction("ln", FUNCTION_LN);
|
||||
registerFunction("sqrt", FUNCTION_SQRT);
|
||||
registerFunction("sin", functionSin);
|
||||
registerFunction("cos", functionCos);
|
||||
registerFunction("tan", functionTan);
|
||||
registerFunction("sec", functionSec);
|
||||
registerFunction("csc", functionCsc);
|
||||
registerFunction("cot", functionCot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n){
|
||||
if(!factorialLists.containsKey(numberClass)){
|
||||
factorialLists.put(numberClass, new ArrayList<NumberInterface>());
|
||||
factorialLists.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
||||
factorialLists.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
||||
}
|
||||
ArrayList<NumberInterface> list = factorialLists.get(numberClass);
|
||||
if(n >= list.size()){
|
||||
while(list.size() < n + 16){
|
||||
list.add(list.get(list.size()-1).multiply(new NaiveNumber(list.size()).promoteTo(numberClass)));
|
||||
}
|
||||
}
|
||||
return list.get(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.nwapw.abacus.tree;
|
||||
/**
|
||||
* A tree node that represents an operation being applied to two operands.
|
||||
*/
|
||||
public class BinaryNode extends TreeNode {
|
||||
public class BinaryInfixNode extends TreeNode {
|
||||
|
||||
/**
|
||||
* The operation being applied.
|
||||
@@ -18,7 +18,7 @@ public class BinaryNode extends TreeNode {
|
||||
*/
|
||||
private TreeNode right;
|
||||
|
||||
private BinaryNode() {
|
||||
private BinaryInfixNode() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ public class BinaryNode extends TreeNode {
|
||||
*
|
||||
* @param operation the operation.
|
||||
*/
|
||||
public BinaryNode(String operation) {
|
||||
public BinaryInfixNode(String operation) {
|
||||
this(operation, null, null);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BinaryNode extends TreeNode {
|
||||
* @param left the left node of the expression.
|
||||
* @param right the right node of the expression.
|
||||
*/
|
||||
public BinaryNode(String operation, TreeNode left, TreeNode right) {
|
||||
public BinaryInfixNode(String operation, TreeNode left, TreeNode right) {
|
||||
this.operation = operation;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
@@ -28,15 +28,15 @@ public class NumberReducer implements Reducer<NumberInterface> {
|
||||
public NumberInterface reduceNode(TreeNode node, Object... children) {
|
||||
if (node instanceof NumberNode) {
|
||||
return ((NumberNode) node).getNumber();
|
||||
} else if (node instanceof BinaryNode) {
|
||||
} else if (node instanceof BinaryInfixNode) {
|
||||
NumberInterface left = (NumberInterface) children[0];
|
||||
NumberInterface right = (NumberInterface) children[1];
|
||||
Function function = abacus.getPluginManager().operatorFor(((BinaryNode) node).getOperation()).getFunction();
|
||||
Function function = abacus.getPluginManager().operatorFor(((BinaryInfixNode) node).getOperation()).getFunction();
|
||||
if (function == null) return null;
|
||||
return function.apply(left, right);
|
||||
} else if (node instanceof UnaryNode) {
|
||||
} else if (node instanceof UnaryPrefixNode) {
|
||||
NumberInterface child = (NumberInterface) children[0];
|
||||
Function functionn = abacus.getPluginManager().operatorFor(((UnaryNode) node).getOperation()).getFunction();
|
||||
Function functionn = abacus.getPluginManager().operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction();
|
||||
if (functionn == null) return null;
|
||||
return functionn.apply(child);
|
||||
} else if (node instanceof FunctionNode) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.nwapw.abacus.tree;
|
||||
|
||||
public class UnaryNode extends TreeNode {
|
||||
public class UnaryPrefixNode extends TreeNode {
|
||||
|
||||
/**
|
||||
* The operation this node will apply.
|
||||
@@ -16,7 +16,7 @@ public class UnaryNode extends TreeNode {
|
||||
*
|
||||
* @param operation the operation for this node.
|
||||
*/
|
||||
public UnaryNode(String operation) {
|
||||
public UnaryPrefixNode(String operation) {
|
||||
this(operation, null);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class UnaryNode extends TreeNode {
|
||||
* @param operation the operation for this node.
|
||||
* @param applyTo the node to apply the function to.
|
||||
*/
|
||||
public UnaryNode(String operation, TreeNode applyTo) {
|
||||
public UnaryPrefixNode(String operation, TreeNode applyTo) {
|
||||
this.operation = operation;
|
||||
this.applyTo = applyTo;
|
||||
}
|
||||
@@ -250,6 +250,6 @@ public class Window extends JFrame {
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
super.setVisible(b);
|
||||
if (b) inputField.requestFocusInWindow();
|
||||
if(b) inputField.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<BorderPane xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
<BorderPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.nwapw.abacus.fx.AbacusController">
|
||||
<center>
|
||||
<TabPane fx:id="coreTabPane">
|
||||
<Tab fx:id="calculateTab" text="Calculator" closable="false">
|
||||
<TabPane>
|
||||
<Tab text="Calculator" closable="false">
|
||||
<BorderPane>
|
||||
<center>
|
||||
<TableView fx:id="historyTable">
|
||||
@@ -34,32 +35,11 @@
|
||||
<TextField fx:id="inputField" onAction="#performCalculation"/>
|
||||
<Button fx:id="inputButton" text="Calculate" maxWidth="Infinity"
|
||||
onAction="#performCalculation"/>
|
||||
<Button fx:id="stopButton" text="Stop" maxWidth="Infinity"
|
||||
onAction="#performStop" disable="true"/>
|
||||
</VBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
</Tab>
|
||||
<Tab fx:id="settingsTab" text="Settings" closable="false">
|
||||
<GridPane hgap="10" vgap="10">
|
||||
<padding>
|
||||
<Insets left="10" right="10" top="10" bottom="10"/>
|
||||
</padding>
|
||||
<Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
|
||||
<ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
|
||||
<ListView fx:id="enabledPluginView"
|
||||
GridPane.rowIndex="1" GridPane.columnIndex="0"
|
||||
GridPane.columnSpan="2" maxHeight="100"/>
|
||||
<Text GridPane.columnIndex="0" GridPane.rowIndex="2" text="Computation Limit"/>
|
||||
<TextField fx:id="computationLimitField" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="3" hgap="10"
|
||||
vgap="10">
|
||||
<Button text="Apply" onAction="#performSave"/>
|
||||
<Button text="Reload Plugins" onAction="#performReload"/>
|
||||
<Button text="Apply and Reload" onAction="#performSaveAndReload"/>
|
||||
</FlowPane>
|
||||
</GridPane>
|
||||
</Tab>
|
||||
<Tab text="Settings" closable="false"/>
|
||||
</TabPane>
|
||||
</center>
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
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(0, "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)", "1.9424263952412559365842088360176992193662086");
|
||||
testOutput("exp(-500)", "exp((500)`)", "7.1245764067412855315491573771227552469277568");
|
||||
}
|
||||
|
||||
@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");
|
||||
testOutput("7^(-sqrt2*17)", "(7^((sqrt(2)*17))`)", "4.81354609155297814551845300063563");
|
||||
testEvalError("0^0", "(0^0)");
|
||||
testEvalError("(-13)^.9999", "((13)`^0.9999)");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.function.Function;
|
||||
import org.nwapw.abacus.function.Operator;
|
||||
import org.nwapw.abacus.function.OperatorAssociativity;
|
||||
@@ -19,7 +18,7 @@ import java.util.List;
|
||||
|
||||
public class TokenizerTests {
|
||||
|
||||
private static Abacus abacus = new Abacus(new Configuration(0, "precise", new String[]{}));
|
||||
private static Abacus abacus = new Abacus();
|
||||
private static LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||
private static Function subtractFunction = new Function() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user