mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-25 16:15:19 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e6cf08ec2 | ||
|
|
fee9f091fd | ||
|
|
4226df72f5 | ||
|
|
1f0e6a7ce4 | ||
|
|
efe76a6fdc | ||
|
|
ca6d8d2ba2 | ||
|
|
1d6957c4d9 | ||
|
|
b6e4c6d2ea | ||
|
|
f8b3559cec | ||
|
|
4cf4ba98a8 | ||
| 12710c625b | |||
| e71b037195 | |||
| fe92929856 | |||
|
|
68fbcd2d7c | ||
|
|
ed92b382f0 |
@@ -216,7 +216,7 @@ public class AbacusController implements PluginListener {
|
|||||||
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
|
Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
|
||||||
param -> new CopyableCell<>();
|
param -> new CopyableCell<>();
|
||||||
Callback<ListView<ToggleablePlugin>, ListCell<ToggleablePlugin>> pluginCellFactory =
|
Callback<ListView<ToggleablePlugin>, ListCell<ToggleablePlugin>> pluginCellFactory =
|
||||||
param -> new CheckBoxListCell<>(ToggleablePlugin::enabledProperty, new StringConverter<ToggleablePlugin>() {
|
param -> new CheckBoxListCell<>(ToggleablePlugin::getEnabledProperty, new StringConverter<ToggleablePlugin>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(ToggleablePlugin object) {
|
public String toString(ToggleablePlugin object) {
|
||||||
return object.getClassName().substring(object.getClassName().lastIndexOf('.') + 1);
|
return object.getClassName().substring(object.getClassName().lastIndexOf('.') + 1);
|
||||||
@@ -224,7 +224,7 @@ public class AbacusController implements PluginListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ToggleablePlugin fromString(String string) {
|
public ToggleablePlugin fromString(String string) {
|
||||||
return new ToggleablePlugin(true, string);
|
return new ToggleablePlugin(string, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -243,11 +243,11 @@ public class AbacusController implements PluginListener {
|
|||||||
enabledPluginView.setItems(enabledPlugins);
|
enabledPluginView.setItems(enabledPlugins);
|
||||||
enabledPluginView.setCellFactory(pluginCellFactory);
|
enabledPluginView.setCellFactory(pluginCellFactory);
|
||||||
inputColumn.setCellFactory(cellFactory);
|
inputColumn.setCellFactory(cellFactory);
|
||||||
inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
|
inputColumn.setCellValueFactory(cell -> cell.getValue().getInputProperty());
|
||||||
parsedColumn.setCellFactory(cellFactory);
|
parsedColumn.setCellFactory(cellFactory);
|
||||||
parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
|
parsedColumn.setCellValueFactory(cell -> cell.getValue().getParsedProperty());
|
||||||
outputColumn.setCellFactory(cellFactory);
|
outputColumn.setCellFactory(cellFactory);
|
||||||
outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
|
outputColumn.setCellValueFactory(cell -> cell.getValue().getOutputProperty());
|
||||||
coreTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
coreTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
|
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
|
||||||
});
|
});
|
||||||
@@ -343,8 +343,8 @@ public class AbacusController implements PluginListener {
|
|||||||
numberImplementationBox.getSelectionModel().select(toSelect);
|
numberImplementationBox.getSelectionModel().select(toSelect);
|
||||||
for (Class<?> pluginClass : abacus.getPluginManager().getLoadedPluginClasses()) {
|
for (Class<?> pluginClass : abacus.getPluginManager().getLoadedPluginClasses()) {
|
||||||
String fullName = pluginClass.getName();
|
String fullName = pluginClass.getName();
|
||||||
ToggleablePlugin plugin = new ToggleablePlugin(!disabledPlugins.contains(fullName), fullName);
|
ToggleablePlugin plugin = new ToggleablePlugin(fullName, !disabledPlugins.contains(fullName));
|
||||||
plugin.enabledProperty().addListener(e -> changesMade = true);
|
plugin.getEnabledProperty().addListener(e -> changesMade = true);
|
||||||
enabledPlugins.add(plugin);
|
enabledPlugins.add(plugin);
|
||||||
}
|
}
|
||||||
functionList.addAll(manager.getAllFunctions());
|
functionList.addAll(manager.getAllFunctions());
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -128,5 +128,9 @@ public class NaiveNumber extends NumberInterface {
|
|||||||
return Double.toString(Math.round(value * shiftBy) / shiftBy);
|
return Double.toString(Math.round(value * shiftBy) / shiftBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface getMaxError(){
|
||||||
|
return new NaiveNumber(Math.pow(10, -18));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,4 +255,11 @@ public abstract class NumberInterface {
|
|||||||
return promoteToInternal(toClass);
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.nwapw.abacus.number;
|
package org.nwapw.abacus.number;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.MathContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A number that uses a BigDecimal to store its value,
|
* A number that uses a BigDecimal to store its value,
|
||||||
@@ -22,6 +22,21 @@ public class PreciseNumber extends NumberInterface {
|
|||||||
*/
|
*/
|
||||||
public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value of the PreciseNumber.
|
* The value of the PreciseNumber.
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +63,7 @@ public class PreciseNumber extends NumberInterface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMaxPrecision() {
|
public int getMaxPrecision() {
|
||||||
return 65;
|
return internalContext.getPrecision();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +73,7 @@ public class PreciseNumber extends NumberInterface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NumberInterface divideInternal(NumberInterface divisor) {
|
public NumberInterface divideInternal(NumberInterface divisor) {
|
||||||
return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.getMaxPrecision(), RoundingMode.HALF_UP));
|
return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, internalContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -147,7 +162,11 @@ public class PreciseNumber extends NumberInterface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP);
|
return value.round(outputContext).toString();
|
||||||
return rounded.stripTrailingZeros().toPlainString();
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInterface getMaxError(){
|
||||||
|
return new PreciseNumber(value.ulp()).multiplyInternal(TEN.intPowInternal(value.precision()-internalContext.getPrecision()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ public class StandardPlugin extends Plugin {
|
|||||||
*/
|
*/
|
||||||
private NumberInterface getLogPartialSum(NumberInterface x) {
|
private NumberInterface getLogPartialSum(NumberInterface x) {
|
||||||
|
|
||||||
NumberInterface maxError = getMaxError(x);
|
NumberInterface maxError = x.getMaxError();
|
||||||
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
||||||
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
||||||
int n = 1;
|
int n = 1;
|
||||||
@@ -207,7 +207,7 @@ public class StandardPlugin extends Plugin {
|
|||||||
* @return the value of log(2) with the appropriate precision.
|
* @return the value of log(2) with the appropriate precision.
|
||||||
*/
|
*/
|
||||||
private NumberInterface getLog2(NumberInterface number) {
|
private NumberInterface getLog2(NumberInterface number) {
|
||||||
NumberInterface maxError = getMaxError(number);
|
NumberInterface maxError = number.getMaxError();
|
||||||
//NumberInterface errorBound = fromInt(number.getClass(), 1);
|
//NumberInterface errorBound = fromInt(number.getClass(), 1);
|
||||||
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
||||||
//In the following, a=1/3^n, b=1/4^n, c = 1/n.
|
//In the following, a=1/3^n, b=1/4^n, c = 1/n.
|
||||||
@@ -301,16 +301,11 @@ public class StandardPlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NumberInterface applyInternal(NumberInterface[] params) {
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
NumberInterface maxError = getMaxError(params[0]);
|
NumberInterface maxError = params[0].getMaxError();
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (params[0].signum() <= 0) {
|
if (params[0].signum() < 0) {
|
||||||
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
|
NumberInterface[] negatedParams = {params[0].negate()};
|
||||||
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
|
return fromInt(params[0].getClass(), 1).divide(applyInternal(negatedParams));
|
||||||
n++;
|
|
||||||
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
|
|
||||||
sum = sum.add(currentTerm);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
} else {
|
} else {
|
||||||
//We need n such that x^(n+1) * 3^ceil(x) <= maxError * (n+1)!.
|
//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.
|
//right and left refer to lhs and rhs in the above inequality.
|
||||||
@@ -352,7 +347,7 @@ public class StandardPlugin extends Plugin {
|
|||||||
return NaiveNumber.ONE.promoteTo(params[1].getClass());
|
return NaiveNumber.ONE.promoteTo(params[1].getClass());
|
||||||
//Detect integer bases:
|
//Detect integer bases:
|
||||||
if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0
|
if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0
|
||||||
&& FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 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){
|
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0){
|
||||||
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
|
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
|
||||||
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams));
|
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams));
|
||||||
@@ -456,6 +451,144 @@ public class StandardPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arcsine function (return type in radians).
|
||||||
|
*/
|
||||||
|
public final Function functionArcsin = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1
|
||||||
|
&& FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if(FUNCTION_ABS.apply(params[0]).compareTo(new NaiveNumber(0.8).promoteTo(params[0].getClass())) >= 0){
|
||||||
|
NumberInterface[] newParams = {FUNCTION_SQRT.apply(fromInt(params[0].getClass(), 1).subtract(params[0].multiply(params[0])))};
|
||||||
|
return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
|
||||||
|
.subtract(applyInternal(newParams)).multiply(fromInt(params[0].getClass(), params[0].signum()));
|
||||||
|
}
|
||||||
|
NumberInterface currentTerm = params[0], sum = currentTerm,
|
||||||
|
multiplier = currentTerm.multiply(currentTerm), summandBound = sum.getMaxError().multiply(fromInt(sum.getClass(), 1).subtract(multiplier)),
|
||||||
|
power = currentTerm, coefficient = fromInt(params[0].getClass(), 1);
|
||||||
|
int exponent = 1;
|
||||||
|
while(FUNCTION_ABS.apply(currentTerm).compareTo(summandBound) > 0){
|
||||||
|
exponent += 2;
|
||||||
|
power = power.multiply(multiplier);
|
||||||
|
coefficient = coefficient.multiply(fromInt(params[0].getClass(), exponent-2))
|
||||||
|
.divide(fromInt(params[0].getClass(), exponent - 1));
|
||||||
|
currentTerm = power.multiply(coefficient).divide(fromInt(power.getClass(), exponent));
|
||||||
|
sum = sum.add(currentTerm);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arccosine function.
|
||||||
|
*/
|
||||||
|
public final Function functionArccos = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
|
||||||
|
.subtract(functionArcsin.apply(params));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arccosecant function.
|
||||||
|
*/
|
||||||
|
public final Function functionArccsc = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
NumberInterface[] reciprocalParamArr = {fromInt(params[0].getClass(), 1).divide(params[0])};
|
||||||
|
return functionArcsin.apply(reciprocalParamArr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arcsecant function.
|
||||||
|
*/
|
||||||
|
public final Function functionArcsec = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1 && FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), 1)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
NumberInterface[] reciprocalParamArr = {fromInt(params[0].getClass(), 1).divide(params[0])};
|
||||||
|
return functionArccos.apply(reciprocalParamArr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arctangent function.
|
||||||
|
*/
|
||||||
|
public final Function functionArctan = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
if(params[0].signum() == -1){
|
||||||
|
NumberInterface[] negatedParams = {params[0].negate()};
|
||||||
|
return applyInternal(negatedParams).negate();
|
||||||
|
}
|
||||||
|
if(params[0].compareTo(fromInt(params[0].getClass(), 1)) > 0){
|
||||||
|
NumberInterface[] reciprocalParams = {fromInt(params[0].getClass(), 1).divide(params[0])};
|
||||||
|
return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
|
||||||
|
.subtract(applyInternal(reciprocalParams));
|
||||||
|
}
|
||||||
|
if(params[0].compareTo(fromInt(params[0].getClass(), 1)) == 0){
|
||||||
|
return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 4));
|
||||||
|
}
|
||||||
|
if(params[0].compareTo(new NaiveNumber(0.9).promoteTo(params[0].getClass())) >= 0){
|
||||||
|
NumberInterface[] newParams = {params[0].multiply(fromInt(params[0].getClass(),2 ))
|
||||||
|
.divide(fromInt(params[0].getClass(), 1).subtract(params[0].multiply(params[0])))};
|
||||||
|
return applyInternal(newParams).divide(fromInt(params[0].getClass(), 2));
|
||||||
|
}
|
||||||
|
NumberInterface currentPower = params[0], currentTerm = currentPower, sum = currentTerm,
|
||||||
|
maxError = params[0].getMaxError(), multiplier = currentPower.multiply(currentPower).negate();
|
||||||
|
int n = 1;
|
||||||
|
while(FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0){
|
||||||
|
n += 2;
|
||||||
|
currentPower = currentPower.multiply(multiplier);
|
||||||
|
currentTerm = currentPower.divide(fromInt(currentPower.getClass(), n));
|
||||||
|
sum = sum.add(currentTerm);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arccotangent function. Range: (0, pi).
|
||||||
|
*/
|
||||||
|
public final Function functionArccot = new Function() {
|
||||||
|
@Override
|
||||||
|
protected boolean matchesParams(NumberInterface[] params) {
|
||||||
|
return params.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
||||||
|
return piFor(params[0].getClass()).divide(fromInt(params[0].getClass(), 2))
|
||||||
|
.subtract(functionArctan.apply(params));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public StandardPlugin(PluginManager manager) {
|
public StandardPlugin(PluginManager manager) {
|
||||||
super(manager);
|
super(manager);
|
||||||
}
|
}
|
||||||
@@ -476,16 +609,6 @@ public class StandardPlugin extends Plugin {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum error based on the precision of the class of number.
|
|
||||||
*
|
|
||||||
* @param number Any instance of the NumberInterface in question (should return an appropriate precision).
|
|
||||||
* @return the maximum error.
|
|
||||||
*/
|
|
||||||
private static NumberInterface getMaxError(NumberInterface number) {
|
|
||||||
return fromInt(number.getClass(), 10).intPow(-number.getMaxPrecision());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factorial function that uses memoization for each number class; it efficiently
|
* A factorial function that uses memoization for each number class; it efficiently
|
||||||
* computes factorials of non-negative integers.
|
* computes factorials of non-negative integers.
|
||||||
@@ -517,7 +640,7 @@ public class StandardPlugin extends Plugin {
|
|||||||
*/
|
*/
|
||||||
private static NumberInterface sinTaylor(NumberInterface x) {
|
private static NumberInterface sinTaylor(NumberInterface x) {
|
||||||
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
|
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
|
||||||
NumberInterface maxError = getMaxError(x);
|
NumberInterface maxError = x.getMaxError();
|
||||||
int n = 1;
|
int n = 1;
|
||||||
do {
|
do {
|
||||||
n += 2;
|
n += 2;
|
||||||
@@ -583,6 +706,12 @@ public class StandardPlugin extends Plugin {
|
|||||||
registerFunction("sec", functionSec);
|
registerFunction("sec", functionSec);
|
||||||
registerFunction("csc", functionCsc);
|
registerFunction("csc", functionCsc);
|
||||||
registerFunction("cot", functionCot);
|
registerFunction("cot", functionCot);
|
||||||
|
registerFunction("arcsin", functionArcsin);
|
||||||
|
registerFunction("arccos", functionArccos);
|
||||||
|
registerFunction("arccsc", functionArccsc);
|
||||||
|
registerFunction("arcsec", functionArcsec);
|
||||||
|
registerFunction("arctan", functionArctan);
|
||||||
|
registerFunction("arccot", functionArccot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
32
src/main/kotlin/org/nwapw/abacus/fx/HistoryModel.kt
Normal file
32
src/main/kotlin/org/nwapw/abacus/fx/HistoryModel.kt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package org.nwapw.abacus.fx
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model representing an input / output in the calculator.
|
||||||
|
*
|
||||||
|
* The HistoryModel class stores a record of a single user-provided input,
|
||||||
|
* its parsed form as it was interpreted by the calculator, and the output
|
||||||
|
* that was provided by the calculator. These are represented as properties
|
||||||
|
* to allow easy access by JavaFX cells.
|
||||||
|
*
|
||||||
|
* @param input the user input
|
||||||
|
* @param parsed the parsed version of the input.
|
||||||
|
* @param output the output string.
|
||||||
|
*/
|
||||||
|
class HistoryModel(input: String, parsed: String, output: String){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property that holds the input.
|
||||||
|
*/
|
||||||
|
val inputProperty = SimpleStringProperty(input)
|
||||||
|
/**
|
||||||
|
* The property that holds the parsed input.
|
||||||
|
*/
|
||||||
|
val parsedProperty = SimpleStringProperty(parsed)
|
||||||
|
/**
|
||||||
|
* The property that holds the output.
|
||||||
|
*/
|
||||||
|
val outputProperty = SimpleStringProperty(output)
|
||||||
|
|
||||||
|
}
|
||||||
31
src/main/kotlin/org/nwapw/abacus/fx/ToggleablePlugin.kt
Normal file
31
src/main/kotlin/org/nwapw/abacus/fx/ToggleablePlugin.kt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package org.nwapw.abacus.fx
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model representing a plugin that can be disabled or enabled.
|
||||||
|
*
|
||||||
|
* ToggleablePlugin is a model that is used to present to the user the option
|
||||||
|
* of disabling / enabling plugins. The class name in this plugin is stored if
|
||||||
|
* its "enabledPropery" is false, essentially blacklisting the plugin.
|
||||||
|
*
|
||||||
|
* @param className the name of the class that this model concerns.
|
||||||
|
* @param enabled whether or not the model should start enabled.
|
||||||
|
*/
|
||||||
|
class ToggleablePlugin (val className: String, enabled: Boolean) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property used to interact with JavaFX components.
|
||||||
|
*/
|
||||||
|
val enabledProperty = SimpleBooleanProperty(enabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this plugin is currently enabled or not.
|
||||||
|
*
|
||||||
|
* @return true if it is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
fun isEnabled(): Boolean {
|
||||||
|
return enabledProperty.value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
src/test/java/org/nwapw/abacus/tests/CalculationTests.java
Normal file → Executable file
7
src/test/java/org/nwapw/abacus/tests/CalculationTests.java
Normal file → Executable file
@@ -88,8 +88,8 @@ public class CalculationTests {
|
|||||||
public void testExp(){
|
public void testExp(){
|
||||||
testOutput("exp0", "exp(0)", "1");
|
testOutput("exp0", "exp(0)", "1");
|
||||||
testOutput("exp1", "exp(1)", "2.718281828459045235360287471352662497757247");
|
testOutput("exp1", "exp(1)", "2.718281828459045235360287471352662497757247");
|
||||||
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
|
testOutput("exp300", "exp(300)", "1.9424263952412559365842088360176992193662086");
|
||||||
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
|
testOutput("exp(-500)", "exp((500)`)", "7.1245764067412855315491573771227552469277568");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -99,6 +99,9 @@ public class CalculationTests {
|
|||||||
testOutput("2^1", "(2^1)", "2");
|
testOutput("2^1", "(2^1)", "2");
|
||||||
testOutput("2^-1", "(2^(1)`)", "0.5");
|
testOutput("2^-1", "(2^(1)`)", "0.5");
|
||||||
testOutput("2^50", "(2^50)", "112589990684262");
|
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)");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user