#include <stdio.h>
#include <vector.h>
#include <stack.h>
#include <math.h>

#include "treenode.h"
#include "visitor.h"
#include "iterators.h"
#include "dstruct.h"
#include "multiplicative.h"

#include "underlying/binom.h"
#include "underlying/binom2.h"
#include "underlying/timeHalvingTrinom.h"

#include "derivative/europeanCall.h"
#include "derivative/europeanPut.h"
#include "derivative/americanPut.h"
#include "derivative/averageStrikeEuropeanCall.h"
#include "derivative/averageStrikeAmericanPut.h"

#include "pricing.h"

int main(void);

template <class underlyingMethod, class inputParameters, class derivativeMethod>
void table8(double K, double S0, double sigma, double r);

template <class derivativeMethod>
void averageStrike(void);

int main(void)
{
  /** one **********************************/
  IP_Binom param;

  param.dt = vector<double>(1);

  param.S0 = 100.0;
  param.N = 3;
  param.dt[0] = 1.0/3.0;
  param.U = 1.1;
  param.D = 0.9091;
  param.r = 0.06;
  
  Pricing<UNDERLYING(Binom), Matrix<Complete, Multiplicative>, EuropeanCall> *oneEcall = new Pricing<UNDERLYING(Binom), Matrix<Complete, Multiplicative>, EuropeanCall>(param, 100.0);
  
  printf("Model 1 (Binom)\n");
  printf("\tEuropean Call, %.4f\n", oneEcall->getDerivativeRootValue());
  
  Pricing<UNDERLYING(Binom), Matrix<Complete, Multiplicative>, AmericanPut> *oneAput = new Pricing<UNDERLYING(Binom), Matrix<Complete, Multiplicative>, AmericanPut>(param, 100.0);

  printf("\tAmerican Put, %.4f\n\n", oneAput->getDerivativeRootValue());

  printf("Model 2 (Binom2).\n");
  printf("EuropeanPut\n");
  table8<UNDERLYING(Binom2), EuropeanPut>(10.0, 5.0, 0.5, 0.12);

  printf("AmericanPut\n");
  table8<UNDERLYING(Binom2), AmericanPut>(10.0, 9.0, 0.5, 0.12);

  printf("Average Strike European Call\n");
  averageStrike<AverageStrikeEuropeanCall>();
  
  printf("Average Strike American Put\n");
  averageStrike<AverageStrikeAmericanPut>();
  
  printf("Model 5 (Time Halving Trinomial)\n");
  table8<UNDERLYING(TimeHalvingTrinom), AmericanPut>(10.0, 9.0, 0.5, 0.12);
  
  printf("\nTree (Complete)\n");
  
  printf("N = 10\n");
  
  double T;

  for(double t = 0.0; t < 1.25; t+= 0.25)
    {
      IP_TimeHalvingTrinom param;

      T = MAX(0.0833, t);

      param.dt = vector<double>(1);
      param.S0 = 9.0;
      param.N = (unsigned int)10;
      param.dt = vector<double>(1);
      param.dt[0] = (double)((double)T/(double)10.0);
      param.sigma = 0.5;
      param.r = 0.12;

      Pricing<UNDERLYING(TimeHalvingTrinom), Tree<Complete, Multiplicative>, AmericanPut> *derivative = new Pricing<UNDERLYING(TimeHalvingTrinom), Tree<Complete, Multiplicative>, AmericanPut>(param, 10.0);
      
      printf("\tdt = %1.4f/%d, %f\n", T, (int)10, derivative->getDerivativeRootValue());
    }
  
  printf("\nTree (Memory Saving)\n");
  printf("N = 10\n");

  for(double t = 0.0; t < 1.25; t+= 0.25)
    {
      IP_TimeHalvingTrinom param;

      T = MAX(0.0833, t);
      
      param.dt = vector<double>(1);
      param.S0 = 9.0;
      param.N = (unsigned int)10;
      param.dt = vector<double>(1);
      param.dt[0] = (double)((double)T/(double)10.0);
      param.sigma = 0.5;
      param.r = 0.12;

      Pricing<UNDERLYING(TimeHalvingTrinom), Tree<MemorySaving, Multiplicative>, AmericanPut> *derivative = new Pricing<UNDERLYING(TimeHalvingTrinom), Tree<MemorySaving, Multiplicative>, AmericanPut>(param, 10.0);
      
      printf("\tdt = %1.4f/%d, %f\n", T, (int)10, derivative->getDerivativeRootValue());
    }
  
  return 0;
}

