// File: ArashPartow[P]exprtk/exprtk_simple_example_23.cpp
// Function: void real_1d_discrete_fourier_transform<double>()
void real_1d_discrete_fourier_transform()
{
   typedef exprtk::symbol_table<T>         symbol_table_t;
   typedef exprtk::expression<T>           expression_t;
   typedef exprtk::parser<T>               parser_t;
   typedef exprtk::function_compositor<T>  compositor_t;
   typedef typename compositor_t::function function_t;

   const T sampling_rate = 1024.0;            // ~1KHz
   const T N             = 8 * sampling_rate; // 8 seconds worth of samples

   std::vector<T> input (static_cast<std::size_t>(N),0.0);
   std::vector<T> output(static_cast<std::size_t>(N),0.0);

   exprtk::rtl::io::println<T> println;

   symbol_table_t symbol_table;
   symbol_table.add_vector   ("input"        , input        );
   symbol_table.add_vector   ("output"       , output       );
   symbol_table.add_function ("println"      , println      );
   symbol_table.add_constant ("N"            , N            );
   symbol_table.add_constant ("sampling_rate", sampling_rate);
   symbol_table.add_pi();

   compositor_t compositor(symbol_table);
   compositor.load_vectors(true);

   compositor.add(
      function_t("dft_1d_real")
      .var("N")
      .expression
      (
         " for (var k := 0; k < N; k += 1)        "
         " {                                      "
         "    var k_real := 0.0;                  "
         "    var k_imag := 0.0;                  "
         "                                        "
         "    for (var i := 0; i < N; i += 1)     "
         "    {                                   "
         "       var theta := 2pi * k * i / N;    "
         "       k_real += input[i] * cos(theta); "
         "       k_imag -= input[i] * sin(theta); "
         "    };                                  "
         "                                        "
         "    output[k] := hypot(k_real,k_imag);  "
         " }                                      "
      ));

   const std::string dft_program =
      "                                                                          "
      " /*                                                                       "
      "    Generate an aggregate waveform comprised of three                     "
      "    sine waves of varying frequencies and amplitudes.                     "
      " */                                                                       "
      " const var num_waves := 3;                                                "
      "                                                                          "
      " var frequencies[num_waves] := { 111.125, 222.250, 333.375 }; /* Hz    */ "
      " var amplitudes [num_waves] := {  11.111,  22.222,  33.333 }; /* Power */ "
      "                                                                          "
      " for (var i := 0; i < N; i += 1)                                          "
      " {                                                                        "
      "    var time := i / sampling_rate;                                        "
      "                                                                          "
      "    for (var j := 0; j < frequencies[]; j += 1)                           "
      "    {                                                                     "
      "       var frequency := frequencies[j];                                   "
      "       var amplitude := amplitudes [j];                                   "
      "       var theta     := 2 * pi * frequency * time;                        "
      "                                                                          "
      "       input[i] += amplitude * sin(theta);                                "
      "    }                                                                     "
      " };                                                                       "
      "                                                                          "
      " dft_1d_real(input[]);                                                    "
      "                                                                          "
      " var freq_bin_size   := sampling_rate / N;                                "
      " var max_bin         := ceil(N / 2);                                      "
      " var max_noise_level := 1e-6;                                             "
      "                                                                          "
      " /* Normalise amplitudes */                                               "
      " output /= max_bin;                                                       "
      "                                                                          "
      " println('1D Real DFT Frequencies');                                      "
      "                                                                          "
      " for (var k := 0; k < max_bin; k += 1)                                    "
      " {                                                                        "
      "    if (output[k] > max_noise_level)                                      "
      "    {                                                                     "
      "       var freq_begin := k * freq_bin_size;                               "
      "       var freq_end   := freq_begin + freq_bin_size;                      "
      "                                                                          "
      "       println('Index: ', k,' ',                                          "
      "               'Freq. range: [', freq_begin, 'Hz, ', freq_end, 'Hz) ',    "
      "               'Amplitude: ', output[k]);                                 "
      "    }                                                                     "
      " }                                                                        "
      "                                                                          ";

   expression_t expression;
   expression.register_symbol_table(symbol_table);

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

   expression.value();
}

