/* $Id$
*
* Copyright (c) 2010 Anders Wallin (anders.e.e.wallin "at" gmail.com).
*
* This file is part of OpenCAMlib
* (see https://github.com/aewallin/opencamlib).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#ifndef BRENT_ZERO_H
#define BRENT_ZERO_H
namespace ocl
{
/// Brent's root finding algorithm
/// http://en.wikipedia.org/wiki/Brent's_method
///
/// find a zero of function f in the interval [a,b]
/// a and b must bracket the root, i.e. f(a) must have different sign than f(b)
/// needs a pointer to an ErrObj which must provide a function
/// double ErrObj::error(double x) for which we try to find a zero
///
/// FIXME: describe tolerance parameters eps and t
template
double brent_zero( double a, double b, double eps, double t, ErrObj* ell) {
// f must have unequal sign at a and b, i.e.
// f(a)*f(b) < 0
// returns the location of a root c where f(c)=0
// to within 6*eps*abs(c)+2*t tolerance
double c,d,e;
double fa,fb,fc; // function values
double m,p,q,r,s;
double tol;
fa = ell->error(a); // f(a);
fb = ell->error(b); // f(b);
if (fa*fb >= 0.0) {// check for opposite signs
std::cout << " brent_zero() called with invalid interval [a,b] !\n";
assert(0);
}
c = a; // set c sln equal to a sln
fc = fa;
e = b-a; // interval width
d = e; // interval width
while (true) {
if (fabs(fc)b was small, or fa is a better solution
e = m;
d = e; // bisection?
} else {
s = fb/fa;
if (a == c) {
p=2.0*m*s;
q=1.0-s;
} else {
q = fa/fc;
r = fb/fc;
p = s*(2.0*m*a*(q-r)-(b-a)*(r-1.0));
q = (q-1.0)*(r-1.0)*(s-1.0);
}
if (p>0.0)
q=-q;
else
p=-p; // make p negative
s=e;
e=d;
if ( (2.0*p < (3.0*m*q-fabs(tol*q))) && (p tol ) // if d is "large"
b = b+d; // push the root by d
else if ( m > 0.0 )
b = b+tol; // otherwise, push root by tol in direction of m
else
b = b-tol;
// std::cout << " brent_zero b=" << b << "\n";
fb = ell->error(b); // f(b);
if (std::signbit(fb) == std::signbit(fc))
{
// if ((fb > 0.0 && fc > 0.0) || (fb <= 0.0 && fc <= 0.0)) {
// fb and fc have the same sign
c = a; // so change c to a
fc = fa;
e = b-a; // interval width
d = e;
}
} // end iteration-loop
return b;
}
} // end namespace
#endif
// end file brent_zero.hpp