2017-07-25 21:50:41 -07:00
|
|
|
package org.nwapw.abacus.plugin;
|
2017-07-24 13:44:38 -07:00
|
|
|
|
2017-09-06 22:54:21 -07:00
|
|
|
import org.nwapw.abacus.context.MutableEvaluationContext;
|
2017-08-08 11:27:59 -07:00
|
|
|
import org.nwapw.abacus.function.*;
|
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-09-02 00:20:18 -07:00
|
|
|
import org.nwapw.abacus.tree.TreeNode;
|
|
|
|
import org.nwapw.abacus.tree.VariableNode;
|
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
|
|
|
|
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-09-02 00:20:18 -07:00
|
|
|
/**
|
|
|
|
* The set operator.
|
|
|
|
*/
|
|
|
|
public final TreeValueOperator opSet = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
|
2017-09-02 00:20:18 -07:00
|
|
|
return params.length == 2 && params[0] instanceof VariableNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
|
2017-09-02 00:20:18 -07:00
|
|
|
String assignTo = ((VariableNode) params[0]).getVariable();
|
2017-09-07 12:53:12 -07:00
|
|
|
NumberInterface value = params[1].reduce(context.getInheritedReducer());
|
2017-09-06 21:43:07 -07:00
|
|
|
context.setVariable(assignTo, value);
|
2017-09-02 00:20:18 -07:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* The define operator.
|
|
|
|
*/
|
|
|
|
public final TreeValueOperator opDefine = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
|
2017-09-02 00:20:18 -07:00
|
|
|
return params.length == 2 && params[0] instanceof VariableNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
|
2017-09-02 00:20:18 -07:00
|
|
|
String assignTo = ((VariableNode) params[0]).getVariable();
|
2017-09-06 21:43:07 -07:00
|
|
|
context.setDefinition(assignTo, params[1]);
|
2017-09-07 12:53:12 -07:00
|
|
|
return params[1].reduce(context.getInheritedReducer());
|
2017-09-02 00:20:18 -07:00
|
|
|
}
|
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The addition operator, +
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_ADD = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-29 18:31:47 -07:00
|
|
|
return params.length == 2;
|
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
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-29 18:31:47 -07:00
|
|
|
return params[0].add(params[1]);
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The subtraction operator, -
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_SUBTRACT = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-07-30 21:10:11 -07:00
|
|
|
return params.length == 2;
|
|
|
|
}
|
2017-07-24 14:48:16 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-07-30 21:10:11 -07:00
|
|
|
return params[0].subtract(params[1]);
|
2017-08-03 15:16:26 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-08-02 11:26:59 -07:00
|
|
|
/**
|
|
|
|
* The negation operator, -
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_NEGATE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0) {
|
2017-08-02 11:26:59 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-02 11:26:59 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-02 11:26:59 -07:00
|
|
|
return params[0].negate();
|
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The multiplication operator, *
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_MULTIPLY = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-29 18:31:47 -07:00
|
|
|
return params.length == 2;
|
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
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-29 18:31:47 -07:00
|
|
|
return params[0].multiply(params[1]);
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-08-14 19:03:52 -07:00
|
|
|
/**
|
2017-08-26 12:19:34 -07:00
|
|
|
* The implementation for double-based naive numbers.
|
2017-08-14 19:03:52 -07:00
|
|
|
*/
|
2017-08-26 12:19:34 -07:00
|
|
|
public static final NumberImplementation IMPLEMENTATION_NAIVE = new NumberImplementation(NaiveNumber.class, 0) {
|
2017-08-14 19:03:52 -07:00
|
|
|
@Override
|
2017-08-26 12:19:34 -07:00
|
|
|
public NumberInterface instanceForString(String string) {
|
|
|
|
return new NaiveNumber(string);
|
2017-08-14 19:03:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-08-26 12:19:34 -07:00
|
|
|
public NumberInterface instanceForPi() {
|
|
|
|
return new NaiveNumber(Math.PI);
|
2017-08-14 19:03:52 -07:00
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-08-14 19:03:52 -07:00
|
|
|
/**
|
|
|
|
* 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() {
|
2017-09-06 22:54:21 -07:00
|
|
|
MutableEvaluationContext dummyContext = new MutableEvaluationContext(null, this, null);
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface C = FUNCTION_SQRT.apply(dummyContext, new PreciseNumber("10005")).multiply(new PreciseNumber("426880"));
|
2017-08-14 19:03:52 -07:00
|
|
|
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 PreciseNumber((12 * i + 2) + ""))
|
|
|
|
.multiply(new PreciseNumber((12 * i + 6) + ""))
|
|
|
|
.multiply(new PreciseNumber((12 * i + 10) + ""))
|
|
|
|
.divide(new PreciseNumber(Math.pow(i + 1, 3) + ""));
|
|
|
|
L = L.add(lSummand);
|
|
|
|
X = X.multiply(xMultiplier);
|
|
|
|
sum = sum.add(M.multiply(L).divide(X));
|
|
|
|
}
|
|
|
|
return C.divide(sum);
|
|
|
|
}
|
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The division operator, /
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_DIVIDE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params.length == 2 && params[1].compareTo(context.getInheritedNumberImplementation().instanceForString(Integer.toString(0))) != 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
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, 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-08-25 14:55:05 -07:00
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The factorial operator, !
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_FACTORIAL = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0) {
|
2017-07-30 21:10:11 -07:00
|
|
|
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-04 10:33:55 -07:00
|
|
|
return params.length == 1
|
2017-09-06 21:43:07 -07:00
|
|
|
&& params[0].fractionalPart().compareTo(context.getInheritedNumberImplementation().instanceForString("0")) == 0
|
2017-08-04 10:33:55 -07:00
|
|
|
&& 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
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-07-30 21:11:32 -07:00
|
|
|
if (params[0].signum() == 0) {
|
2017-09-01 18:07:28 -07:00
|
|
|
return implementation.instanceForString("1");
|
2017-07-27 16:55:18 -07:00
|
|
|
}
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface one = implementation.instanceForString("1");
|
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-09 19:04:32 -07:00
|
|
|
while ((multiplier = multiplier.subtract(one)).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-08-25 14:55:05 -07:00
|
|
|
};
|
2017-08-09 12:34:02 -07:00
|
|
|
/**
|
|
|
|
* The permutation operator.
|
|
|
|
*/
|
2017-08-25 14:55:05 -07:00
|
|
|
public static final NumberOperator OP_NPR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
|
2017-08-09 12:34:02 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-09 12:34:02 -07:00
|
|
|
return params.length == 2 && params[0].fractionalPart().signum() == 0
|
|
|
|
&& params[1].fractionalPart().signum() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-08-14 19:03:52 -07:00
|
|
|
if (params[0].compareTo(params[1]) < 0 ||
|
2017-08-09 12:34:02 -07:00
|
|
|
params[0].signum() < 0 ||
|
2017-09-01 18:07:28 -07:00
|
|
|
(params[0].signum() == 0 && params[1].signum() != 0)) return implementation.instanceForString("0");
|
|
|
|
NumberInterface total = implementation.instanceForString("1");
|
2017-08-09 12:34:02 -07:00
|
|
|
NumberInterface multiplyBy = params[0];
|
|
|
|
NumberInterface remainingMultiplications = params[1];
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface halfway = params[0].divide(implementation.instanceForString("2"));
|
2017-08-14 19:03:52 -07:00
|
|
|
if (remainingMultiplications.compareTo(halfway) > 0) {
|
2017-08-09 12:47:22 -07:00
|
|
|
remainingMultiplications = params[0].subtract(remainingMultiplications);
|
|
|
|
}
|
2017-08-14 19:03:52 -07:00
|
|
|
while (remainingMultiplications.signum() > 0) {
|
2017-08-09 12:34:02 -07:00
|
|
|
total = total.multiply(multiplyBy);
|
2017-09-01 18:07:28 -07:00
|
|
|
remainingMultiplications = remainingMultiplications.subtract(implementation.instanceForString("1"));
|
|
|
|
multiplyBy = multiplyBy.subtract(implementation.instanceForString("1"));
|
2017-08-09 12:34:02 -07:00
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
2017-08-25 14:55:05 -07:00
|
|
|
};
|
2017-08-26 12:19:34 -07:00
|
|
|
/**
|
|
|
|
* The combination operator.
|
|
|
|
*/
|
|
|
|
public static final NumberOperator OP_NCR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) {
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-26 12:19:34 -07:00
|
|
|
return params.length == 2 && params[0].fractionalPart().signum() == 0
|
|
|
|
&& params[1].fractionalPart().signum() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return OP_NPR.apply(context, params).divide(OP_FACTORIAL.apply(context, params[1]));
|
2017-08-26 12:19:34 -07:00
|
|
|
}
|
|
|
|
};
|
2017-07-30 21:15:01 -07:00
|
|
|
/**
|
|
|
|
* The absolute value function, abs(-3) = 3
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public static final NumberFunction FUNCTION_ABS = new NumberFunction() {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-07-30 21:10:11 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
2017-07-26 15:26:06 -07:00
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params[0].multiply(context.getInheritedNumberImplementation().instanceForString(Integer.toString(params[0].signum())));
|
2017-07-30 21:10:11 -07:00
|
|
|
}
|
|
|
|
};
|
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-08-25 14:56:36 -07:00
|
|
|
public static final NumberFunction FUNCTION_LN = new NumberFunction() {
|
2017-07-30 21:10:11 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params.length == 1 && params[0].compareTo(context.getInheritedNumberImplementation().instanceForString("0")) > 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
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-07-30 21:10:11 -07:00
|
|
|
NumberInterface param = params[0];
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface one = implementation.instanceForString("1");
|
2017-07-30 21:10:11 -07:00
|
|
|
int powersOf2 = 0;
|
2017-09-06 21:43:07 -07:00
|
|
|
while (FUNCTION_ABS.apply(context, param.subtract(one)).compareTo(implementation.instanceForString(".1")) >= 0) {
|
2017-08-09 19:04:32 -07:00
|
|
|
if (param.subtract(one).signum() == 1) {
|
2017-09-01 18:07:28 -07:00
|
|
|
param = param.divide(implementation.instanceForString("2"));
|
2017-07-30 21:10:11 -07:00
|
|
|
powersOf2++;
|
2017-08-09 19:04:32 -07:00
|
|
|
if (param.subtract(one).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-09-01 18:07:28 -07:00
|
|
|
param = param.multiply(implementation.instanceForString("2"));
|
2017-07-30 21:10:11 -07:00
|
|
|
powersOf2--;
|
2017-08-09 19:04:32 -07:00
|
|
|
if (param.subtract(one).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-09-06 21:43:07 -07:00
|
|
|
return getLog2(context.getInheritedNumberImplementation(), param).multiply(implementation.instanceForString(Integer.toString(powersOf2))).add(getLogPartialSum(context, 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-09-06 22:54:21 -07:00
|
|
|
private NumberInterface getLogPartialSum(MutableEvaluationContext context, NumberInterface x) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-08-07 17:58:18 -07:00
|
|
|
NumberInterface maxError = x.getMaxError();
|
2017-09-01 18:07:28 -07:00
|
|
|
x = x.subtract(implementation.instanceForString("1")); //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-09-06 21:43:07 -07:00
|
|
|
while (FUNCTION_ABS.apply(context, 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-09-01 18:07:28 -07:00
|
|
|
currentTerm = currentNumerator.divide(implementation.instanceForString(Integer.toString(n)));
|
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-09-01 17:45:32 -07:00
|
|
|
private NumberInterface getLog2(NumberImplementation implementation, NumberInterface number) {
|
2017-08-07 17:58:18 -07:00
|
|
|
NumberInterface maxError = number.getMaxError();
|
2017-09-01 18:07:28 -07:00
|
|
|
//NumberInterface errorBound = implementation.instanceForString("1");
|
2017-07-30 21:10:11 -07:00
|
|
|
//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.
|
2017-09-21 23:09:13 -07:00
|
|
|
NumberInterface a = implementation.instanceForString("1"), b = a, c;
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface sum = implementation.instanceForString("0");
|
|
|
|
NumberInterface one = implementation.instanceForString("1");
|
2017-07-30 21:10:11 -07:00
|
|
|
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++;
|
2017-09-01 18:07:28 -07:00
|
|
|
a = a.divide(implementation.instanceForString("3"));
|
|
|
|
b = b.divide(implementation.instanceForString("4"));
|
|
|
|
c = one.divide(implementation.instanceForString(Integer.toString(n)));
|
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-08-09 11:51:12 -07:00
|
|
|
/**
|
|
|
|
* Gets a random number smaller or equal to the given number's integer value.
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public static final NumberFunction FUNCTION_RAND_INT = new NumberFunction() {
|
2017-08-09 11:51:12 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-09 11:51:12 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return context.getInheritedNumberImplementation().instanceForString(Long.toString(Math.round(Math.random() * params[0].floor().intValue())));
|
2017-08-09 11:51:12 -07:00
|
|
|
}
|
|
|
|
};
|
2017-09-01 18:07:48 -07:00
|
|
|
/**
|
|
|
|
* The caret / pow operator, ^
|
|
|
|
*/
|
|
|
|
public static final NumberOperator OP_CARET = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) {
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface zero = context.getInheritedNumberImplementation().instanceForString("0");
|
2017-09-01 18:07:48 -07:00
|
|
|
return params.length == 2
|
|
|
|
&& !(params[0].compareTo(zero) == 0
|
|
|
|
&& params[1].compareTo(zero) == 0)
|
|
|
|
&& !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(zero) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-09-01 18:07:48 -07:00
|
|
|
NumberInterface zero = implementation.instanceForString("0");
|
|
|
|
if (params[0].compareTo(zero) == 0)
|
|
|
|
return zero;
|
|
|
|
else if (params[1].compareTo(zero) == 0)
|
|
|
|
return implementation.instanceForString("1");
|
|
|
|
//Detect integer bases:
|
|
|
|
if (params[0].fractionalPart().compareTo(implementation.instanceForString("0")) == 0
|
2017-09-06 21:43:07 -07:00
|
|
|
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))) < 0
|
|
|
|
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString("1")) >= 0) {
|
2017-09-01 18:07:48 -07:00
|
|
|
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
|
2017-09-06 21:43:07 -07:00
|
|
|
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(context, newParams));
|
2017-09-01 18:07:48 -07:00
|
|
|
}
|
2017-09-06 21:43:07 -07:00
|
|
|
return FUNCTION_EXP.apply(context, FUNCTION_LN.apply(context, FUNCTION_ABS.apply(context, params[0])).multiply(params[1]));
|
2017-09-01 18:07:48 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* The square root function.
|
|
|
|
*/
|
|
|
|
public static final NumberFunction FUNCTION_SQRT = new NumberFunction() {
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-01 18:07:48 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return OP_CARET.apply(context, params[0], context.getInheritedNumberImplementation().instanceForString(".5"));
|
2017-09-01 18:07:48 -07:00
|
|
|
}
|
|
|
|
};
|
2017-09-01 18:07:28 -07:00
|
|
|
private static final HashMap<NumberImplementation, ArrayList<NumberInterface>> FACTORIAL_LISTS = new HashMap<>();
|
2017-08-04 14:29:24 -07:00
|
|
|
/**
|
|
|
|
* The exponential function, exp(1) = e^1 = 2.71...
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public static final NumberFunction FUNCTION_EXP = new NumberFunction() {
|
2017-08-04 14:29:24 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-04 14:29:24 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-08-07 17:58:18 -07:00
|
|
|
NumberInterface maxError = params[0].getMaxError();
|
2017-08-04 14:29:24 -07:00
|
|
|
int n = 0;
|
2017-08-07 18:13:18 -07:00
|
|
|
if (params[0].signum() < 0) {
|
|
|
|
NumberInterface[] negatedParams = {params[0].negate()};
|
2017-09-06 21:43:07 -07:00
|
|
|
return implementation.instanceForString("1").divide(applyInternal(context, negatedParams));
|
2017-08-04 14:29:24 -07:00
|
|
|
} 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.
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface sum = implementation.instanceForString("1");
|
2017-08-04 14:29:24 -07:00
|
|
|
NumberInterface nextNumerator = params[0];
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface left = params[0].multiply(implementation.instanceForString("3").intPow(params[0].ceiling().intValue())), right = maxError;
|
2017-08-04 14:29:24 -07:00
|
|
|
do {
|
2017-09-01 18:07:28 -07:00
|
|
|
sum = sum.add(nextNumerator.divide(factorial(implementation, n + 1)));
|
2017-08-04 14:29:24 -07:00
|
|
|
n++;
|
|
|
|
nextNumerator = nextNumerator.multiply(params[0]);
|
|
|
|
left = left.multiply(params[0]);
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface nextN = implementation.instanceForString(Integer.toString(n + 1));
|
2017-08-04 14:29:24 -07:00
|
|
|
right = right.multiply(nextN);
|
|
|
|
//System.out.println(left + ", " + right);
|
|
|
|
}
|
|
|
|
while (left.compareTo(right) > 0);
|
|
|
|
//System.out.println(n+1);
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2017-08-02 15:13:04 -07:00
|
|
|
/**
|
|
|
|
* The sine function (the argument is interpreted in radians).
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionSin = new NumberFunction() {
|
2017-08-01 15:36:54 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-01 15:36:54 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-08-06 21:56:49 -07:00
|
|
|
NumberInterface pi = piFor(params[0].getClass());
|
2017-09-01 18:07:28 -07:00
|
|
|
NumberInterface twoPi = pi.multiply(implementation.instanceForString("2"));
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface theta = getSmallAngle(context, params[0], pi);
|
2017-08-02 13:27:39 -07:00
|
|
|
//System.out.println(theta);
|
2017-09-01 18:07:28 -07:00
|
|
|
if (theta.compareTo(pi.multiply(implementation.instanceForString("1.5"))) >= 0) {
|
2017-08-01 15:36:54 -07:00
|
|
|
theta = theta.subtract(twoPi);
|
2017-09-01 18:07:28 -07:00
|
|
|
} else if (theta.compareTo(pi.divide(implementation.instanceForString("2"))) > 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-09-06 21:43:07 -07:00
|
|
|
return sinTaylor(context, theta);
|
2017-08-01 15:36:54 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-04 12:52:02 -07:00
|
|
|
/**
|
|
|
|
* The cosine function (the argument is in radians).
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionCos = new NumberFunction() {
|
2017-08-03 09:52:56 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-03 09:52:56 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return functionSin.apply(context, piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
|
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-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionTan = new NumberFunction() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-03 10:30:42 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return functionSin.apply(context, params[0]).divide(functionCos.apply(context, 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-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionSec = new NumberFunction() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-03 10:30:42 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return context.getInheritedNumberImplementation().instanceForString("1").divide(functionCos.apply(context, 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-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionCsc = new NumberFunction() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-03 10:30:42 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return context.getInheritedNumberImplementation().instanceForString("1").divide(functionSin.apply(context, 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-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionCot = new NumberFunction() {
|
2017-08-03 10:30:42 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-03 10:30:42 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return functionCos.apply(context, params[0]).divide(functionSin.apply(context, params[0]));
|
2017-08-04 11:54:12 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-09 12:47:43 -07:00
|
|
|
/**
|
2017-08-09 16:04:26 -07:00
|
|
|
* The arcsine function (return type in radians).
|
2017-08-09 12:47:43 -07:00
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArcsin = new NumberFunction() {
|
2017-08-09 12:47:43 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-09 12:47:43 -07:00
|
|
|
return params.length == 1
|
2017-09-06 21:43:07 -07:00
|
|
|
&& FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0;
|
2017-08-09 12:47:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
|
|
|
if (FUNCTION_ABS.apply(context, params[0]).compareTo(implementation.instanceForString(".8")) >= 0) {
|
|
|
|
NumberInterface[] newParams = {FUNCTION_SQRT.apply(context, implementation.instanceForString("1").subtract(params[0].multiply(params[0])))};
|
2017-09-01 18:07:28 -07:00
|
|
|
return piFor(params[0].getClass()).divide(implementation.instanceForString("2"))
|
2017-09-06 21:43:07 -07:00
|
|
|
.subtract(applyInternal(context, newParams)).multiply(implementation.instanceForString(Integer.toString(params[0].signum())));
|
2017-08-09 12:47:43 -07:00
|
|
|
}
|
|
|
|
NumberInterface currentTerm = params[0], sum = currentTerm,
|
2017-09-01 18:07:28 -07:00
|
|
|
multiplier = currentTerm.multiply(currentTerm), summandBound = sum.getMaxError().multiply(implementation.instanceForString("1").subtract(multiplier)),
|
|
|
|
power = currentTerm, coefficient = implementation.instanceForString("1");
|
2017-08-09 12:47:43 -07:00
|
|
|
int exponent = 1;
|
2017-09-06 21:43:07 -07:00
|
|
|
while (FUNCTION_ABS.apply(context, currentTerm).compareTo(summandBound) > 0) {
|
2017-08-09 12:47:43 -07:00
|
|
|
exponent += 2;
|
|
|
|
power = power.multiply(multiplier);
|
2017-09-01 18:07:48 -07:00
|
|
|
coefficient = coefficient.multiply(implementation.instanceForString(Integer.toString(exponent - 2)))
|
|
|
|
.divide(implementation.instanceForString(Integer.toString(exponent - 1)));
|
|
|
|
currentTerm = power.multiply(coefficient).divide(implementation.instanceForString(Integer.toString(exponent)));
|
2017-08-09 12:47:43 -07:00
|
|
|
sum = sum.add(currentTerm);
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-09 15:08:54 -07:00
|
|
|
/**
|
2017-08-09 16:04:26 -07:00
|
|
|
* The arccosine function.
|
2017-08-09 15:08:54 -07:00
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArccos = new NumberFunction() {
|
2017-08-09 15:08:54 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0;
|
2017-08-09 15:08:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
|
|
|
|
.subtract(functionArcsin.apply(context, params));
|
2017-08-09 15:08:54 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-09 15:57:02 -07:00
|
|
|
/**
|
2017-08-09 16:04:26 -07:00
|
|
|
* The arccosecant function.
|
2017-08-09 15:57:02 -07:00
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArccsc = new NumberFunction() {
|
2017-08-09 15:57:02 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0;
|
2017-08-09 15:57:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])};
|
|
|
|
return functionArcsin.apply(context, reciprocalParamArr);
|
2017-08-09 15:57:02 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-09 16:04:26 -07:00
|
|
|
/**
|
|
|
|
* The arcsecant function.
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArcsec = new NumberFunction() {
|
2017-08-09 16:04:26 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0;
|
2017-08-09 16:04:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])};
|
|
|
|
return functionArccos.apply(context, reciprocalParamArr);
|
2017-08-09 16:04:26 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-11 01:19:13 -07:00
|
|
|
/**
|
|
|
|
* The arctangent function.
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArctan = new NumberFunction() {
|
2017-08-11 01:19:13 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-11 01:19:13 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberImplementation implementation = context.getInheritedNumberImplementation();
|
2017-08-14 19:03:52 -07:00
|
|
|
if (params[0].signum() == -1) {
|
2017-08-11 01:19:13 -07:00
|
|
|
NumberInterface[] negatedParams = {params[0].negate()};
|
2017-09-06 21:43:07 -07:00
|
|
|
return applyInternal(context, negatedParams).negate();
|
2017-08-11 01:19:13 -07:00
|
|
|
}
|
2017-09-01 18:07:28 -07:00
|
|
|
if (params[0].compareTo(implementation.instanceForString("1")) > 0) {
|
|
|
|
NumberInterface[] reciprocalParams = {implementation.instanceForString("1").divide(params[0])};
|
|
|
|
return piFor(params[0].getClass()).divide(implementation.instanceForString("2"))
|
2017-09-06 21:43:07 -07:00
|
|
|
.subtract(applyInternal(context, reciprocalParams));
|
2017-08-11 01:19:13 -07:00
|
|
|
}
|
2017-09-01 18:07:28 -07:00
|
|
|
if (params[0].compareTo(implementation.instanceForString("1")) == 0) {
|
|
|
|
return piFor(params[0].getClass()).divide(implementation.instanceForString("4"));
|
2017-08-11 01:19:13 -07:00
|
|
|
}
|
2017-09-01 18:07:28 -07:00
|
|
|
if (params[0].compareTo(implementation.instanceForString(".9")) >= 0) {
|
|
|
|
NumberInterface[] newParams = {params[0].multiply(implementation.instanceForString("2"))
|
|
|
|
.divide(implementation.instanceForString("1").subtract(params[0].multiply(params[0])))};
|
2017-09-06 21:43:07 -07:00
|
|
|
return applyInternal(context, newParams).divide(implementation.instanceForString("2"));
|
2017-08-11 01:19:13 -07:00
|
|
|
}
|
|
|
|
NumberInterface currentPower = params[0], currentTerm = currentPower, sum = currentTerm,
|
|
|
|
maxError = params[0].getMaxError(), multiplier = currentPower.multiply(currentPower).negate();
|
|
|
|
int n = 1;
|
2017-09-06 21:43:07 -07:00
|
|
|
while (FUNCTION_ABS.apply(context, currentTerm).compareTo(maxError) > 0) {
|
2017-08-11 01:19:13 -07:00
|
|
|
n += 2;
|
|
|
|
currentPower = currentPower.multiply(multiplier);
|
2017-09-01 18:07:48 -07:00
|
|
|
currentTerm = currentPower.divide(implementation.instanceForString(Integer.toString(n)));
|
2017-08-11 01:19:13 -07:00
|
|
|
sum = sum.add(currentTerm);
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The arccotangent function. Range: (0, pi).
|
|
|
|
*/
|
2017-08-25 14:56:36 -07:00
|
|
|
public final NumberFunction functionArccot = new NumberFunction() {
|
2017-08-11 01:19:13 -07:00
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-08-11 01:19:13 -07:00
|
|
|
return params.length == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-09-06 22:54:21 -07:00
|
|
|
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
|
2017-09-06 21:43:07 -07:00
|
|
|
return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
|
|
|
|
.subtract(functionArctan.apply(context, params));
|
2017-08-11 01:19:13 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-07-30 21:10:11 -07:00
|
|
|
public StandardPlugin(PluginManager manager) {
|
|
|
|
super(manager);
|
|
|
|
}
|
|
|
|
|
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-09-01 18:07:28 -07:00
|
|
|
* @param implementation type of number to return.
|
2017-09-01 18:07:48 -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-09-10 20:17:49 -07:00
|
|
|
synchronized public static NumberInterface factorial(NumberImplementation implementation, int n) {
|
2017-09-01 18:07:28 -07:00
|
|
|
if (!FACTORIAL_LISTS.containsKey(implementation)) {
|
|
|
|
FACTORIAL_LISTS.put(implementation, new ArrayList<>());
|
|
|
|
FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1"));
|
|
|
|
FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1"));
|
2017-07-31 14:49:25 -07:00
|
|
|
}
|
2017-09-01 18:07:28 -07:00
|
|
|
ArrayList<NumberInterface> list = FACTORIAL_LISTS.get(implementation);
|
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-09-01 18:07:48 -07:00
|
|
|
list.add(list.get(list.size() - 1).multiply(implementation.instanceForString(Integer.toString(list.size()))));
|
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-09-06 22:54:21 -07:00
|
|
|
private static NumberInterface sinTaylor(MutableEvaluationContext context, NumberInterface x) {
|
2017-09-21 23:09:13 -07:00
|
|
|
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm, sum = x;
|
2017-08-07 17:58:18 -07:00
|
|
|
NumberInterface maxError = x.getMaxError();
|
2017-08-01 15:36:54 -07:00
|
|
|
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);
|
2017-09-06 21:43:07 -07:00
|
|
|
currentTerm = power.divide(factorial(context.getInheritedNumberImplementation(), n));
|
2017-08-01 15:36:54 -07:00
|
|
|
sum = sum.add(currentTerm);
|
2017-09-06 21:43:07 -07:00
|
|
|
} while (FUNCTION_ABS.apply(context, currentTerm).compareTo(maxError) > 0);
|
2017-08-01 15:36:54 -07:00
|
|
|
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-09-06 22:54:21 -07:00
|
|
|
private static NumberInterface getSmallAngle(MutableEvaluationContext context, NumberInterface phi, NumberInterface pi) {
|
2017-09-06 21:43:07 -07:00
|
|
|
NumberInterface twoPi = pi.multiply(context.getInheritedNumberImplementation().instanceForString("2"));
|
|
|
|
NumberInterface theta = FUNCTION_ABS.apply(context, phi).subtract(twoPi
|
|
|
|
.multiply(FUNCTION_ABS.apply(context, phi).divide(twoPi).floor())); //Now theta is in [0, 2pi).
|
2017-08-04 13:20:57 -07:00
|
|
|
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
|
|
|
|
|
|
|
@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);
|
|
|
|
|
2017-09-02 00:20:18 -07:00
|
|
|
registerTreeValueOperator("=", opSet);
|
|
|
|
registerTreeValueOperator(":=", opDefine);
|
|
|
|
|
2017-08-09 15:08:08 -07:00
|
|
|
registerOperator("nPr", OP_NPR);
|
|
|
|
registerOperator("nCr", OP_NCR);
|
2017-08-09 12:34:02 -07:00
|
|
|
|
2017-08-04 13:20:57 -07:00
|
|
|
registerFunction("abs", FUNCTION_ABS);
|
|
|
|
registerFunction("exp", FUNCTION_EXP);
|
|
|
|
registerFunction("ln", FUNCTION_LN);
|
|
|
|
registerFunction("sqrt", FUNCTION_SQRT);
|
2017-08-11 22:13:20 -07:00
|
|
|
|
2017-08-04 13:20:57 -07:00
|
|
|
registerFunction("sin", functionSin);
|
|
|
|
registerFunction("cos", functionCos);
|
|
|
|
registerFunction("tan", functionTan);
|
|
|
|
registerFunction("sec", functionSec);
|
|
|
|
registerFunction("csc", functionCsc);
|
|
|
|
registerFunction("cot", functionCot);
|
2017-08-11 22:13:20 -07:00
|
|
|
|
2017-08-09 12:47:43 -07:00
|
|
|
registerFunction("arcsin", functionArcsin);
|
2017-08-09 15:08:54 -07:00
|
|
|
registerFunction("arccos", functionArccos);
|
2017-08-11 01:19:13 -07:00
|
|
|
registerFunction("arctan", functionArctan);
|
2017-08-09 16:04:26 -07:00
|
|
|
registerFunction("arcsec", functionArcsec);
|
2017-08-11 22:13:20 -07:00
|
|
|
registerFunction("arccsc", functionArccsc);
|
2017-08-11 01:19:13 -07:00
|
|
|
registerFunction("arccot", functionArccot);
|
2017-08-08 11:27:59 -07:00
|
|
|
|
2017-08-09 11:51:12 -07:00
|
|
|
registerFunction("random_int", FUNCTION_RAND_INT);
|
|
|
|
|
2017-08-08 11:27:59 -07:00
|
|
|
registerDocumentation(new Documentation("abs", "Absolute Value", "Finds the distance " +
|
|
|
|
"from zero of a number.", "Given a number, this function finds the distance form " +
|
2017-08-08 14:18:14 -07:00
|
|
|
"zero of a number, effectively turning negative numbers into positive ones.\n\n" +
|
|
|
|
"Example: abs(-2) -> 2", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("exp", "Exponentiate", "Brings e to the given power.",
|
|
|
|
"This function evaluates e to the power of the given value, and is the inverse " +
|
|
|
|
"of the natural logarithm.\n\n" +
|
|
|
|
"Example: exp(1) -> 2.718...", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("ln", "Natural Logarithm", "Gets the natural " +
|
2017-08-08 14:39:41 -07:00
|
|
|
"logarithm of the given value.", "The natural logarithm of a number is " +
|
2017-08-08 14:18:14 -07:00
|
|
|
"the power that e has to be brought to to be equal to the number.\n\n" +
|
|
|
|
"Example: ln(2.718) -> 1", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("sqrt", "Square Root", "Finds the square root " +
|
2017-08-11 01:23:01 -07:00
|
|
|
"of the number.", "A square root a of a number is defined as the non-negative a such that a times a is equal " +
|
2017-08-08 14:18:14 -07:00
|
|
|
"to that number.\n\n" +
|
|
|
|
"Example: sqrt(4) -> 2", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("sin", "Sine", "Computes the sine of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: sin(pi/6) -> 0.5", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("cos", "Cosine", "Computes the cosine of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: cos(pi/6) -> 0.866... (the exact result is sqrt(3)/2)", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("tan", "Tangent", "Computes the tangent of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: tan(pi/6) -> 0.577... (the exact result is 1/sqrt(3))", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("sec", "Secant", "Computes the secant of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: sec(pi/6) -> 1.154... (the exact result is 2/sqrt(3))", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("csc", "Cosecant", "Computes the cosecant of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: csc(pi/6) -> 2", DocumentationType.FUNCTION));
|
2017-08-08 14:39:41 -07:00
|
|
|
registerDocumentation(new Documentation("cot", "Cotangent", "Computes the cotangent of the given angle, " +
|
2017-08-11 20:32:39 -07:00
|
|
|
"in radians.", "Example: cot(pi/6) -> 1.732... (the exact result is sqrt(3))", DocumentationType.FUNCTION));
|
2017-08-09 11:51:12 -07:00
|
|
|
registerDocumentation(new Documentation("random_int", "Random Integer", "Generates a random integer [0, n].",
|
|
|
|
"Generates a pseudorandom number using the standard JVM random mechanism, keeping it less than or " +
|
|
|
|
"equal to the given number.\n\n" +
|
|
|
|
"Example: random_int(5) -> 4\n" +
|
|
|
|
"random_int(5) -> 3\n" +
|
|
|
|
"random_int(5) -> 3\n", DocumentationType.FUNCTION));
|
2017-08-11 21:07:34 -07:00
|
|
|
registerDocumentation(new Documentation("arcsin", "Arcsine", "Computes the arcsine of x. (The result is in radians.)",
|
|
|
|
"Example: arcsin(0.5) -> 0.523... (the exact result is pi/6)", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("arccos", "Arccosine", "Computes the arccosine of x. (The result is in radians.)",
|
|
|
|
"Example: arccos(0.5) -> 1.047... (the exact result is pi/3)", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("arctan", "Arctangent", "Computes the arctangent of x. (The result is in radians.)",
|
|
|
|
"Example: arctan(1) -> 0.785... (the exact result is pi/4)", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("arcsec", "Arcsecant", "Computes the arcsecant of x. (The result is in radians.)",
|
|
|
|
"Example: arcsec(2) -> 1.047... (the exact result is pi/3)", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("arccsc", "Arccosecant", "Computes the arcscosecant of x. (The result is in radians.)",
|
|
|
|
"Example: arccsc(2) -> 0.523... (the exact result is pi/6)", DocumentationType.FUNCTION));
|
|
|
|
registerDocumentation(new Documentation("arccot", "Arccotangent", "Computes the arccotangent of x. (The result is in radians," +
|
|
|
|
" in the range (0, pi).)",
|
|
|
|
"Example: arccot(0) -> 1.570... (the exact result is pi/2)", DocumentationType.FUNCTION));
|
2017-08-04 13:20:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDisable() {
|
2017-09-01 18:07:28 -07:00
|
|
|
FACTORIAL_LISTS.clear();
|
2017-08-04 13:20:57 -07:00
|
|
|
}
|
2017-07-24 13:44:38 -07:00
|
|
|
}
|