// File: /workspace/github_cpp_makefile_star2_O2/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:
pushq %r14
pushq %rbx
subq $0x1348, %rsp           # imm = 0x1348
leaq 0x12fdf2(%rip), %rsi    # 0x242106
leaq 0xd0(%rsp), %rdi
leaq 0x240(%rsp), %rdx
callq 0x1122c8
movabsq $0x4059000000000000, %rax # imm = 0x4059000000000000
movq %rax, 0x48(%rsp)
movabsq $0x405b800000000000, %rax # imm = 0x405B800000000000
movq %rax, 0x40(%rsp)
movabsq $0x3fd3333333333333, %rax # imm = 0x3FD3333333333333
movq %rax, 0x80(%rsp)
movabsq $0x4001c28f5c28f5c3, %rax # imm = 0x4001C28F5C28F5C3
movq %rax, 0x38(%rsp)
movabsq $0x3fa999999999999a, %rax # imm = 0x3FA999999999999A
movq %rax, 0x30(%rsp)
movabsq $0x409f400000000000, %rax # imm = 0x409F400000000000
movq %rax, 0x78(%rsp)
leaq 0xa0(%rsp), %rax
movq %rax, -0x10(%rax)
andq $0x0, -0x8(%rax)
movb $0x0, (%rax)
leaq 0xb68(%rsp), %rdi
callq 0x112a7a
leaq 0x10(%rsp), %rdi
pushq $0x1
popq %rsi
callq 0x112ea0
leaq 0x12fbfb(%rip), %rsi    # 0x241fb6
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x48(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112ec4
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x12fb57(%rip), %rsi    # 0x241f54
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x40(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112ec4
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x130e4a(%rip), %rsi    # 0x243289
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x38(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112ec4
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x12fb20(%rip), %rsi    # 0x241fa1
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x30(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112ec4
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x130484(%rip), %rsi    # 0x242947
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x80(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112ec4
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x12fac9(%rip), %rsi    # 0x241fd1
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x78(%rsp), %rdx
callq 0x112f30
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0x130401(%rip), %rsi    # 0x242949
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rdx
callq 0x1122c8
leaq 0x10(%rsp), %rdi
leaq 0x240(%rsp), %rsi
leaq 0x90(%rsp), %rdx
xorl %ecx, %ecx
callq 0x112fac
leaq 0x240(%rsp), %rdi
callq 0x1105c8
leaq 0xb68(%rsp), %rdi
leaq 0x10(%rsp), %rsi
callq 0x1195b0
leaq 0xb0(%rsp), %rdi
callq 0x11301e
leaq 0xb0(%rsp), %rdi
leaq 0x10(%rsp), %rsi
callq 0x11307e
leaq 0xf0(%rsp), %rdi
pushq $0x7f
popq %rsi
callq 0x1130da
leaq 0x240(%rsp), %rdi
leaq 0xf0(%rsp), %rsi
callq 0x1131ea
leaq 0xf0(%rsp), %rdi
callq 0x113802
leaq 0x240(%rsp), %rdi
leaq 0xd0(%rsp), %rsi
leaq 0xb0(%rsp), %rdx
callq 0x11384a
leaq 0x133edf(%rip), %rsi    # 0x2464ec
leaq 0x90(%rsp), %rdi
callq 0x110580
leaq 0xf0(%rsp), %rdi
movb $0x0, (%rdi)
xorpd %xmm0, %xmm0
movupd %xmm0, 0x8(%rdi)
movupd %xmm0, 0x18(%rdi)
callq 0x113ccc
xorpd %xmm0, %xmm0
movl $0x7d0, %ebx            # imm = 0x7D0
subq $0x1, %rbx
jb 0x11265b
movq 0xb0(%rsp), %rax
movq 0x8(%rax), %rdi
movq (%rdi), %rax
callq *0x18(%rax)
jmp 0x112641
movsd %xmm0, 0x28(%rsp)
leaq 0xf0(%rsp), %r14
movq %r14, %rdi
callq 0x113cda
movq 0x90(%rsp), %rbx
movsd 0x48(%rsp), %xmm0
movsd %xmm0, 0x18(%rsp)
movsd 0x40(%rsp), %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x70(%rsp)
movsd 0x30(%rsp), %xmm0
movsd %xmm0, 0x68(%rsp)
movsd 0x80(%rsp), %xmm0
movsd %xmm0, 0x60(%rsp)
movsd 0x78(%rsp), %xmm0
movsd %xmm0, 0x58(%rsp)
movq %r14, %rdi
callq 0x113cee
movsd %xmm0, 0x50(%rsp)
leaq 0xf0(%rsp), %rdi
callq 0x113cee
movsd 0x12a921(%rip), %xmm1   # 0x23d008
divsd %xmm0, %xmm1
movsd %xmm1, (%rsp)
leaq 0x13025f(%rip), %rdi    # 0x242956
movq %rbx, %rsi
movsd 0x18(%rsp), %xmm0
movsd 0x20(%rsp), %xmm1
movsd 0x70(%rsp), %xmm2
movsd 0x68(%rsp), %xmm3
movsd 0x60(%rsp), %xmm4
movsd 0x58(%rsp), %xmm5
movsd 0x28(%rsp), %xmm6
movsd 0x50(%rsp), %xmm7
movb $0x8, %al
callq 0x110060
leaq 0x1302c1(%rip), %rsi    # 0x2429f9
leaq 0x90(%rsp), %rdi
callq 0x110580
leaq 0xf0(%rsp), %rdi
movb $0x0, (%rdi)
xorpd %xmm0, %xmm0
movupd %xmm0, 0x8(%rdi)
movupd %xmm0, 0x18(%rdi)
callq 0x113ccc
xorpd %xmm0, %xmm0
movl $0x7d0, %ebx            # imm = 0x7D0
subq $0x1, %rbx
jb 0x112786
movq 0xb0(%rsp), %rax
movq 0x8(%rax), %rdi
movq (%rdi), %rax
callq *0x18(%rax)
jmp 0x11276c
movsd %xmm0, 0x18(%rsp)
leaq 0xf0(%rsp), %r14
movq %r14, %rdi
callq 0x113cda
movq 0x90(%rsp), %rbx
movsd 0x48(%rsp), %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0x40(%rsp), %xmm0
movsd %xmm0, 0x70(%rsp)
movsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x68(%rsp)
movsd 0x30(%rsp), %xmm0
movsd %xmm0, 0x60(%rsp)
movsd 0x80(%rsp), %xmm0
movsd %xmm0, 0x58(%rsp)
movsd 0x78(%rsp), %xmm0
movsd %xmm0, 0x50(%rsp)
movq %r14, %rdi
callq 0x113cee
movsd %xmm0, 0x88(%rsp)
leaq 0xf0(%rsp), %rdi
callq 0x113cee
movsd 0x12a7f3(%rip), %xmm1   # 0x23d008
divsd %xmm0, %xmm1
movsd %xmm1, (%rsp)
leaq 0x130131(%rip), %rdi    # 0x242956
movq %rbx, %rsi
movsd 0x20(%rsp), %xmm0
movsd 0x70(%rsp), %xmm1
movsd 0x68(%rsp), %xmm2
movsd 0x60(%rsp), %xmm3
movsd 0x58(%rsp), %xmm4
movsd 0x50(%rsp), %xmm5
movsd 0x18(%rsp), %xmm6
movsd 0x88(%rsp), %xmm7
movb $0x8, %al
callq 0x110060
movsd 0x28(%rsp), %xmm0
subsd 0x18(%rsp), %xmm0
movsd %xmm0, 0x28(%rsp)
movsd 0x48(%rsp), %xmm0
movsd %xmm0, 0x18(%rsp)
movsd 0x40(%rsp), %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0x30(%rsp), %xmm0
xorpd 0x12a846(%rip), %xmm0   # 0x23d0e0
mulsd 0x38(%rsp), %xmm0
callq 0x1100d0
mulsd 0x20(%rsp), %xmm0
movsd 0x18(%rsp), %xmm1
subsd %xmm0, %xmm1
movsd 0x28(%rsp), %xmm0
subsd %xmm1, %xmm0
leaq 0x130137(%rip), %rdi    # 0x2429fd
movb $0x1, %al
callq 0x110060
leaq 0x240(%rsp), %rdi
callq 0x113d20
leaq 0xb0(%rsp), %rdi
callq 0x113e5a
leaq 0x10(%rsp), %rdi
callq 0x113e70
leaq 0xb68(%rsp), %rdi
callq 0x113eb2
leaq 0x90(%rsp), %rdi
callq 0x1105c8
leaq 0xd0(%rsp), %rdi
callq 0x1105c8
addq $0x1348, %rsp           # imm = 0x1348
popq %rbx
popq %r14
retq
jmp 0x112983
movq %rax, %rbx
leaq 0xf0(%rsp), %rdi
callq 0x113802
jmp 0x112993
jmp 0x112939
movq %rax, %rbx
jmp 0x112993
jmp 0x11296c
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
jmp 0x11295a
jmp 0x11296c
movq %rax, %rbx
leaq 0x240(%rsp), %rdi
callq 0x1105c8
jmp 0x1129a0
movq %rax, %rbx
jmp 0x1129a0
movq %rax, %rbx
jmp 0x1129aa
movq %rax, %rbx
jmp 0x1129b7
jmp 0x112983
jmp 0x112983
jmp 0x112983
jmp 0x112983
movq %rax, %rbx
leaq 0x240(%rsp), %rdi
callq 0x113d20
leaq 0xb0(%rsp), %rdi
callq 0x113e5a
leaq 0x10(%rsp), %rdi
callq 0x113e70
leaq 0xb68(%rsp), %rdi
callq 0x113eb2
leaq 0x90(%rsp), %rdi
callq 0x1105c8
leaq 0xd0(%rsp), %rdi
callq 0x1105c8
movq %rbx, %rdi
callq 0x110320