/*-----------------------------------------------------------------------------

Copyright (C) 2012.

A. Ronald Gallant
Post Office Box 659 
Chapel Hill NC 27514 
USA   

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

-----------------------------------------------------------------------------*/

#include "libscl.h"
#include "flagmap.h"

using namespace scl;
using namespace std;
using namespace flagmap;

namespace flagmap {

  void define_flag(std::map<std::string, flagmap::fval>& flag)
  {
    flag["h"] = fval("-h           help message"); 
    //flag["f"] = fval("-f           filter, reads from standard input");
    flag["filenames_expected"] = fval("");
    flag["filenames_expected"].set = false;

    flag["t"] = fval("-t    value  place or odds, string argument",'s');
    flag["n"] = fval("-n    value  point for first bet, int argument",'i');
    flag["m"] = fval("-m    value  point for second bet, int argument",'i');
    flag["r"] = fval("-r    value  rolls between bets, int argument",'i');
    flag["a"] = fval("-a           additional output");
  }

  std::istream& process_filename(std::istream& fin,
    std::string progname,
    std::map<std::string,flagmap::fval> flag,
    std::vector<std::string> file)
  {
    //return process_filename_example(fin, progname, flag, file);
    return fin;
  }

}

std::string progname;

struct bet_t {
  char type;          // type p is a place bet, type o is free odds
  INTEGER point;
  REAL ways;
  REAL odds;
  REAL amnt;
};

bet_t make_bet(const char* type, INTEGER point)
{
   bet_t bet;
   bet.type = type[0];

   bool place;

   if (bet.type == 'p') {
     place = true;
   }
   else if (bet.type == 'o') {
     place = false;
   }
   else {
     place = false;
     error("Error, " + progname + ", type must be p or o");
   }
     
   bet.point = point;

   switch(bet.point) {
     case  4 :
     case 10 : 
       {
         bet.ways = 3;
         bet.odds = place ? 9.0/5.0 : 2.0/1.0;
         bet.amnt = place ? 5.0     : 15.0;
       }
       break;
     case  5 :
     case  9 :
       {
         bet.ways = 4;
         bet.odds = place ? 7.0/5.0 : 3.0/2.0;
         bet.amnt = place ? 5.0     : 20.0;
       }
       break;
     case  6 :
     case  8 :
       {
         bet.ways = 5;
         bet.odds = place ? 7.0/6.0 : 6.0/5.0;
         bet.amnt = place ? 5.0     : 30.0;
       }
       break;
     default :
       string msg = "Error, ";
       msg += progname; 
       msg += ", point must be 4, 5, 6, 8, 9, or 10, saw";
       msg += fmt('d',3,bet.point)();
       error(msg);
       break;
     } 

  return bet;
}

REAL f(REAL p1, REAL p2, INTEGER r) {return p1*(1.0 - pow(p2,r))/(1.0 - p2);}

