1
0
mirror of https://github.com/DanilaFe/abacus synced 2026-01-25 16:15:19 +00:00

Compare commits

..

9 Commits

8 changed files with 66 additions and 76 deletions

View File

@@ -5,7 +5,6 @@ import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxListCell;
@@ -73,8 +72,6 @@ public class AbacusController implements PluginListener {
@FXML
private Tab settingsTab;
@FXML
private Tab functionListTab;
@FXML
private TableView<HistoryModel> historyTable;
@FXML
private TableColumn<HistoryModel, String> inputColumn;
@@ -96,10 +93,6 @@ public class AbacusController implements PluginListener {
private ListView<ToggleablePlugin> enabledPluginView;
@FXML
private TextField computationLimitField;
@FXML
private ListView<String> functionListView;
@FXML
private TextField functionListSearchField;
/**
* The list of history entries, created by the users.
@@ -117,14 +110,6 @@ public class AbacusController implements PluginListener {
* and, when reloaded, get added to the plugin manager's black list.
*/
private ObservableList<ToggleablePlugin> enabledPlugins;
/**
* The list of functions that are registered in the calculator.
*/
private ObservableList<String> functionList;
/**
* The filtered list displayed to the user.
*/
private FilteredList<String> functionFilter;
/**
* The abacus instance used for changing the plugin configuration.
@@ -228,11 +213,6 @@ public class AbacusController implements PluginListener {
}
});
functionList = FXCollections.observableArrayList();
functionFilter = new FilteredList<>(functionList, (s) -> true);
functionListView.setItems(functionFilter);
functionListSearchField.textProperty().addListener((observable, oldValue, newValue) ->
functionFilter.setPredicate((newValue.length() == 0) ? ((s) -> true) : ((s) -> s.contains(newValue))));
historyData = FXCollections.observableArrayList();
historyTable.setItems(historyData);
numberImplementationOptions = FXCollections.observableArrayList();
@@ -347,13 +327,10 @@ public class AbacusController implements PluginListener {
plugin.enabledProperty().addListener(e -> changesMade = true);
enabledPlugins.add(plugin);
}
functionList.addAll(manager.getAllFunctions());
functionList.sort(String::compareTo);
}
@Override
public void onUnload(PluginManager manager) {
functionList.clear();
enabledPlugins.clear();
numberImplementationOptions.clear();
}

View File

@@ -128,5 +128,9 @@ public class NaiveNumber extends NumberInterface {
return Double.toString(Math.round(value * shiftBy) / shiftBy);
}
@Override
public NumberInterface getMaxError(){
return new NaiveNumber(Math.pow(10, -18));
}
}

View File

@@ -182,7 +182,7 @@ public abstract class NumberInterface {
* Also, checks if the thread has been interrupted, and if so, throws
* an exception.
*
* @return the least integer bigger or equal to the number, if int can hold the value.
* @return the least integer bigger or equal to the number.
*/
public final NumberInterface ceiling(){
checkInterrupted();
@@ -192,7 +192,7 @@ public abstract class NumberInterface {
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest integer smaller or equal the number, if int can hold the value.
* @return the greatest integer smaller or equal the number.
*/
protected abstract NumberInterface floorInternal();
@@ -201,7 +201,7 @@ public abstract class NumberInterface {
* Also, checks if the thread has been interrupted, and if so, throws
* an exception.
*
* @return the greatest int smaller or equal to the number, if int can hold the value.
* @return the greatest int smaller than or equal to the number.
*/
public final NumberInterface floor(){
checkInterrupted();
@@ -216,7 +216,7 @@ public abstract class NumberInterface {
protected abstract NumberInterface fractionalPartInternal();
/**
* Returns the fractional part of the number.
* Returns the fractional part of the number, specifically x - floor(x).
* Also, checks if the thread has been interrupted,
* and if so, throws an exception.
* @return the fractional part of the number.
@@ -255,4 +255,11 @@ public abstract class NumberInterface {
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();
}

View File

@@ -1,7 +1,7 @@
package org.nwapw.abacus.number;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.math.MathContext;
/**
* 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);
/**
* 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.
*/
@@ -48,7 +63,7 @@ public class PreciseNumber extends NumberInterface {
@Override
public int getMaxPrecision() {
return 65;
return internalContext.getPrecision();
}
@Override
@@ -58,7 +73,7 @@ public class PreciseNumber extends NumberInterface {
@Override
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
@@ -113,19 +128,18 @@ public class PreciseNumber extends NumberInterface {
String str = value.toPlainString();
int decimalIndex = str.indexOf('.');
if (decimalIndex != -1) {
return new PreciseNumber(str.substring(0, decimalIndex));
NumberInterface floor = new PreciseNumber(str.substring(0, decimalIndex));
if(signum() == -1){
floor = floor.subtract(ONE);
}
return floor;
}
return this;
}
@Override
public NumberInterface fractionalPartInternal() {
String str = value.toPlainString();
int decimalIndex = str.indexOf('.');
if (decimalIndex != -1) {
return new PreciseNumber(str.substring(decimalIndex + 1));
}
return ZERO;
return this.subtractInternal(floorInternal());
}
@Override
@@ -148,7 +162,11 @@ public class PreciseNumber extends NumberInterface {
@Override
public String toString() {
BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP);
return rounded.stripTrailingZeros().toPlainString();
return value.round(outputContext).toString();
}
@Override
public NumberInterface getMaxError(){
return new PreciseNumber(value.ulp()).multiplyInternal(TEN.intPowInternal(value.precision()-internalContext.getPrecision()));
}
}

View File

@@ -209,9 +209,6 @@ public class PluginManager {
if (disabledPlugins.contains(plugin.getClass().getName())) continue;
plugin.disable();
}
registeredFunctions.clear();
registeredOperators.clear();
registeredNumberImplementations.clear();
cachedInterfaceImplementations.clear();
cachedPi.clear();
listeners.forEach(e -> e.onUnload(this));

View File

@@ -188,7 +188,7 @@ public class StandardPlugin extends Plugin {
*/
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).
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
int n = 1;
@@ -207,7 +207,7 @@ public class StandardPlugin extends Plugin {
* @return the value of log(2) with the appropriate precision.
*/
private NumberInterface getLog2(NumberInterface number) {
NumberInterface maxError = getMaxError(number);
NumberInterface maxError = number.getMaxError();
//NumberInterface errorBound = fromInt(number.getClass(), 1);
//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.
@@ -301,16 +301,11 @@ public class StandardPlugin extends Plugin {
@Override
protected NumberInterface applyInternal(NumberInterface[] params) {
NumberInterface maxError = getMaxError(params[0]);
NumberInterface maxError = params[0].getMaxError();
int n = 0;
if (params[0].signum() <= 0) {
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
n++;
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
sum = sum.add(currentTerm);
}
return sum;
if (params[0].signum() < 0) {
NumberInterface[] negatedParams = {params[0].negate()};
return fromInt(params[0].getClass(), 1).divide(applyInternal(negatedParams));
} else {
//We need n such that x^(n+1) * 3^ceil(x) <= maxError * (n+1)!.
//right and left refer to lhs and rhs in the above inequality.
@@ -340,7 +335,8 @@ public class StandardPlugin extends Plugin {
protected boolean matchesParams(NumberInterface[] params) {
return params.length == 2
&& !(params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
&& params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) == 0);
&& params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) == 0)
&& !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0);
}
@Override
@@ -349,6 +345,13 @@ public class StandardPlugin extends Plugin {
return NaiveNumber.ZERO.promoteTo(params[0].getClass());
else if (params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0)
return NaiveNumber.ONE.promoteTo(params[1].getClass());
//Detect integer bases:
if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 0
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0){
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams));
}
return FUNCTION_EXP.apply(FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0])).multiply(params[1]));
}
});
@@ -468,16 +471,6 @@ public class StandardPlugin extends Plugin {
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
* computes factorials of non-negative integers.
@@ -509,7 +502,7 @@ public class StandardPlugin extends Plugin {
*/
private static NumberInterface sinTaylor(NumberInterface x) {
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
NumberInterface maxError = getMaxError(x);
NumberInterface maxError = x.getMaxError();
int n = 1;
do {
n += 2;

View File

@@ -60,15 +60,6 @@
</FlowPane>
</GridPane>
</Tab>
<Tab fx:id="functionListTab" text="Functions" closable="false">
<VBox spacing="10">
<padding>
<Insets left="10" right="10" top="10" bottom="10"/>
</padding>
<TextField fx:id="functionListSearchField" maxWidth="Infinity"/>
<ListView maxWidth="Infinity" fx:id="functionListView"/>
</VBox>
</Tab>
</TabPane>
</center>

View File

@@ -88,8 +88,8 @@ public class CalculationTests {
public void testExp(){
testOutput("exp0", "exp(0)", "1");
testOutput("exp1", "exp(1)", "2.718281828459045235360287471352662497757247");
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
testOutput("exp300", "exp(300)", "19424263952412559365842088360176992193662086");
testOutput("exp300", "exp(300)", "1.9424263952412559365842088360176992193662086");
testOutput("exp(-500)", "exp((500)`)", "7.1245764067412855315491573771227552469277568");
}
@Test
@@ -99,6 +99,9 @@ public class CalculationTests {
testOutput("2^1", "(2^1)", "2");
testOutput("2^-1", "(2^(1)`)", "0.5");
testOutput("2^50", "(2^50)", "112589990684262");
testOutput("7^(-sqrt2*17)", "(7^((sqrt(2)*17))`)", "4.81354609155297814551845300063563");
testEvalError("0^0", "(0^0)");
testEvalError("(-13)^.9999", "((13)`^0.9999)");
}
}