2017-07-25 21:50:41 -07:00
|
|
|
package org.nwapw.abacus.plugin;
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-07-25 21:57:14 -07:00
|
|
|
import org.nwapw.abacus.function.Function;
|
2017-07-25 21:50:41 -07:00
|
|
|
import org.nwapw.abacus.number.NaiveNumber;
|
|
|
|
import org.nwapw.abacus.number.NumberInterface;
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-07-26 15:26:06 -07:00
|
|
|
import java.util.function.BiFunction;
|
|
|
|
|
2017-07-26 10:10:37 -07:00
|
|
|
/**
|
|
|
|
* The plugin providing standard functions such as addition and subtraction to
|
|
|
|
* the calculator.
|
|
|
|
*/
|
2017-07-25 21:50:41 -07:00
|
|
|
public class StandardPlugin extends Plugin {
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
public StandardPlugin(PluginManager manager) {
|
|
|
|
super(manager);
|
|
|
|
}
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
@Override
|
|
|
|
public void load() {
|
|
|
|
registerFunction("+", new Function() {
|
2017-07-24 14:48:16 -07:00
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params.length >= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface sum = params[0];
|
2017-07-24 14:48:16 -07:00
|
|
|
for(int i = 1; i < params.length; i++){
|
|
|
|
sum = sum.add(params[i]);
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
registerFunction("-", new Function() {
|
2017-07-24 14:48:16 -07:00
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params.length == 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params[0].subtract(params[1]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
registerFunction("*", new Function() {
|
2017-07-24 14:48:16 -07:00
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params.length >= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-07-25 14:08:46 -07:00
|
|
|
NumberInterface product = params[0];
|
2017-07-24 14:48:16 -07:00
|
|
|
for(int i = 1; i < params.length; i++){
|
|
|
|
product = product.multiply(params[i]);
|
|
|
|
}
|
|
|
|
return product;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
registerFunction("/", new Function() {
|
2017-07-24 14:48:16 -07:00
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params.length == 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-07-24 14:48:16 -07:00
|
|
|
return params[0].divide(params[1]);
|
|
|
|
}
|
|
|
|
});
|
2017-07-25 11:12:25 -07:00
|
|
|
|
2017-07-25 21:50:41 -07:00
|
|
|
registerFunction("!", new Function() {
|
2017-07-27 10:03:26 -07:00
|
|
|
//private ArrayLi
|
2017-07-25 11:12:25 -07:00
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-07-25 11:12:25 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-25 13:58:09 -07:00
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-07-26 11:05:12 -07:00
|
|
|
if(params[0].signum() == 0){
|
|
|
|
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
|
|
|
|
}
|
2017-07-25 13:58:09 -07:00
|
|
|
NumberInterface factorial = params[0];
|
|
|
|
NumberInterface multiplier = params[0];
|
2017-07-25 11:12:25 -07:00
|
|
|
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
|
2017-07-25 13:58:09 -07:00
|
|
|
while((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1){
|
2017-07-25 11:12:25 -07:00
|
|
|
factorial = factorial.multiply(multiplier);
|
|
|
|
}
|
|
|
|
return factorial;
|
|
|
|
}
|
|
|
|
});
|
2017-07-26 11:05:12 -07:00
|
|
|
|
2017-07-27 10:03:26 -07:00
|
|
|
registerFunction("abs", new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
return params[0].multiply((new NaiveNumber(params[0].signum())).promoteTo(params[0].getClass()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-07-26 15:26:06 -07:00
|
|
|
registerFunction("exp", new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
return sumSeries(params[0], StandardPlugin.this::getExpSeriesTerm, getNTermsExp(getMaxError(params[0]), params[0]));
|
|
|
|
}
|
|
|
|
});
|
2017-07-26 11:05:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the nth term of the Taylor series (centered at 0) of e^x
|
|
|
|
* @param n the term required (n >= 0).
|
|
|
|
* @param x the real number at which the series is evaluated.
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private NumberInterface getExpSeriesTerm(int n, NumberInterface x){
|
|
|
|
return x.intPow(n).divide(this.getFunction("!").apply((new NaiveNumber(n)).promoteTo(x.getClass())));
|
2017-07-24 13:44:38 -07:00
|
|
|
}
|
|
|
|
|
2017-07-26 15:26:06 -07:00
|
|
|
/**
|
|
|
|
* Returns the number of terms needed to evaluate the exponential function (at x)
|
|
|
|
* such that the error is at most maxError.
|
|
|
|
* @param maxError Maximum error permissible (This should probably be positive.)
|
|
|
|
* @param x where the function is evaluated.
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private int getNTermsExp(NumberInterface maxError, NumberInterface x){
|
2017-07-27 10:03:26 -07:00
|
|
|
//We need n such that |x^(n+1)| <= (n+1)! * maxError
|
2017-07-26 15:26:06 -07:00
|
|
|
//The variables LHS and RHS refer to the above inequality.
|
|
|
|
int n = 0;
|
2017-07-27 10:03:26 -07:00
|
|
|
x = this.getFunction("abs").apply(x);
|
|
|
|
NumberInterface LHS = x, RHS = maxError;
|
2017-07-26 15:26:06 -07:00
|
|
|
while(LHS.compareTo(RHS) > 0){
|
|
|
|
n++;
|
|
|
|
LHS = LHS.multiply(x);
|
2017-07-27 10:03:26 -07:00
|
|
|
RHS = RHS.multiply(new NaiveNumber(n+1).promoteTo(RHS.getClass()));
|
2017-07-26 15:26:06 -07:00
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a partial sum of a series whose terms are given by the nthTermFunction, evaluated at x.
|
|
|
|
* @param x the value at which the series is evaluated.
|
|
|
|
* @param nthTermFunction the function that returns the nth term of the series, in the format term(x, n).
|
|
|
|
* @param n the number of terms in the partial sum.
|
|
|
|
* @return the value of the partial sum that has the same class as x.
|
|
|
|
*/
|
|
|
|
private NumberInterface sumSeries(NumberInterface x, BiFunction<Integer, NumberInterface, NumberInterface> nthTermFunction, int n){
|
|
|
|
NumberInterface sum = NaiveNumber.ZERO.promoteTo(x.getClass());
|
|
|
|
for(int i = 0; i <= n; i++){
|
|
|
|
sum = sum.add(nthTermFunction.apply(i, x));
|
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
private NumberInterface getMaxError(NumberInterface number){
|
|
|
|
return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.precision());
|
|
|
|
}
|
|
|
|
|
2017-07-24 13:44:38 -07:00
|
|
|
}
|