template <class underlyingMethod, class inputParameters, class derivativeMethod>
void table8(double K, double S0, double sigma, double r)
{
  double N;
  double T;

  printf("Matrix (Complete)\n");
  
  for(unsigned int n = 4; n < 9; n++)
    {
      N = pow(2, n);

      printf("N = %3.0f\n", N);

      for(double t = 0.0; t < 1.25; t += 0.25)
	{
	  T = MAX(0.0833, t);

	  inputParameters param;
  
	  param.S0 = S0;
	  param.N = (unsigned int)N;
	  param.dt = vector<double>(1);
	  param.dt[0] = (double)((double)T/(double)N);
	  param.sigma = sigma;
	  param.r = r;
  
	  Pricing<underlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, derivativeMethod> *derivative = new Pricing<underlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, derivativeMethod>(param, K);
  
	  printf("\tdt = %1.4f/%d, %f\n", T, (int)N, derivative->getDerivativeRootValue());
	}
    }

  printf("\nMatrix (Memory Saving)\n");

  for(unsigned int n = 4; n < 9; n++)
    {
      N = pow(2, n);

      printf("N = %3.0f\n", N);

      for(double t = 0.0; t < 1.25; t += 0.25)
	{
	  T = MAX(0.0833, t);

	  inputParameters param;
  
	  param.S0 = S0;
	  param.N = (unsigned int)N;
	  param.dt = vector<double>(1);
	  param.dt[0] = (double)((double)T/(double)N);
	  param.sigma = sigma;
	  param.r = r;
  
	  Pricing<underlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, derivativeMethod> *derivative = new Pricing<underlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, derivativeMethod>(param, K);
  
	  printf("\tdt = %1.4f/%d, %f\n", T, (int)N, derivative->getDerivativeRootValue());
	}
    }
}

template <class derivativeMethod>
void averageStrike(void)
{
  double T;

  printf("Tree (Complete)\n");
  
  for(unsigned int N = 8; N < 24; N+=8)
    {
      printf("N = %3d\n", N);

      for(double t = 0.0; t < 1.25; t += 0.25)
	{
	  T = MAX(0.0833, t);

	  IP_Binom2 param2;
  
	  param2.S0 = 10.0;
	  param2.N = (unsigned int)N;
	  param2.dt = vector<double>(1);
	  param2.dt[0] = (double)(T/(double)N);
	  param2.sigma = 0.5;
	  param2.r = 0.12;
  
	  Pricing<UNDERLYING(Binom2), Tree<Complete, Multiplicative>, derivativeMethod> *derivative = new Pricing<UNDERLYING(Binom2), Tree<Complete, Multiplicative>, derivativeMethod>(param2, 10.0);
	  
	  printf("\tdt = %1.4f/%d, %f\n", T, (int)N, derivative->getDerivativeRootValue());
	}
      printf("\n");
    }  

  printf("\nTree (Memory Saving)\n");
  
  for(unsigned int N = 8; N < 24; N+=8)
    {
      printf("N = %3d\n", N);

      for(double t = 0.0; t < 1.25; t += 0.25)
	{
	  T = MAX(0.0833, t);

	  IP_Binom2 param2;
  
	  param2.S0 = 10.0;
	  param2.N = (unsigned int)N;
	  param2.dt = vector<double>(1);
	  param2.dt[0] = (double)(T/(double)N);
	  param2.sigma = 0.5;
	  param2.r = 0.12;
  
	  Pricing<UNDERLYING(Binom2), Tree<MemorySaving, Multiplicative>, derivativeMethod> *derivative = new Pricing<UNDERLYING(Binom2), Tree<MemorySaving, Multiplicative>, derivativeMethod>(param2, 10.0);
	  
	  printf("\tdt = %1.4f/%d, %f\n", T, (int)N, derivative->getDerivativeRootValue());
	}
      printf("\n");
    }  

}
