// File: ArashPartow[P]exprtk-extras/exprtk_vectorized_binomial_model.cpp
// Function: void vectorized_binomial_option_pricing_model<double>()
void vectorized_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 european_option_binomial_model_program =
      " var dt     := t / n;                                           "
      " var z      := exp(r * dt);                                     "
      " var z_inv  := 1 / z;                                           "
      " var u      := exp(v * sqrt(dt));                               "
      " var u_inv  := 1 / u;                                           "
      " var p_up   := (z - u_inv) / (u - u_inv);                       "
      " var p_down := 1 - p_up;                                        "
      "                                                                "
      " var option_price[n + 1] := [0];                                "
      "                                                                "
      " for (var i := 0; i <= n; i += 1)                               "
      " {                                                              "
      "    var base_price  := s * u^(n - 2i);                          "
      "    option_price[i] :=                                          "
      "       switch                                                   "
      "       {                                                        "
      "          case callput_flag == 'call' : max(base_price - k, 0); "
      "          case callput_flag == 'put'  : max(k - base_price, 0); "
      "       };                                                       "
      " };                                                             "
      "                                                                "
      " var p_u_zinv := z_inv * p_up;                                  "
      " var p_d_zinv := z_inv * p_down;                                "
      "                                                                "
      " for (var j := n - 1; j >= 0; j -= 1)                           "
      " {                                                              "
      "    /* y_i <- a * x_i + b * y_(i+1) i:[0,j] */                  "
      "    axpbsy(p_u_zinv,    option_price,                           "
      "           p_d_zinv, 1, option_price,                           "
      "           0, j);                                               "
      " };                                                             "
      "                                                                "
      " 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(2000.00); // Number of time steps

   std::string callput_flag;

   exprtk::rtl::vecops::package<T> vecops_package;

   symbol_table_t symbol_table;
   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);
   symbol_table.add_package(vecops_package);

   expression_t expression;
   expression.register_symbol_table(symbol_table);

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

   const std::size_t rounds = 2000;

   T binomial_call_option_price = T(0);
   T binomial_put_option_price  = T(0);

   {
      callput_flag = "call";

      exprtk::timer timer;
      timer.start();

      for (std::size_t i = 0; i < rounds; ++i)
      {
         binomial_call_option_price = expression.value();
      }

      timer.stop();

      printf("BinomialOptionPrice(Type: %4s, BasePx: %5.3f, Strike: %5.3f, Time: %5.3f, RFR: %5.3f, Vol: %5.3f, Steps: %4.1f) = %10.6f "
             "total time: %6.3fsec rate: %6.3fcalc/sec\n",
             callput_flag.c_str(),
             s, k, t, r, v, n,
             binomial_call_option_price,
             timer.time(),
             rounds / timer.time());
   }

   {
      callput_flag = "put";

      exprtk::timer timer;
      timer.start();

      for (std::size_t i = 0; i < rounds; ++i)
      {
         binomial_put_option_price = expression.value();
      }

      timer.stop();

      printf("BinomialOptionPrice(Type: %4s, BasePx: %5.3f, Strike: %5.3f, Time: %5.3f, RFR: %5.3f, Vol: %5.3f, Steps: %4.1f) = %10.6f "
             "total time: %6.3fsec rate: %6.3fcalc/sec\n",
             callput_flag.c_str(),
             s, k, t, r, v, n,
             binomial_put_option_price,
             timer.time(),
             rounds / timer.time());
   }

   const T put_call_parity_diff =
      (binomial_call_option_price - binomial_put_option_price) -
      (s - k * std::exp(-r * t));

   printf("Put-Call parity difference: %20.17f\n", put_call_parity_diff);
}

