// File: /workspace/github_cpp_makefile_star2_O2/ArashPartow[P]exprtk-extras/exprtk_american_option_binomial_model.cpp
// Function: void american_option_binomial_option_pricing_model<double>()
void american_option_binomial_option_pricing_model()
{
   typedef exprtk::symbol_table<T> symbol_table_t;
   typedef exprtk::expression<T>   expression_t;
   typedef exprtk::parser<T>       parser_t;

   const std::string american_option_binomial_model_program =
      " var dt       := t / n;                                                               "
      " var z        := exp(r * dt);                                                         "
      " var u        := exp(v * sqrt(dt));                                                   "
      " var p_up     := (z * u - 1) / (u^2 - 1);                                             "
      " var p_down   := 1 - p_up;                                                            "
      " var discount := 1 / z;                                                               "
      "                                                                                      "
      " var option_price[n + 1] := [0];                                                      "
      "                                                                                      "
      " for (var i := 0; i <= n; i += 1)                                                     "
      " {                                                                                    "
      "    var base_price := s * u^(2 * i - n);                                              "
      "                                                                                      "
      "    option_price[i] :=                                                                "
      "       switch                                                                         "
      "       {                                                                              "
      "          case callput_flag == 'call' : max(base_price - k, 0);                       "
      "          case callput_flag == 'put'  : max(k - base_price, 0);                       "
      "       };                                                                             "
      " };                                                                                   "
      "                                                                                      "
      " for (var j := n - 1; j >= 0; j -= 1)                                                 "
      " {                                                                                    "
      "    for (var i := 0; i <= j; i += 1)                                                  "
      "    {                                                                                 "
      "       var base_price_at_node := s * u^(2 * i - j);                                   "
      "                                                                                      "
      "       var intrinsic_value :=                                                         "
      "          switch                                                                      "
      "          {                                                                           "
      "             case callput_flag == 'call' : base_price_at_node - k;                    "
      "             case callput_flag == 'put'  : k - base_price_at_node;                    "
      "          };                                                                          "
      "                                                                                      "
      "       var expected_value := discount *                                               "
      "                             (p_up * option_price[i + 1] + p_down * option_price[i]); "
      "                                                                                      "
      "       option_price[i] := max(expected_value, intrinsic_value);                       "
      "    }                                                                                 "
      " };                                                                                   "
      "                                                                                      "
      " option_price[0];                                                                     ";

   T s = T( 100.00); // Spot / Stock / Underlying / Base price
   T k = T( 110.00); // Strike price
   T v = T(   0.30); // Volatility
   T t = T(   2.22); // Years to maturity
   T r = T(   0.05); // Risk free rate
   T n = T(1000.00); // Number of time steps

   std::string callput_flag;

   symbol_table_t symbol_table(symbol_table_t::e_immutable);
   symbol_table.add_variable("s", s);
   symbol_table.add_variable("k", k);
   symbol_table.add_variable("t", t);
   symbol_table.add_variable("r", r);
   symbol_table.add_variable("v", v);
   symbol_table.add_constant("n", n);
   symbol_table.add_stringvar("callput_flag",callput_flag);

   expression_t expression;
   expression.register_symbol_table(symbol_table);

   parser_t parser;
   parser.compile(american_option_binomial_model_program,expression);

   callput_flag = "call";

   const T binomial_call_option_price = expression.value();

   callput_flag = "put";

   const T binomial_put_option_price = expression.value();

   printf("American BinomialPrice(call, %5.3f, %5.3f, %5.3f, %5.3f, %5.3f) = %22.18f\n",
          s, k, t, r, v,
          binomial_call_option_price);

   printf("American BinomialPrice(put , %5.3f, %5.3f, %5.3f, %5.3f, %5.3f) = %22.18f\n",
          s, k, t, r, v,
          binomial_put_option_price);

   // American option put-call 'parity': s - k < call - put < s - k * e^(-rt)
   const T    callput_diff    = (binomial_call_option_price - binomial_put_option_price);
   const T    basestrike_diff = s - k;
   const T    basepv_diff     = s - k * std::exp(-r * t);
   const bool put_call_parity = (basestrike_diff < callput_diff) &&
                                (callput_diff    < basepv_diff ) ;

   const T call_price_r0 = binomial_put_option_price + basestrike_diff;
   const T call_price_r1 = binomial_put_option_price + basepv_diff;

   const T put_price_r0  = binomial_call_option_price - basepv_diff;
   const T put_price_r1  = binomial_call_option_price - basestrike_diff;

   printf("Put-Call parity: %s\n", put_call_parity ? "True" : "False");

   printf("Call price range: %7.4f < %7.4f < %7.4f\n",
          call_price_r0,
          binomial_call_option_price,
          call_price_r1);

   printf("Put price range:  %7.4f < %7.4f < %7.4f\n",
          put_price_r0,
          binomial_put_option_price,
          put_price_r1);
}

