// File: redpony[P]cpyp/crp_test.cc
// Function: test_mfcrp()
void test_mfcrp() {
  vector<double> ref_up = {0.0152302, 0.0754287, 0.153957, 0.209485, 0.213071, 0.16726, 0.101233, 0.0460308, 0.0149256, 0.003075, 0.00030408};
  vector<double> ref_down = {0.185252, 0.32385, 0.271888, 0.145849, 0.0548223, 0.0149361, 0.00294062, 0.0004185, 4.074e-05, 2.3e-06, 4e-08};

  cpyp::MT19937 eng;
  long double p0[2]{1.0,1.0}; // pa(0), pb(0)
  double lam[2]{0.3,0.7};
  vector<double> hist_upstairs(11, 0);
  vector<double> hist_downstairs(11, 0);
  int c = 0;
  for (int n = 0; n < 200000; ++n) {
    cpyp::mf_crp<2, unsigned> crp(0.5, 1.0);
    int upstairs = 0;
    int downstairs = 0;
    for (int i = 0; i < 10; ++i) {
      unsigned obs = 0;
      pair<unsigned, int> floor_count = crp.increment(obs, p0, lam, eng);
      if (floor_count.second) (floor_count.first ? upstairs : downstairs)++;
    }
    // resample some stuff
    for (int j = 0; j < 4; ++j) {
      for (int i = 0; i < 10; ++i) {
        unsigned obs = 0;
        pair<unsigned, int> floor_count = crp.decrement(obs, eng);
        if (floor_count.second) (floor_count.first ? upstairs : downstairs)--;
        floor_count = crp.increment(obs, p0, lam, eng);
        if (floor_count.second) (floor_count.first ? upstairs : downstairs)++;
      }
    }
    hist_downstairs[downstairs] += 1.0;
    hist_upstairs[upstairs] += 1.0;
    ++c;
  }
  cerr << "Upstairs:\n";
  int j = 0;
  double max_up = 0;
  double tot_up = 0;
  for (auto& d : hist_upstairs) {
    double diff = d / c - ref_up[j++];
    cerr << diff << endl;
    if (fabs(diff) > max_up) max_up = fabs(diff);
    tot_up += fabs(diff);
  }
  if (max_up > 0.01) { cerr << "** TOO BIG "; }
  cerr << "max_up=" << max_up << endl;
  tot_up /= 11;
  if (tot_up > 0.005) { cerr << "*** TOO BIG "; }
  cerr << "avg_up=" << tot_up << endl;
  cerr << "Downstairs:\n";
  j = 0;
  double tot_down = 0;
  double max_down = 0;
  for (auto& d : hist_downstairs) {
    double diff = (d / c - ref_down[j++]);
    cerr << diff << endl;
    if (fabs(diff) > max_down) max_down = fabs(diff);
    tot_down += fabs(diff);
  }
  if (max_down > 0.01) { cerr << "** TOO BIG "; }
  cerr << "max_down=" << max_down << endl;
  tot_down /= 11;
  if (tot_down > 0.005) { cerr << "*** TOO BIG "; }
  cerr << "avg_down=" << tot_down << endl;
}

