#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>
int testAll(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K);

template<class UnderlyingMethod, class inputParameters, class DerivativeMethod>
int testTree(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K);

template<class UnderlyingMethod, class inputParameters, class DerivativeMethod>
int testMatrix(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K);

int main(void)
{
  printf("Running tests.\nYou should not see any more messages, if you do there is an error.\n");

  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;

  testAll<UNDERLYING(Binom), EuropeanCall>(param, "Binom", "European Call", "10.145", 100.0);
  testAll<UNDERLYING(Binom), AmericanPut>(param, "Binom", "American Put", "4.654", 100.0);

  IP_Binom2 param2;
  
  param2.S0 = 5.0;
  param2.N = 16;
  param2.dt = vector<double>(1);
  param2.dt[0] = 1.0/16.0;
  param2.sigma = 0.5;
  param2.r = 0.12;
  
  testAll<UNDERLYING(Binom2), EuropeanPut>(param2, "Binom2", "European Put", "4.069", 10.0);
  
  param2.S0 = 9.0;
  testAll<UNDERLYING(Binom2), AmericanPut>(param2, "Binom2", "American Put", "1.925", 10.0);

  param2.S0 = 10.0;
  param2.N = 8;
  param2.dt[0] = 1.0 / 8.0;
  testTree<UNDERLYING(Binom2), AverageStrikeEuropeanCall>(param2, "Binom2", "Average Strike European Call", "1.398", 10.0);
  
  testTree<UNDERLYING(Binom2), AverageStrikeAmericanPut>(param2, "Binom2", "Average Strike American Put", "1.171", 10.0);

  IP_TimeHalvingTrinom param3;
  
  param3.S0 = 9.0;
  param3.N = 16;
  param3.dt = vector<double>(1);
  param3.dt[0] = 1.0 / 16.0;
  param3.sigma = 0.5;
  param3.r = 0.12;
  
  testMatrix<UNDERLYING(TimeHalvingTrinom), AmericanPut>(param3, "Time Halving Trinom", "American Put", "1.910", 10.0);

  return 0;
}

template<class UnderlyingMethod, class inputParameters, class DerivativeMethod>
int testAll(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K)
{
  char buf[256];

  // complete tree
  Pricing<UnderlyingMethod, inputParameters, Tree<Complete, Multiplicative>, DerivativeMethod > *ctree = new Pricing<UnderlyingMethod, inputParameters, Tree<Complete, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", ctree->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Complete Tree - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

  // memory saving tree
  Pricing<UnderlyingMethod, inputParameters, Tree<MemorySaving, Multiplicative>, DerivativeMethod > *mtree = new Pricing<UnderlyingMethod, inputParameters, Tree<MemorySaving, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", mtree->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Memory Saving Tree - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

  // complete matrix
  Pricing<UnderlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, DerivativeMethod > *cmatrix = new Pricing<UnderlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", cmatrix->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Complete Matrix - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

  // memory saving matrix
  Pricing<UnderlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, DerivativeMethod > *mmatrix = new Pricing<UnderlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", mmatrix->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Memory Saving Matrix - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

}

template<class UnderlyingMethod, class inputParameters, class DerivativeMethod>
int testTree(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K)
{
  char buf[256];

  // complete tree
  Pricing<UnderlyingMethod, inputParameters, Tree<Complete, Multiplicative>, DerivativeMethod > *ctree = new Pricing<UnderlyingMethod, inputParameters, Tree<Complete, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", ctree->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Complete Tree - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

  // memory saving tree
  Pricing<UnderlyingMethod, inputParameters, Tree<MemorySaving, Multiplicative>, DerivativeMethod > *mtree = new Pricing<UnderlyingMethod, inputParameters, Tree<MemorySaving, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", mtree->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Memory Saving Tree - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);
}

template<class UnderlyingMethod, class inputParameters, class DerivativeMethod>
int testMatrix(inputParameters param, char *underlyingMethodName, char *derivativeMethodName, char *correctValue, double K)
{
  char buf[256];

  // complete matrix
  Pricing<UnderlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, DerivativeMethod > *cmatrix = new Pricing<UnderlyingMethod, inputParameters, Matrix<Complete, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", cmatrix->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Complete Matrix - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);

  // memory saving matrix
  Pricing<UnderlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, DerivativeMethod > *mmatrix = new Pricing<UnderlyingMethod, inputParameters, Matrix<MemorySaving, Multiplicative>, DerivativeMethod >(param, K);
  
  sprintf(buf, "%.3f", mmatrix->getDerivativeRootValue());
  
  if(strcmp(buf, correctValue) != 0)
    printf("Error: %s, %s, Memory Saving Matrix - calculated %s expected %s\n", underlyingMethodName, derivativeMethodName, buf, correctValue);
}
