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-27 14:15:45 -07:00
|
|
|
import org.nwapw.abacus.function.Operator;
|
|
|
|
import org.nwapw.abacus.function.OperatorAssociativity;
|
2017-07-28 10:26:25 -07:00
|
|
|
import org.nwapw.abacus.function.OperatorType;
|
2017-07-25 21:50:41 -07:00
|
|
|
import org.nwapw.abacus.number.NaiveNumber;
|
|
|
|
import org.nwapw.abacus.number.NumberInterface;
|
2017-07-28 22:17:22 -07:00
|
|
|
import org.nwapw.abacus.number.PreciseNumber;
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-07-31 14:49:25 -07:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
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-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The addition operator, +
|
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Operator OP_ADD = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0, new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length >= 1;
|
|
|
|
}
|
2017-07-24 14:48:16 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface sum = params[0];
|
2017-07-30 21:11:32 -07:00
|
|
|
for (int i = 1; i < params.length; i++) {
|
2017-07-30 21:10:11 -07:00
|
|
|
sum = sum.add(params[i]);
|
2017-07-24 14:48:16 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
});
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The subtraction operator, -
|
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Operator OP_SUBTRACT = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0, new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 2;
|
|
|
|
}
|
2017-07-24 14:48:16 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
return params[0].subtract(params[1]);
|
2017-08-03 15:16:26 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
|
|
|
});
|
2017-08-02 11:26:59 -07:00
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
});
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The multiplication operator, *
|
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
public static final Operator OP_MULTIPLY = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length >= 1;
|
|
|
|
}
|
2017-07-24 14:48:16 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface product = params[0];
|
2017-07-30 21:11:32 -07:00
|
|
|
for (int i = 1; i < params.length; i++) {
|
2017-07-30 21:10:11 -07:00
|
|
|
product = product.multiply(params[i]);
|
2017-07-24 14:48:16 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
return product;
|
|
|
|
}
|
|
|
|
});
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The division operator, /
|
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-08-04 10:05:18 -07:00
|
|
|
return params.length == 2 && params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) != 0;
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-07-24 14:48:16 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-02 11:28:49 -07:00
|
|
|
return params[0].divide(params[1]);
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
|
|
|
});
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The factorial operator, !
|
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Operator OP_FACTORIAL = new Operator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0, new Function() {
|
|
|
|
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-08-04 10:33:55 -07:00
|
|
|
return params.length == 1
|
|
|
|
&& params[0].fractionalPart().compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
|
|
|
&& params[0].signum() >= 0;
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-07-27 16:55:18 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-07-30 21:11:32 -07:00
|
|
|
if (params[0].signum() == 0) {
|
2017-07-30 21:10:11 -07:00
|
|
|
return (new NaiveNumber(1)).promoteTo(params[0].getClass());
|
2017-07-27 16:55:18 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
NumberInterface factorial = params[0];
|
|
|
|
NumberInterface multiplier = params[0];
|
|
|
|
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
|
2017-08-05 13:26:29 -07:00
|
|
|
while ((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
factorial = factorial.multiply(multiplier);
|
2017-07-25 11:12:25 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
return factorial;
|
2017-07-27 13:39:19 -07:00
|
|
|
/*if(!storedList.containsKey(params[0].getClass())){
|
|
|
|
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
|
|
|
|
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
|
|
|
|
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
|
|
|
|
}*/
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
|
|
|
});
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The absolute value function, abs(-3) = 3
|
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Function FUNCTION_ABS = new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
2017-07-26 15:26:06 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
return params[0].multiply((new NaiveNumber(params[0].signum())).promoteTo(params[0].getClass()));
|
|
|
|
}
|
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
2017-07-31 14:49:25 -07:00
|
|
|
* The natural log function.
|
2017-07-30 21:15:01 -07:00
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Function FUNCTION_LN = new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
2017-08-04 10:05:18 -07:00
|
|
|
return params.length == 1 && params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) > 0;
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-07-27 13:04:41 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface param = params[0];
|
|
|
|
int powersOf2 = 0;
|
2017-08-05 13:26:29 -07:00
|
|
|
while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo(new NaiveNumber(0.1).promoteTo(param.getClass())) >= 0) {
|
|
|
|
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
param = param.divide(new NaiveNumber(2).promoteTo(param.getClass()));
|
|
|
|
powersOf2++;
|
2017-08-05 13:26:29 -07:00
|
|
|
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
break;
|
|
|
|
//No infinite loop for you.
|
2017-07-27 13:04:41 -07:00
|
|
|
}
|
2017-07-30 21:11:32 -07:00
|
|
|
} else {
|
2017-07-30 21:10:11 -07:00
|
|
|
param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass()));
|
|
|
|
powersOf2--;
|
2017-08-05 14:34:57 -07:00
|
|
|
if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != -1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
break;
|
|
|
|
//No infinite loop for you.
|
2017-07-27 13:04:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-05 13:26:29 -07:00
|
|
|
return getLog2(param).multiply((new NaiveNumber(powersOf2)).promoteTo(param.getClass())).add(getLogPartialSum(param));
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-07-27 13:04:41 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
/**
|
|
|
|
* Returns the partial sum of the Taylor series for logx (around x=1).
|
|
|
|
* Automatically determines the number of terms needed based on the precision of x.
|
|
|
|
* @param x value at which the series is evaluated. 0 < x < 2. (x=2 is convergent but impractical.)
|
|
|
|
* @return the partial sum.
|
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
private NumberInterface getLogPartialSum(NumberInterface x) {
|
2017-08-04 13:45:29 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
NumberInterface maxError = getMaxError(x);
|
|
|
|
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
|
2017-07-31 12:39:56 -07:00
|
|
|
NumberInterface currentNumerator = x, currentTerm = x, sum = x;
|
2017-07-30 21:10:11 -07:00
|
|
|
int n = 1;
|
2017-08-05 13:26:29 -07:00
|
|
|
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
|
2017-07-30 21:10:11 -07:00
|
|
|
n++;
|
2017-08-05 13:26:29 -07:00
|
|
|
currentNumerator = currentNumerator.multiply(x).negate();
|
2017-07-31 12:39:56 -07:00
|
|
|
currentTerm = currentNumerator.divide(new NaiveNumber(n).promoteTo(x.getClass()));
|
2017-07-30 21:10:11 -07:00
|
|
|
sum = sum.add(currentTerm);
|
2017-07-27 13:04:41 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
return sum;
|
|
|
|
}
|
2017-07-27 13:04:41 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
/**
|
|
|
|
* Returns natural log of 2 to the required precision of the class of number.
|
|
|
|
* @param number a number of the same type as the return type. (Used for precision.)
|
|
|
|
* @return the value of log(2) with the appropriate precision.
|
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
private NumberInterface getLog2(NumberInterface number) {
|
2017-07-30 21:10:11 -07:00
|
|
|
NumberInterface maxError = getMaxError(number);
|
|
|
|
//NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass());
|
|
|
|
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
|
|
|
|
//In the following, a=1/3^n, b=1/4^n, c = 1/n.
|
|
|
|
//a is also an error bound.
|
|
|
|
NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a;
|
|
|
|
NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass());
|
|
|
|
int n = 0;
|
2017-08-05 13:26:29 -07:00
|
|
|
while (a.compareTo(maxError) >= 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
n++;
|
|
|
|
a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass()));
|
|
|
|
b = b.divide((new NaiveNumber(4)).promoteTo(number.getClass()));
|
|
|
|
c = NaiveNumber.ONE.promoteTo(number.getClass()).divide((new NaiveNumber(n)).promoteTo(number.getClass()));
|
2017-08-05 13:26:29 -07:00
|
|
|
sum = sum.add(a.add(b).multiply(c));
|
2017-07-27 13:04:41 -07:00
|
|
|
}
|
2017-07-30 21:10:11 -07:00
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
2017-07-31 14:53:41 -07:00
|
|
|
* The square root function.
|
2017-07-30 21:15:01 -07:00
|
|
|
*/
|
2017-07-30 21:10:11 -07:00
|
|
|
public static final Function FUNCTION_SQRT = new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
2017-07-27 13:17:22 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
return OP_CARET.getFunction().apply(params[0], ((new NaiveNumber(0.5)).promoteTo(params[0].getClass())));
|
|
|
|
}
|
|
|
|
};
|
2017-08-04 13:20:57 -07:00
|
|
|
/**
|
|
|
|
* The implementation for double-based naive numbers.
|
|
|
|
*/
|
|
|
|
public static final NumberImplementation IMPLEMENTATION_NAIVE = new NumberImplementation(NaiveNumber.class, 0) {
|
|
|
|
@Override
|
|
|
|
public NumberInterface instanceForString(String string) {
|
|
|
|
return new NaiveNumber(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public NumberInterface instanceForPi() {
|
|
|
|
return new NaiveNumber(Math.PI);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* The implementation for the infinite-precision BigDecimal.
|
|
|
|
*/
|
|
|
|
public static final NumberImplementation IMPLEMENTATION_PRECISE = new NumberImplementation(PreciseNumber.class, 0) {
|
|
|
|
@Override
|
|
|
|
public NumberInterface instanceForString(String string) {
|
|
|
|
return new PreciseNumber(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public NumberInterface instanceForPi() {
|
|
|
|
NumberInterface C = FUNCTION_SQRT.apply(new PreciseNumber("10005")).multiply(new PreciseNumber("426880"));
|
|
|
|
NumberInterface M = PreciseNumber.ONE;
|
|
|
|
NumberInterface L = new PreciseNumber("13591409");
|
|
|
|
NumberInterface X = M;
|
|
|
|
NumberInterface sum = L;
|
|
|
|
int termsNeeded = C.getMaxPrecision() / 13 + 1;
|
|
|
|
|
|
|
|
NumberInterface lSummand = new PreciseNumber("545140134");
|
|
|
|
NumberInterface xMultiplier = new PreciseNumber("262537412")
|
|
|
|
.multiply(new PreciseNumber("1000000000"))
|
|
|
|
.add(new PreciseNumber("640768000"))
|
|
|
|
.negate();
|
|
|
|
for (int i = 0; i < termsNeeded; i++) {
|
|
|
|
M = M
|
|
|
|
.multiply(new NaiveNumber(12 * i + 2).promoteTo(PreciseNumber.class))
|
|
|
|
.multiply(new NaiveNumber(12 * i + 6).promoteTo(PreciseNumber.class))
|
|
|
|
.multiply(new NaiveNumber(12 * i + 10).promoteTo(PreciseNumber.class))
|
|
|
|
.divide(new NaiveNumber(Math.pow(i + 1, 3)).promoteTo(PreciseNumber.class));
|
|
|
|
L = L.add(lSummand);
|
|
|
|
X = X.multiply(xMultiplier);
|
|
|
|
sum = sum.add(M.multiply(L).divide(X));
|
|
|
|
}
|
|
|
|
return C.divide(sum);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
private static final HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> FACTORIAL_LISTS = new HashMap<>();
|
2017-08-04 14:29:24 -07:00
|
|
|
/**
|
|
|
|
* The exponential function, exp(1) = e^1 = 2.71...
|
|
|
|
*/
|
|
|
|
public static final Function FUNCTION_EXP = new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface maxError = getMaxError(params[0]);
|
|
|
|
int n = 0;
|
|
|
|
if (params[0].signum() <= 0) {
|
|
|
|
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
|
|
|
|
while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) {
|
|
|
|
n++;
|
|
|
|
currentTerm = currentTerm.multiply(params[0]).divide((new NaiveNumber(n)).promoteTo(params[0].getClass()));
|
|
|
|
sum = sum.add(currentTerm);
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
} else {
|
|
|
|
//We need n such that x^(n+1) * 3^ceil(x) <= maxError * (n+1)!.
|
|
|
|
//right and left refer to lhs and rhs in the above inequality.
|
|
|
|
NumberInterface sum = NaiveNumber.ONE.promoteTo(params[0].getClass());
|
|
|
|
NumberInterface nextNumerator = params[0];
|
2017-08-05 18:11:16 -07:00
|
|
|
NumberInterface left = params[0].multiply((new NaiveNumber(3)).promoteTo(params[0].getClass()));
|
|
|
|
left = intPow(left, left.getClass(), new NaiveNumber(params[0].ceiling().intValue()).promoteTo(left.getClass()));
|
|
|
|
NumberInterface right = maxError;
|
2017-08-04 14:29:24 -07:00
|
|
|
do {
|
|
|
|
sum = sum.add(nextNumerator.divide(factorial(params[0].getClass(), n + 1)));
|
|
|
|
n++;
|
|
|
|
nextNumerator = nextNumerator.multiply(params[0]);
|
|
|
|
left = left.multiply(params[0]);
|
|
|
|
NumberInterface nextN = (new NaiveNumber(n + 1)).promoteTo(params[0].getClass());
|
|
|
|
right = right.multiply(nextN);
|
|
|
|
//System.out.println(left + ", " + right);
|
|
|
|
}
|
|
|
|
while (left.compareTo(right) > 0);
|
|
|
|
//System.out.println(n+1);
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* The caret / pow operator, ^
|
|
|
|
*/
|
|
|
|
public static final Operator OP_CARET = new Operator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2, new Function() {
|
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 2
|
|
|
|
&& !(params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0
|
|
|
|
&& params[1].compareTo(NaiveNumber.ZERO.promoteTo(params[1].getClass())) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-05 13:26:29 -07:00
|
|
|
if (params[0].compareTo(NaiveNumber.ZERO.promoteTo(params[0].getClass())) == 0)
|
2017-08-04 14:29:24 -07:00
|
|
|
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());
|
2017-08-05 13:26:29 -07:00
|
|
|
return FUNCTION_EXP.apply(FUNCTION_LN.apply(FUNCTION_ABS.apply(params[0])).multiply(params[1]));
|
2017-08-04 14:29:24 -07:00
|
|
|
}
|
|
|
|
});
|
2017-08-02 15:13:04 -07:00
|
|
|
/**
|
|
|
|
* The sine function (the argument is interpreted in radians).
|
|
|
|
*/
|
2017-08-04 09:55:24 -07:00
|
|
|
public final Function functionSin = new Function() {
|
2017-08-01 15:36:54 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
|
|
|
NumberInterface pi = getPi(params[0].getClass());
|
|
|
|
NumberInterface twoPi = pi.multiply(new NaiveNumber(2).promoteTo(pi.getClass()));
|
2017-08-04 09:55:24 -07:00
|
|
|
NumberInterface theta = getSmallAngle(params[0], pi);
|
2017-08-02 13:27:39 -07:00
|
|
|
//System.out.println(theta);
|
2017-08-04 13:20:57 -07:00
|
|
|
if (theta.compareTo(pi.multiply(new NaiveNumber(1.5).promoteTo(twoPi.getClass()))) >= 0) {
|
2017-08-01 15:36:54 -07:00
|
|
|
theta = theta.subtract(twoPi);
|
2017-08-04 13:20:57 -07:00
|
|
|
} else if (theta.compareTo(pi.divide(new NaiveNumber(2).promoteTo(pi.getClass()))) > 0) {
|
2017-08-01 15:36:54 -07:00
|
|
|
theta = pi.subtract(theta);
|
|
|
|
}
|
2017-08-02 13:27:39 -07:00
|
|
|
//System.out.println(theta);
|
2017-08-01 15:36:54 -07:00
|
|
|
return sinTaylor(theta);
|
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The cosine function (the argument is in radians).
|
|
|
|
*/
|
2017-08-04 11:54:12 -07:00
|
|
|
public final Function functionCos = new Function() {
|
2017-08-03 09:52:56 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-04 11:54:12 -07:00
|
|
|
return functionSin.apply(getPi(params[0].getClass()).divide(new NaiveNumber(2).promoteTo(params[0].getClass()))
|
2017-08-04 13:20:57 -07:00
|
|
|
.subtract(params[0]));
|
2017-08-03 09:52:56 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The tangent function (the argument is in radians).
|
|
|
|
*/
|
2017-08-04 11:54:12 -07:00
|
|
|
public final Function functionTan = new Function() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-04 11:54:12 -07:00
|
|
|
return functionSin.apply(params[0]).divide(functionCos.apply(params[0]));
|
2017-08-03 10:30:42 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The secant function (the argument is in radians).
|
|
|
|
*/
|
2017-08-04 11:54:12 -07:00
|
|
|
public final Function functionSec = new Function() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-04 11:54:12 -07:00
|
|
|
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionCos.apply(params[0]));
|
2017-08-03 10:30:42 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The cosecant function (the argument is in radians).
|
|
|
|
*/
|
2017-08-04 11:54:12 -07:00
|
|
|
public final Function functionCsc = new Function() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-04 11:54:12 -07:00
|
|
|
return NaiveNumber.ONE.promoteTo(params[0].getClass()).divide(functionSin.apply(params[0]));
|
2017-08-03 10:30:42 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The cotangent function (the argument is in radians).
|
|
|
|
*/
|
2017-08-04 11:54:12 -07:00
|
|
|
public final Function functionCot = new Function() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
|
|
|
protected boolean matchesParams(NumberInterface[] params) {
|
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected NumberInterface applyInternal(NumberInterface[] params) {
|
2017-08-04 11:54:12 -07:00
|
|
|
return functionCos.apply(params[0]).divide(functionCos.apply(params[0]));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
public StandardPlugin(PluginManager manager) {
|
|
|
|
super(manager);
|
|
|
|
}
|
|
|
|
|
2017-07-26 15:26:06 -07:00
|
|
|
/**
|
|
|
|
* Returns a partial sum of a series whose terms are given by the nthTermFunction, evaluated at x.
|
2017-07-30 21:11:32 -07:00
|
|
|
*
|
|
|
|
* @param x the value at which the series is evaluated.
|
2017-07-26 15:26:06 -07:00
|
|
|
* @param nthTermFunction the function that returns the nth term of the series, in the format term(x, n).
|
2017-07-30 21:11:32 -07:00
|
|
|
* @param n the number of terms in the partial sum.
|
2017-07-26 15:26:06 -07:00
|
|
|
* @return the value of the partial sum that has the same class as x.
|
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
private static NumberInterface sumSeries(NumberInterface x, BiFunction<Integer, NumberInterface, NumberInterface> nthTermFunction, int n) {
|
2017-07-26 15:26:06 -07:00
|
|
|
NumberInterface sum = NaiveNumber.ZERO.promoteTo(x.getClass());
|
2017-07-30 21:11:32 -07:00
|
|
|
for (int i = 0; i <= n; i++) {
|
2017-07-26 15:26:06 -07:00
|
|
|
sum = sum.add(nthTermFunction.apply(i, x));
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the maximum error based on the precision of the class of number.
|
2017-07-30 21:11:32 -07:00
|
|
|
*
|
2017-07-26 15:26:06 -07:00
|
|
|
* @param number Any instance of the NumberInterface in question (should return an appropriate precision).
|
2017-07-27 14:06:25 -07:00
|
|
|
* @return the maximum error.
|
2017-07-26 15:26:06 -07:00
|
|
|
*/
|
2017-07-30 21:11:32 -07:00
|
|
|
private static NumberInterface getMaxError(NumberInterface number) {
|
2017-08-05 18:11:16 -07:00
|
|
|
return intPow(new NaiveNumber(10).promoteTo(number.getClass()), number.getClass(), new NaiveNumber(-number.getMaxPrecision()).promoteTo(number.getClass()));
|
2017-07-26 15:26:06 -07:00
|
|
|
}
|
|
|
|
|
2017-08-02 15:13:04 -07:00
|
|
|
/**
|
|
|
|
* A factorial function that uses memoization for each number class; it efficiently
|
|
|
|
* computes factorials of non-negative integers.
|
2017-08-04 13:20:57 -07:00
|
|
|
*
|
2017-08-02 15:13:04 -07:00
|
|
|
* @param numberClass type of number to return.
|
2017-08-04 13:20:57 -07:00
|
|
|
* @param n non-negative integer.
|
2017-08-02 15:13:04 -07:00
|
|
|
* @return a number of numClass with value n factorial.
|
|
|
|
*/
|
2017-08-04 14:29:24 -07:00
|
|
|
public static NumberInterface factorial(Class<? extends NumberInterface> numberClass, int n) {
|
|
|
|
if (!FACTORIAL_LISTS.containsKey(numberClass)) {
|
2017-08-04 12:52:02 -07:00
|
|
|
FACTORIAL_LISTS.put(numberClass, new ArrayList<>());
|
|
|
|
FACTORIAL_LISTS.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
|
|
|
FACTORIAL_LISTS.get(numberClass).add(NaiveNumber.ONE.promoteTo(numberClass));
|
2017-07-31 14:49:25 -07:00
|
|
|
}
|
2017-08-04 12:52:02 -07:00
|
|
|
ArrayList<NumberInterface> list = FACTORIAL_LISTS.get(numberClass);
|
2017-08-04 14:29:24 -07:00
|
|
|
if (n >= list.size()) {
|
2017-08-05 13:26:29 -07:00
|
|
|
while (list.size() < n + 16) {
|
2017-08-04 14:29:24 -07:00
|
|
|
list.add(list.get(list.size() - 1).multiply(new NaiveNumber(list.size()).promoteTo(numberClass)));
|
2017-07-31 14:49:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return list.get(n);
|
|
|
|
}
|
|
|
|
|
2017-08-01 15:36:54 -07:00
|
|
|
/**
|
|
|
|
* Returns the value of the Taylor series for sin (centered at 0) at x.
|
2017-08-04 13:20:57 -07:00
|
|
|
*
|
2017-08-01 15:36:54 -07:00
|
|
|
* @param x where the series is evaluated.
|
|
|
|
* @return the value of the series
|
|
|
|
*/
|
2017-08-04 13:20:57 -07:00
|
|
|
private static NumberInterface sinTaylor(NumberInterface x) {
|
2017-08-01 15:36:54 -07:00
|
|
|
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
|
|
|
|
NumberInterface maxError = getMaxError(x);
|
|
|
|
int n = 1;
|
2017-08-04 13:20:57 -07:00
|
|
|
do {
|
2017-08-01 15:36:54 -07:00
|
|
|
n += 2;
|
|
|
|
power = power.multiply(multiplier);
|
|
|
|
currentTerm = power.divide(factorial(x.getClass(), n));
|
|
|
|
sum = sum.add(currentTerm);
|
|
|
|
} while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0);
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an equivalent angle in the interval [0, 2pi)
|
2017-08-04 13:20:57 -07:00
|
|
|
*
|
2017-08-01 15:36:54 -07:00
|
|
|
* @param phi an angle (in radians).
|
|
|
|
* @return theta in [0, 2pi) that differs from phi by a multiple of 2pi.
|
|
|
|
*/
|
2017-08-04 13:20:57 -07:00
|
|
|
private static NumberInterface getSmallAngle(NumberInterface phi, NumberInterface pi) {
|
2017-08-04 09:55:24 -07:00
|
|
|
NumberInterface twoPi = pi.multiply(new NaiveNumber("2").promoteTo(phi.getClass()));
|
2017-08-01 15:36:54 -07:00
|
|
|
NumberInterface theta = FUNCTION_ABS.apply(phi).subtract(twoPi
|
2017-08-04 13:20:57 -07:00
|
|
|
.multiply(FUNCTION_ABS.apply(phi).divide(twoPi).floor())); //Now theta is in [0, 2pi).
|
|
|
|
if (phi.signum() < 0) {
|
2017-08-01 15:36:54 -07:00
|
|
|
theta = twoPi.subtract(theta);
|
|
|
|
}
|
|
|
|
return theta;
|
|
|
|
}
|
2017-08-04 13:20:57 -07:00
|
|
|
|
2017-08-04 14:29:24 -07:00
|
|
|
public static NumberInterface intPow(NumberInterface number, Class<? extends NumberInterface> numberClass, NumberInterface exponent) {
|
|
|
|
if (exponent.compareTo((new NaiveNumber(0)).promoteTo(numberClass)) == 0) {
|
|
|
|
return (new NaiveNumber(1)).promoteTo(numberClass);
|
|
|
|
}
|
|
|
|
boolean takeReciprocal = exponent.compareTo((new NaiveNumber(0)).promoteTo(numberClass)) < 0;
|
|
|
|
exponent = FUNCTION_ABS.apply(exponent);
|
|
|
|
NumberInterface power = number;
|
|
|
|
for (NumberInterface currentExponent = (new NaiveNumber(1)).promoteTo(numberClass); currentExponent.compareTo(exponent) < 0; currentExponent = currentExponent.add((new NaiveNumber(1)).promoteTo(numberClass))) {
|
|
|
|
power = power.multiply(number);
|
|
|
|
}
|
|
|
|
if (takeReciprocal) {
|
|
|
|
power = (new NaiveNumber(1)).promoteTo(numberClass).divide(power);
|
|
|
|
}
|
|
|
|
return power;
|
|
|
|
}
|
|
|
|
|
2017-08-04 13:20:57 -07:00
|
|
|
@Override
|
|
|
|
public void onEnable() {
|
|
|
|
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);
|
|
|
|
registerOperator("!", OP_FACTORIAL);
|
|
|
|
|
|
|
|
registerFunction("abs", FUNCTION_ABS);
|
|
|
|
registerFunction("exp", FUNCTION_EXP);
|
|
|
|
registerFunction("ln", FUNCTION_LN);
|
|
|
|
registerFunction("sqrt", FUNCTION_SQRT);
|
|
|
|
registerFunction("sin", functionSin);
|
|
|
|
registerFunction("cos", functionCos);
|
|
|
|
registerFunction("tan", functionTan);
|
|
|
|
registerFunction("sec", functionSec);
|
|
|
|
registerFunction("csc", functionCsc);
|
|
|
|
registerFunction("cot", functionCot);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDisable() {
|
|
|
|
|
|
|
|
}
|
2017-07-24 13:44:38 -07:00
|
|
|
}
|