// File: epfml[P]Bi-Sent2Vec/src/productquantizer.cc
// Function: fasttext::ProductQuantizer::MStep(float const*, float*, unsigned char const*, int, int)
void ProductQuantizer::MStep(const real* x0, real* centroids,
                             const uint8_t* codes,
                             int32_t d, int32_t n) {
  std::vector<int32_t> nelts(ksub_, 0);
  memset(centroids, 0, sizeof(real) * d * ksub_);
  const real* x = x0;
  for (auto i = 0; i < n; i++) {
    auto k = codes[i];
    real* c = centroids + k * d;
    for (auto j = 0; j < d; j++) {
      c[j] += x[j];
    }
    nelts[k]++;
    x += d;
  }

  real* c = centroids;
  for (auto k = 0; k < ksub_; k++) {
    real z = (real) nelts[k];
    if (z != 0) {
      for (auto j = 0; j < d; j++) {
        c[j] /= z;
      }
    }
    c += d;
  }

  std::uniform_real_distribution<> runiform(0,1);
  for (auto k = 0; k < ksub_; k++) {
    if (nelts[k] == 0) {
      int32_t m = 0;
      while (runiform(rng) * (n - ksub_) >= nelts[m] - 1) {
        m = (m + 1) % ksub_;
      }
      memcpy(centroids + k * d, centroids + m * d, sizeof(real) * d);
      for (auto j = 0; j < d; j++) {
        int32_t sign = (j % 2) * 2 - 1;
        centroids[k * d + j] += sign * eps_;
        centroids[m * d + j] -= sign * eps_;
      }
      nelts[k] = nelts[m] / 2;
      nelts[m] -= nelts[k];
    }
  }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xe0, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movl %r8d, -0x24(%rbp)
movl %r9d, -0x28(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0xc8(%rbp)
movslq 0x4(%rax), %rax
movq %rax, -0xc0(%rbp)
movl $0x0, -0x44(%rbp)
leaq -0x45(%rbp), %rdi
movq %rdi, -0xb8(%rbp)
callq 0xe090
movq -0xc0(%rbp), %rsi
movq -0xb8(%rbp), %rcx
leaq -0x40(%rbp), %rdi
leaq -0x44(%rbp), %rdx
callq 0xe0b0
jmp 0x1c35d
leaq -0x45(%rbp), %rdi
callq 0xe130
movq -0xc8(%rbp), %rax
movq -0x18(%rbp), %rdi
movslq -0x24(%rbp), %rdx
shlq $0x2, %rdx
movslq 0x4(%rax), %rax
imulq %rax, %rdx
xorl %esi, %esi
callq 0x5260
movq -0x10(%rbp), %rax
movq %rax, -0x60(%rbp)
movl $0x0, -0x64(%rbp)
movl -0x64(%rbp), %eax
cmpl -0x28(%rbp), %eax
jge 0x1c457
movq -0x20(%rbp), %rax
movslq -0x64(%rbp), %rcx
movb (%rax,%rcx), %al
movb %al, -0x65(%rbp)
movq -0x18(%rbp), %rax
movzbl -0x65(%rbp), %ecx
imull -0x24(%rbp), %ecx
movslq %ecx, %rcx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, -0x70(%rbp)
movl $0x0, -0x74(%rbp)
movl -0x74(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x1c41e
movq -0x60(%rbp), %rax
movslq -0x74(%rbp), %rcx
movss (%rax,%rcx,4), %xmm0
movq -0x70(%rbp), %rax
movslq -0x74(%rbp), %rcx
addss (%rax,%rcx,4), %xmm0
movss %xmm0, (%rax,%rcx,4)
movl -0x74(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x74(%rbp)
jmp 0x1c3d2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x45(%rbp), %rdi
callq 0xe130
jmp 0x1c7bb
movzbl -0x65(%rbp), %eax
movl %eax, %esi
leaq -0x40(%rbp), %rdi
callq 0xe240
movl (%rax), %ecx
addl $0x1, %ecx
movl %ecx, (%rax)
movl -0x24(%rbp), %ecx
movq -0x60(%rbp), %rax
movslq %ecx, %rcx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, -0x60(%rbp)
movl -0x64(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x64(%rbp)
jmp 0x1c397
movq -0x18(%rbp), %rax
movq %rax, -0x80(%rbp)
movl $0x0, -0x84(%rbp)
movq -0xc8(%rbp), %rcx
movl -0x84(%rbp), %eax
cmpl 0x4(%rcx), %eax
jge 0x1c521
movslq -0x84(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
cvtsi2ssl (%rax), %xmm0
movss %xmm0, -0x88(%rbp)
movss -0x88(%rbp), %xmm0
xorps %xmm1, %xmm1
ucomiss %xmm1, %xmm0
jne 0x1c4af
jp 0x1c4af
jmp 0x1c4f8
movl $0x0, -0x8c(%rbp)
movl -0x8c(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x1c4f6
movss -0x88(%rbp), %xmm1
movq -0x80(%rbp), %rax
movslq -0x8c(%rbp), %rcx
movss (%rax,%rcx,4), %xmm0
divss %xmm1, %xmm0
movss %xmm0, (%rax,%rcx,4)
movl -0x8c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8c(%rbp)
jmp 0x1c4b9
jmp 0x1c4f8
movl -0x24(%rbp), %ecx
movq -0x80(%rbp), %rax
movslq %ecx, %rcx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, -0x80(%rbp)
movl -0x84(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x84(%rbp)
jmp 0x1c469
leaq -0xa0(%rbp), %rdi
xorps %xmm0, %xmm0
movsd 0x26385(%rip), %xmm1    # 0x428b8
callq 0xf070
jmp 0x1c53a
movl $0x0, -0xa4(%rbp)
movq -0xc8(%rbp), %rcx
movl -0xa4(%rbp), %eax
cmpl 0x4(%rcx), %eax
jge 0x1c7a9
movslq -0xa4(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
cmpl $0x0, (%rax)
jne 0x1c793
movl $0x0, -0xa8(%rbp)
movq -0xc8(%rbp), %rsi
addq $0x48, %rsi
leaq -0xa0(%rbp), %rdi
callq 0xf170
movsd %xmm0, -0xd0(%rbp)
jmp 0x1c59e
movsd -0xd0(%rbp), %xmm0
movq -0xc8(%rbp), %rcx
movl -0x28(%rbp), %eax
subl 0x4(%rcx), %eax
cvtsi2sd %eax, %xmm1
mulsd %xmm1, %xmm0
movsd %xmm0, -0xd8(%rbp)
movslq -0xa8(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
movsd -0xd8(%rbp), %xmm0
movl (%rax), %eax
subl $0x1, %eax
cvtsi2sd %eax, %xmm1
ucomisd %xmm1, %xmm0
jb 0x1c623
movq -0xc8(%rbp), %rcx
movl -0xa8(%rbp), %eax
addl $0x1, %eax
cltd
idivl 0x4(%rcx)
movl %edx, -0xa8(%rbp)
jmp 0x1c57d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x40(%rbp), %rdi
callq 0xe530
jmp 0x1c7bb
movq -0x18(%rbp), %rdi
movl -0xa4(%rbp), %eax
imull -0x24(%rbp), %eax
cltq
shlq $0x2, %rax
addq %rax, %rdi
movq -0x18(%rbp), %rsi
movl -0xa8(%rbp), %eax
imull -0x24(%rbp), %eax
cltq
shlq $0x2, %rax
addq %rax, %rsi
movslq -0x24(%rbp), %rdx
shlq $0x2, %rdx
callq 0x5390
movl $0x0, -0xac(%rbp)
movl -0xac(%rbp), %eax
cmpl -0x24(%rbp), %eax
jge 0x1c727
movl -0xac(%rbp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movq -0xc8(%rbp), %rax
movl %edx, %ecx
shll %ecx
subl $0x1, %ecx
movl %ecx, -0xb0(%rbp)
cvtsi2ssl -0xb0(%rbp), %xmm0
movss 0x18(%rax), %xmm2
movq -0x18(%rbp), %rcx
movl -0xa4(%rbp), %edx
imull -0x24(%rbp), %edx
addl -0xac(%rbp), %edx
movslq %edx, %rdx
movss (%rcx,%rdx,4), %xmm1
mulss %xmm2, %xmm0
addss %xmm1, %xmm0
movss %xmm0, (%rcx,%rdx,4)
cvtsi2ssl -0xb0(%rbp), %xmm0
movss 0x18(%rax), %xmm2
movq -0x18(%rbp), %rax
movl -0xa8(%rbp), %ecx
imull -0x24(%rbp), %ecx
addl -0xac(%rbp), %ecx
movslq %ecx, %rcx
movss (%rax,%rcx,4), %xmm1
movd %xmm0, %edx
xorl $0x80000000, %edx       # imm = 0x80000000
movd %edx, %xmm0
mulss %xmm2, %xmm0
addss %xmm1, %xmm0
movss %xmm0, (%rax,%rcx,4)
movl -0xac(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xac(%rbp)
jmp 0x1c668
movslq -0xa8(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
movl (%rax), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, -0xe0(%rbp)
movslq -0xa4(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
movl -0xe0(%rbp), %ecx
movl %ecx, (%rax)
movslq -0xa4(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
movl (%rax), %eax
movl %eax, -0xdc(%rbp)
movslq -0xa8(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xe240
movl -0xdc(%rbp), %edx
movl (%rax), %ecx
subl %edx, %ecx
movl %ecx, (%rax)
jmp 0x1c795
movl -0xa4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa4(%rbp)
jmp 0x1c544
leaq -0x40(%rbp), %rdi
callq 0xe530
addq $0xe0, %rsp
popq %rbp
retq
movq -0x50(%rbp), %rdi
callq 0x57a0
nopw %cs:(%rax,%rax)