/*
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.advanced;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.plugin.Operation;
/**
* Taylor series of a function (GeoFunction)
*
* @author Markus Hohenwarter
*/
public class AlgoTaylorSeries extends AlgoElement {
// works OK for simple functions eg sin(x)
// sin(cos(x)) very slow for n=10 and above
private static final int MAX_ORDER = 80;
private GeoFunction f; // input
private GeoNumberValue a; // series for f about the point x = a
private GeoNumberValue n; // order of series
private GeoFunction g; // output g = f'
private GeoElement ageo, ngeo;
/**
* Creates new Taylor series for function f about the point x=a of order n.
*
* @param cons
* construction
* @param label
* output label
* @param f
* function
* @param a
* start value
* @param n
* degree
*/
public AlgoTaylorSeries(Construction cons, String label, GeoFunction f,
GeoNumberValue a, GeoNumberValue n) {
super(cons);
this.f = f;
this.a = a;
this.n = n;
ageo = a.toGeoElement();
ngeo = n.toGeoElement();
g = new GeoFunction(cons); // output
setInputOutput(); // for AlgoElement
compute();
g.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.TaylorSeries;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[3];
input[0] = f;
input[1] = ageo;
input[2] = ngeo;
super.setOutputLength(1);
super.setOutput(0, g);
setDependencies(); // done by AlgoElement
}
/**
* @return result
*/
public GeoFunction getPolynomial() {
return g;
}
// ON CHANGE: similiar code is in AlgoPolynomialForFunction
@Override
public final void compute() {
if (!f.isDefined() || !ageo.isDefined() || !ngeo.isDefined()) {
g.setUndefined();
return;
}
if (f.getFunction().getExpression().isSecret()) {
g.setUndefined();
return;
}
// check order
double nd = n.getDouble();
if (nd < 0) {
g.setUndefined();
return;
} else if (nd > MAX_ORDER) {
nd = MAX_ORDER;
}
int order = (int) Math.round(nd);
// build expression of polynomial
double ad = a.getDouble();
// first part f(a)
double coeff = f.value(ad);
if (Double.isNaN(coeff) || Double.isInfinite(coeff)) {
g.setUndefined();
return;
}
ExpressionNode series = null; // expression for the Taylor series
if (!Kernel.isZero(coeff)) {
series = new ExpressionNode(kernel, new MyDouble(kernel, coeff));
}
FunctionVariable fVar = new FunctionVariable(kernel);
// other parts (k-thDerivative of f at a) / k! * (x - a)^k
if (order > 0) {
ExpressionValue diffExp;
// build the expression (x - a)
if (Kernel.isZero(ad)) { // only x
diffExp = fVar;
} else if (ad > 0) { // (x - a)
diffExp = new ExpressionNode(kernel, fVar, Operation.MINUS,
new MyDouble(kernel, ad));
} else { // (x + a)
diffExp = new ExpressionNode(kernel, fVar, Operation.PLUS,
new MyDouble(kernel, -ad));
}
Function deriv = f.getFunction();
for (int k = 1; k <= order; k++) {
// calculate next derivative from the last one
deriv = deriv.getDerivative(1, true);
if (deriv == null) {
g.setUndefined();
return;
}
coeff = deriv.value(ad);
// Log.debug("deriv = "
// + deriv.toValueString(StringTemplate.giacTemplate));
// Log.debug("coeff(" + k + ") = " + coeff);
if (Double.isNaN(coeff) || Double.isInfinite(coeff)) {
g.setUndefined();
return;
} else if (Kernel.isZero(coeff))
{
continue; // this part vanished
}
boolean negativeCoeff = coeff < 0;
// build the expression (x - a) ^ k
ExpressionValue powerExp;
switch (k) {
case 1:
powerExp = diffExp;
break;
default:
powerExp = new ExpressionNode(kernel,
new ExpressionNode(kernel, diffExp, Operation.POWER,
new MyDouble(kernel, k)),
Operation.DIVIDE,
new ExpressionNode(kernel, new MyDouble(kernel, k),
Operation.FACTORIAL, null));
}
// build the expression
// (k-thDerivative of f at a) * (x - a)^k / k!
ExpressionValue partExp;
MyDouble coeffMyDouble = null;
if (Kernel.isEqual(coeff, 1.0)) {
partExp = powerExp;
} else {
coeffMyDouble = new MyDouble(kernel, coeff);
partExp = new ExpressionNode(kernel, coeffMyDouble,
Operation.MULTIPLY, powerExp);
}
// add part to series
if (series == null) {
series = new ExpressionNode(kernel, partExp);
} else {
if (negativeCoeff) {
if (coeffMyDouble != null)
{
coeffMyDouble.set(-coeff); // change sign
}
series = new ExpressionNode(kernel, series,
Operation.MINUS, partExp);
} else {
series = new ExpressionNode(kernel, series,
Operation.PLUS, partExp);
}
}
}
}
if (series == null) { // this means f(x) = 0
series = new ExpressionNode(kernel, new MyDouble(kernel, 0));
}
// Function series
Function seriesFun = new Function(series, fVar);
g.setFunction(seriesFun);
g.setDefined(true);
}
}