/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.common.kernel.arithmetic;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
import org.apache.commons.math3.analysis.UnivariateFunction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.Traversing.VariableReplacer;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.roots.RealRootDerivFunction;
import org.geogebra.common.plugin.Operation;
import org.geogebra.common.util.debug.Log;
/**
* Function of one variable x that returns either a number or a boolean. This
* depends on the expression this function is based on.
*
* @author Markus Hohenwarter
*/
@SuppressWarnings("deprecation")
public class Function extends FunctionNVar
implements Functional, RealRootDerivFunction,
DifferentiableUnivariateFunction {
/** function expression */
private Function derivative;
private static final double MAX_EXPAND_DEGREE = 10;
/**
* Creates new Function from expression where x is the variable. Note: call
* {@link #initFunction()} after this constructor.
*
* @param expression
* function expression
*/
public Function(ExpressionNode expression) {
super(expression);
}
/**
* Creates new Function from expression where the function variable in
* expression is already known.
*
* @param exp
* function expression
* @param fVar
* function variable
*/
public Function(ExpressionNode exp, FunctionVariable fVar) {
super(exp, new FunctionVariable[] { fVar });
}
/**
* Creates a Function that has no expression yet. Use setExpression() to do
* this later.
*
* @param kernel
* kernel
*/
public Function(Kernel kernel) {
super(kernel);
fVars = new FunctionVariable[1];
}
/**
* Copy constructor
*
* @param f
* source function
* @param kernel
* kernel
*/
public Function(Function f, Kernel kernel) {
super(f.expression.getCopy(kernel));
fVars = f.fVars; // no deep copy of function variable
isBooleanFunction = f.isBooleanFunction;
isConstantFunction = f.isConstantFunction;
this.kernel = kernel;
}
@Override
public Function deepCopy(Kernel kernel1) {
return new Function(this, kernel1);
}
/**
* Use this method only if you really know what you are doing.
*
* @param exp
* expression
* @param var
* variable
*/
public void setExpression(ExpressionNode exp, FunctionVariable var) {
super.setExpression(exp, new FunctionVariable[] { var });
derivative = null;
}
@Override
final public Function getFunction() {
return this;
}
/**
* @return variable
*/
public FunctionVariable getFunctionVariable() {
return fVars[0];
}
@Override
final public String getVarString(final StringTemplate tpl) {
if (fVars == null) {
return tpl.printVariableName("x");
}
return fVars[0].toString(tpl);
}
/**
* Call this function to resolve variables and init the function. May throw
* MyError (InvalidFunction).
*/
@Override
public boolean initFunction(boolean simplifyInt) {
if (fVars == null) {
// try function variable x
fVars = new FunctionVariable[] { new FunctionVariable(kernel) };
}
return super.initFunction(simplifyInt);
}
/**
* Initializes function variables without resolving commands in the
* expression
*/
public void initFunctionVars() {
if (fVars == null) {
// try function variable x
fVars = new FunctionVariable[] { new FunctionVariable(kernel) };
}
}
/**
* Returns this function's value at position x.
*
* @param x
* position
* @return f(x)
*/
@Override
public double value(double x) {
if (isBooleanFunction) {
// BooleanValue
return evaluateBoolean(x) ? 1 : 0;
}
// NumberValue
fVars[0].set(x);
return expression.evaluateDouble();
}
/**
* Returns this function's value at position x. (Note: use this method if
* isBooleanFunction() returns true.
*
* @param x
* position
* @return f(x)
*/
final public boolean evaluateBoolean(double x) {
fVars[0].set(x);
return expression.evaluateBoolean();
}
/**
* Shifts the function by vx to right and by vy up
*
* @param vx
* horizontal shift
* @param vy
* vertical shift
*/
@Override
final public void translate(double vx, double vy) {
boolean isLeaf = expression.isLeaf();
ExpressionValue left = expression.getLeft();
// translate x
if (!Kernel.isZero(vx)) {
if (isLeaf && left == fVars[0]) { // special case: f(x) = x
expression = shiftXnode(vx, 0);
} else {
// replace every x in tree by (x - vx)
// i.e. replace fVar with (fvar - vx)
translateX(expression, vx, 0);
}
}
// translate y
if (!Kernel.isZero(vy)) {
// f(x) = f(x) + vy
translateY(vy);
}
// make sure that expression object is changed!
// this is needed to know that the expression has changed
if (expression.isLeaf() && expression.getLeft().isExpressionNode()) {
expression = new ExpressionNode(
(ExpressionNode) expression.getLeft());
} else {
expression = new ExpressionNode(expression);
}
}
/**
* translates in y-coordinate
*
* @param vy
* y-coord difference
*/
final public void translateY(double vy) {
expression = translateY(expression, fVars, vy);
}
/**
* Shifts the function by vy up
*
* @param expr
* original expression
* @param fVars
* variables
*
* @param vy
* vertical translation
* @return translated expression
*/
final public static ExpressionNode translateY(ExpressionNode expr,
FunctionVariable[] fVars, double vy) {
ExpressionNode expression = expr.unwrap().wrap();
// special case: constant
if (expression.isLeaf() && expression.getLeft() != fVars[0]
&& (expression.getLeft() instanceof NumberValue)) {
MyDouble c = ((NumberValue) expression.getLeft()).getNumber();
c.set(Kernel.checkDecimalFraction(c.getDouble() + vy));
expression.setLeft(c);
return expression;
} else if (expression.getOperation() == Operation.IF) {
expression.setRight(
translateY(expression.getRight().wrap(), fVars, vy));
return expression;
} else if (expression.getOperation() == Operation.IF_ELSE) {
MyNumberPair left = (MyNumberPair) expression.getLeft();
left.setY(translateY(left.getY().unwrap().wrap(), fVars, vy));
expression.setRight(
translateY(expression.getRight().wrap(), fVars, vy));
return expression;
} else if (expression.getOperation() == Operation.IF_LIST) {
MyList left = (MyList) expression.getRight();
for (int i = 0; i < left.size(); i++) {
left.setListElement(i, translateY(
left.getListElement(i).unwrap().wrap(), fVars, vy));
}
return expression;
} else if (expression.getOperation() == Operation.MULTIPLY
&& ExpressionNode.isConstantDouble(expression.getLeft(), -1)) {
expression.setRight(
translateY(expression.getRight().wrap(), fVars, -vy));
return expression;
} else if (expression.getOperation() == Operation.PLUS) {
expression.setRight(
translateY(expression.getRight().wrap(), fVars, vy));
return expression;
} else if (expression.getOperation() == Operation.MINUS) {
expression.setRight(
translateY(expression.getRight().wrap(), fVars, -vy));
return expression;
}
return addNumber(expression, vy);
}
final private static ExpressionNode addNumber(ExpressionNode expression,
double n) {
Kernel kernel = expression.getKernel();
if (n > 0) {
return new ExpressionNode(kernel, expression, Operation.PLUS,
new MyDouble(kernel, n));
}
return new ExpressionNode(kernel, expression, Operation.MINUS,
new MyDouble(kernel, -n));
}
/*
* ******************** POLYNOMIAL FACTORING *******************
*/
// remember calculated factors
// do factoring only if expression changed
private ExpressionNode factorParentExp;
// factors of polynomial function
private LinkedList<PolyFunction> symbolicPolyFactorList;
private LinkedList<PolyFunction> numericPolyFactorList;
private boolean symbolicPolyFactorListDefined;
/**
* Returns all non-constant polynomial factors of this function relevant for
* root finding. A list of PolyFunction (resp. SymbolicPolyFunction) objects
* is returned. Note: may return null if this function is no polynomial.
*
* @param rootFindingSimplification
* for root finding factors may be simplified, e.g. sqrt(x) may
* be simplified to x
* @param avoidCAS
* true to only use internal code without calling CAS
* @return all non-constant polynomial factors of this function
*
*/
final public LinkedList<PolyFunction> getPolynomialFactors(
boolean rootFindingSimplification, boolean avoidCAS) {
// try to get symbolic polynomial factors
LinkedList<PolyFunction> result = getSymbolicPolynomialFactors(
rootFindingSimplification, avoidCAS);
// if this didn't work try to get numeric polynomial factors
if (result == null) {
result = getNumericPolynomialFactors(rootFindingSimplification,
avoidCAS);
}
return result;
}
/**
* Returns all non-constant polynomial factors of the n-th derivative of
* this function relevant for root finding. A list of PolyFunction (resp.
* SymbolicPolyFunction) objects is returned. Note: may return null if the
* n-th derivative is no polynomial.
*
* @param n
* derivative order
*
* @param rootFindingSimplification
* for root finding factors may be simplified, e.g. sqrt(x) may
* be simplified to x
* @return all non-constant polynomial factors of the n-th derivative
*/
final public LinkedList<PolyFunction> getSymbolicPolynomialDerivativeFactors(
int n, boolean rootFindingSimplification) {
Function deriv = getDerivative(n, false);
if (deriv == null) {
return null;
}
// try to get symbolic polynomial factors
return deriv.getSymbolicPolynomialFactors(rootFindingSimplification,
false);
}
/**
* Tries to expand this function to a polynomial with numeric coefficients
* and returns its n-th derivative as a PolyFunction object. Note: may
* return null if the n-th derivative is no polynomial.
*
* @param n
* order
* @param skipCASfallback
* when true, answer is computed without CAS; in case of failure
* null is returned
* @return derivative
*
*/
final public PolyFunction getNumericPolynomialDerivative(int n,
boolean skipCASfallback) {
// we expand the numerical expression of this function (all variables
// are
// replaced by their values) and try to get a polynomial.
// Then we take the derivative of this polynomial.
PolyFunction poly = expandToPolyFunction(expression, false,
skipCASfallback);
if (poly != null) { // we got a polynomial
for (int i = 0; i < n; i++) {
poly = poly.getDerivative();
}
}
return poly;
}
/**
* Tries to expand this function to a polynomial with numeric coefficients
* and returns its integral as a PolyFunction object. (without +c) Note: may
* return null if it's not a polynomial.
*
* @return integral
*
*/
final public PolyFunction getNumericPolynomialIntegral() {
// we expand the numerical expression of this function (all variables
// are
// replaced by their values) and try to get a polynomial.
// Then we take the integral of this polynomial.
PolyFunction poly = expandToPolyFunction(expression, false, true);
if (poly != null) { // we got a polynomial
poly = poly.getIntegral();
}
return poly;
}
/**
* Returns all symbolic non-constant polynomial factors of this function
* relevant for root finding. A list of PolyFunction (resp.
* SymbolicPolyFunction) objects is returned. Note: may return null if this
* function is no polynomial.
*
* @param rootFindingSimplification
* for root finding factors may be simplified, e.g. sqrt(x) may
* be simplified to x
* @param assumeFalseIfCASNeeded
* if we can't resolve this as polynomial without CAS and this
* flag is tue, we assume it's not a polynomial
* @return all symbolic non-constant polynomial factors of this function
*/
public LinkedList<PolyFunction> getSymbolicPolynomialFactors(
boolean rootFindingSimplification, boolean assumeFalseIfCASNeeded) {
if (factorParentExp != expression) {
// new expression
factorParentExp = expression;
if (symbolicPolyFactorList == null) {
symbolicPolyFactorList = new LinkedList<PolyFunction>();
} else {
symbolicPolyFactorList.clear();
}
symbolicPolyFactorListDefined = addPolynomialFactors(expression,
symbolicPolyFactorList, true, rootFindingSimplification,
assumeFalseIfCASNeeded);
}
if (symbolicPolyFactorListDefined
&& symbolicPolyFactorList.size() > 0) {
return symbolicPolyFactorList;
}
return null;
}
/**
* Returns all numeric non-constant polynomial factors of this function
* relevant for root finding. A list of SymbolicPolyFunction objects is
* returned. Note: may return null if this function is no polynomial.
*
* Note: we use the values of variables here (different to
* getSymbolicPolynomialFactors()).
*
* @param rootFindingSimplification
* for root finding factors may be simplified, e.g. sqrt(x) may
* be simplified to x
*/
private LinkedList<PolyFunction> getNumericPolynomialFactors(
boolean rootFindingSimplification, boolean avoidCAS) {
if (numericPolyFactorList == null) {
numericPolyFactorList = new LinkedList<PolyFunction>();
} else {
numericPolyFactorList.clear();
}
boolean success = addPolynomialFactors(expression,
numericPolyFactorList, false, rootFindingSimplification,
avoidCAS);
if (success && numericPolyFactorList.size() > 0) {
return numericPolyFactorList;
}
return null;
}
/**
* Adds all polynomial factors in ev to the given list (ev is an
* ExpressionNode in the beginning).
*
* @return false when a non-polynomial was found (e.g. sin(x))
* @param symbolic
* true for symbolic coefficients, false for numeric coefficients
* @param rootFindingSimplification
* for root finding factors may be simplified, e.g. sqrt(x) may
* be simplified to x
*/
private boolean addPolynomialFactors(ExpressionValue ev,
List<PolyFunction> l, boolean symbolic,
boolean rootFindingSimplification, boolean assumeFalseIfCASNeeded) {
if (ev.isExpressionNode()) {
ExpressionNode node = (ExpressionNode) ev;
if (node.isConditionalDeep()) {
return false;
}
switch (node.getOperation()) {
case MULTIPLY:
return addPolynomialFactors(node.getLeft(), l, symbolic,
rootFindingSimplification, assumeFalseIfCASNeeded)
&& addPolynomialFactors(node.getRight(), l, symbolic,
rootFindingSimplification,
assumeFalseIfCASNeeded);
// try some simplifications of factors for root finding
case POWER:
case DIVIDE:
if (!rootFindingSimplification) {
break;
}
// divide: x in denominator: no polynomial
// power: x in exponent: no polynomial
if (node.getRight().contains(fVars[0])) {
return false;
}
// power:
// symbolic: non-zero constants in exponent may be omitted
// numeric: non-zero values in exponent may be omitted
if (!symbolic || node.getRight().isConstant()) {
double rightVal;
try {
rightVal = node.getRight().evaluateDouble();
} catch (Exception e) {
e.printStackTrace();
return false;
}
if (node.getOperation().equals(Operation.POWER)) {
if (Kernel.isZero(rightVal)) {
// left^0 = 1
return addPolynomialFactors(new MyDouble(kernel, 1),
l, symbolic, rootFindingSimplification,
assumeFalseIfCASNeeded);
} else if (rightVal > 0) {
// left ^ right = 0 <=> left = 0 for right > 0
return addPolynomialFactors(node.getLeft(), l,
symbolic, rootFindingSimplification,
assumeFalseIfCASNeeded);
}
} else { // division
if (Kernel.isZero(rightVal)) {
// left / 0 = undefined
return false;
}
// left / right = 0 <=> left = 0 for right != null
return addPolynomialFactors(node.getLeft(), l, symbolic,
rootFindingSimplification,
assumeFalseIfCASNeeded);
}
}
break;
case ABS:
case SGN:
case SQRT:
if (!rootFindingSimplification) {
break;
}
// these functions can be omitted as f(x) = 0 iff x = 0
return addPolynomialFactors(node.getLeft(), l, symbolic,
rootFindingSimplification, assumeFalseIfCASNeeded);
default:
break;
}
}
// if we get here we have to add the ExpressionValue ev
// add only non constant factors that are relevant for root finding
if (!ev.isConstant()) {
// build the factor: expanded ev, get the coefficients and build
// a polynomial with them
PolyFunction factor = expandToPolyFunction(ev, symbolic,
assumeFalseIfCASNeeded);
if (factor == null)
{
return false; // did not work
}
l.add(factor);
}
return true;
}
/**
* Expands the given expression and builds a PolyFunction (or
* SymbolicPolyFunction) object with the coefficients of the resulting
* polynomial.
*
* @param ev
* expression value to be expanded
*
* @return null when node is not a polynomial
* @param symbolic
* true for symbolic coefficients (SymbolicPolyFunction), false
* for numeric coefficients (PolyFunction)
* @param assumeFalseIfCASNeeded
* true to assume that function is not polynomial if we couldn't
* prove it's polynomial without CAS
*/
public PolyFunction expandToPolyFunction(ExpressionValue ev,
boolean symbolic, boolean assumeFalseIfCASNeeded) {
PolyFunction polyFunNoCas = expandToPolyFunctionNoCas(ev, symbolic);
// TODO: make sure expandToPolyFunctionNoCas does not mess with ev
// instead of the next line
initFunction();
if (polyFunNoCas != null || assumeFalseIfCASNeeded) {
return polyFunNoCas;
}
ExpressionNode node;
if (ev.isExpressionNode()) {
node = (ExpressionNode) ev;
} else {
// wrap expressionValue
node = new ExpressionNode(kernel, ev);
}
// get coefficients as strings
String function, var;
StringTemplate tpl = StringTemplate.giacTemplate;
// See #1322
try {
function = node.getCASstring(tpl, symbolic);
var = fVars[0].toString(tpl);
} catch (NullPointerException e) {
// this is not a valid polynomial
return null;
}
String[] strCoeffs = kernel.getPolynomialCoeffs(function, var);
if (strCoeffs == null) {
// this is not a valid polynomial
return null;
}
// convert sring coefficients to coefficients of a SymbolicPolyFunction
// resp. PolyFunction
int degree = strCoeffs.length - 1;
if (symbolic) {
// build SymbolicPolyFunction
SymbolicPolyFunction symbPolyFun = new SymbolicPolyFunction(degree);
ExpressionNode[] symbCoeffs = symbPolyFun.getSymbolicCoeffs();
for (int i = 0; i < strCoeffs.length; i++) {
symbCoeffs[degree - i] = evaluateToExpressionNode(strCoeffs[i]);
if (symbCoeffs[degree - i] == null) {
return null;
}
symbCoeffs[degree - i].simplifyConstantIntegers();
}
return symbPolyFun;
}
// build PolyFunction
PolyFunction polyFun = new PolyFunction(degree);
for (int i = 0; i < strCoeffs.length; i++) {
ExpressionNode coeff = evaluateToExpressionNode(strCoeffs[i]);
if (coeff == null) {
Log.warn("error in buildPolyFunction:" + strCoeffs[i]);
return null;
}
try {
polyFun.coeffs[degree - i] = coeff.evaluateDouble();
} catch (Exception e) {
Log.warn("error in buildPolyFunction:" + e.getMessage());
e.printStackTrace();
return null;
}
}
return polyFun;
}
private ExpressionNode zeroExpr = new ExpressionNode(kernel,
new MyDouble(kernel, 0));
private PolyFunction expandToPolyFunctionNoCas(ExpressionValue ev,
boolean symbolic) {
PolyFunction polyFun = null;
FunctionVariable xVar = new FunctionVariable(kernel, "x");
ExpressionValue[][] coeff = null;
int terms = -1;
ExpressionValue evCopy = ev.deepCopy(kernel);
ExpressionNode replaced;
VariableReplacer varep = VariableReplacer.getReplacer(
fVars[0].toString(StringTemplate.defaultTemplate), xVar,
kernel);
if (evCopy instanceof ExpressionNode) {
replaced = ((ExpressionNode) evCopy).traverse(varep).wrap();
} else {
replaced = (new ExpressionNode(kernel, evCopy)).traverse(varep)
.wrap();
}
Equation equ = new Equation(kernel, replaced, new MyDouble(kernel, 0));
try {
coeff = Polynomial.fromNode(replaced, equ).getCoeff();
terms = coeff.length;
} catch (Throwable t) {
Log.warn(ev + " couldn't be transformed to polynomial:"
+ t.getMessage());
return null;
}
if (!equ.isPolynomial()) {
return null;
}
if (!symbolic) {
double[] coeffValues = new double[terms];
for (int i = 0; i < coeff.length; i++) {
if (coeff[i][0] instanceof ExpressionNode) {
coeffValues[i] = coeff[i][0].evaluateDouble(); // for ticket
// #2276
// ---Tam
} else {
coeffValues[i] = coeff[i][0] instanceof NumberValue
? ((NumberValue) coeff[i][0]).getDouble() : 0;
}
}
polyFun = new PolyFunction(coeffValues);
} else {
ExpressionNode[] coeffExpr = new ExpressionNode[terms];
for (int i = 0; i < coeff.length; i++) {
coeffExpr[i] = coeff[i][0] == null ? zeroExpr
: new ExpressionNode(kernel, coeff[i][0]);
}
polyFun = new SymbolicPolyFunction(coeffExpr);
}
return polyFun;
}
/**
* Parses given String str and tries to evaluate it to an ExpressionNode.
* Returns null if something went wrong.
*/
private ExpressionNode evaluateToExpressionNode(String str) {
try {
ExpressionNode en = kernel.getParser().parseExpression(str);
en.resolveVariables(new EvalInfo(false));
return en;
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (Error e) {
e.printStackTrace();
return null;
}
}
/*
* *************** CALULUS **************
*/
/**
* Returns n-th derivative of this function wrapped as a GeoFunction object.
*/
@Override
public GeoFunction getGeoDerivative(int n, boolean fast) {
if (geoDeriv == null) {
geoDeriv = new GeoFunction(kernel.getConstruction());
}
Function deriv = getDerivative(n, fast);
geoDeriv.setFunction(deriv);
geoDeriv.setDefined(deriv != null);
return geoDeriv;
}
private GeoFunction geoDeriv;
/**
* Returns n-th derivative of this function
*
* @param n
* order
* @param fast
* true = don't use CAS
* @return derivative
*/
final public Function getDerivative(int n, boolean fast) {
return getDerivative(n, true, fast);
}
/**
* Returns n-th derivative of this function where fractions are not kept
* (faster).
*
* @param n
* order
* @param fast
* don't use CAS
* @return derivative
*/
final public Function getDerivativeNoFractions(int n, boolean fast) {
return getDerivative(n, false, fast);
}
/**
*
* @param n
* derivative order
* @param keepFractions
* true for 123/100, false for 1.23 in coefficients
* @param fast
* if true -> use fast non-CAS derivatives
* @return n-th derivative
*/
final Function getDerivative(int n, boolean keepFractions, boolean fast) {
// check if it's a polynomial
PolyFunction polyDeriv = getNumericPolynomialDerivative(n, true);
// it it is...
if (polyDeriv != null) {
// ... we can calculate the derivative without loading the CAS
// (*much* faster, especially in web)
// NB keepFractions ignored, so different answer given for f(x) =
// 3x^2 / 5, f'(x)
boolean factor = getExpression().inspect(new Inspecting() {
@Override
public boolean check(ExpressionValue v) {
if (v instanceof ExpressionNode && ((ExpressionNode) v)
.getOperation() == Operation.POWER) {
if (((ExpressionNode) v).getLeft().unwrap()
.isExpressionNode()
&& ((ExpressionNode) v).getRight()
.evaluateDouble() > Function.MAX_EXPAND_DEGREE) {
return true;
}
}
return false;
}
});
if (factor) {
return getDerivativeNoCAS(n);
}
Function ret = polyDeriv.getFunction(kernel, getFunctionVariable());
if (fast) {
// ret.setSecret();
}
return ret;
}
if (fast || !kernel.useCASforDerivatives()) {
return getDerivativeNoCAS(n);
}
// get variable string with tmp prefix,
// e.g. "x" becomes "ggbtmpvarx" here
String varStr = fVars[0].toString(StringTemplate.prefixedDefault);
StringBuilder sb = new StringBuilder();
sb.append("Derivative[");
if (!keepFractions) {
sb.append("Numeric[");
}
sb.append("%");
if (!keepFractions) {
sb.append("]");
}
sb.append(",");
sb.append(varStr);
sb.append(",");
sb.append(n);
sb.append("]");
// for derivative we don't need arbconst
return (Function) evalCasCommand(sb.toString(), true, null);
}
/**
* @return Function y'(t)/x'(t) needed for parametric derivative
* @param funX
* function x(t)
* @param funY
* function y(t)
*/
public static Function getDerivativeQuotient(Function funX, Function funY) {
if (funX.fVars == null) {
return null;
}
// use fast non-CAS method
Function xDashed = funX.getDerivativeNoCAS(1);
Function yDashed = funY.getDerivativeNoCAS(1);
FunctionVariable fv = xDashed.getFunctionVariable();
// make sure both functions use same variable
ExpressionValue yDashedEv = yDashed.getExpression()
.replace(yDashed.getFunctionVariable(), fv);
ExpressionNode en = new ExpressionNode(funX.getKernel(), yDashedEv,
Operation.DIVIDE, xDashed.getExpression());
return new Function(en, fv);
}
/**
* Creates the difference expression (a - b) and stores the result in
* Function c.
*
* @param a
* minuend
* @param b
* subtrahend
* @param c
* difference
*/
final public static void difference(Function a, Function b, Function c) {
// copy only the second function and replace b.fVar by a.fVar
ExpressionNode left = a.expression;
ExpressionNode right = b.expression.getCopy(a.kernel);
// replace b.fVar in right by a.fVar to have only one function
// variable in our function
right.replace(b.fVars[0], a.fVars[0]);
ExpressionNode diffExp = new ExpressionNode(a.kernel, left,
Operation.MINUS, right);
c.setExpression(diffExp);
c.fVars[0] = a.fVars[0];
}
/**
* Creates the difference expression (a - line) and stores the result in
* Function c. This is needed for the intersection of function a and line ax
* + by + c = 0. b != 0 is assumed.
*
* @param f
* minuend
* @param line
* subtrahend (as line)
* @param c
* difference
*/
final public static void difference(Function f, GeoLine line, Function c) {
// build expression for line: ax + by + c = 0 (with b != 0)
// explicit form: line: y = -a/b x - c/b
// we need f - line: f(x) + a/b x + c/b
double coeffX = line.getX() / line.getY();
double coeffConst = line.getZ() / line.getY();
// build expression f - line: f(x) + a/b x + c/b
ExpressionNode temp;
// f(x) + a/b * x
if (coeffX > 0) {
temp = new ExpressionNode(f.kernel, f.expression, Operation.PLUS,
new ExpressionNode(f.kernel, new MyDouble(f.kernel, coeffX),
Operation.MULTIPLY, f.fVars[0]));
} else {
temp = new ExpressionNode(f.kernel, f.expression, Operation.MINUS,
new ExpressionNode(f.kernel,
new MyDouble(f.kernel, -coeffX), Operation.MULTIPLY,
f.fVars[0]));
}
// f(x) + a/b * x + c/b
if (coeffConst > 0) {
temp = new ExpressionNode(f.kernel, temp, Operation.PLUS,
new MyDouble(f.kernel, coeffConst));
} else {
temp = new ExpressionNode(f.kernel, temp, Operation.MINUS,
new MyDouble(f.kernel, -coeffConst));
}
c.setExpression(temp);
c.fVars[0] = f.fVars[0];
}
/*
* for root finding
*/
/**
* Decides whether function includes division by expression containing
* function variable
*
* @return true if function includes division by variable
*/
public final boolean includesDivisionByVariable() {
if (expression == null) {
return false;
}
return expression.includesDivisionBy(fVars[0]);
}
/**
* Decides whether function includes DataFunction, Freehand function
*
* @return true if function includes DataFunction, Freehand
*/
public final boolean includesFreehandOrDataFunction() {
if (expression == null) {
return false;
}
return expression.includesFreehandOrData();
}
/**
* Decides whether function includes eg If[], abs() function
*
* @return true if function includes abs(), If[] etc
*/
public final boolean includesNonContinuousIntegral() {
if (expression == null) {
return false;
}
return expression.includesNonContinuousIntegral();
}
@Override
public GeoFunction getGeoFunction() {
GeoFunction gf = new GeoFunction(kernel.getConstruction());
gf.setFunction(this);
return gf;
}
/**
* @param n
* order of derivative
* @return derivative calculated without the CAS
*/
public Function getDerivativeNoCAS(int n) {
ExpressionNode expDeriv = expression;
for (int i = 0; i < n; i++) {
expDeriv = expDeriv.derivative(fVars[0], kernel);
}
expDeriv.simplifyConstantIntegers();
return new Function(expDeriv, fVars[0]);
}
/**
* @return integral calculated without the CAS (will work only for very
* simple functions eg sin(3x))
*/
public Function getIntegralNoCAS() {
return new Function(expression.integral(fVars[0], kernel), fVars[0]);
}
/**
* Evaluates polynomial and its derivative
*/
@Override
public double[] evaluateDerivFunc(double x) {
double[] ret = new double[2];
ret[0] = this.value(x);
if (isBooleanFunction) {
ret[1] = Double.NaN;
return ret;
}
if (derivative == null) {
derivative = getDerivative(1, false, true);
}
ret[1] = derivative.value(x);
return ret;
}
@Override
public ExpressionValue derivative(FunctionVariable fv, Kernel kernel0) {
return expression.derivative(fv, kernel0);
}
/**
* @param algo
* secret parent algo
*/
public void setSecret(AlgoElement algo) {
getExpression().setSecret(algo);
}
@Override
public void updateCASEvalMap(TreeMap<String, String> map) {
if (map == null) {
return;
}
kernel.getConstruction()
.registerFunctionVariable(this.fVars[0].getSetVarString());
for (Entry<String, String> entry : map.entrySet()) {
GeoFunction gfun = kernel.getAlgebraProcessor()
.evaluateToFunction(entry.getValue(), true, true);
if (gfun != null) {
getCasEvalMap().put(entry.getKey(), gfun.getFunction());
}
}
kernel.getConstruction().registerFunctionVariable(null);
}
/**
* @param scale
* scale along x-axis
*/
public void dilateX(double scale) {
dilateX(expression, scale, 0);
}
/**
* Tries to build a RealRootDerivFunction out of this function and its
* derivative. This can be used for root finding. Note: changes to the
* function will not affect the returned RealRootDerivFunction.
*
* Switched to fast derivatives because of #4929
*
* @Deprecated, replace with UnivariateFunction derivative() (probably)
*
* @return real root function
*/
public UnivariateFunction derivative() {
Function deriv = getDerivativeNoFractions(1, true);
if (deriv == null) {
return null;
}
return deriv;
}
/**
* @param forRootFinding
* whether to ignore top level sqrt, abs
* @param symbolic
* function's symbolic expression must be a polynomial, e.g. x^2
* is ok but not x^a
* @return whether this function is polynomial
*/
public boolean isPolynomialFunction(boolean forRootFinding,
boolean symbolic) {
return isConstantFunction() || (symbolic
? getSymbolicPolynomialFactors(forRootFinding, false)
: getPolynomialFactors(forRootFinding, false)) != null;
}
}