// File: 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:
subq $0xcf8, %rsp            # imm = 0xCF8
leaq 0xcd7(%rsp), %rdi
movq %rdi, 0x60(%rsp)
callq 0x111640
movq 0x60(%rsp), %rdx
leaq 0x330d20(%rip), %rsi    # 0x449545
leaq 0xcd8(%rsp), %rdi
callq 0x118740
jmp 0x118834
leaq 0xcd7(%rsp), %rdi
callq 0x111760
movabsq $0x4059000000000000, %rax # imm = 0x4059000000000000
movq %rax, 0xcb8(%rsp)
movabsq $0x405b800000000000, %rax # imm = 0x405B800000000000
movq %rax, 0xcb0(%rsp)
movabsq $0x3fd3333333333333, %rax # imm = 0x3FD3333333333333
movq %rax, 0xca8(%rsp)
movabsq $0x4001c28f5c28f5c3, %rax # imm = 0x4001C28F5C28F5C3
movq %rax, 0xca0(%rsp)
movabsq $0x3fa999999999999a, %rax # imm = 0x3FA999999999999A
movq %rax, 0xc98(%rsp)
movabsq $0x408f400000000000, %rax # imm = 0x408F400000000000
movq %rax, 0xc90(%rsp)
leaq 0xc70(%rsp), %rdi
callq 0x111260
leaq 0xc68(%rsp), %rdi
movl $0x2, %esi
callq 0x1194f0
jmp 0x1188ce
leaq 0xc47(%rsp), %rdi
movq %rdi, 0x58(%rsp)
callq 0x111640
movq 0x58(%rsp), %rdx
leaq 0x330b09(%rip), %rsi    # 0x4493f5
leaq 0xc48(%rsp), %rdi
callq 0x118740
jmp 0x1188fb
leaq 0xc68(%rsp), %rdi
leaq 0xc48(%rsp), %rsi
leaq 0xcb8(%rsp), %rdx
xorl %ecx, %ecx
callq 0x119530
jmp 0x11891c
leaq 0xc48(%rsp), %rdi
callq 0x111778
leaq 0xc47(%rsp), %rdi
callq 0x111760
leaq 0xc1f(%rsp), %rdi
movq %rdi, 0x50(%rsp)
callq 0x111640
movq 0x50(%rsp), %rdx
leaq 0x330a34(%rip), %rsi    # 0x449388
leaq 0xc20(%rsp), %rdi
callq 0x118740
jmp 0x118963
leaq 0xc68(%rsp), %rdi
leaq 0xc20(%rsp), %rsi
leaq 0xcb0(%rsp), %rdx
xorl %ecx, %ecx
callq 0x119530
jmp 0x118984
leaq 0xc20(%rsp), %rdi
callq 0x111778
leaq 0xc1f(%rsp), %rdi
callq 0x111760
leaq 0xbf7(%rsp), %rdi
movq %rdi, 0x48(%rsp)
callq 0x111640
movq 0x48(%rsp), %rdx
leaq 0x332150(%rip), %rsi    # 0x44ab0c
leaq 0xbf8(%rsp), %rdi
callq 0x118740
jmp 0x1189cb
leaq 0xc68(%rsp), %rdi
leaq 0xbf8(%rsp), %rsi
leaq 0xca0(%rsp), %rdx
xorl %ecx, %ecx
callq 0x119530
jmp 0x1189ec
leaq 0xbf8(%rsp), %rdi
callq 0x111778
leaq 0xbf7(%rsp), %rdi
callq 0x111760
leaq 0xbcf(%rsp), %rdi
movq %rdi, 0x40(%rsp)
callq 0x111640
movq 0x40(%rsp), %rdx
leaq 0x3309bc(%rip), %rsi    # 0x4493e0
leaq 0xbd0(%rsp), %rdi
callq 0x118740
jmp 0x118a33
leaq 0xc68(%rsp), %rdi
leaq 0xbd0(%rsp), %rsi
leaq 0xc98(%rsp), %rdx
xorl %ecx, %ecx
callq 0x119530
jmp 0x118a54
leaq 0xbd0(%rsp), %rdi
callq 0x111778
leaq 0xbcf(%rsp), %rdi
callq 0x111760
leaq 0xba7(%rsp), %rdi
movq %rdi, 0x38(%rsp)
callq 0x111640
movq 0x38(%rsp), %rdx
leaq 0x3318d6(%rip), %rsi    # 0x44a362
leaq 0xba8(%rsp), %rdi
callq 0x118740
jmp 0x118a9b
leaq 0xc68(%rsp), %rdi
leaq 0xba8(%rsp), %rsi
leaq 0xca8(%rsp), %rdx
xorl %ecx, %ecx
callq 0x119530
jmp 0x118abc
leaq 0xba8(%rsp), %rdi
callq 0x111778
leaq 0xba7(%rsp), %rdi
callq 0x111760
leaq 0xb7f(%rsp), %rdi
movq %rdi, 0x30(%rsp)
callq 0x111640
movq 0x30(%rsp), %rdx
leaq 0x33091c(%rip), %rsi    # 0x449410
leaq 0xb80(%rsp), %rdi
callq 0x118740
jmp 0x118b03
leaq 0xc68(%rsp), %rdi
leaq 0xb80(%rsp), %rsi
leaq 0xc90(%rsp), %rdx
callq 0x1195e0
jmp 0x118b22
leaq 0xb80(%rsp), %rdi
callq 0x111778
leaq 0xb7f(%rsp), %rdi
callq 0x111760
leaq 0xb57(%rsp), %rdi
movq %rdi, 0x28(%rsp)
callq 0x111640
movq 0x28(%rsp), %rdx
leaq 0x33180a(%rip), %rsi    # 0x44a364
leaq 0xb58(%rsp), %rdi
callq 0x118740
jmp 0x118b69
leaq 0xc68(%rsp), %rdi
leaq 0xb58(%rsp), %rsi
leaq 0xc70(%rsp), %rdx
xorl %ecx, %ecx
callq 0x1196c0
jmp 0x118b8a
leaq 0xb58(%rsp), %rdi
callq 0x111778
leaq 0xb57(%rsp), %rdi
callq 0x111760
leaq 0xb30(%rsp), %rdi
callq 0x119780
jmp 0x118bb3
leaq 0xb30(%rsp), %rdi
leaq 0xc68(%rsp), %rsi
callq 0x119830
jmp 0x118bca
leaq 0xb8(%rsp), %rdi
movl $0x7f, %esi
callq 0x1198e0
jmp 0x118bde
leaq 0x208(%rsp), %rdi
leaq 0xb8(%rsp), %rsi
callq 0x119a10
jmp 0x118bf5
leaq 0xb8(%rsp), %rdi
callq 0x11a240
leaq 0x208(%rsp), %rdi
leaq 0xcd8(%rsp), %rsi
leaq 0xb30(%rsp), %rdx
callq 0x11a2b0
jmp 0x118c21
leaq 0x33516e(%rip), %rsi    # 0x44dd96
leaq 0xc70(%rsp), %rdi
callq 0x1112e0
jmp 0x118c37
leaq 0xb30(%rsp), %rdi
callq 0x11ab40
movsd %xmm0, 0x20(%rsp)
jmp 0x118c4c
movsd 0x20(%rsp), %xmm0
movsd %xmm0, 0xb0(%rsp)
leaq 0x33170f(%rip), %rsi    # 0x44a371
leaq 0xc70(%rsp), %rdi
callq 0x1112e0
jmp 0x118c71
leaq 0xb30(%rsp), %rdi
callq 0x11ab40
movsd %xmm0, 0x18(%rsp)
jmp 0x118c86
movsd 0x18(%rsp), %xmm0
movsd %xmm0, 0xa8(%rsp)
movsd 0xcb8(%rsp), %xmm0
movsd 0xcb0(%rsp), %xmm1
movsd 0xca0(%rsp), %xmm2
movsd 0xc98(%rsp), %xmm3
movsd 0xca8(%rsp), %xmm4
movsd 0xb0(%rsp), %xmm5
leaq 0x3316a3(%rip), %rdi    # 0x44a375
movb $0x6, %al
callq 0x111080
jmp 0x118cdb
movsd 0xcb8(%rsp), %xmm0
movsd 0xcb0(%rsp), %xmm1
movsd 0xca0(%rsp), %xmm2
movsd 0xc98(%rsp), %xmm3
movsd 0xca8(%rsp), %xmm4
movsd 0xa8(%rsp), %xmm5
leaq 0x3316a8(%rip), %rdi    # 0x44a3c0
movb $0x6, %al
callq 0x111080
jmp 0x118d21
movsd 0xb0(%rsp), %xmm0
subsd 0xa8(%rsp), %xmm0
movsd %xmm0, 0xa0(%rsp)
movsd 0xcb8(%rsp), %xmm0
subsd 0xcb0(%rsp), %xmm0
movsd %xmm0, 0x98(%rsp)
movsd 0xcb8(%rsp), %xmm0
movsd %xmm0, (%rsp)
movsd 0xcb0(%rsp), %xmm0
movsd %xmm0, 0x8(%rsp)
movsd 0xc98(%rsp), %xmm0
movq %xmm0, %rax
movabsq $-0x8000000000000000, %rcx # imm = 0x8000000000000000
xorq %rcx, %rax
movq %rax, %xmm0
mulsd 0xca0(%rsp), %xmm0
callq 0x111130
movsd (%rsp), %xmm1
movaps %xmm0, %xmm2
movsd 0x8(%rsp), %xmm0
movq %xmm0, %rax
movabsq $-0x8000000000000000, %rcx # imm = 0x8000000000000000
xorq %rcx, %rax
movq %rax, %xmm0
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, 0x90(%rsp)
movsd 0x98(%rsp), %xmm1
movsd 0xa0(%rsp), %xmm0
xorl %eax, %eax
ucomisd %xmm1, %xmm0
movb %al, 0x17(%rsp)
jbe 0x118e13
movsd 0xa0(%rsp), %xmm1
movsd 0x90(%rsp), %xmm0
ucomisd %xmm1, %xmm0
seta %al
movb %al, 0x17(%rsp)
movb 0x17(%rsp), %al
andb $0x1, %al
movb %al, 0x8f(%rsp)
movsd 0xa8(%rsp), %xmm0
movsd 0x98(%rsp), %xmm1
addsd %xmm1, %xmm0
movsd %xmm0, 0x80(%rsp)
movsd 0xa8(%rsp), %xmm0
movsd 0x90(%rsp), %xmm1
addsd %xmm1, %xmm0
movsd %xmm0, 0x78(%rsp)
movsd 0xb0(%rsp), %xmm0
movsd 0x90(%rsp), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0x70(%rsp)
movsd 0xb0(%rsp), %xmm0
movsd 0x98(%rsp), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0x68(%rsp)
movb 0x8f(%rsp), %cl
leaq 0x33157f(%rip), %rax    # 0x44a420
leaq 0x33157d(%rip), %rsi    # 0x44a425
testb $0x1, %cl
cmovneq %rax, %rsi
leaq 0x331555(%rip), %rdi    # 0x44a40b
xorl %eax, %eax
callq 0x111080
jmp 0x118ebf
movsd 0x80(%rsp), %xmm0
movsd 0xb0(%rsp), %xmm1
movsd 0x78(%rsp), %xmm2
leaq 0x33154d(%rip), %rdi    # 0x44a42b
movb $0x3, %al
callq 0x111080
jmp 0x118ee7
movsd 0x70(%rsp), %xmm0
movsd 0xa8(%rsp), %xmm1
movsd 0x68(%rsp), %xmm2
leaq 0x331551(%rip), %rdi    # 0x44a454
movb $0x3, %al
callq 0x111080
jmp 0x118f0c
leaq 0x208(%rsp), %rdi
callq 0x11ab60
leaq 0xb30(%rsp), %rdi
callq 0x11ad60
leaq 0xc68(%rsp), %rdi
callq 0x11ada0
leaq 0xc70(%rsp), %rdi
callq 0x111778
leaq 0xcd8(%rsp), %rdi
callq 0x111778
addq $0xcf8, %rsp            # imm = 0xCF8
retq
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xcd7(%rsp), %rdi
callq 0x111760
jmp 0x119234
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x11921a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x118fcb
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xc48(%rsp), %rdi
callq 0x111778
leaq 0xc47(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x119014
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xc20(%rsp), %rdi
callq 0x111778
leaq 0xc1f(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x11905d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xbf8(%rsp), %rdi
callq 0x111778
leaq 0xbf7(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x1190a6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xbd0(%rsp), %rdi
callq 0x111778
leaq 0xbcf(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x1190ef
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xba8(%rsp), %rdi
callq 0x111778
leaq 0xba7(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x119138
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xb80(%rsp), %rdi
callq 0x111778
leaq 0xb7f(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x119181
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xb58(%rsp), %rdi
callq 0x111778
leaq 0xb57(%rsp), %rdi
callq 0x111760
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x11920d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
jmp 0x119200
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0xb8(%rsp), %rdi
callq 0x11a240
jmp 0x119200
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xcc8(%rsp)
movl %eax, 0xcc4(%rsp)
leaq 0x208(%rsp), %rdi
callq 0x11ab60
leaq 0xb30(%rsp), %rdi
callq 0x11ad60
leaq 0xc68(%rsp), %rdi
callq 0x11ada0
leaq 0xc70(%rsp), %rdi
callq 0x111778
leaq 0xcd8(%rsp), %rdi
callq 0x111778
movq 0xcc8(%rsp), %rdi
callq 0x111440
nopw %cs:(%rax,%rax)
nopl (%rax,%rax)