/*

    Example of using dual numbers for minimization.
 
    Gradient descent: to find x where f(x) is minimal
       1) start at a guess x
       2) walking in the opposite direction of df/dx   hence x[n]=x[n-1] - step*df/dx
       3) exit when |df/dx|<tolerance

    https://en.wikipedia.org/wiki/Gradient_descent
    
*/

#include <cmath>
#include <iostream>
using namespace std;

// This is the header defining DualNumber class.
#include "DualNumber.hpp"


template <typename Number>
Number f(Number x)
{
   return sqrt(x+100)*(x+5)*(x+6);
}


int main()
{
   double x=2.0;
   double step=0.1;
   double tol=1e-5;

   int iter=0;   
   int itermax=500;
   double fval,dfdx;
   double xprev,dfdxprev=0;
   do 
     {
        DualNumber w(x,1.0);
        DualNumber fw=f(w);
        
        fval=fw.real();    // f(x)
        dfdx=fw.infi();    // df(x)/dx
     
        cout << iter << "\t" << x << "\t" << fval << "\t" << dfdx << "\n";

        x-=step*dfdx;              //  much faster: x-=step*(0.9*dfdx+0.1*dfdxprev);
        iter++;
        
        dfdxprev=dfdx;
     }
    while (fabs(dfdx)>tol && iter<itermax);
      
   
   return 0;
}