// Assembly:
subq $0x1718, %rsp           # imm = 0x1718
movups 0x10a5a(%rip), %xmm0    # 0x15378
movaps %xmm0, 0x16d0(%rsp)
movups 0x10a3b(%rip), %xmm0    # 0x15368
movaps %xmm0, 0x16c0(%rsp)
movups 0x10a1c(%rip), %xmm0    # 0x15358
movaps %xmm0, 0x16b0(%rsp)
movups 0x109fd(%rip), %xmm0    # 0x15348
movaps %xmm0, 0x16a0(%rsp)
movups 0x109de(%rip), %xmm0    # 0x15338
movaps %xmm0, 0x1690(%rsp)
movabsq $0x3f33ed9da7c62d20, %rax # imm = 0x3F33ED9DA7C62D20
movq %rax, 0x16e0(%rsp)
leaq 0x1690(%rsp), %rax
movq %rax, 0x16f0(%rsp)
movq $0xb, 0x16f8(%rsp)
leaq 0x168f(%rsp), %rdi
movq %rdi, 0xb0(%rsp)
callq 0x5e60
movq 0xb0(%rsp), %rcx
movq 0x16f0(%rsp), %rsi
movq 0x16f8(%rsp), %rdx
leaq 0x1700(%rsp), %rdi
callq 0x5e70
jmp 0x49cc
leaq 0x168f(%rsp), %rdi
callq 0x5f00
movabsq $0x3e65798ee2308c3a, %rax # imm = 0x3E65798EE2308C3A
movq %rax, 0x1640(%rsp)
movups 0x109de(%rip), %xmm0    # 0x153d0
movaps %xmm0, 0x1630(%rsp)
movups 0x109bf(%rip), %xmm0    # 0x153c0
movaps %xmm0, 0x1620(%rsp)
movups 0x109a0(%rip), %xmm0    # 0x153b0
movaps %xmm0, 0x1610(%rsp)
movups 0x10981(%rip), %xmm0    # 0x153a0
movaps %xmm0, 0x1600(%rsp)
movups 0x10962(%rip), %xmm0    # 0x15390
movaps %xmm0, 0x15f0(%rsp)
leaq 0x15f0(%rsp), %rax
movq %rax, 0x1650(%rsp)
movq $0xb, 0x1658(%rsp)
leaq 0x15ef(%rsp), %rdi
movq %rdi, 0xa8(%rsp)
callq 0x5e60
movq 0xa8(%rsp), %rcx
movq 0x1650(%rsp), %rsi
movq 0x1658(%rsp), %rdx
leaq 0x1660(%rsp), %rdi
callq 0x5e70
jmp 0x4a8e
leaq 0x15ef(%rsp), %rdi
callq 0x5f00
leaq 0x260(%rsp), %rdi
callq 0x5e30
jmp 0x4aaa
movaps 0x111cf(%rip), %xmm0    # 0x15c80
movaps %xmm0, 0x250(%rsp)
movaps 0x111b0(%rip), %xmm0    # 0x15c70
movaps %xmm0, 0x240(%rsp)
movaps 0x105e1(%rip), %xmm0    # 0x150b0
movaps %xmm0, 0x230(%rsp)
movq $0x0, 0x210(%rsp)
leaq 0x20f(%rsp), %rdi
movq %rdi, 0xa0(%rsp)
callq 0x5e60
movq 0xa0(%rsp), %rcx
leaq 0x218(%rsp), %rdi
movl $0xb, %esi
leaq 0x210(%rsp), %rdx
callq 0x6bd0
jmp 0x4b1c
leaq 0x20f(%rsp), %rdi
callq 0x5f00
movq $0x0, 0x1e8(%rsp)
leaq 0x1e7(%rsp), %rdi
movq %rdi, 0x98(%rsp)
callq 0x5e60
movq 0x98(%rsp), %rcx
leaq 0x1f0(%rsp), %rdi
movl $0xb, %esi
leaq 0x1e8(%rsp), %rdx
callq 0x6bd0
jmp 0x4b6e
leaq 0x1e7(%rsp), %rdi
callq 0x5f00
movl $0x0, 0x1e0(%rsp)
movl $0x0, 0x1dc(%rsp)
cmpl $0x30d40, 0x1dc(%rsp) # imm = 0x30D40
jge 0x4f92
leaq 0x160(%rsp), %rdi
movsd 0x10456(%rip), %xmm0    # 0x15008
movsd 0x10456(%rip), %xmm1    # 0x15010
callq 0x6c50
jmp 0x4bc1
movl $0x0, 0x15c(%rsp)
movl $0x0, 0x158(%rsp)
movl $0x0, 0x154(%rsp)
cmpl $0xa, 0x154(%rsp)
jge 0x4d9a
movl $0x0, 0x150(%rsp)
leaq 0x160(%rsp), %rdi
leaq 0x150(%rsp), %rsi
leaq 0x240(%rsp), %rdx
leaq 0x230(%rsp), %rcx
leaq 0x260(%rsp), %r8
callq 0x6d10
movq %rax, 0x90(%rsp)
jmp 0x4c32
movq 0x90(%rsp), %rax
movq %rax, 0x148(%rsp)
cmpl $0x0, 0x14c(%rsp)
je 0x4d82
cmpl $0x0, 0x148(%rsp)
je 0x4c6c
leaq 0x15c(%rsp), %rax
movq %rax, 0x88(%rsp)
jmp 0x4c7e
leaq 0x158(%rsp), %rax
movq %rax, 0x88(%rsp)
jmp 0x4c7e
movq 0x88(%rsp), %rax
movl (%rax), %ecx
addl $0x1, %ecx
movl %ecx, (%rax)
jmp 0x4d82
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
leaq 0x168f(%rsp), %rdi
callq 0x5f00
jmp 0x554e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
leaq 0x15ef(%rsp), %rdi
callq 0x5f00
jmp 0x5541
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
jmp 0x5534
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
leaq 0x20f(%rsp), %rdi
callq 0x5f00
jmp 0x5534
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
leaq 0x1e7(%rsp), %rdi
callq 0x5f00
jmp 0x5527
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
jmp 0x551a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x1680(%rsp)
movl %eax, 0x167c(%rsp)
leaq 0x160(%rsp), %rdi
callq 0x7390
jmp 0x551a
jmp 0x4d84
movl 0x154(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x154(%rsp)
jmp 0x4be2
movl $0x0, 0x144(%rsp)
cmpl $0x4, 0x144(%rsp)
jge 0x4f14
movl $0x0, 0x140(%rsp)
cmpl $0xa, 0x140(%rsp)
jge 0x4efc
movl $0x0, 0x13c(%rsp)
xorl %eax, %eax
movl %eax, %ecx
leaq 0x160(%rsp), %rdi
leaq 0x13c(%rsp), %rsi
leaq 0x260(%rsp), %rdx
callq 0x7030
movq %rax, 0x80(%rsp)
jmp 0x4e02
movq 0x80(%rsp), %rax
movq %rax, 0x134(%rsp)
cmpl $0x0, 0x138(%rsp)
je 0x4e50
cmpl $0x0, 0x134(%rsp)
je 0x4e35
leaq 0x15c(%rsp), %rax
movq %rax, 0x78(%rsp)
jmp 0x4e44
leaq 0x158(%rsp), %rax
movq %rax, 0x78(%rsp)
jmp 0x4e44
movq 0x78(%rsp), %rax
movl (%rax), %ecx
addl $-0x1, %ecx
movl %ecx, (%rax)
leaq 0x160(%rsp), %rdi
leaq 0x13c(%rsp), %rsi
leaq 0x240(%rsp), %rdx
leaq 0x230(%rsp), %rcx
leaq 0x260(%rsp), %r8
callq 0x6d10
movq %rax, 0x70(%rsp)
jmp 0x4e84
movq 0x70(%rsp), %rax
movq %rax, 0x12c(%rsp)
leaq 0x134(%rsp), %rdi
leaq 0x12c(%rsp), %rsi
callq 0x7360
cmpl $0x0, 0x138(%rsp)
je 0x4ee4
cmpl $0x0, 0x134(%rsp)
je 0x4ec9
leaq 0x15c(%rsp), %rax
movq %rax, 0x68(%rsp)
jmp 0x4ed8
leaq 0x158(%rsp), %rax
movq %rax, 0x68(%rsp)
jmp 0x4ed8
movq 0x68(%rsp), %rax
movl (%rax), %ecx
addl $0x1, %ecx
movl %ecx, (%rax)
jmp 0x4ee6
movl 0x140(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x140(%rsp)
jmp 0x4dbe
jmp 0x4efe
movl 0x144(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x144(%rsp)
jmp 0x4da5
movslq 0x158(%rsp), %rsi
leaq 0x1f0(%rsp), %rdi
callq 0x6ba0
movsd 0x100df(%rip), %xmm0    # 0x15010
addsd (%rax), %xmm0
movsd %xmm0, (%rax)
movslq 0x15c(%rsp), %rsi
leaq 0x218(%rsp), %rdi
callq 0x6ba0
movsd 0x100ba(%rip), %xmm0    # 0x15010
addsd (%rax), %xmm0
movsd %xmm0, (%rax)
movl 0x1e0(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x1e0(%rsp)
leaq 0x160(%rsp), %rdi
callq 0x7390
movl 0x1dc(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x1dc(%rsp)
jmp 0x4b91
movq 0x19057(%rip), %rdi     # 0x1dff0
leaq 0x104ee(%rip), %rsi     # 0x1548e
callq 0x2180
jmp 0x4fa7
movl $0x0, 0x128(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x120(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x118(%rsp)
leaq 0x218(%rsp), %rax
movq %rax, 0x110(%rsp)
movq 0x110(%rsp), %rdi
callq 0x73b0
movq %rax, 0x108(%rsp)
movq 0x110(%rsp), %rdi
callq 0x73e0
movq %rax, 0x100(%rsp)
leaq 0x108(%rsp), %rdi
leaq 0x100(%rsp), %rsi
callq 0x7410
testb $0x1, %al
jne 0x5022
jmp 0x5130
leaq 0x108(%rsp), %rdi
callq 0x7450
movq %rax, 0xf8(%rsp)
movq 0xf8(%rsp), %rax
movsd (%rax), %xmm0
cvtsi2sdl 0x1e0(%rsp), %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0x58(%rsp)
movslq 0x128(%rsp), %rsi
movl %esi, %eax
incl %eax
movl %eax, 0x128(%rsp)
leaq 0x1700(%rsp), %rdi
callq 0x6ba0
movsd 0x58(%rsp), %xmm0
movsd (%rax), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0xf0(%rsp)
movsd 0xf0(%rsp), %xmm0
movq 0x18f53(%rip), %rdi     # 0x1dff0
callq 0x2290
movq %rax, 0x60(%rsp)
jmp 0x50a9
movq 0x60(%rsp), %rdi
movq 0x18f13(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x50bc
movsd 0xf0(%rsp), %xmm0
movaps 0xffb4(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
ucomisd 0x120(%rsp), %xmm0
jbe 0x50f8
movsd 0xf0(%rsp), %xmm0
movaps 0xff95(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
movsd %xmm0, 0x120(%rsp)
movsd 0xf0(%rsp), %xmm0
movaps 0xff78(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
addsd 0x118(%rsp), %xmm0
movsd %xmm0, 0x118(%rsp)
leaq 0x108(%rsp), %rdi
callq 0x7460
jmp 0x5004
movsd 0x120(%rsp), %xmm0
movsd 0xfedf(%rip), %xmm1     # 0x15020
ucomisd %xmm1, %xmm0
jbe 0x515e
movq 0x18ea2(%rip), %rdi     # 0x1dff0
leaq 0x1034d(%rip), %rsi     # 0x154a2
callq 0x2180
jmp 0x515c
jmp 0x515e
movq 0x18e8b(%rip), %rdi     # 0x1dff0
leaq 0x1032d(%rip), %rsi     # 0x15499
callq 0x2180
movq %rax, 0x50(%rsp)
jmp 0x5178
movq 0x50(%rsp), %rdi
movsd 0x120(%rsp), %xmm0
callq 0x2290
movq %rax, 0x48(%rsp)
jmp 0x5192
movq 0x48(%rsp), %rdi
movq 0x18e2a(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x51a5
movsd 0x118(%rsp), %xmm0
movsd 0xfe82(%rip), %xmm1     # 0x15038
divsd %xmm1, %xmm0
movsd %xmm0, 0x118(%rsp)
movsd 0x118(%rsp), %xmm0
movsd 0xfe5c(%rip), %xmm1     # 0x15030
ucomisd %xmm1, %xmm0
jbe 0x51f1
movq 0x18e0f(%rip), %rdi     # 0x1dff0
leaq 0x102b9(%rip), %rsi     # 0x154a1
callq 0x2180
jmp 0x51ef
jmp 0x51f1
movq 0x18df8(%rip), %rdi     # 0x1dff0
leaq 0x102af(%rip), %rsi     # 0x154ae
callq 0x2180
movq %rax, 0x40(%rsp)
jmp 0x520b
movq 0x40(%rsp), %rdi
movsd 0x118(%rsp), %xmm0
callq 0x2290
movq %rax, 0x38(%rsp)
jmp 0x5225
movq 0x38(%rsp), %rdi
movq 0x18d97(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x5238
movq 0x18db1(%rip), %rdi     # 0x1dff0
leaq 0x10270(%rip), %rsi     # 0x154b6
callq 0x2180
jmp 0x524d
movl $0x0, 0x128(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0xe8(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0xe0(%rsp)
leaq 0x1f0(%rsp), %rax
movq %rax, 0xd8(%rsp)
movq 0xd8(%rsp), %rdi
callq 0x73b0
movq %rax, 0xd0(%rsp)
movq 0xd8(%rsp), %rdi
callq 0x73e0
movq %rax, 0xc8(%rsp)
leaq 0xd0(%rsp), %rdi
leaq 0xc8(%rsp), %rsi
callq 0x7410
testb $0x1, %al
jne 0x52c8
jmp 0x53d6
leaq 0xd0(%rsp), %rdi
callq 0x7450
movq %rax, 0xc0(%rsp)
movq 0xc0(%rsp), %rax
movsd (%rax), %xmm0
cvtsi2sdl 0x1e0(%rsp), %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0x28(%rsp)
movslq 0x128(%rsp), %rsi
movl %esi, %eax
incl %eax
movl %eax, 0x128(%rsp)
leaq 0x1660(%rsp), %rdi
callq 0x6ba0
movsd 0x28(%rsp), %xmm0
movsd (%rax), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, 0xb8(%rsp)
movsd 0xb8(%rsp), %xmm0
movq 0x18cad(%rip), %rdi     # 0x1dff0
callq 0x2290
movq %rax, 0x30(%rsp)
jmp 0x534f
movq 0x30(%rsp), %rdi
movq 0x18c6d(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x5362
movsd 0xb8(%rsp), %xmm0
movaps 0xfd0e(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
ucomisd 0xe0(%rsp), %xmm0
jbe 0x539e
movsd 0xb8(%rsp), %xmm0
movaps 0xfcef(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
movsd %xmm0, 0xe0(%rsp)
movsd 0xb8(%rsp), %xmm0
movaps 0xfcd2(%rip), %xmm1     # 0x15080
pand %xmm1, %xmm0
addsd 0xe8(%rsp), %xmm0
movsd %xmm0, 0xe8(%rsp)
leaq 0xd0(%rsp), %rdi
callq 0x7460
jmp 0x52aa
movsd 0xe0(%rsp), %xmm0
movsd 0xfc39(%rip), %xmm1     # 0x15020
ucomisd %xmm1, %xmm0
jbe 0x5404
movq 0x18bfc(%rip), %rdi     # 0x1dff0
leaq 0x100a7(%rip), %rsi     # 0x154a2
callq 0x2180
jmp 0x5402
jmp 0x5404
movq 0x18be5(%rip), %rdi     # 0x1dff0
leaq 0x100b1(%rip), %rsi     # 0x154c3
callq 0x2180
movq %rax, 0x20(%rsp)
jmp 0x541e
movq 0x20(%rsp), %rdi
movsd 0xe0(%rsp), %xmm0
callq 0x2290
movq %rax, 0x18(%rsp)
jmp 0x5438
movq 0x18(%rsp), %rdi
movq 0x18b84(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x544b
movsd 0xe8(%rsp), %xmm0
movsd 0xfbdc(%rip), %xmm1     # 0x15038
divsd %xmm1, %xmm0
movsd %xmm0, 0xe8(%rsp)
movsd 0xe8(%rsp), %xmm0
movsd 0xfbb6(%rip), %xmm1     # 0x15030
ucomisd %xmm1, %xmm0
jbe 0x5497
movq 0x18b69(%rip), %rdi     # 0x1dff0
leaq 0x10013(%rip), %rsi     # 0x154a1
callq 0x2180
jmp 0x5495
jmp 0x5497
movq 0x18b52(%rip), %rdi     # 0x1dff0
leaq 0x10028(%rip), %rsi     # 0x154cd
callq 0x2180
movq %rax, 0x10(%rsp)
jmp 0x54b1
movq 0x10(%rsp), %rdi
movsd 0xe8(%rsp), %xmm0
callq 0x2290
movq %rax, 0x8(%rsp)
jmp 0x54cb
movq 0x8(%rsp), %rdi
movq 0x18af1(%rip), %rsi     # 0x1dfc8
callq 0x21a0
jmp 0x54de
leaq 0x1f0(%rsp), %rdi
callq 0x6740
leaq 0x218(%rsp), %rdi
callq 0x6740
leaq 0x1660(%rsp), %rdi
callq 0x6740
leaq 0x1700(%rsp), %rdi
callq 0x6740
addq $0x1718, %rsp           # imm = 0x1718
retq
leaq 0x1f0(%rsp), %rdi
callq 0x6740
leaq 0x218(%rsp), %rdi
callq 0x6740
leaq 0x1660(%rsp), %rdi
callq 0x6740
leaq 0x1700(%rsp), %rdi
callq 0x6740
movq 0x1680(%rsp), %rdi
callq 0x2260
nopl (%rax,%rax)