/*
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.algos;
import org.apache.commons.math3.analysis.solvers.BrentSolver;
import org.apache.commons.math3.analysis.solvers.NewtonSolver;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.NumberValue;
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.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.roots.RealRootUtil;
import org.geogebra.common.util.debug.Log;
/**
* Finds one real root of a function in the given interval using Brent's method.
*/
public class AlgoRootInterval extends AlgoElement {
private GeoFunction f; // input
private NumberValue a, b; // interval bounds
private GeoPoint rootPoint; // output
private GeoElement aGeo, bGeo;
private BrentSolver rootFinder;
NewtonSolver rootPolisher;
public AlgoRootInterval(Construction cons, String label, GeoFunction f,
GeoNumberValue a, GeoNumberValue b) {
super(cons);
this.f = f;
this.a = a;
this.b = b;
aGeo = a.toGeoElement();
bGeo = b.toGeoElement();
// output
rootPoint = new GeoPoint(cons);
setInputOutput(); // for AlgoElement
compute();
rootPoint.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.Root;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[3];
input[0] = f;
input[1] = aGeo;
input[2] = bGeo;
super.setOutputLength(1);
super.setOutput(0, rootPoint);
setDependencies();
}
public GeoPoint getRootPoint() {
return rootPoint;
}
@Override
public final void compute() {
rootPoint.setCoords(calcRoot(), 0.0, 1.0);
}
final double calcRoot() {
if (!(f.isDefined() && aGeo.isDefined() && bGeo.isDefined())) {
return Double.NaN;
}
double root = Double.NaN;
Function fun = f.getFunction();
if (rootFinder == null) {
rootFinder = new BrentSolver();
rootPolisher = new NewtonSolver();
}
double min = a.getDouble();
double max = b.getDouble();
double newtonRoot = Double.NaN;
try {
// Brent's method (Apache)
root = rootFinder.solve(AlgoRootNewton.MAX_ITERATIONS, fun, min,
max);
} catch (Exception e) {
// e.printStackTrace();
Log.debug("problem finding root: " + e.getMessage());
try {
// Let's try again by searching for a valid domain first
double[] borders = RealRootUtil.getDefinedInterval(fun, min,
max);
root = rootFinder.solve(AlgoRootNewton.MAX_ITERATIONS, fun,
borders[0],
borders[1]);
} catch (Exception ex) {
// ex.printStackTrace();
Log.debug("problem finding root: " + ex.getMessage());
return Double.NaN;
}
}
// Log.debug("result from Brent: " + root);
// ******** Polish Root ***************
// adpated from EquationSolver
// #4691
try {
newtonRoot = rootPolisher.solve(AlgoRootNewton.MAX_ITERATIONS, fun,
min,
max, root);
if (Math.abs(fun.value(newtonRoot)) < Math.abs(fun.value(root))) {
root = newtonRoot;
// Log.debug("polished result from Newton is better: " +
// newtonRoot);
}
} catch (Exception e) {
Log.debug("problem polishing root: " + e.getMessage());
}
// check result
if (Math.abs(fun.value(root)) < Kernel.MIN_PRECISION) {
return root;
}
Log.debug("problem with root accuracy");
return Double.NaN;
}
@Override
final public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-30
// simplified to allow better Chinese translation
return getLoc().getPlain("RootOfAonIntervalBC", f.getLabel(tpl),
aGeo.getLabel(tpl), bGeo.getLabel(tpl));
}
}