// L-36 MCS 360 Mon 20 Nov 2017 : chebyshev.cpp

/*

  The value of the n-th Chebyshev polynomial T(n,x) is recursively
  computed as follows T(0,x) = 1, T(1,x) = x, 
  T(n,x) = 2x*T(n-1,x) - T(n-2,x), n >= 2.

(1) Define a recursive function T which takes on input an integer value
    for n and a double for x and then returns the value of T(n,x).

(2) Explain why the straightforward recursive definition is wasteful.
    Write an efficient recursive function using memoization.

(3) Use a stack to write an iterative version of the function T,
    recursively defined above.

*/

#include <iostream>
#include <vector>
#include <utility>
#include <stack>

using namespace std;

double T ( int n, double x );
// Returns the value of the n-th Chebyshev polynomial in x,
// computed recursively with the definition.

double memoT ( int n, double x );
// Returns the value of T(n,x) with memoization.

double iterT ( int n, double x );
// Returns the value of the n-th Chebyshev polynomial in x,
// computed iteratively with a stack of function calls.

double memoT ( int n, double x );
// Returns the value of T(n,x) with memoization.

int main ( void )
{
   cout << "Give the degree : ";
   int n; cin >> n;

   cout << "Give a value for x : ";
   double x; cin >> x;

   cout << "Run without memoization ? (y/n) ";
   char nomemo; cin >> nomemo;
   cout << "Run iterative version ? (y/n) ";
   char runiter; cin >> runiter;

   if(nomemo == 'y')
   {
      double tx = T(n,x);
      cout << "recursive T(" << n << "," << x << ") = " << tx << endl;
   }
   if(runiter == 'y')
   {
      double itx = iterT(n,x);
      cout << "iterative T(" << n << "," << x << ") = " << itx << endl;
   }
   double mtx = memoT(n,x);
   cout << " memoized T(" << n << "," << x << ") = " << mtx << endl;

   return 0;
}

double T ( int n, double x )
{
   if(n == 0)
      return 1.0;
   else if(n == 1)
      return x;
   else
      return 2*x*T(n-1,x) - T(n-2,x);
}

double memoT ( int n, double x )
{
   static vector<double> mem;

   if(n > mem.capacity())
   {
      size_t vsz = mem.capacity();
      mem.reserve(n-vsz+1);
      for(int i=vsz; i<=n; i++) mem[i] = -1;
   }
   if(mem[n] != -1)
      return mem[n];
   else
   {
      double result;
      if(n == 0)
         result = 1.0;
      else if(n == 1)
         result = x;
      else
         result = 2*x*memoT(n-1,x) - memoT(n-2,x);
      mem[n] = result;
      return result;
   }
}

double iterT ( int n, double x )
{
   double result = 0.0;
   stack< pair<int,double> > stk;

   pair<int,double> call;
   call.first = n;    // degree of the Chebyshev polynomial
   call.second = 1.0; // accumulates coefficient before call
   stk.push(call);

   do
   {
      pair<int, double> topstack = stk.top();
      stk.pop();
      if(topstack.first == 0)
         result = result + topstack.second*1.0;
      else if(topstack.first == 1)
         result = result + topstack.second*x;
      else
      {
         pair<int, double> firstcall;
         firstcall.first = topstack.first-1;
         firstcall.second = 2.0*x*topstack.second;
         stk.push(firstcall);

         pair<int, double> secondcall;
         secondcall.first = topstack.first-2;
         secondcall.second = -topstack.second;
         stk.push(secondcall);
      }
   }
   while(not stk.empty());

   return result;
}