// Assembly:
subq $0x15b8, %rsp           # imm = 0x15B8
leaq 0x1597(%rsp), %rdi
movq %rdi, 0x110(%rsp)
callq 0x113660
movq 0x110(%rsp), %rdx
leaq 0x33ccf3(%rip), %rsi    # 0x45754e
leaq 0x1598(%rsp), %rdi
callq 0x11a770
jmp 0x11a86a
leaq 0x1597(%rsp), %rdi
callq 0x113790
movabsq $0x4059000000000000, %rax # imm = 0x4059000000000000
movq %rax, 0x1578(%rsp)
movabsq $0x405b800000000000, %rax # imm = 0x405B800000000000
movq %rax, 0x1570(%rsp)
movabsq $0x3fd3333333333333, %rax # imm = 0x3FD3333333333333
movq %rax, 0x1568(%rsp)
movabsq $0x4001c28f5c28f5c3, %rax # imm = 0x4001C28F5C28F5C3
movq %rax, 0x1560(%rsp)
movabsq $0x3fa999999999999a, %rax # imm = 0x3FA999999999999A
movq %rax, 0x1558(%rsp)
movabsq $0x409f400000000000, %rax # imm = 0x409F400000000000
movq %rax, 0x1550(%rsp)
leaq 0x1530(%rsp), %rdi
callq 0x113270
leaq 0xd50(%rsp), %rdi
callq 0x11b710
jmp 0x11a8ff
leaq 0xd48(%rsp), %rdi
movl $0x1, %esi
callq 0x11be10
jmp 0x11a913
leaq 0xd27(%rsp), %rdi
movq %rdi, 0x108(%rsp)
callq 0x113660
movq 0x108(%rsp), %rdx
leaq 0x33cac7(%rip), %rsi    # 0x4573fe
leaq 0xd28(%rsp), %rdi
callq 0x11a770
jmp 0x11a946
leaq 0xd48(%rsp), %rdi
leaq 0xd28(%rsp), %rsi
leaq 0x1578(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11be50
jmp 0x11a967
leaq 0xd28(%rsp), %rdi
callq 0x1137a8
leaq 0xd27(%rsp), %rdi
callq 0x113790
leaq 0xcff(%rsp), %rdi
movq %rdi, 0x100(%rsp)
callq 0x113660
movq 0x100(%rsp), %rdx
leaq 0x33c9f7(%rip), %rsi    # 0x45739c
leaq 0xd00(%rsp), %rdi
callq 0x11a770
jmp 0x11a9b4
leaq 0xd48(%rsp), %rdi
leaq 0xd00(%rsp), %rsi
leaq 0x1570(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11be50
jmp 0x11a9d5
leaq 0xd00(%rsp), %rdi
callq 0x1137a8
leaq 0xcff(%rsp), %rdi
callq 0x113790
leaq 0xcd7(%rsp), %rdi
movq %rdi, 0xf8(%rsp)
callq 0x113660
movq 0xf8(%rsp), %rdx
leaq 0x33dcac(%rip), %rsi    # 0x4586bf
leaq 0xcd8(%rsp), %rdi
callq 0x11a770
jmp 0x11aa22
leaq 0xd48(%rsp), %rdi
leaq 0xcd8(%rsp), %rsi
leaq 0x1560(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11be50
jmp 0x11aa43
leaq 0xcd8(%rsp), %rdi
callq 0x1137a8
leaq 0xcd7(%rsp), %rdi
callq 0x113790
leaq 0xcaf(%rsp), %rdi
movq %rdi, 0xf0(%rsp)
callq 0x113660
movq 0xf0(%rsp), %rdx
leaq 0x33c968(%rip), %rsi    # 0x4573e9
leaq 0xcb0(%rsp), %rdi
callq 0x11a770
jmp 0x11aa90
leaq 0xd48(%rsp), %rdi
leaq 0xcb0(%rsp), %rsi
leaq 0x1558(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11be50
jmp 0x11aab1
leaq 0xcb0(%rsp), %rdi
callq 0x1137a8
leaq 0xcaf(%rsp), %rdi
callq 0x113790
leaq 0xc87(%rsp), %rdi
movq %rdi, 0xe8(%rsp)
callq 0x113660
movq 0xe8(%rsp), %rdx
leaq 0x33d2a0(%rip), %rsi    # 0x457d8f
leaq 0xc88(%rsp), %rdi
callq 0x11a770
jmp 0x11aafe
leaq 0xd48(%rsp), %rdi
leaq 0xc88(%rsp), %rsi
leaq 0x1568(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11be50
jmp 0x11ab1f
leaq 0xc88(%rsp), %rdi
callq 0x1137a8
leaq 0xc87(%rsp), %rdi
callq 0x113790
leaq 0xc5f(%rsp), %rdi
movq %rdi, 0xe0(%rsp)
callq 0x113660
movq 0xe0(%rsp), %rdx
leaq 0x33c8bc(%rip), %rsi    # 0x457419
leaq 0xc60(%rsp), %rdi
callq 0x11a770
jmp 0x11ab6c
leaq 0xd48(%rsp), %rdi
leaq 0xc60(%rsp), %rsi
leaq 0x1550(%rsp), %rdx
callq 0x11bf00
jmp 0x11ab8b
leaq 0xc60(%rsp), %rdi
callq 0x1137a8
leaq 0xc5f(%rsp), %rdi
callq 0x113790
leaq 0xc37(%rsp), %rdi
movq %rdi, 0xd8(%rsp)
callq 0x113660
movq 0xd8(%rsp), %rdx
leaq 0x33d1c8(%rip), %rsi    # 0x457d91
leaq 0xc38(%rsp), %rdi
callq 0x11a770
jmp 0x11abd8
leaq 0xd48(%rsp), %rdi
leaq 0xc38(%rsp), %rsi
leaq 0x1530(%rsp), %rdx
xorl %ecx, %ecx
callq 0x11bfe0
jmp 0x11abf9
leaq 0xc38(%rsp), %rdi
callq 0x1137a8
leaq 0xc37(%rsp), %rdi
callq 0x113790
leaq 0xd48(%rsp), %rdi
leaq 0xd50(%rsp), %rsi
callq 0x11c0a0
jmp 0x11ac2a
leaq 0xc10(%rsp), %rdi
callq 0x11c0d0
jmp 0x11ac39
leaq 0xc10(%rsp), %rdi
leaq 0xd48(%rsp), %rsi
callq 0x11c180
jmp 0x11ac50
leaq 0x198(%rsp), %rdi
movl $0x7f, %esi
callq 0x11c230
jmp 0x11ac64
leaq 0x2e8(%rsp), %rdi
leaq 0x198(%rsp), %rsi
callq 0x11c360
jmp 0x11ac7b
leaq 0x198(%rsp), %rdi
callq 0x11cb90
leaq 0x2e8(%rsp), %rdi
leaq 0x1598(%rsp), %rsi
leaq 0xc10(%rsp), %rdx
callq 0x11cc00
jmp 0x11aca7
movq $0x7d0, 0x190(%rsp) # imm = 0x7D0
movq $0x0, 0x188(%rsp)
movq $0x0, 0x180(%rsp)
leaq 0x340c77(%rip), %rsi    # 0x45b949
leaq 0x1530(%rsp), %rdi
callq 0x1132f0
jmp 0x11ace1
leaq 0x158(%rsp), %rdi
callq 0x11d490
jmp 0x11acf0
leaq 0x158(%rsp), %rdi
callq 0x11d4c0
jmp 0x11acff
movq $0x0, 0x150(%rsp)
cmpq $0x7d0, 0x150(%rsp) # imm = 0x7D0
jae 0x11b032
leaq 0xc10(%rsp), %rdi
callq 0x11d4e0
movsd %xmm0, 0xd0(%rsp)
jmp 0x11ad35
movsd 0xd0(%rsp), %xmm0
movsd %xmm0, 0x188(%rsp)
movq 0x150(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x150(%rsp)
jmp 0x11ad0b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0x1597(%rsp), %rdi
callq 0x113790
jmp 0x11b45c
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11b442
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11b435
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11adec
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xd28(%rsp), %rdi
callq 0x1137a8
leaq 0xd27(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11ae35
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xd00(%rsp), %rdi
callq 0x1137a8
leaq 0xcff(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11ae7e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xcd8(%rsp), %rdi
callq 0x1137a8
leaq 0xcd7(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11aec7
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xcb0(%rsp), %rdi
callq 0x1137a8
leaq 0xcaf(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11af10
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xc88(%rsp), %rdi
callq 0x1137a8
leaq 0xc87(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11af59
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xc60(%rsp), %rdi
callq 0x1137a8
leaq 0xc5f(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11afa2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0xc38(%rsp), %rdi
callq 0x1137a8
leaq 0xc37(%rsp), %rdi
callq 0x113790
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11b428
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
jmp 0x11b41b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0x198(%rsp), %rdi
callq 0x11cb90
jmp 0x11b41b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1588(%rsp)
movl %eax, 0x1584(%rsp)
leaq 0x2e8(%rsp), %rdi
callq 0x11d580
jmp 0x11b41b
leaq 0x158(%rsp), %rdi
callq 0x11d500
jmp 0x11b041
leaq 0x1530(%rsp), %rdi
callq 0x1130f0
movq %rax, 0x88(%rsp)
movsd 0x1578(%rsp), %xmm0
movsd %xmm0, 0x90(%rsp)
movsd 0x1570(%rsp), %xmm0
movsd %xmm0, 0x98(%rsp)
movsd 0x1560(%rsp), %xmm0
movsd %xmm0, 0xa0(%rsp)
movsd 0x1558(%rsp), %xmm0
movsd %xmm0, 0xa8(%rsp)
movsd 0x1568(%rsp), %xmm0
movsd %xmm0, 0xb0(%rsp)
movsd 0x1550(%rsp), %xmm0
movsd %xmm0, 0xb8(%rsp)
movsd 0x188(%rsp), %xmm0
movsd %xmm0, 0xc0(%rsp)
leaq 0x158(%rsp), %rdi
callq 0x11d530
movsd %xmm0, 0xc8(%rsp)
jmp 0x11b0ec
leaq 0x158(%rsp), %rdi
callq 0x11d530
movsd %xmm0, 0x80(%rsp)
jmp 0x11b104
movsd 0xc8(%rsp), %xmm7
movsd 0xc0(%rsp), %xmm6
movsd 0xb8(%rsp), %xmm5
movsd 0xb0(%rsp), %xmm4
movsd 0xa8(%rsp), %xmm3
movsd 0xa0(%rsp), %xmm2
movsd 0x98(%rsp), %xmm1
movsd 0x90(%rsp), %xmm0
movq 0x88(%rsp), %rsi
movsd 0x80(%rsp), %xmm9
movsd 0x336ea1(%rip), %xmm8   # 0x452008
divsd %xmm9, %xmm8
movq %rsp, %rax
movsd %xmm8, (%rax)
leaq 0x33cc23(%rip), %rdi    # 0x457d9e
movb $0x8, %al
callq 0x113080
jmp 0x11b184
leaq 0x33ccb6(%rip), %rsi    # 0x457e41
leaq 0x1530(%rsp), %rdi
callq 0x1132f0
jmp 0x11b19a
leaq 0x128(%rsp), %rdi
callq 0x11d490
jmp 0x11b1a9
leaq 0x128(%rsp), %rdi
callq 0x11d4c0
jmp 0x11b1b8
movq $0x0, 0x120(%rsp)
cmpq $0x7d0, 0x120(%rsp) # imm = 0x7D0
jae 0x11b20c
leaq 0xc10(%rsp), %rdi
callq 0x11d4e0
movsd %xmm0, 0x78(%rsp)
jmp 0x11b1e7
movsd 0x78(%rsp), %xmm0
movsd %xmm0, 0x180(%rsp)
movq 0x120(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x120(%rsp)
jmp 0x11b1c4
leaq 0x128(%rsp), %rdi
callq 0x11d500
jmp 0x11b21b
leaq 0x1530(%rsp), %rdi
callq 0x1130f0
movq %rax, 0x30(%rsp)
movsd 0x1578(%rsp), %xmm0
movsd %xmm0, 0x38(%rsp)
movsd 0x1570(%rsp), %xmm0
movsd %xmm0, 0x40(%rsp)
movsd 0x1560(%rsp), %xmm0
movsd %xmm0, 0x48(%rsp)
movsd 0x1558(%rsp), %xmm0
movsd %xmm0, 0x50(%rsp)
movsd 0x1568(%rsp), %xmm0
movsd %xmm0, 0x58(%rsp)
movsd 0x1550(%rsp), %xmm0
movsd %xmm0, 0x60(%rsp)
movsd 0x180(%rsp), %xmm0
movsd %xmm0, 0x68(%rsp)
leaq 0x128(%rsp), %rdi
callq 0x11d530
movsd %xmm0, 0x70(%rsp)
jmp 0x11b2ab
leaq 0x128(%rsp), %rdi
callq 0x11d530
movsd %xmm0, 0x28(%rsp)
jmp 0x11b2c0
movsd 0x70(%rsp), %xmm7
movsd 0x68(%rsp), %xmm6
movsd 0x60(%rsp), %xmm5
movsd 0x58(%rsp), %xmm4
movsd 0x50(%rsp), %xmm3
movsd 0x48(%rsp), %xmm2
movsd 0x40(%rsp), %xmm1
movsd 0x38(%rsp), %xmm0
movq 0x30(%rsp), %rsi
movsd 0x28(%rsp), %xmm9
movsd 0x336d03(%rip), %xmm8   # 0x452008
divsd %xmm9, %xmm8
movq %rsp, %rax
movsd %xmm8, (%rax)
leaq 0x33ca85(%rip), %rdi    # 0x457d9e
movb $0x8, %al
callq 0x113080
jmp 0x11b322
movsd 0x188(%rsp), %xmm0
movsd 0x180(%rsp), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0x1578(%rsp), %xmm0
movsd %xmm0, 0x18(%rsp)
movsd 0x1570(%rsp), %xmm0
movsd %xmm0, 0x10(%rsp)
movaps 0x336d5d(%rip), %xmm1   # 0x4520c0
movsd 0x1558(%rsp), %xmm0
pxor %xmm1, %xmm0
movsd 0x1560(%rsp), %xmm1
mulsd %xmm1, %xmm0
callq 0x113140
movsd 0x10(%rsp), %xmm2
movsd 0x18(%rsp), %xmm1
movaps %xmm0, %xmm3
movsd 0x20(%rsp), %xmm0
mulsd %xmm3, %xmm2
subsd %xmm2, %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0x118(%rsp)
movsd 0x118(%rsp), %xmm0
leaq 0x33ca89(%rip), %rdi    # 0x457e45
movb $0x1, %al
callq 0x113080
jmp 0x11b3c5
leaq 0x2e8(%rsp), %rdi
callq 0x11d580
leaq 0xc10(%rsp), %rdi
callq 0x11d780
leaq 0xd48(%rsp), %rdi
callq 0x11d7c0
leaq 0xd50(%rsp), %rdi
callq 0x11d840
leaq 0x1530(%rsp), %rdi
callq 0x1137a8
leaq 0x1598(%rsp), %rdi
callq 0x1137a8
addq $0x15b8, %rsp           # imm = 0x15B8
retq
leaq 0xc10(%rsp), %rdi
callq 0x11d780
leaq 0xd48(%rsp), %rdi
callq 0x11d7c0
leaq 0xd50(%rsp), %rdi
callq 0x11d840
leaq 0x1530(%rsp), %rdi
callq 0x1137a8
leaq 0x1598(%rsp), %rdi
callq 0x1137a8
movq 0x1588(%rsp), %rdi
callq 0x113460
nopl (%rax)