package org.geogebra.common.kernel.algos;
import java.util.ArrayList;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math3.ode.FirstOrderIntegrator;
import org.apache.commons.math3.ode.nonstiff.DormandPrince54Integrator;
import org.apache.commons.math3.ode.sampling.StepHandler;
import org.apache.commons.math3.ode.sampling.StepInterpolator;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.MyPoint;
import org.geogebra.common.kernel.SegmentType;
import org.geogebra.common.kernel.arithmetic.FunctionalNVar;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoLocus;
import org.geogebra.common.kernel.geos.GeoNumeric;
/**
* @author Bencze Balazs
*/
public class AlgoNSolveODE extends AlgoElement {
private GeoList fun; // input
private GeoList startY; // input
private GeoNumeric startX; // input
private GeoNumeric endX; // input
private GeoLocus out[]; // output
protected ArrayList<MyPoint> al[];
private double t0, y0[];
protected int dim;
/**
* @param cons
* cons
* @param labels
* labels
* @param fun
* the list of the functions
* @param startX
* from where should be integrated (X-coords)
* @param startY
* from where should be integrated (y-coords)
* @param endX
* until when should be integrated (X-coords)
*/
public AlgoNSolveODE(Construction cons, String[] labels, GeoList fun,
GeoNumeric startX, GeoList startY, GeoNumeric endX) {
super(cons);
this.fun = fun;
this.startY = startY;
this.startX = startX;
this.endX = endX;
dim = fun.size();
y0 = new double[dim];
setInputOutput();
compute();
GeoElement.setLabels(labels, out);
}
@Override
public Commands getClassName() {
return Commands.NSolveODE;
}
/**
* @return locus
*/
public GeoLocus[] getResult() {
return out;
}
@Override
protected void setInputOutput() {
input = new GeoElement[4];
input[0] = fun;
input[1] = startX;
input[2] = startY;
input[3] = endX;
out = new GeoLocus[dim];
for (int i = 0; i < dim; i++) {
out[i] = new GeoLocus(cons);
}
super.setOutputLength(dim);
for (int i = 0; i < dim; i++) {
super.setOutput(i, out[i]);
}
setDependencies();
}
@Override
public void compute() {
for (int i = 0; i < dim; i++) {
if (!fun.get(i).isDefined() || !startY.get(i).isDefined()) {
setUndefined();
return;
}
}
if (!startX.isDefined() || !endX.isDefined()) {
setUndefined();
return;
}
t0 = startX.getDouble();
for (int i = 0; i < dim; i++) {
y0[i] = ((GeoNumeric) startY.get(i)).getDouble();
}
al = new ArrayList[dim];
for (int i = 0; i < dim; i++) {
al[i] = new ArrayList<MyPoint>();
}
FirstOrderIntegrator integrator = new DormandPrince54Integrator(0.001,
0.01, 0.000001, 0.0001);
FirstOrderDifferentialEquations ode = new ODEN(fun);
integrator.addStepHandler(stepHandler);
for (int i = 0; i < dim; i++) {
al[i].add(new MyPoint(startX.getDouble(), y0[i],
SegmentType.MOVE_TO));
}
try {
integrator.integrate(ode, t0, y0, endX.getDouble(), y0);
} catch (RuntimeException e) {
// catches ArithmeticException, IllegalStateException and
// ArithmeticException
setUndefined();
return;
}
for (int i = 0; i < dim; i++) {
out[i].setPoints(al[i]);
out[i].setDefined(true);
}
}
private void setUndefined() {
for (int i = 0; i < out.length; i++) {
out[i].setUndefined();
}
}
private StepHandler stepHandler = new StepHandler() {
public void init(double t0, double[] y0, double t) {
//
}
@Override
public void handleStep(StepInterpolator interpolator, boolean isLast) {
double t = interpolator.getCurrentTime();
double[] y1 = interpolator.getInterpolatedState();
for (int i = 0; i < y1.length; i++) {
al[i].add(new MyPoint(t, y1[i], SegmentType.LINE_TO));
}
}
};
private class ODEN implements FirstOrderDifferentialEquations {
private GeoList fun1;
public ODEN(GeoList fun) {
this.fun1 = fun;
}
@Override
public int getDimension() {
return dim;
}
@Override
public void computeDerivatives(double t, double[] y, double[] yDot) {
double input1[] = new double[dim + 1];
input1[0] = t;
for (int i = 0; i < dim; i++) {
input1[i + 1] = y[i];
}
for (int i = 0; i < dim; i++) {
yDot[i] = ((FunctionalNVar) fun1.get(i)).evaluate(input1);
}
}
}
}