// Copyright (c) 1997-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.


#include <math_BracketedRoot.hxx>
#include <math_Function.hxx>
#include <StdFail_NotDone.hxx>

// reference algorithme:  
//                   Brent method 
//                   numerical recipes in C  p 269
math_BracketedRoot::math_BracketedRoot (math_Function& F, 
                                           const Standard_Real Bound1, 
                                           const Standard_Real Bound2, 
                                           const Standard_Real Tolerance, 
                                           const Standard_Integer NbIterations,
                                           const Standard_Real ZEPS ) {

    Standard_Real Fa,Fc,a,c=0,d=0,e=0;
    Standard_Real min1,min2,p,q,r,s,tol1,xm;
  
    a = Bound1;
    TheRoot = Bound2;
    F.Value(a,Fa);
    F.Value(TheRoot,TheError);
    if (Fa*TheError > 0.) { Done = Standard_False;}
    else {
      Fc = TheError ;
      for (NbIter = 1; NbIter <= NbIterations; NbIter++) {
          if (TheError*Fc > 0.) {
             c = a;      // rename a TheRoot c and adjust bounding interval d
             Fc = Fa;
             d = TheRoot - a;
             e = d;
          } 
          if ( Abs(Fc) < Abs(Fa) ) {
             a = TheRoot;
             TheRoot = c;
             c = a;
             Fa = TheError;
             TheError = Fc;
             Fc = Fa;
          }
          tol1 = 2.*ZEPS * Abs(TheRoot) + 0.5 * Tolerance; // convergence check
          xm = 0.5 * ( c - TheRoot );
          if (Abs(xm) <= tol1 || TheError == 0. ) {
               Done = Standard_True;
               return;
	  }
          if (Abs(e) >= tol1 && Abs(Fa) > Abs(TheError) ) {
             s = TheError / Fa; // attempt inverse quadratic interpolation
             if (a == c) {
                p = 2.*xm*s;
                q = 1. - s;
             }
             else {
                q = Fa / Fc;
                r = TheError / Fc;
                p = s * (2.*xm *q * (q - r) - (TheRoot - a)*(r - 1.)); 
                q = (q -1.) * (r - 1.) * (s - 1.);
             }
            if ( p > 0. ) { q = -q;} // check whether in bounds
            p = Abs(p);
            min1 = 3.* xm* q - Abs(tol1 *q);
            min2 = Abs(e * q);
            if (2.* p < (min1 < min2 ? min1 : min2) ) {
               e = d ;  // accept interpolation
               d = p / q;
            }
            else {
               d = xm;  // interpolation failed,use bissection
               e = d;
            }
          }
          else {   // bounds decreasing too slowly ,use bissection
              d = xm;
              e =d;            
          }
          a = TheRoot ;   // move last best guess to a
          Fa = TheError;
          if (Abs(d) > tol1) {  // evaluate new trial root
             TheRoot += d;
          }
          else {
             TheRoot += (xm > 0. ? Abs(tol1) : -Abs(tol1));
          }
          F.Value(TheRoot,TheError);
      }  
     Done = Standard_False;
    }  
  }


    void math_BracketedRoot::Dump(Standard_OStream& o) const {

       o << "math_BracketedRoot ";
       if(Done) {
         o << " Status = Done \n";
	 o << " Number of iterations = " << NbIter << std::endl;
	 o << " The Root is: " << TheRoot << std::endl;
	 o << " The value at the root is: " << TheError << std::endl;
       }
       else {
         o << " Status = not Done \n";
       }
}
