/*@@@@@@@@@@@@@@@@@@@@@@@@ gop.cpp @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 Random Option Pricing
 Summer 2002
 Authors: A. Korobchevsky
          S. Ferrando
 Ryerson University ALL RIGHTS RESERVED
 
 Purpose: Generic Option Calculator

 Input file format: 
		sigma: 0.5
		r:     0.2
		T:     0.3
		N:     40
		S:     20
		M0:    20000
		gap:   300
		steps: 100
 Other parameters are prompted by the user
 Command Line Arg. : random generator to be used (mt, taus, gsfr) 
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/


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

#include "../Src/Alg/BasicMC.h"
#include "../Src/Alg/LocalMC.h"
#include "../Src/Alg/ExactValues.h"
#include "../Src/Alg/ContExactValues.h"
#include "../Src/Und/Underlying.h"
#include "../Src/Und/Binom.h"
#include "../Src/Und/Continium.h"
#include "../Src/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);
  }

  const char results[20] = "plots.ps ";
  const char errors[20] = "errors.ps ";
  char resCommand[100] = "./extractor ";
  strcat(resCommand, results);
  char errCommand[100] = "./extractor ";
  strcat(errCommand, errors);
  bool firstFile = true;
 
  // Compute as many options as required, store each result in file
  while (1)
  { int und_number, der_number, alg_number;

    cout << "Select Underlying\n";
    cout << "1: Binomial Tree\n";
    cout << "2: Continious\n";
    cin >> und_number;

    ifstream in("input", ios::in);
    string garbage;
    int M, M0, gap, steps;
    double sigma, S;

    Underlying* ptr_und;
    in >> garbage;  in >> sigma;  

    switch (und_number)
    {	case 1:   ptr_und = new Binom(arg);
                break;
      case 2:   ptr_und = new Continium(arg, sigma);
					 ptr_und -> sigma = sigma;
                break;
      default: cerr << "Illegal underlying \n";  exit(0);
    }

    // Input text file required with fixed format
    in >> garbage;  in >> ptr_und->r;    
    in >> garbage;  in >> ptr_und->T;   
    in >> garbage;  in >> ptr_und->N;   
    in >> garbage;  in >> S;     // Starting option value, needed for algorithm  
    in >> garbage;  in >> M0;     // Number of simulations, redundant for Exact Values Algorithm  
    in >> garbage;  in >> gap; 
    in >> garbage;  in >> steps; 

    char temp[20];
    if (firstFile)
    { sprintf(temp, "%d ", steps);
      strcat(resCommand, temp);
      strcat(errCommand, temp);
    }

    M = M0 + gap * steps;  // Compute total number of simulations

    // Compute probabilities 
    ptr_und->U = exp(sigma * sqrt(ptr_und->T / ptr_und->N));
    ptr_und->D = 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);
    in.close();

    cout << "Select Derivative: \n";
    cout << "1: European Call\n";
    cout << "2: Average Strike\n";
    cout << "3: Look Back\n";
    cout << "4: Fixed Strike\n";
    cin >> der_number;
    Derivative* ptr_der;

    switch (der_number)
    { case 1:   double strike;
                cout << "Enter strike price value\n";
                cin >> strike;
                ptr_der = new EuropeanCall(strike);
                break;
      case 2:   ptr_der = new AverageStrikeEuropeanCall(ptr_und->N);
                break;
      case 3:   ptr_der = new LookBack();
                break;
      case 4:   cout << "Enter strike price value\n";
                cin >> strike;
		ptr_der = new FixedStrikeEuropeanCall(ptr_und->N, strike);  

                break;
      default: cerr << "Illegal derivative \n";  exit(0);
    }

    cout << "Select Algorithm: \n";
    cout << "1: Basic Monte Carlo\n";
    cout << "2: Exact Values on Tree\n";
    cout << "3: Local Monte Carlo\n";
    cout << "4: Local Monte Carlo with Shift \n";
    cout << "5: Exact Values Continium\n";
    cin >> alg_number;

    Algorithm* ptr_a;
    switch (alg_number)
    {	case 1:   ptr_a = new BasicMC(S, M, M0, gap);
                break;
      case 2:   ptr_a = new ExactValues(S);
                break;
      case 3:   ptr_a = new LocalMC(S, M, M0, gap);
                break;
      case 4:   ptr_a = new LocalMC(S, M, M0, gap, ptr_und->N);
                break;
      case 5:   ptr_a = new ContExactValues(S);
                break;
      default: cerr << "Illegal algorithm \n";  exit(0);
    }

    // Compute option value

// BEGIN Critical
    vector<double> cv;
    vector<double> se;
    cout << "Generator: " << ptr_und->gen->getName() << endl;
    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";
// END Critical
    
    // Update the plotter; fields applicable for first file only 
    if (firstFile)      
    { sprintf(temp, "%d %d ", M0, M);
      strcat(resCommand, temp);
      strcat(errCommand, temp);
    }

    // Save results to file
    char filename[20];
    cout << "Enter file name\n";
    cin >> filename;

    ofstream out(filename, ios::out);
    out << "##############Option values###################\n";        /* Start of Header */
    switch (und_number)
    {	case 1:   out << "Underlying: Binom\n"; break;
      case 2:   out << "Underlying: Continium\n"; break;
      default: cerr << "Illegal underlying \n";  exit(0);
    }

    switch (der_number)
    {	case 1:   out << "Derivative: Call\n"; break;
      case 2:   out << "Derivative: Strike\n"; break;
      case 3:   out << "Derivative: Look back\n"; break;
      case 4:   out << "Derivative: Down Out\n"; break;
      case 5:   out << "Derivative: Fixed Strike\n"; break;
      default: cerr << "Illegal derivative \n";  exit(0);
    }

    switch (alg_number)
    {	case 1:   out << "Algorithm: Basic Monte Carlo\n"; break;
      case 2:   out << "Algorithm: Exact values\n"; break;
      case 3:   out << "Algorithm: Local Monte Carlo\n"; break;
      case 4:   out << "Algorithm: Local Monte Carlo with shift\n"; break;
      case 5:   out << "Algorithm: Exact Coninium\n"; break;
      default: cerr << "Illegal algorithm \n";  exit(0);
    }

    out << "U = " << ptr_und->U;    
    out << "\nD = " << ptr_und->D;    
    out << "\nr = " << ptr_und->r;    
    out << "\nT = " << ptr_und->T;   
    out << "\nN = " << ptr_und->N;   
    out << "\nP = " << ptr_und->P;  
    out << "\nS = " << S;
    out << "\nM = " << M;

    out << "\n#############################################" << "$" << endl;
    copy(cv.begin(), cv.end(), ostream_iterator<double>(out, " "));
    out.close();




    // Save errors to file
    char errorFile[20] = "err_";
    strcat(errorFile, filename);



    strcat(resCommand, filename);
    strcat(errCommand, errorFile);
    strcat(resCommand, " ");
    strcat(errCommand, " ");

    firstFile = false;   
     

    ofstream eout(errorFile, ios::out);
    eout << "######### Error file #########################\n";        /* Start of Header */
    switch (und_number)
    {	case 1:   eout << "Underlying: Binom\n"; break;
      case 2:   eout << "Underlying: Continium\n"; break;
      default: cerr << "Illegal underlying \n";  exit(0);
    }

    switch (der_number)
    {	case 1:   eout << "Derivative: Call\n"; break;
      case 2:   eout << "Derivative: Strike\n"; break;
      case 3:   eout << "Derivative: Look back\n"; break;
      case 4:   eout << "Derivative: Down Out\n"; break;
      case 5:   eout << "Derivative: Fixed Strike\n"; break;
      default: cerr << "Illegal derivative \n";  exit(0);
    }

    switch (alg_number)
    {	case 1:   eout << "Algorithm: Basic Monte Carlo\n"; break;
      case 2:   eout << "Algorithm: Exact values\n"; break;
      case 3:   eout << "Algorithm: Local Monte Carlo\n"; break;
      case 4:   eout << "Algorithm: Local Monte Carlo with shift\n"; break;
      case 5:   eout << "Algorithm: Exact Coninium\n"; break;
      default: cerr << "Illegal algorithm \n";  exit(0);
    }

    eout << "U = " << ptr_und->U;    
    eout << "\nD = " << ptr_und->D;    
    eout << "\nr = " << ptr_und->r;    
    eout << "\nT = " << ptr_und->T;   
    eout << "\nN = " << ptr_und->N;   
    eout << "\nP = " << ptr_und->P;  
    eout << "\nS = " << S;
    eout << "\nM = " << M;

    eout << "\n#############################################" << "$" << endl;
    copy(se.begin(), se.end(), ostream_iterator<double>(eout, " "));
    eout.close();


    char yesNo;
    cout << "Compute more options? y/n" << endl;
    cin >> yesNo;
    if (yesNo == 'n') break;
  }

  // Invoke the plotter
  system(resCommand);
  system("gv plots.ps &");
  system(errCommand);
  system("gv errors.ps &");

  cout << resCommand << endl;
  cout << errCommand << endl;

  // Save extractor call into a file called E
  ofstream e("E", ios::out);
  e << resCommand;
  e.close();
  system("chmod u+x E");
}
