mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-25 08:05:19 +00:00
Compare commits
59 Commits
constant-p
...
v0.1.1-ALP
| Author | SHA1 | Date | |
|---|---|---|---|
| b036b6c242 | |||
| eb3410f854 | |||
| f967053e3d | |||
| 75824a2a77 | |||
| da602876e7 | |||
|
|
8df468e04a | ||
|
|
e029ab1fea | ||
| af56d31723 | |||
| 34ae4b42c6 | |||
| b680215f57 | |||
| e6cc08043e | |||
| 3e10ea223f | |||
| 44c52b412c | |||
| eb51d5d3e4 | |||
| 8ae28f2dab | |||
| 0bade4a7df | |||
| f0e1b85dcf | |||
| 37261c2f58 | |||
|
|
95845a1585 | ||
|
|
8cf0c94947 | ||
| 20f6e0b0b2 | |||
| 4056013d1f | |||
| c7b5d4c4fc | |||
| be28e26607 | |||
| 2f1ed5f0d1 | |||
| 2615273d28 | |||
| 6e1d2ce629 | |||
| 44b8efd9bc | |||
| 2502c90837 | |||
| e49f28a850 | |||
| 88e4a87d81 | |||
| cda09518c3 | |||
| 56510d97de | |||
|
|
86533d53c9 | ||
| c2ae0b4138 | |||
| 16938b4e06 | |||
| d964fbfb6f | |||
| 9713f24ed2 | |||
| 5de9453bec | |||
| d205651332 | |||
| 6f99f07150 | |||
| 2cf41c1029 | |||
| 76677ef494 | |||
| 0cd40b028a | |||
| 7cb04a1222 | |||
| 0a97eeb442 | |||
| 05d0755526 | |||
| 0b97a935bf | |||
| 211e963db0 | |||
| d8145acc8f | |||
| 63b8162a9b | |||
| c655c63233 | |||
| 27ff1a47b5 | |||
| 2941252f7d | |||
| 44ed0199d4 | |||
| 5b582a7dbe | |||
| a02086e791 | |||
| 21d88fe256 | |||
| 3d61ead0f6 |
@@ -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 provides too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
|
||||
Abacus is a calculator built with extensibility and usability in mind. It provides a plugin interface, via Java, as Lua proves too difficult to link up to the Java core. The description of the internals of the project can be found on the wiki page.
|
||||
|
||||
## Current State
|
||||
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in treams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
|
||||
Abacus is being built for the Northwest Advanced Programming Workshop, a 3 week program in which students work in teams to complete a single project, following principles of agile development. Because of its short timeframe, Abacus is not even close to completed state. Below is a list of the current features and problems.
|
||||
- [x] Basic number class
|
||||
- [x] Implementation of basic functions
|
||||
- [x] Implementation of `exp`, `ln`, `sqrt` using the basic functions and Taylor Series
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
package org.nwapw.abacus;
|
||||
|
||||
import org.nwapw.abacus.config.ConfigurationObject;
|
||||
import org.nwapw.abacus.number.NaiveNumber;
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.fx.AbacusApplication;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.parsing.LexerTokenizer;
|
||||
import org.nwapw.abacus.parsing.ShuntingYardParser;
|
||||
import org.nwapw.abacus.parsing.TreeBuilder;
|
||||
import org.nwapw.abacus.plugin.ClassFinder;
|
||||
import org.nwapw.abacus.plugin.NumberImplementation;
|
||||
import org.nwapw.abacus.plugin.PluginManager;
|
||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||
import org.nwapw.abacus.tree.NumberReducer;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
import org.nwapw.abacus.window.Window;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* The main calculator class. This is responsible
|
||||
@@ -25,10 +23,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
*/
|
||||
public class Abacus {
|
||||
|
||||
/**
|
||||
* The default implementation to use for the number representation.
|
||||
*/
|
||||
public static final Class<? extends NumberInterface> DEFAULT_NUMBER = NaiveNumber.class;
|
||||
public static final NumberImplementation DEFAULT_IMPLEMENTATION = StandardPlugin.IMPLEMENTATION_NAIVE;
|
||||
/**
|
||||
* The file used for saving and loading configuration.
|
||||
*/
|
||||
@@ -47,7 +42,7 @@ public class Abacus {
|
||||
/**
|
||||
* The configuration loaded from a file.
|
||||
*/
|
||||
private ConfigurationObject configuration;
|
||||
private Configuration configuration;
|
||||
/**
|
||||
* The tree builder used to construct a tree
|
||||
* from a string.
|
||||
@@ -58,10 +53,10 @@ public class Abacus {
|
||||
* Creates a new instance of the Abacus calculator.
|
||||
*/
|
||||
public Abacus() {
|
||||
pluginManager = new PluginManager();
|
||||
pluginManager = new PluginManager(this);
|
||||
numberReducer = new NumberReducer(this);
|
||||
configuration = new ConfigurationObject(CONFIG_FILE);
|
||||
configuration.save(CONFIG_FILE);
|
||||
configuration = new Configuration(CONFIG_FILE);
|
||||
configuration.saveTo(CONFIG_FILE);
|
||||
LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||
ShuntingYardParser shuntingYardParser = new ShuntingYardParser(this);
|
||||
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
|
||||
@@ -79,13 +74,7 @@ public class Abacus {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
new Window(new Abacus()).setVisible(true);
|
||||
AbacusApplication.launch(AbacusApplication.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +110,7 @@ public class Abacus {
|
||||
*
|
||||
* @return the configuration object.
|
||||
*/
|
||||
public ConfigurationObject getConfiguration() {
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@@ -154,15 +143,10 @@ public class Abacus {
|
||||
* @return the resulting number.
|
||||
*/
|
||||
public NumberInterface numberFromString(String numberString) {
|
||||
Class<? extends NumberInterface> toInstantiate =
|
||||
pluginManager.numberFor(configuration.getNumberImplementation());
|
||||
if (toInstantiate == null) toInstantiate = DEFAULT_NUMBER;
|
||||
NumberImplementation toInstantiate =
|
||||
pluginManager.numberImplementationFor(configuration.getNumberImplementation());
|
||||
if (toInstantiate == null) toInstantiate = DEFAULT_IMPLEMENTATION;
|
||||
|
||||
try {
|
||||
return toInstantiate.getConstructor(String.class).newInstance(numberString);
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
return toInstantiate.instanceForString(numberString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,108 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Serializable class that will be used to load TOML
|
||||
* configurations.
|
||||
* The configuration object that stores
|
||||
* options that the user can change.
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
/**
|
||||
* The type of number this calculator should use.
|
||||
* The defaults TOML string.
|
||||
*/
|
||||
public String numberType;
|
||||
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 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 with the given values.
|
||||
* @param numberImplementation the number implementation, like "naive" or "precise"
|
||||
* @param disabledPlugins the list of disabled plugins.
|
||||
*/
|
||||
public Configuration(String numberImplementation, String[] disabledPlugins){
|
||||
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.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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
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,10 +1,6 @@
|
||||
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
|
||||
@@ -12,15 +8,6 @@ import java.util.HashMap;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
BINARY_INFIX, UNARY_POSTFIX, UNARY_PREFIX
|
||||
}
|
||||
|
||||
24
src/main/java/org/nwapw/abacus/fx/AbacusApplication.java
Normal file
24
src/main/java/org/nwapw/abacus/fx/AbacusApplication.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package org.nwapw.abacus.fx;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* The main application class for JavaFX responsible for loading
|
||||
* and displaying the fxml file.
|
||||
*/
|
||||
public class AbacusApplication extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
Parent parent = FXMLLoader.load(getClass().getResource("/abacus.fxml"));
|
||||
Scene mainScene = new Scene(parent, 320, 480);
|
||||
primaryStage.setScene(mainScene);
|
||||
primaryStage.setTitle("Abacus");
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
}
|
||||
240
src/main/java/org/nwapw/abacus/fx/AbacusController.java
Normal file
240
src/main/java/org/nwapw/abacus/fx/AbacusController.java
Normal file
@@ -0,0 +1,240 @@
|
||||
package org.nwapw.abacus.fx;
|
||||
|
||||
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.NumberInterface;
|
||||
import org.nwapw.abacus.plugin.PluginListener;
|
||||
import org.nwapw.abacus.plugin.PluginManager;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The controller for the abacus FX UI, responsible
|
||||
* for all the user interaction.
|
||||
*/
|
||||
public class AbacusController implements PluginListener {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static final String ERR_SYNTAX = "Syntax Error";
|
||||
/**
|
||||
* Constant string that is displayed if the tree could not be reduced.
|
||||
*/
|
||||
private static final String ERR_EVAL = "Evaluation Error";
|
||||
|
||||
@FXML
|
||||
private TabPane coreTabPane;
|
||||
@FXML
|
||||
private Tab calculateTab;
|
||||
@FXML
|
||||
private Tab settingsTab;
|
||||
@FXML
|
||||
private TableView<HistoryModel> historyTable;
|
||||
@FXML
|
||||
private TableColumn<HistoryModel, String> inputColumn;
|
||||
@FXML
|
||||
private TableColumn<HistoryModel, String> parsedColumn;
|
||||
@FXML
|
||||
private TableColumn<HistoryModel, String> outputColumn;
|
||||
@FXML
|
||||
private Text outputText;
|
||||
@FXML
|
||||
private TextField inputField;
|
||||
@FXML
|
||||
private Button inputButton;
|
||||
@FXML
|
||||
private ComboBox<String> numberImplementationBox;
|
||||
@FXML
|
||||
private ListView<ToggleablePlugin> enabledPluginView;
|
||||
|
||||
/**
|
||||
* The list of history entries, created by the users.
|
||||
*/
|
||||
private ObservableList<HistoryModel> historyData;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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(){
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
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();
|
||||
abacus.getPluginManager().addListener(this);
|
||||
abacus.getPluginManager().reload();
|
||||
|
||||
changesMade = false;
|
||||
reloadAlertShown = false;
|
||||
|
||||
reloadAlert = new Alert(Alert.AlertType.WARNING);
|
||||
reloadAlert.setTitle(APPLY_MSG_TITLE);
|
||||
reloadAlert.setHeaderText(APPLY_MSG_HEADER);
|
||||
reloadAlert.setContentText(APPLY_MSG_TEXT);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void performCalculation(){
|
||||
inputButton.setDisable(true);
|
||||
TreeNode constructedTree = abacus.parseString(inputField.getText());
|
||||
if(constructedTree == null){
|
||||
outputText.setText(ERR_SYNTAX);
|
||||
inputButton.setDisable(false);
|
||||
return;
|
||||
}
|
||||
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()));
|
||||
|
||||
inputButton.setDisable(false);
|
||||
inputField.setText("");
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void performSaveAndReload(){
|
||||
performSave();
|
||||
performReload();
|
||||
changesMade = false;
|
||||
reloadAlertShown = false;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void performReload(){
|
||||
alertIfApplyNeeded(true);
|
||||
abacus.getPluginManager().reload();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void performSave(){
|
||||
Configuration configuration = abacus.getConfiguration();
|
||||
configuration.setNumberImplementation(numberImplementationBox.getSelectionModel().getSelectedItem());
|
||||
Set<String> disabledPlugins = configuration.getDisabledPlugins();
|
||||
disabledPlugins.clear();
|
||||
for(ToggleablePlugin pluginEntry : enabledPlugins){
|
||||
if(!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
|
||||
}
|
||||
configuration.saveTo(Abacus.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();
|
||||
}
|
||||
}
|
||||
35
src/main/java/org/nwapw/abacus/fx/CopyableCell.java
Normal file
35
src/main/java/org/nwapw/abacus/fx/CopyableCell.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package org.nwapw.abacus.fx;
|
||||
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
import java.awt.*;
|
||||
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.
|
||||
*/
|
||||
public class CopyableCell<S, T> extends TableCell<S, T> {
|
||||
|
||||
/**
|
||||
* Creates a new copyable cell.
|
||||
*/
|
||||
public CopyableCell(){
|
||||
addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if(event.getClickCount() == 2){
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||
.setContents(new StringSelection(getText()), null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(T item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText((empty || item == null) ? null : item.toString());
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
87
src/main/java/org/nwapw/abacus/fx/HistoryModel.java
Normal file
87
src/main/java/org/nwapw/abacus/fx/HistoryModel.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package org.nwapw.abacus.fx;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
/**
|
||||
* The data model used for storing history entries.
|
||||
*/
|
||||
public class HistoryModel {
|
||||
|
||||
/**
|
||||
* The property used for displaying the column
|
||||
* for the user input.
|
||||
*/
|
||||
private final StringProperty input;
|
||||
/**
|
||||
* The property used for displaying the column
|
||||
* that contains the parsed input.
|
||||
*/
|
||||
private final StringProperty parsed;
|
||||
/**
|
||||
* The property used for displaying the column
|
||||
* that contains the program output.
|
||||
*/
|
||||
private final StringProperty output;
|
||||
|
||||
/**
|
||||
* Creates a new history model with the given variables.
|
||||
* @param input the user input
|
||||
* @param parsed the parsed input
|
||||
* @param output the program output.
|
||||
*/
|
||||
public HistoryModel(String input, String parsed, String output){
|
||||
this.input = new SimpleStringProperty();
|
||||
this.parsed = new SimpleStringProperty();
|
||||
this.output = new SimpleStringProperty();
|
||||
this.input.setValue(input);
|
||||
this.parsed.setValue(parsed);
|
||||
this.output.setValue(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input property.
|
||||
* @return the input property.
|
||||
*/
|
||||
public StringProperty inputProperty() {
|
||||
return input;
|
||||
}
|
||||
/**
|
||||
* Gets the input.
|
||||
* @return the input.
|
||||
*/
|
||||
public String getInput() {
|
||||
return input.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return parsed.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output property.
|
||||
* @return the output property.
|
||||
*/
|
||||
public StringProperty outputProperty() {
|
||||
return output;
|
||||
}
|
||||
/**
|
||||
* Gets the program output.
|
||||
* @return the output.
|
||||
*/
|
||||
public String getOutput() {
|
||||
return output.get();
|
||||
}
|
||||
|
||||
}
|
||||
56
src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
Normal file
56
src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
Normal file
@@ -0,0 +1,56 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +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 implements NumberInterface {
|
||||
|
||||
/**
|
||||
* The number one.
|
||||
*/
|
||||
static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
||||
public static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
|
||||
/**
|
||||
* The number zero.
|
||||
*/
|
||||
static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
||||
public static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
|
||||
/**
|
||||
* The number ten.
|
||||
*/
|
||||
static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
||||
public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
|
||||
|
||||
/**
|
||||
* The value of the PreciseNumber.
|
||||
|
||||
@@ -55,9 +55,12 @@ 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);
|
||||
TokenType matchType = match.getType();
|
||||
previousType = matchType;
|
||||
matchType = match.getType();
|
||||
if (matchType == TokenType.NUM) {
|
||||
output.add(match);
|
||||
} else if (matchType == TokenType.FUNCTION) {
|
||||
@@ -74,13 +77,19 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!tokenStack.empty()) {
|
||||
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) {
|
||||
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(match.getContent());
|
||||
int otherPrecedence = precedenceMap.get(otherMatch.getContent());
|
||||
if (otherPrecedence < precedence ||
|
||||
(associativity == OperatorAssociativity.RIGHT && otherPrecedence == precedence)) {
|
||||
break;
|
||||
@@ -103,8 +112,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
|
||||
}
|
||||
while (!tokenStack.empty()) {
|
||||
Match<TokenType> match = tokenStack.peek();
|
||||
TokenType matchType = match.getType();
|
||||
if (!(matchType == TokenType.OP || matchType == TokenType.FUNCTION)) return null;
|
||||
TokenType newMatchType = match.getType();
|
||||
if (!(newMatchType == TokenType.OP || newMatchType == TokenType.FUNCTION)) return null;
|
||||
output.add(tokenStack.pop());
|
||||
}
|
||||
return output;
|
||||
@@ -127,11 +136,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 BinaryInfixNode(operator, left, right);
|
||||
else return new BinaryNode(operator, left, right);
|
||||
} else {
|
||||
TreeNode applyTo = constructRecursive(matches);
|
||||
if (applyTo == null) return null;
|
||||
else return new UnaryPrefixNode(operator, applyTo);
|
||||
else return new UnaryNode(operator, applyTo);
|
||||
}
|
||||
} else if (matchType == TokenType.NUM) {
|
||||
return new NumberNode(abacus.numberFromString(match.getContent()));
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.nwapw.abacus.plugin;
|
||||
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A class that holds data about a number implementation.
|
||||
*/
|
||||
public abstract class NumberImplementation {
|
||||
|
||||
/**
|
||||
* The list of paths through which this implementation can be promoted.
|
||||
*/
|
||||
protected Map<Class<? extends NumberInterface>, Function<NumberInterface, NumberInterface>> promotionPaths;
|
||||
/**
|
||||
* The implementation class for this implementation.
|
||||
*/
|
||||
private Class<? extends NumberInterface> implementation;
|
||||
/**
|
||||
* The priority of converting into this number implementation.
|
||||
*/
|
||||
private int priority;
|
||||
|
||||
/**
|
||||
* Creates a new number implementation with the given data.
|
||||
* @param implementation the implementation class.
|
||||
* @param priority the priority, higher -> more likely to be converted into.
|
||||
*/
|
||||
public NumberImplementation(Class<? extends NumberInterface> implementation, int priority){
|
||||
this.implementation = implementation;
|
||||
this.priority = priority;
|
||||
promotionPaths = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all promotion paths this implementation can take.
|
||||
* @return the map of documentation paths.
|
||||
*/
|
||||
public final Map<Class<? extends NumberInterface>, Function<NumberInterface, NumberInterface>> getPromotionPaths(){
|
||||
return promotionPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class used by this implementation.
|
||||
* @return the implementation class.
|
||||
*/
|
||||
public final Class<? extends NumberInterface> getImplementation(){
|
||||
return implementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the priority of this number implementation.
|
||||
* @return the priority.
|
||||
*/
|
||||
public final int getPriority(){
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract function to create a new instance from a string.
|
||||
* @param string the string to create a number from.
|
||||
* @return the resulting number.
|
||||
*/
|
||||
public abstract NumberInterface instanceForString(String string);
|
||||
|
||||
/**
|
||||
* Get the instance of pi with the given implementation.
|
||||
* @return pi
|
||||
*/
|
||||
public abstract NumberInterface instanceForPi();
|
||||
|
||||
}
|
||||
@@ -26,13 +26,9 @@ public abstract class Plugin {
|
||||
*/
|
||||
private Map<String, Operator> operators;
|
||||
/**
|
||||
* A hash map of operators mapped to their string names.
|
||||
* The map of the number implementations this plugin provides.
|
||||
*/
|
||||
private Map<String, Class<? extends NumberInterface>> numbers;
|
||||
/**
|
||||
* A hash map of constant providers for each number type.
|
||||
*/
|
||||
private Map<Class<?>, java.util.function.Function<String, NumberInterface>> constantProviders;
|
||||
private Map<String, NumberImplementation> numberImplementations;
|
||||
/**
|
||||
* The plugin manager in which to search for functions
|
||||
* not inside this package,
|
||||
@@ -55,8 +51,7 @@ public abstract class Plugin {
|
||||
this.manager = manager;
|
||||
functions = new HashMap<>();
|
||||
operators = new HashMap<>();
|
||||
numbers = new HashMap<>();
|
||||
constantProviders = new HashMap<>();
|
||||
numberImplementations = new HashMap<>();
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
@@ -79,20 +74,12 @@ public abstract class Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all numbers provided by this plugin.
|
||||
* Gets the list of number implementations provided by this plugin.
|
||||
*
|
||||
* @return the list of registered numbers.
|
||||
* @return the list of registered number implementations.
|
||||
*/
|
||||
public final Set<String> providedNumbers() {
|
||||
return numbers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all constant providers provided by this plugin.
|
||||
* @return the list of constant providers.
|
||||
*/
|
||||
public final Set<Class<?>> providedConstantProviders() {
|
||||
return constantProviders.keySet();
|
||||
public final Set<String> providedNumberImplementations(){
|
||||
return numberImplementations.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,23 +103,13 @@ public abstract class Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class under the given name.
|
||||
* Gets the number implementation under the given name.
|
||||
*
|
||||
* @param numberName the name of the class.
|
||||
* @return the class, or null if the plugin doesn't provide it.
|
||||
* @param name the name of the number implementation to look up.
|
||||
* @return the number implementation associated with that name, or null if the plugin doesn't provide it.
|
||||
*/
|
||||
public final Class<? extends NumberInterface> getNumber(String numberName) {
|
||||
return numbers.get(numberName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant provider for the given class.
|
||||
*
|
||||
* @param pluginClass the class for which to provide constants.
|
||||
* @return the provider, or null, if the plugin doesn't provide it.
|
||||
*/
|
||||
public final java.util.function.Function<String, NumberInterface> getConstantProvider(Class<?> pluginClass){
|
||||
return constantProviders.get(pluginClass);
|
||||
public final NumberImplementation getNumberImplementation(String name){
|
||||
return numberImplementations.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +131,6 @@ public abstract class Plugin {
|
||||
onDisable();
|
||||
functions.clear();
|
||||
operators.clear();
|
||||
numbers.clear();
|
||||
constantProviders.clear();
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
@@ -183,29 +158,13 @@ public abstract class Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 to register it under.
|
||||
* @param toRegister the class to register.
|
||||
* To be used in load(). Registers a new number implementation with the plugin.
|
||||
* This makes it accessible to the plugin manager.
|
||||
* @param name the name of the implementation.
|
||||
* @param implementation the actual implementation class to register.
|
||||
*/
|
||||
protected final void registerNumber(String name, Class<? extends NumberInterface> toRegister) {
|
||||
numbers.put(name, toRegister);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in load(). Registers a constant provider
|
||||
* with the plugin internally, which makes it possible
|
||||
* for the calculations to look up constants for each different
|
||||
* number type.
|
||||
* @param providerFor the class the provider works with.
|
||||
* @param constantProvider the provider to register.
|
||||
*/
|
||||
protected final void registerConstantProvider(Class<?> providerFor,
|
||||
java.util.function.Function<String, NumberInterface> constantProvider) {
|
||||
constantProviders.put(providerFor, constantProvider);
|
||||
protected final void registerNumberImplementation(String name, NumberImplementation implementation){
|
||||
numberImplementations.put(name, implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,27 +192,27 @@ public abstract class Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the PluginManager for the given number implementation.
|
||||
* This can be used by the plugins internally in order to
|
||||
* find classes by name that they do not provide.
|
||||
* 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 class.
|
||||
* @param name the name for which to search.
|
||||
* @return the resulting number implementation, or null if none was found.
|
||||
*/
|
||||
protected final Class<? extends NumberInterface> numberFor(String name) {
|
||||
return manager.numberFor(name);
|
||||
protected final NumberImplementation numberImplementationFor(String name){
|
||||
return manager.numberImplementationFor(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the PluginManager for the given constant provider.
|
||||
* This can be used by the plugins internally in order
|
||||
* to find constant providers for number provider they do not provide.
|
||||
*
|
||||
* @param forClass the class for which to get a generator for.
|
||||
* @return the resulting generator
|
||||
* 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 java.util.function.Function<String, NumberInterface> constantProviderFor(Class<?> forClass){
|
||||
return manager.constantProviderFor(forClass);
|
||||
protected final NumberInterface getPi(Class<? extends NumberInterface> forClass){
|
||||
return manager.piFor(forClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -32,15 +33,19 @@ public class PluginManager {
|
||||
*/
|
||||
private Map<String, Operator> cachedOperators;
|
||||
/**
|
||||
* List of registered number implementations that have
|
||||
* The list of number implementations that have
|
||||
* been cached, that is, found in a plugin and returned.
|
||||
*/
|
||||
private Map<String, Class<? extends NumberInterface>> cachedNumbers;
|
||||
private Map<String, NumberImplementation> cachedNumberImplementations;
|
||||
/**
|
||||
* List of registered constant providers for every
|
||||
* number class.
|
||||
* The list of number implementations that have been
|
||||
* found by their implementation class.
|
||||
*/
|
||||
private Map<Class<?>, java.util.function.Function<String, NumberInterface>> cachedConstantProviders;
|
||||
private Map<Class<? extends NumberInterface>, NumberImplementation> cachedInterfaceImplementations;
|
||||
/**
|
||||
* The pi values for each implementation class that have already been computer.
|
||||
*/
|
||||
private Map<Class<? extends NumberInterface>, NumberInterface> cachedPi;
|
||||
/**
|
||||
* List of all functions loaded by the plugins.
|
||||
*/
|
||||
@@ -50,32 +55,34 @@ public class PluginManager {
|
||||
*/
|
||||
private Set<String> allOperators;
|
||||
/**
|
||||
* List of all numbers loaded by the plugins.
|
||||
* List of all the number implementations loaded by the plugins.
|
||||
*/
|
||||
private Set<String> allNumbers;
|
||||
/**
|
||||
* List of all the constant providers loaded by the plugins.
|
||||
*/
|
||||
private Set<Class<?>> allConstantProviders;
|
||||
private Set<String> allNumberImplementations;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public PluginManager() {
|
||||
public PluginManager(Abacus abacus) {
|
||||
this.abacus = abacus;
|
||||
loadedPluginClasses = new HashSet<>();
|
||||
plugins = new HashSet<>();
|
||||
cachedFunctions = new HashMap<>();
|
||||
cachedOperators = new HashMap<>();
|
||||
cachedNumbers = new HashMap<>();
|
||||
cachedConstantProviders = new HashMap<>();
|
||||
cachedNumberImplementations = new HashMap<>();
|
||||
cachedInterfaceImplementations = new HashMap<>();
|
||||
cachedPi = new HashMap<>();
|
||||
allFunctions = new HashSet<>();
|
||||
allOperators = new HashSet<>();
|
||||
allNumbers = new HashSet<>();
|
||||
allConstantProviders = new HashSet<>();
|
||||
allNumberImplementations = new HashSet<>();
|
||||
listeners = new HashSet<>();
|
||||
}
|
||||
|
||||
@@ -133,22 +140,51 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a numer implementation under the given name.
|
||||
*
|
||||
* Gets the number implementation under the given name.
|
||||
* @param name the name of the implementation.
|
||||
* @return the implementation class
|
||||
* @return the implementation.
|
||||
*/
|
||||
public Class<? extends NumberInterface> numberFor(String name) {
|
||||
return searchCached(plugins, cachedNumbers, Plugin::providedNumbers, Plugin::getNumber, name);
|
||||
public NumberImplementation numberImplementationFor(String name){
|
||||
return searchCached(plugins, cachedNumberImplementations, Plugin::providedNumberImplementations,
|
||||
Plugin::getNumberImplementation, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant provider for the given class.
|
||||
* @param forClass the class to get the provider for.
|
||||
* @return the provider.
|
||||
* Gets the number implementation for the given implementation class.
|
||||
* @param name the class for which to find the implementation.
|
||||
* @return the implementation.
|
||||
*/
|
||||
public java.util.function.Function<String, NumberInterface> constantProviderFor(Class<?> forClass){
|
||||
return searchCached(plugins, cachedConstantProviders, Plugin::providedConstantProviders, Plugin::getConstantProvider, forClass);
|
||||
public NumberImplementation interfaceImplementationFor(Class<? extends NumberInterface> name){
|
||||
if(cachedInterfaceImplementations.containsKey(name)) return cachedInterfaceImplementations.get(name);
|
||||
NumberImplementation toReturn = null;
|
||||
outside:
|
||||
for(Plugin plugin : plugins){
|
||||
for(String implementationName : plugin.providedNumberImplementations()){
|
||||
NumberImplementation implementation = plugin.getNumberImplementation(implementationName);
|
||||
if(implementation.getImplementation().equals(name)) {
|
||||
toReturn = implementation;
|
||||
break outside;
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedInterfaceImplementations.put(name, toReturn);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mathematical constant pi for the given implementation class.
|
||||
* @param forClass the class for which to find pi.
|
||||
* @return pi
|
||||
*/
|
||||
public NumberInterface piFor(Class<? extends NumberInterface> forClass){
|
||||
if(cachedPi.containsKey(forClass)) return cachedPi.get(forClass);
|
||||
NumberImplementation implementation = interfaceImplementationFor(forClass);
|
||||
NumberInterface generatedPi = null;
|
||||
if(implementation != null){
|
||||
generatedPi = implementation.instanceForPi();
|
||||
}
|
||||
cachedPi.put(forClass, generatedPi);
|
||||
return generatedPi;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,12 +217,16 @@ public class PluginManager {
|
||||
* Loads all the plugins in the PluginManager.
|
||||
*/
|
||||
public void load() {
|
||||
for (Plugin plugin : plugins) plugin.enable();
|
||||
Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
|
||||
for (Plugin plugin : plugins) {
|
||||
if(disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||
plugin.enable();
|
||||
}
|
||||
for (Plugin plugin : plugins) {
|
||||
if(disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||
allFunctions.addAll(plugin.providedFunctions());
|
||||
allOperators.addAll(plugin.providedOperators());
|
||||
allNumbers.addAll(plugin.providedNumbers());
|
||||
allConstantProviders.addAll(plugin.providedConstantProviders());
|
||||
allNumberImplementations.addAll(plugin.providedNumberImplementations());
|
||||
}
|
||||
listeners.forEach(e -> e.onLoad(this));
|
||||
}
|
||||
@@ -195,11 +235,20 @@ public class PluginManager {
|
||||
* Unloads all the plugins in the PluginManager.
|
||||
*/
|
||||
public void unload() {
|
||||
for (Plugin plugin : plugins) plugin.disable();
|
||||
listeners.forEach(e -> e.onUnload(this));
|
||||
Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
|
||||
for (Plugin plugin : plugins) {
|
||||
if(disabledPlugins.contains(plugin.getClass().getName())) continue;
|
||||
plugin.disable();
|
||||
}
|
||||
cachedFunctions.clear();
|
||||
cachedOperators.clear();
|
||||
cachedNumberImplementations.clear();
|
||||
cachedInterfaceImplementations.clear();
|
||||
cachedPi.clear();
|
||||
allFunctions.clear();
|
||||
allOperators.clear();
|
||||
allNumbers.clear();
|
||||
allConstantProviders.clear();
|
||||
allNumberImplementations.clear();
|
||||
listeners.forEach(e -> e.onUnload(this));
|
||||
}
|
||||
|
||||
@@ -208,7 +257,7 @@ public class PluginManager {
|
||||
*/
|
||||
public void reload() {
|
||||
unload();
|
||||
reload();
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,20 +279,12 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the number implementations loaded by the Plugin Manager
|
||||
* Gets all the number implementations loaded by the Plugin Manager.
|
||||
*
|
||||
* @return the set of all implementations that were loaded
|
||||
* @return the set of all implementations that were loaded.
|
||||
*/
|
||||
public Set<String> getAllNumbers() {
|
||||
return allNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the constant providers loaded by the Plugin Manager.
|
||||
* @return the set of all constant providers that were loaded.
|
||||
*/
|
||||
public Set<Class<?>> getAllConstantProviders() {
|
||||
return allConstantProviders;
|
||||
public Set<String> getAllNumberImplementations(){
|
||||
return allNumberImplementations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,4 +305,12 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.nwapw.abacus.number.NaiveNumber;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.number.PreciseNumber;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -19,8 +18,7 @@ import java.util.function.BiFunction;
|
||||
*/
|
||||
public class StandardPlugin extends Plugin {
|
||||
|
||||
private static HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> factorialLists = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
||||
static HashMap<Class<? extends NumberInterface>, NumberInterface> piValues = new HashMap<Class<? extends NumberInterface>, NumberInterface>();
|
||||
private static final HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> FACTORIAL_LISTS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The addition operator, +
|
||||
@@ -54,6 +52,20 @@ public class StandardPlugin extends Plugin {
|
||||
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();
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The multiplication operator, *
|
||||
*/
|
||||
@@ -78,16 +90,12 @@ 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 >= 1;
|
||||
return params.length == 2 && params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
NumberInterface product = params[0];
|
||||
for (int i = 1; i < params.length; i++) {
|
||||
product = product.multiply(params[i]);
|
||||
}
|
||||
return product;
|
||||
return params[0].divide(params[1]);
|
||||
}
|
||||
});
|
||||
/**
|
||||
@@ -97,7 +105,9 @@ 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;
|
||||
return params.length == 1
|
||||
&& params[0].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
||||
&& params[0].signum() >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,7 +206,7 @@ public class StandardPlugin extends Plugin {
|
||||
public static final Function FUNCTION_LN = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
return params.length == 1 && params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -214,7 +224,7 @@ public class StandardPlugin extends Plugin {
|
||||
} else {
|
||||
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.
|
||||
}
|
||||
@@ -285,7 +295,7 @@ public class StandardPlugin extends Plugin {
|
||||
/**
|
||||
* The sine function (the argument is interpreted in radians).
|
||||
*/
|
||||
public static final Function FUNCTION_SIN = new Function() {
|
||||
public final Function functionSin = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
@@ -295,7 +305,7 @@ public class StandardPlugin extends Plugin {
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
NumberInterface pi = getPi(params[0].getClass());
|
||||
NumberInterface twoPi = pi.multiply(new NaiveNumber(2).promoteTo(pi.getClass()));
|
||||
NumberInterface theta = getSmallAngle(params[0]);
|
||||
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);
|
||||
@@ -308,6 +318,134 @@ public class StandardPlugin extends Plugin {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The cosine function (the argument is in radians).
|
||||
*/
|
||||
public final Function functionCos = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return functionSin.apply(getPi(params[0].getClass()).divide(new NaiveNumber(2).promoteTo(params[0].getClass()))
|
||||
.subtract(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The tangent function (the argument is in radians).
|
||||
*/
|
||||
public final Function functionTan = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return functionSin.apply(params[0]).divide(functionCos.apply(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The secant function (the argument is in radians).
|
||||
*/
|
||||
public final Function functionSec = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionCos.apply(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The cosecant function (the argument is in radians).
|
||||
*/
|
||||
public final Function functionCsc = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionSin.apply(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The cotangent function (the argument is in radians).
|
||||
*/
|
||||
public final Function functionCot = new Function() {
|
||||
@Override
|
||||
protected boolean matchesParams(NumberInterface[] params) {
|
||||
return params.length == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||
return functionCos.apply(params[0]).divide(functionCos.apply(params[0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
||||
public StandardPlugin(PluginManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
@@ -340,11 +478,12 @@ public class StandardPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
registerNumber("naive", NaiveNumber.class);
|
||||
registerNumber("precise", PreciseNumber.class);
|
||||
registerNumberImplementation("naive", IMPLEMENTATION_NAIVE);
|
||||
registerNumberImplementation("precise", IMPLEMENTATION_PRECISE);
|
||||
|
||||
registerOperator("+", OP_ADD);
|
||||
registerOperator("-", OP_SUBTRACT);
|
||||
registerOperator("`", OP_NEGATE);
|
||||
registerOperator("*", OP_MULTIPLY);
|
||||
registerOperator("/", OP_DIVIDE);
|
||||
registerOperator("^", OP_CARET);
|
||||
@@ -354,7 +493,12 @@ public class StandardPlugin extends Plugin {
|
||||
registerFunction("exp", FUNCTION_EXP);
|
||||
registerFunction("ln", FUNCTION_LN);
|
||||
registerFunction("sqrt", FUNCTION_SQRT);
|
||||
registerFunction("sin", FUNCTION_SIN);
|
||||
registerFunction("sin", functionSin);
|
||||
registerFunction("cos", functionCos);
|
||||
registerFunction("tan", functionTan);
|
||||
registerFunction("sec", functionSec);
|
||||
registerFunction("csc", functionCsc);
|
||||
registerFunction("cot", functionCot);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -370,12 +514,12 @@ public class StandardPlugin extends Plugin {
|
||||
* @return a number of numClass with value n factorial.
|
||||
*/
|
||||
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));
|
||||
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 = factorialLists.get(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)));
|
||||
@@ -402,48 +546,13 @@ public class StandardPlugin extends Plugin {
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an approximation of Pi, with appropriate accuracy for given number class.
|
||||
* @param numClass type of number.
|
||||
* @return A number of class numClass, with value approximately Pi = 3.1415...
|
||||
*/
|
||||
public static NumberInterface getPi(Class<? extends NumberInterface> numClass){
|
||||
if(!piValues.containsKey(numClass)){
|
||||
//https://en.wikipedia.org/wiki/Chudnovsky_algorithm
|
||||
NumberInterface C = FUNCTION_SQRT.apply(new NaiveNumber(10005).promoteTo(numClass)).multiply(new NaiveNumber(426880).promoteTo(numClass));
|
||||
NumberInterface M = NaiveNumber.ONE.promoteTo(numClass);
|
||||
NumberInterface L = new NaiveNumber(13591409).promoteTo(numClass);
|
||||
NumberInterface X = M;
|
||||
NumberInterface sum = L;
|
||||
int termsNeeded = C.getMaxPrecision()/13 + 1;
|
||||
|
||||
NumberInterface lSummand = new NaiveNumber(545140134).promoteTo(L.getClass());
|
||||
NumberInterface xMultiplier = new NaiveNumber(262537412).promoteTo(X.getClass())
|
||||
.multiply(new NaiveNumber(1000000000).promoteTo(X.getClass()))
|
||||
.add(new NaiveNumber(640768000).promoteTo(X.getClass()))
|
||||
.negate();
|
||||
for(int i = 0; i < termsNeeded; i++){
|
||||
M = M
|
||||
.multiply(new NaiveNumber(12*i+2).promoteTo(M.getClass()))
|
||||
.multiply(new NaiveNumber(12*i+6).promoteTo(M.getClass()))
|
||||
.multiply(new NaiveNumber(12*i+10).promoteTo(M.getClass()))
|
||||
.divide(new NaiveNumber(Math.pow(i+1,3)).promoteTo(M.getClass()));
|
||||
L = L.add(lSummand);
|
||||
X = X.multiply(xMultiplier);
|
||||
sum = sum.add(M.multiply(L).divide(X));
|
||||
}
|
||||
piValues.put(numClass, C.divide(sum));
|
||||
}
|
||||
return piValues.get(numClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 twoPi = getPi(phi.getClass()).multiply(new NaiveNumber("2").promoteTo(phi.getClass()));
|
||||
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){
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.nwapw.abacus.tree;
|
||||
/**
|
||||
* A tree node that represents an operation being applied to two operands.
|
||||
*/
|
||||
public class BinaryInfixNode extends TreeNode {
|
||||
public class BinaryNode extends TreeNode {
|
||||
|
||||
/**
|
||||
* The operation being applied.
|
||||
@@ -18,7 +18,7 @@ public class BinaryInfixNode extends TreeNode {
|
||||
*/
|
||||
private TreeNode right;
|
||||
|
||||
private BinaryInfixNode() {
|
||||
private BinaryNode() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ public class BinaryInfixNode extends TreeNode {
|
||||
*
|
||||
* @param operation the operation.
|
||||
*/
|
||||
public BinaryInfixNode(String operation) {
|
||||
public BinaryNode(String operation) {
|
||||
this(operation, null, null);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BinaryInfixNode extends TreeNode {
|
||||
* @param left the left node of the expression.
|
||||
* @param right the right node of the expression.
|
||||
*/
|
||||
public BinaryInfixNode(String operation, TreeNode left, TreeNode right) {
|
||||
public BinaryNode(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 BinaryInfixNode) {
|
||||
} else if (node instanceof BinaryNode) {
|
||||
NumberInterface left = (NumberInterface) children[0];
|
||||
NumberInterface right = (NumberInterface) children[1];
|
||||
Function function = abacus.getPluginManager().operatorFor(((BinaryInfixNode) node).getOperation()).getFunction();
|
||||
Function function = abacus.getPluginManager().operatorFor(((BinaryNode) node).getOperation()).getFunction();
|
||||
if (function == null) return null;
|
||||
return function.apply(left, right);
|
||||
} else if (node instanceof UnaryPrefixNode) {
|
||||
} else if (node instanceof UnaryNode) {
|
||||
NumberInterface child = (NumberInterface) children[0];
|
||||
Function functionn = abacus.getPluginManager().operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction();
|
||||
Function functionn = abacus.getPluginManager().operatorFor(((UnaryNode) 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 UnaryPrefixNode extends TreeNode {
|
||||
public class UnaryNode extends TreeNode {
|
||||
|
||||
/**
|
||||
* The operation this node will apply.
|
||||
@@ -16,7 +16,7 @@ public class UnaryPrefixNode extends TreeNode {
|
||||
*
|
||||
* @param operation the operation for this node.
|
||||
*/
|
||||
public UnaryPrefixNode(String operation) {
|
||||
public UnaryNode(String operation) {
|
||||
this(operation, null);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class UnaryPrefixNode extends TreeNode {
|
||||
* @param operation the operation for this node.
|
||||
* @param applyTo the node to apply the function to.
|
||||
*/
|
||||
public UnaryPrefixNode(String operation, TreeNode applyTo) {
|
||||
public UnaryNode(String operation, TreeNode applyTo) {
|
||||
this.operation = operation;
|
||||
this.applyTo = applyTo;
|
||||
}
|
||||
63
src/main/resources/abacus.fxml
Normal file
63
src/main/resources/abacus.fxml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<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">
|
||||
<BorderPane>
|
||||
<center>
|
||||
<TableView fx:id="historyTable">
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||
</columnResizePolicy>
|
||||
<columns>
|
||||
<TableColumn fx:id="inputColumn" text="Input" sortable="false"/>
|
||||
<TableColumn fx:id="parsedColumn" text="Parsed" sortable="false"/>
|
||||
<TableColumn fx:id="outputColumn" text="Output" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
</center>
|
||||
<bottom>
|
||||
<VBox>
|
||||
<ScrollPane prefHeight="50" vbarPolicy="NEVER">
|
||||
<padding>
|
||||
<Insets top="10" bottom="10" left="10" right="10"/>
|
||||
</padding>
|
||||
<Text fx:id="outputText"/>
|
||||
</ScrollPane>
|
||||
<TextField fx:id="inputField" onAction="#performCalculation"/>
|
||||
<Button fx:id="inputButton" text="Calculate" maxWidth="Infinity"
|
||||
onAction="#performCalculation"/>
|
||||
</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"/>
|
||||
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2" hgap="10" vgap="10">
|
||||
<Button text="Apply" onAction="#performSave"/>
|
||||
<Button text="Reload Plugins" onAction="#performReload"/>
|
||||
<Button text="Apply and Reload" onAction="#performSaveAndReload"/>
|
||||
</FlowPane>
|
||||
</GridPane>
|
||||
</Tab>
|
||||
</TabPane>
|
||||
</center>
|
||||
|
||||
</BorderPane>
|
||||
Reference in New Issue
Block a user