#ifndef CPL_FINDROOT_HPP
#define CPL_FINDROOT_HPP

#include <iostream>

namespace cpl {

//      Implementations of elementary root finding algorithms

class RootFinder {                        //    base class for other algorithms
  public:
        RootFinder ( ) {                  //    default parameters
                x0 = 0;                   //    initial guess
                x1 = 1;                   //    second guess
                dx = 1;                   //    step size
                accuracy = 1.0e-6;        //    desired accuracy
                maxSteps = 20;            //    to check runaway algorithms
                verbose = 0;              //    =0 turns off =1 turns on printing
                os = &cout;               //    pointer to print stream
        }

        void guessRoot ( double xGuess );
        void guessStep ( double stepSize );
        void secondGuessRoot ( double xGuess );
        int bracketRoot ( double f ( double x ) );
        void setAccuracy ( double epsilon );
        double getAccuracy ( ) { return accuracy; }
        int getSteps ( ) { return steps; }
        void setMaxSteps ( int steps );
        int getMaxSteps ( ) { return maxSteps; }
        void togglePrinting ( ) { verbose = verbose ? 0 : 1; }
        void setPrintStream ( std::ostream& ostr ) { os = &ostr; }

  protected:
        double x0;
        double x1;
        double dx;
        double accuracy;
        int steps;
        int maxSteps;
        int verbose;
        std::ostream *os;
};

class SimpleSearch : public RootFinder {
  public:
        SimpleSearch ( ) { maxSteps = 1000; }
        double findRoot ( double f ( double x ) );
        double findRoot ( double f ( double x ),
                                   double xGuess, double dxGuess ) {
                guessRoot(xGuess);
                guessStep(dxGuess);
                return findRoot(f);
        }
};

class BisectionSearch : public RootFinder {
  public:
        BisectionSearch ( ) { maxSteps = 100; }
        double findRoot ( double f ( double x ) );
        double findRoot ( double f ( double x ),
                                   double xGuess, double xSecondGuess ) {
                guessRoot(xGuess);
                secondGuessRoot(xSecondGuess);
                return findRoot(f);
        }
};

class SecantSearch : public RootFinder {
  public:
        SecantSearch ( ) { maxSteps = 30; }
        double findRoot ( double f ( double x ) );
        double findRoot ( double f ( double x ),
                                   double xGuess, double xSecondGuess ) {
                guessRoot(xGuess);
                secondGuessRoot(xSecondGuess);
                return findRoot(f);
        }
};

class TangentSearch : public RootFinder {
  public:
        TangentSearch ( ) { maxSteps = 20; }
        double findRoot ( double f ( double x ),
                                   double fPrime ( double x ) );
        double findRoot ( double f ( double x ),
                                   double fPrime ( double x ),
                                   double xGuess ) {
                guessRoot(xGuess);
                return findRoot(f, fPrime);
        }
};

}  /* end namespace cpl */

#endif /* CPL_FINDROOT_HPP */