// Assembly:
subq $0x17f8, %rsp           # imm = 0x17F8
movabsq $0x4090000000000000, %rax # imm = 0x4090000000000000
movq %rax, 0x17f0(%rsp)
movabsq $0x40c0000000000000, %rax # imm = 0x40C0000000000000
movq %rax, 0x17e8(%rsp)
movq $0x0, 0x17c8(%rsp)
leaq 0x17c7(%rsp), %rdi
movq %rdi, 0x70(%rsp)
callq 0x11c4e0
movq 0x70(%rsp), %rcx
leaq 0x17d0(%rsp), %rdi
movl $0x2000, %esi           # imm = 0x2000
leaq 0x17c8(%rsp), %rdx
callq 0x11c4f0
jmp 0x11b89a
leaq 0x17c7(%rsp), %rdi
callq 0x11c570
movq $0x0, 0x1790(%rsp)
leaq 0x178f(%rsp), %rdi
movq %rdi, 0x68(%rsp)
callq 0x11c4e0
movq 0x68(%rsp), %rcx
leaq 0x1798(%rsp), %rdi
movl $0x2000, %esi           # imm = 0x2000
leaq 0x1790(%rsp), %rdx
callq 0x11c4f0
jmp 0x11b8e6
leaq 0x178f(%rsp), %rdi
callq 0x11c570
leaq 0x16ff(%rsp), %rdi
movq %rdi, 0x60(%rsp)
callq 0x114660
movq 0x60(%rsp), %rdx
leaq 0x348c54(%rip), %rsi    # 0x464565
leaq 0x1700(%rsp), %rdi
callq 0x11b770
jmp 0x11b920
leaq 0x1720(%rsp), %rdi
leaq 0x1700(%rsp), %rsi
callq 0x11c580
jmp 0x11b937
leaq 0x1700(%rsp), %rdi
callq 0x1147a8
leaq 0x16ff(%rsp), %rdi
callq 0x114790
leaq 0x16f0(%rsp), %rdi
movl $0x1, %esi
callq 0x11c690
jmp 0x11b965
leaq 0x16cf(%rsp), %rdi
movq %rdi, 0x58(%rsp)
callq 0x114660
movq 0x58(%rsp), %rdx
leaq 0x348be9(%rip), %rsi    # 0x46456c
leaq 0x16d0(%rsp), %rdi
callq 0x11b770
jmp 0x11b992
leaq 0x16f0(%rsp), %rdi
leaq 0x16d0(%rsp), %rsi
leaq 0x17d0(%rsp), %rdx
callq 0x11c6d0
jmp 0x11b9b1
leaq 0x16d0(%rsp), %rdi
callq 0x1147a8
leaq 0x16cf(%rsp), %rdi
callq 0x114790
leaq 0x16a7(%rsp), %rdi
movq %rdi, 0x50(%rsp)
callq 0x114660
movq 0x50(%rsp), %rdx
leaq 0x348b89(%rip), %rsi    # 0x464572
leaq 0x16a8(%rsp), %rdi
callq 0x11b770
jmp 0x11b9f8
leaq 0x16f0(%rsp), %rdi
leaq 0x16a8(%rsp), %rsi
leaq 0x1798(%rsp), %rdx
callq 0x11c6d0
jmp 0x11ba17
leaq 0x16a8(%rsp), %rdi
callq 0x1147a8
leaq 0x16a7(%rsp), %rdi
callq 0x114790
leaq 0x167f(%rsp), %rdi
movq %rdi, 0x48(%rsp)
callq 0x114660
movq 0x48(%rsp), %rdx
leaq 0x348b2a(%rip), %rsi    # 0x464579
leaq 0x1680(%rsp), %rdi
callq 0x11b770
jmp 0x11ba5e
leaq 0x16f0(%rsp), %rdi
leaq 0x1680(%rsp), %rsi
leaq 0x1720(%rsp), %rdx
callq 0x11c790
jmp 0x11ba7d
leaq 0x1680(%rsp), %rdi
callq 0x1147a8
leaq 0x167f(%rsp), %rdi
callq 0x114790
leaq 0x1657(%rsp), %rdi
movq %rdi, 0x40(%rsp)
callq 0x114660
movq 0x40(%rsp), %rdx
leaq 0x34a25d(%rip), %rsi    # 0x465d12
leaq 0x1658(%rsp), %rdi
callq 0x11b770
jmp 0x11bac4
leaq 0x16f0(%rsp), %rdi
leaq 0x1658(%rsp), %rsi
leaq 0x17e8(%rsp), %rdx
callq 0x11c990
jmp 0x11bae3
leaq 0x1658(%rsp), %rdi
callq 0x1147a8
leaq 0x1657(%rsp), %rdi
callq 0x114790
leaq 0x162f(%rsp), %rdi
movq %rdi, 0x38(%rsp)
callq 0x114660
movq 0x38(%rsp), %rdx
leaq 0x348a66(%rip), %rsi    # 0x464581
leaq 0x1630(%rsp), %rdi
callq 0x11b770
jmp 0x11bb2a
leaq 0x16f0(%rsp), %rdi
leaq 0x1630(%rsp), %rsi
leaq 0x17f0(%rsp), %rdx
callq 0x11c990
jmp 0x11bb49
leaq 0x1630(%rsp), %rdi
callq 0x1147a8
leaq 0x162f(%rsp), %rdi
callq 0x114790
leaq 0x16f0(%rsp), %rdi
callq 0x11ca70
jmp 0x11bb72
leaq 0xc40(%rsp), %rdi
leaq 0x16f0(%rsp), %rsi
callq 0x11cb90
jmp 0x11bb89
leaq 0xc40(%rsp), %rdi
movl $0x1, %esi
callq 0x11cd30
jmp 0x11bb9d
leaq 0xb8f(%rsp), %rdi
movq %rdi, 0x30(%rsp)
callq 0x114660
movq 0x30(%rsp), %rdx
leaq 0x3489d4(%rip), %rsi    # 0x46458f
leaq 0xb90(%rsp), %rdi
callq 0x11b770
jmp 0x11bbca
leaq 0xbb0(%rsp), %rdi
leaq 0xb90(%rsp), %rsi
callq 0x11cdb0
jmp 0x11bbe1
leaq 0xb67(%rsp), %rdi
movq %rdi, 0x28(%rsp)
callq 0x114660
movq 0x28(%rsp), %rdx
leaq 0x34a113(%rip), %rsi    # 0x465d12
leaq 0xb68(%rsp), %rdi
callq 0x11b770
jmp 0x11bc0e
leaq 0xbb0(%rsp), %rdi
leaq 0xb68(%rsp), %rsi
callq 0x11ce30
movq %rax, 0x20(%rsp)
jmp 0x11bc2a
leaq 0xb3f(%rsp), %rdi
movq %rdi, 0x18(%rsp)
callq 0x114660
movq 0x18(%rsp), %rdx
leaq 0x348953(%rip), %rsi    # 0x46459b
leaq 0xb40(%rsp), %rdi
callq 0x11b770
jmp 0x11bc57
movq 0x20(%rsp), %rdi
leaq 0xb40(%rsp), %rsi
callq 0x11ce60
movq %rax, 0x10(%rsp)
jmp 0x11bc70
movq 0x10(%rsp), %rsi
leaq 0xc40(%rsp), %rdi
xorl %edx, %edx
callq 0x11cd60
jmp 0x11bc86
leaq 0xb40(%rsp), %rdi
callq 0x1147a8
leaq 0xb3f(%rsp), %rdi
callq 0x114790
leaq 0xb68(%rsp), %rdi
callq 0x1147a8
leaq 0xb67(%rsp), %rdi
callq 0x114790
leaq 0xbb0(%rsp), %rdi
callq 0x11ce90
leaq 0xb90(%rsp), %rdi
callq 0x1147a8
leaq 0xb8f(%rsp), %rdi
callq 0x114790
leaq 0xb17(%rsp), %rdi
movq %rdi, 0x8(%rsp)
callq 0x114660
movq 0x8(%rsp), %rdx
leaq 0x348acd(%rip), %rsi    # 0x4647cc
leaq 0xb18(%rsp), %rdi
callq 0x11b770
jmp 0x11bd0e
leaq 0xb17(%rsp), %rdi
callq 0x114790
leaq 0xaf0(%rsp), %rdi
callq 0x11ced0
jmp 0x11bd2a
leaq 0xaf0(%rsp), %rdi
leaq 0x16f0(%rsp), %rsi
callq 0x11cf80
jmp 0x11bd41
leaq 0x78(%rsp), %rdi
movl $0x7f, %esi
callq 0x11d030
jmp 0x11bd52
leaq 0x1c8(%rsp), %rdi
leaq 0x78(%rsp), %rsi
callq 0x11d160
jmp 0x11bd66
leaq 0x78(%rsp), %rdi
callq 0x11d990
leaq 0x1c8(%rsp), %rdi
leaq 0xb18(%rsp), %rsi
leaq 0xaf0(%rsp), %rdx
callq 0x11da00
jmp 0x11bd8f
leaq 0xaf0(%rsp), %rdi
callq 0x11e290
jmp 0x11bd9e
leaq 0x1c8(%rsp), %rdi
callq 0x11e2b0
leaq 0xaf0(%rsp), %rdi
callq 0x11e4b0
leaq 0xb18(%rsp), %rdi
callq 0x1147a8
leaq 0xc40(%rsp), %rdi
callq 0x11e4f0
leaq 0x16f0(%rsp), %rdi
callq 0x11e580
leaq 0x1720(%rsp), %rdi
callq 0x11e600
leaq 0x1798(%rsp), %rdi
callq 0x11e630
leaq 0x17d0(%rsp), %rdi
callq 0x11e630
addq $0x17f8, %rsp           # imm = 0x17F8
retq
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x17c7(%rsp), %rdi
callq 0x11c570
jmp 0x11c231
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x178f(%rsp), %rdi
callq 0x11c570
jmp 0x11c224
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11be91
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x1700(%rsp), %rdi
callq 0x1147a8
leaq 0x16ff(%rsp), %rdi
callq 0x114790
jmp 0x11c217
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c20a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11bef3
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x16d0(%rsp), %rdi
callq 0x1147a8
leaq 0x16cf(%rsp), %rdi
callq 0x114790
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11bf3c
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x16a8(%rsp), %rdi
callq 0x1147a8
leaq 0x16a7(%rsp), %rdi
callq 0x114790
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11bf85
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x1680(%rsp), %rdi
callq 0x1147a8
leaq 0x167f(%rsp), %rdi
callq 0x114790
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11bfce
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x1658(%rsp), %rdi
callq 0x1147a8
leaq 0x1657(%rsp), %rdi
callq 0x114790
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c017
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x1630(%rsp), %rdi
callq 0x1147a8
leaq 0x162f(%rsp), %rdi
callq 0x114790
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c1fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c1f0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c131
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c124
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c10a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c0fd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c0f0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0xb40(%rsp), %rdi
callq 0x1147a8
leaq 0xb3f(%rsp), %rdi
callq 0x114790
leaq 0xb68(%rsp), %rdi
callq 0x1147a8
leaq 0xb67(%rsp), %rdi
callq 0x114790
leaq 0xbb0(%rsp), %rdi
callq 0x11ce90
leaq 0xb90(%rsp), %rdi
callq 0x1147a8
leaq 0xb8f(%rsp), %rdi
callq 0x114790
jmp 0x11c1f0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0xb17(%rsp), %rdi
callq 0x114790
jmp 0x11c1f0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c1e3
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
jmp 0x11c1d6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x78(%rsp), %rdi
callq 0x11d990
jmp 0x11c1d6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x17b8(%rsp)
movl %eax, 0x17b4(%rsp)
leaq 0x1c8(%rsp), %rdi
callq 0x11e2b0
leaq 0xaf0(%rsp), %rdi
callq 0x11e4b0
leaq 0xb18(%rsp), %rdi
callq 0x1147a8
leaq 0xc40(%rsp), %rdi
callq 0x11e4f0
leaq 0x16f0(%rsp), %rdi
callq 0x11e580
leaq 0x1720(%rsp), %rdi
callq 0x11e600
leaq 0x1798(%rsp), %rdi
callq 0x11e630
leaq 0x17d0(%rsp), %rdi
callq 0x11e630
movq 0x17b8(%rsp), %rdi
callq 0x114450
nop