int main(int argc, char** argp, char** envp)
{
  std::map<std::string, flagmap::fval> flag;
  std::vector<std::string> file;
  //std::string progname;
  flagmap::define_flag(flag);
  if (flagmap::process_flag(argc, argp, progname, flag, file) != 0) return 1; 
  //return flagmap::process_file(progname, flag, file);

  bet_t bet_m;
  bet_t bet_n;
  INTEGER r = 0;

  bool flags_set 
    = flag["t"].set && flag["m"].set && flag["n"].set && flag["r"].set;

  if (flags_set) {
    bet_m = make_bet(flag["t"].s, flag["m"].i);
    bet_n = make_bet(flag["t"].s, flag["n"].i);
    r = flag["r"].i;
    if (r < 1) error("Error, r < 1");
  }
  else {
    cerr << '\n';
    cerr << "Error, " << progname << ", missing flags " << '\n';
    cerr << '\n';
    usage_message(cerr, progname, flag);
    cerr << '\n';
    return 1;
  }

  REAL a = bet_m.ways;
  REAL b = bet_n.ways;

  REAL p1  = a/(a + b + 6);  // m before n or 7 in continuous rolling
  REAL p2  = b/(a + b + 6);  // n before m or 7 in continuous rolling
  REAL p3  = 6/(a + b + 6);  // 7 before m or n in continuous rolling

  REAL q1  = a/(a + 6);      // m before 7 in continuous rolling
  REAL q2  = 6/(a + 6);      // 7 before m in continuous rolling

  REAL r1  = b/(b + 6);      // n before 7 in continuous rolling
  REAL r2  = 6/(b + 6);      // 7 before n in continuous rolling

  REAL s1 = a/36.0;          // m single roll
  REAL s2 = b/36.0;          // n single roll
  REAL s3 = 6/36.0;          // 7 single roll
  REAL s4 = (30 - a)/36.0;   // not 7 or m single roll
  REAL s5 = (30 - b)/36.0;   // not 7 or n single roll
  REAL s6 = 30/36.0;         // not 7 single roll

  INTEGER m = bet_m.point;
  INTEGER n = bet_n.point;

  if (m == n) {
    cerr << '\n';
    cerr << "Error, " << progname 
         << ", the points must differ, saw " 
         << m << " then " << n << '\n';
    cerr << '\n';
    usage_message(cerr, progname, flag);
    cerr << '\n';
    return 1;
  }

  if (flag["a"].set) {
    cout << '\n';
    cout << " Probability   Event" << '\n';
    cout << '\n';
    cout << fmt('f',12,6,p1) << "   " << m 
         << " before " << n << " or 7 in continuous rolling" <<'\n';
    cout << fmt('f',12,6,p2) << "   " << n 
         << " before " << m << " or 7 in continuous rolling" <<'\n';
    cout << fmt('f',12,6,p3) << "   7 before " << m 
         << " or " << n << " in continuous rolling" <<'\n';
    cout << '\n' << '\n';
    cout << fmt('f',12,6,q1) << "   " << m 
         << " before 7 in continuous rolling" << '\n';
    cout << fmt('f',12,6,q2) << "   7 before " << m 
         << " in continuous rolling" << '\n';
    cout << '\n';
    cout << fmt('f',12,6,r1) << "   " << n 
         << " before 7 in continuous rolling" << '\n';
    cout << fmt('f',12,6,r2) << "   7 before " << n 
         << " in continuous rolling" << '\n';
    cout << '\n' << '\n';
    cout << fmt('f',12,6,s1) << "   " << m 
         << " single roll" << '\n';
    cout << fmt('f',12,6,s2) << "   " << n 
         << " single roll" << '\n';
    cout << fmt('f',12,6,s3) << "   7 single roll" << '\n';
    cout << fmt('f',12,6,s4) << "   not 7 or " 
         << m << " single roll" << '\n';
    cout << fmt('f',12,6,s5) << "   not 7 or " << n 
         << " single roll" << '\n';
    cout << fmt('f',12,6,s6) 
         <<  "   not 7 single roll" << '\n';
    cout << '\n';
   }

  REAL pay_m = bet_m.amnt + bet_m.amnt*bet_m.odds;
  REAL pay_n = bet_n.amnt + bet_n.amnt*bet_n.odds;

  // Bet m and n independently

  REAL r_WW = q1*r1; 
  REAL r_WL = q1*r2;
  REAL r_LW = q2*r1;
  REAL r_LL = q2*r2;

  REAL r_sum  = r_WW + r_WL + r_LW + r_LL;

  REAL E0 = (pay_m + pay_n)*r_WW + pay_m*r_WL + pay_n*r_LW;
  REAL V0 = pow(pay_m + pay_n,2)*r_WW 
            + pow(pay_m,2)*r_WL + pow(pay_n,2)*r_LW - pow(E0,2);

  REAL E0_m = pay_m*q1;
  REAL E0_n = pay_n*r1;
  REAL V0_m = pow(pay_m,2)*q1 - pow(E0_m,2);
  REAL V0_n = pow(pay_n,2)*r1 - pow(E0_n,2);

  if (fabs(V0-V0_m-V0_n) > V0*EPS) warn("Warning, possible rounding error");

  // Bet m and n simultaneously

  REAL p_W1W2 = p1*r1; 
  REAL p_W2W1 = p2*q1;
  REAL p_WL   = p1*r2;
  REAL p_LW   = p2*q2;
  REAL p_LL   = p3;

  REAL p_sum  = p_W1W2 + p_W2W1 + p_WL + p_LW + p_LL;

  REAL E1 = (pay_m + pay_n)*(p_W1W2 + p_W2W1) + pay_m*p_WL + pay_n*p_LW;
  
  REAL V1 = pow(pay_m + pay_n,2)*(p_W1W2 + p_W2W1) 
            + pow(pay_m,2)*p_WL + pow(pay_n,2)*p_LW - pow(E1,2);

  REAL C1 = (V1 - V0)/2.0;
  REAL R1 = C1/sqrt(V0_m*V0_m); 

  if (fabs(E1 - E0) > EPS) warn("Warning, possible rounding error");

  // First bet m then bet n after r rolls

  REAL q_W1W2 = pow(s4,r)*p_W1W2 + f(s1,s4,r)*r1;
  REAL q_W2W1 = pow(s4,r)*p_W2W1;
  REAL q_WL   = pow(s4,r)*p_WL   + f(s1,s4,r)*r2;
  REAL q_LW   = pow(s4,r)*p_LW   + f(s3,s4,r)*r1;
  REAL q_LL   = pow(s4,r)*p_LL   + f(s3,s4,r)*r2;

  REAL q_sum  = q_W1W2 + q_W2W1 + q_WL + q_LW + q_LL;

  REAL E2 = (pay_m + pay_n)*(q_W1W2 + q_W2W1) + pay_m*q_WL + pay_n*q_LW;

  REAL V2 = pow(pay_m + pay_n,2)*(q_W1W2 + q_W2W1) 
            + pow(pay_m,2)*q_WL + pow(pay_n,2)*q_LW - pow(E2,2);

  REAL C2 = (V2 - V0)/2.0;
  REAL R2 = C2/sqrt(V0_m*V0_n);

  if (fabs(E2 - E0) > EPS) warn("Warning, possible rounding error"); 

  cout << '\n';
  cout << (bet_n.type == 'p' ? "Place bets: " : "Free odds bets: ")
       << "Independently bet $" << bet_m.amnt << " on the " << bet_m.point 
       << " and $" << bet_n.amnt << " on the " << bet_n.point << '\n';
  cout << '\n';
  cout << " Probability   Event" << '\n';
  cout << '\n';
  cout << fmt('f',12,6,r_WW) 
       << "   both win" << '\n';
  cout << fmt('f',12,6,r_WL)
       << "   " << m << " wins " << n << " loses "<<'\n';
  cout << fmt('f',12,6,r_LW)
       << "   " << n << " wins " << m << " loses "<<'\n';
  cout << fmt('f',12,6,r_LL)
       << "   both lose "<<'\n';
  cout << fmt('f',12,6,r_sum) << '\n';
  cout << '\n';
  cout << "Expected payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << E0 << '\n';
  cout << "Sdev of payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << sqrt(V0) << '\n';

  cout << '\n';
  cout << (bet_n.type == 'p' ? "Place bets: " : "Free odds bets: ")
       << "Simultanously bet $" << bet_m.amnt << " on the " << bet_m.point 
       << " and $" << bet_n.amnt << " on the " << bet_n.point << '\n';
  cout << '\n';
  cout << " Probability   Event" << '\n';
  cout << '\n';
  cout << fmt('f',12,6,p_W1W2) 
       << "   first " << m << " wins then " << n << " wins "<<'\n';
  cout << fmt('f',12,6,p_W2W1) 
       << "   first " << n << " wins then " << m << " wins "<<'\n';
  cout << fmt('f',12,6,p_WL)
       << "   first " << m << " wins then " << n << " loses "<<'\n';
  cout << fmt('f',12,6,p_LW)
       << "   first " << n << " wins then " << m << " loses "<<'\n';
  cout << fmt('f',12,6,p_LL)
       << "   both lose simultaneously "<<'\n';
  cout << fmt('f',12,6,p_sum) << '\n';
  cout << '\n';
  cout << "Expected payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << E1 << '\n';
  cout << "Sdev of payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << sqrt(V1) << '\n';
  cout << "Corr of payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is " << sqrt(R1) << '\n';

  cout << '\n';
  cout << (bet_n.type == 'p' ? "Place bets: " : "Free odds bets: ")
       << "First bet $" << bet_m.amnt << " on the " << bet_m.point
       << " then bet $" << bet_n.amnt << " on the " << bet_n.point
       << " after " << r << " rolls" << '\n';
  cout << '\n';
  cout << " Probability   Event" << '\n';
  cout << '\n';
  cout << fmt('f',12,6,q_W1W2) 
       << "   first " << m << " wins then " << n << " wins "<<'\n';
  cout << fmt('f',12,6,q_W2W1) 
       << "   first " << n << " wins then " << m << " wins "<<'\n';
  cout << fmt('f',12,6,q_WL)
       << "   first " << m << " wins then " << n << " loses "<<'\n';
  cout << fmt('f',12,6,q_LW)
       << "   first " << n << " wins then " << m << " loses "<<'\n';
  cout << fmt('f',12,6,q_LL)
       << "   simultaneous loss or " << m << " loses then " 
       << n << " loses "<<'\n';
  cout << fmt('f',12,6,q_sum) << '\n';
  cout << '\n';
  cout << "Expected payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << E2 << '\n';
  cout << "Sdev of payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is $" << sqrt(V2) << '\n';
  cout << "Corr of payoff on the $" <<  bet_m.amnt + bet_n.amnt 
       << " bet is " << sqrt(R2) << '\n';
  cout << '\n';

  // This eliminates a compiler warning message:
  (s1 + s2 + s3 + s4 + s5 + s6);

  return 0;
}