// Assembly:
pushq %rbx
subq $0xb60, %rsp            # imm = 0xB60
leaq 0x12adff(%rip), %rsi    # 0x23b101
leaq 0xc8(%rsp), %rdi
leaq 0x238(%rsp), %rdx
callq 0x1102b8
movabsq $0x4059000000000000, %rax # imm = 0x4059000000000000
movq %rax, 0x38(%rsp)
movabsq $0x405b800000000000, %rax # imm = 0x405B800000000000
movq %rax, 0x30(%rsp)
movabsq $0x3fd3333333333333, %rax # imm = 0x3FD3333333333333
movq %rax, 0x48(%rsp)
movabsq $0x4001c28f5c28f5c3, %rax # imm = 0x4001C28F5C28F5C3
movq %rax, 0x28(%rsp)
movabsq $0x3fa999999999999a, %rax # imm = 0x3FA999999999999A
movq %rax, 0x20(%rsp)
movabsq $0x408f400000000000, %rax # imm = 0x408F400000000000
movq %rax, 0x80(%rsp)
leaq 0xb8(%rsp), %rax
movq %rax, -0x10(%rax)
andq $0x0, -0x8(%rax)
movb $0x0, (%rax)
leaq 0x8(%rsp), %rdi
pushq $0x2
popq %rsi
callq 0x110936
leaq 0x12ac15(%rip), %rsi    # 0x23afb1
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x38(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11095a
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12ab66(%rip), %rsi    # 0x23af44
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x30(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11095a
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12c2ba(%rip), %rsi    # 0x23c6da
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x28(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11095a
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12ab3a(%rip), %rsi    # 0x23af9c
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x20(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11095a
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12ba7a(%rip), %rsi    # 0x23bf1e
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x48(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11095a
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12aae6(%rip), %rsi    # 0x23afcc
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0x80(%rsp), %rdx
callq 0x1109c6
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x12b9f7(%rip), %rsi    # 0x23bf20
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rdx
callq 0x1102b8
leaq 0x8(%rsp), %rdi
leaq 0x238(%rsp), %rsi
leaq 0xa8(%rsp), %rdx
xorl %ecx, %ecx
callq 0x110a42
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
leaq 0x88(%rsp), %rdi
callq 0x110ab4
leaq 0x88(%rsp), %rdi
leaq 0x8(%rsp), %rsi
callq 0x110b14
leaq 0xe8(%rsp), %rdi
pushq $0x7f
popq %rsi
callq 0x110b70
leaq 0x238(%rsp), %rdi
leaq 0xe8(%rsp), %rsi
callq 0x110c80
leaq 0xe8(%rsp), %rdi
callq 0x111298
leaq 0x238(%rsp), %rdi
leaq 0xc8(%rsp), %rsi
leaq 0x88(%rsp), %rdx
callq 0x1112e0
leaq 0x12f361(%rip), %rsi    # 0x23f93d
leaq 0xa8(%rsp), %rdi
callq 0x10e570
movq 0x88(%rsp), %rax
movq 0x8(%rax), %rdi
movq (%rdi), %rax
callq *0x18(%rax)
movsd %xmm0, 0x18(%rsp)
leaq 0x12b925(%rip), %rsi    # 0x23bf2d
leaq 0xa8(%rsp), %rdi
callq 0x10e570
movq 0x88(%rsp), %rax
movq 0x8(%rax), %rdi
movq (%rdi), %rax
callq *0x18(%rax)
movsd %xmm0, 0x10(%rsp)
movsd 0x38(%rsp), %xmm0
movsd 0x30(%rsp), %xmm1
movsd 0x28(%rsp), %xmm2
movsd 0x20(%rsp), %xmm3
movsd 0x48(%rsp), %xmm4
leaq 0x12b8df(%rip), %rdi    # 0x23bf31
movsd 0x18(%rsp), %xmm5
movb $0x6, %al
callq 0x10e060
movsd 0x38(%rsp), %xmm0
movsd 0x30(%rsp), %xmm1
movsd 0x28(%rsp), %xmm2
movsd 0x20(%rsp), %xmm3
movsd 0x48(%rsp), %xmm4
leaq 0x12b8f8(%rip), %rdi    # 0x23bf7c
movsd 0x10(%rsp), %xmm5
movb $0x6, %al
callq 0x10e060
movsd 0x18(%rsp), %xmm0
subsd 0x10(%rsp), %xmm0
movapd %xmm0, 0x70(%rsp)
movsd 0x38(%rsp), %xmm0
movapd %xmm0, 0x50(%rsp)
movsd 0x30(%rsp), %xmm1
movsd %xmm1, 0x40(%rsp)
subsd %xmm1, %xmm0
movapd %xmm0, 0x60(%rsp)
movsd 0x20(%rsp), %xmm0
xorpd 0x12593d(%rip), %xmm0   # 0x236010
mulsd 0x28(%rsp), %xmm0
callq 0x10e0d0
mulsd 0x40(%rsp), %xmm0
movapd 0x50(%rsp), %xmm2
subsd %xmm0, %xmm2
movapd 0x60(%rsp), %xmm1
movapd %xmm1, %xmm0
movapd 0x70(%rsp), %xmm3
cmpltpd %xmm3, %xmm0
cmpltpd %xmm2, %xmm3
andpd %xmm0, %xmm3
movd %xmm3, %eax
movsd 0x10(%rsp), %xmm0
movapd %xmm0, %xmm3
addsd %xmm1, %xmm3
movsd %xmm3, 0x50(%rsp)
addsd %xmm2, %xmm0
movsd %xmm0, 0x40(%rsp)
movsd 0x18(%rsp), %xmm0
movapd %xmm0, %xmm3
subsd %xmm2, %xmm3
movsd %xmm3, 0x70(%rsp)
movapd %xmm0, %xmm2
subsd %xmm1, %xmm2
movsd %xmm2, 0x60(%rsp)
leaq 0x12b885(%rip), %rcx    # 0x23bfdc
leaq 0x12b883(%rip), %rsi    # 0x23bfe1
testb $0x1, %al
cmovneq %rcx, %rsi
leaq 0x12b85c(%rip), %rdi    # 0x23bfc7
xorl %eax, %eax
callq 0x10e060
leaq 0x12b86e(%rip), %rdi    # 0x23bfe7
movsd 0x50(%rsp), %xmm0
movsd 0x18(%rsp), %xmm1
movsd 0x40(%rsp), %xmm2
movb $0x3, %al
callq 0x10e060
leaq 0x12b877(%rip), %rdi    # 0x23c010
movsd 0x70(%rsp), %xmm0
movsd 0x10(%rsp), %xmm1
movsd 0x60(%rsp), %xmm2
movb $0x3, %al
callq 0x10e060
leaq 0x238(%rsp), %rdi
callq 0x111762
leaq 0x88(%rsp), %rdi
callq 0x11189c
leaq 0x8(%rsp), %rdi
callq 0x1118b2
leaq 0xa8(%rsp), %rdi
callq 0x10e5b8
leaq 0xc8(%rsp), %rdi
callq 0x10e5b8
addq $0xb60, %rsp            # imm = 0xB60
popq %rbx
retq
jmp 0x11084c
movq %rax, %rbx
leaq 0xe8(%rsp), %rdi
callq 0x111298
jmp 0x11085c
jmp 0x11080f
movq %rax, %rbx
jmp 0x11085c
jmp 0x110840
jmp 0x11082e
jmp 0x110840
jmp 0x11082e
jmp 0x110840
jmp 0x11082e
jmp 0x110840
jmp 0x11082e
jmp 0x110840
jmp 0x11082e
jmp 0x110840
jmp 0x11082e
jmp 0x110840
movq %rax, %rbx
leaq 0x238(%rsp), %rdi
callq 0x10e5b8
jmp 0x110869
movq %rax, %rbx
jmp 0x110869
movq %rax, %rbx
jmp 0x110873
jmp 0x11084c
movq %rax, %rbx
leaq 0x238(%rsp), %rdi
callq 0x111762
leaq 0x88(%rsp), %rdi
callq 0x11189c
leaq 0x8(%rsp), %rdi
callq 0x1118b2
leaq 0xa8(%rsp), %rdi
callq 0x10e5b8
leaq 0xc8(%rsp), %rdi
callq 0x10e5b8
movq %rbx, %rdi
callq 0x10e310