/*@@@@@@@@@@@@@@@@@@@@@@@@ test.cpp @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 Random Option Pricing
 Summer 2002
 Authors: A. Korobchevsky
          S. Ferrando
 Ryerson University ALL RIGHTS RESERVED
 
 Purpose: Tests the algorithms. Underlying, derivative and self 
          parameters are hardcoded (FOR NOW). Generated option value
	  with a standard error. Can be compared with Brendan Thesis Code
	  which generates true values (no random variables used).
 Command Line Arg. : random generator to be used (mt, taus, gsfr) 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

#include <fstream>
#include <typeinfo>
#include <vector>
#include <ctime>
#include <iostream>
using namespace std;

#include "../BasicMC.h"
#include "../LocalMC.h"
#include "../ExactValues.h"
#include "../../Und/Underlying.h"
#include "../../Und/Binom.h"
#include "../../Und/Continium.h"
#include "../../Rng/GSL_RandomGen.h"


int main(int argc, char **argv)
{ // Let default RNG be mnt
  char defArg[] = "mt";
  char arg[10];
  if (argc == 1)
    strcpy(arg, defArg);
  else if (argc == 2)
    strcpy(arg, argv[1]);
  else
  { cerr << "Extra command line args\n";
    exit(1);
  }
  Binom binom(arg);
  Underlying* ptr_und = &binom;

  vector<double> cv;
  vector<double> se;
 
  ptr_und->r = 0.0;
  ptr_und->T = 1;
  ptr_und->N = 2; 
  ptr_und->U = 1.1;  //exp(0.3 * sqrt(ptr_und->T / ptr_und->N));
  ptr_und->D = 0.9;  //1 / ptr_und->U;
  ptr_und->P = (ptr_und->U - exp(ptr_und->r * ptr_und->T / ptr_und->N)) / (ptr_und->U - ptr_und->D);

  //cout << "Down prob. is " << binom.P << endl;

  // BEGIN Critical
  BasicMC mc;
  Algorithm *ptr_a = &mc;
  mc.S = 100;
  ptr_a->M = 5000;

  cout << "Generator: " << binom.gen->getName() << endl;
  cout << "Algorithm used: Basic Monte Carlo" << endl;

  // European Call
  EuropeanCall ec(100);
  Derivative* ptr_der = &ec;
  long start = clock();
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Call Value " << cv.back() << ", standard error " << se.back() <<  ", time = " << (clock() - start)/1000000.0 << "sec\n";
  //copy(cv.begin(), cv.end(), ostream_iterator<double>(cout, " "));
  cv.clear();


  // European Strike
  AverageStrikeEuropeanCall es(binom.N);
  ptr_der = &es;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Strike Value " << cv.back() << ", standard error " << se.back() << endl;
  //copy(cv.begin(), cv.end(), ostream_iterator<double>(cout, " "));
  cv.clear();

  // Look back
  LookBack lb;
  ptr_der = &lb;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Look Back Value " << cv.back() << ", standard error " << se.back() << endl;
  //copy(cv.begin(), cv.end(), ostream_iterator<double>(cout, " "));
  cv.clear();

  // Down Out
  DownOut dout(100, 99);
  ptr_der = &dout;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Down Out Value " << cv.back() << ", standard error " << se.back() << endl; 
  //copy(cv.begin(), cv.end(), ostream_iterator<double>(cout, " "));
  cv.clear();


  ExactValues ex;
  ptr_a = &ex;
  ex.S = 100;
  cout << "Algorithm used: Exact Values" << endl;

  // European Call
  ptr_der = &ec;
  start = clock();
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Call Value " << cv.front() << ", standard error " << se.back() <<  ", time = " << (clock() - start)/1000000.0 << "sec\n";
  cv.clear();

  // European Strike
  ptr_der = &es;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Strike Value " << cv.front() << ", standard error " << se.back() << endl;
  cv.clear();

  // Look back
  ptr_der = &lb;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Look Back Value " << cv.front() << ", standard error " << se.back() << endl;
  cv.clear();

  // Down Out
  ptr_der = &dout;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Down Out Value " << cv.front() << ", standard error " << se.back() << endl; 
  cv.clear();



  LocalMC lmc;
  ptr_a = &lmc;
  lmc.S = 100;
  lmc.shift = 2;
  ptr_a->M = 10000;

  cout << "Algorithm used: Local Monte Carlo" << endl;

  // European Call
  ptr_der = &ec;
  start = clock();
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Call Value " << cv.back() << ", standard error " << se.back() <<  ", time = " << (clock() - start)/1000000.0 << "sec\n";
  cv.clear();

  // European Strike
  ptr_der = &es;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Strike Value " << cv.back() << ", standard error " << se.back() << endl;
  //copy(se.begin(), se.end(), ostream_iterator<double>(cout, " "));
  cv.clear();

  // Look back
  ptr_der = &lb;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Look Back Value " << cv.back() << ", standard error " << se.back() << endl;
  cv.clear();

  // Down Out
  ptr_der = &dout;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Down Out Value " << cv.back() << ", standard error " << se.back() << endl; 
  cv.clear();   



  Continium cont(arg);
  ptr_und = &cont;
  cont.sigma = 0.3;

  ptr_und->r = 0.0;
  ptr_und->T = 1.0;
  ptr_und->N = 2; 
  ptr_und->P = (ptr_und->U - exp(ptr_und->r * ptr_und->T / ptr_und->N)) / (ptr_und->U - ptr_und->D);
  //cout << "Down prob. is " << binom.P << endl;

  ptr_a = &mc;
  mc.S = 100;
  ptr_a->M = 1000;

  cout << "Underlying: continium " << endl;
  cout << "Algorithm used: Basic Monte Carlo" << endl;

  // European Call
  ptr_der = &ec;
  start = clock();
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Call Value " << cv.back() << ", standard error " << se.back() <<  ", time = " << (clock() - start)/1000000.0 << "sec\n";
  cv.clear();
  
  // European Strike
  ptr_der = &es;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Strike Value " << cv.back() << ", standard error " << se.back() << endl;
  cv.clear();

  // Look back
  ptr_der = &lb;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Look Back Value " << cv.back() << ", standard error " << se.back() << endl;
  cv.clear();

  // Down Out
  ptr_der = &dout;
  ptr_a->calcResults(ptr_und, ptr_der, cv, se);
  cout << "  Down Out Value " << cv.back() << ", standard error " << se.back() << endl; 
  cv.clear();  

  // END Critical  
}
