// File: lemire[P]backward_multiplication/include/backwardmultiply.h
// Function: backwardmultiply::multiplication_backward_limit(unsigned long, unsigned long const*, unsigned long, unsigned long*, unsigned long)
size_t multiplication_backward_limit(uint64_t w, const uint64_t *b, size_t n,
                                     uint64_t *output, size_t minexactwords) {
  if ((w == 0) || (n == 0)) { // special case
    std::fill(output, output + n + 1, 0);
    return n + 1;
  }

  auto p = full_multiplication(w, b[n - 1]);
  output[n - 1] = p.low;
  output[n] = p.high;
  if (minexactwords <= 1) {
    // check if addition overflows
    if (p.low + w - 1 >= p.low) {
      // we are good at output[n]
      return 1;
    }
  }
  for (size_t i = n - 2; i != SIZE_MAX; i--) {
    p = full_multiplication(w, b[i]);
    output[i] = p.low;
    bool overflow = (output[i + 1] + p.high < output[i + 1]);
    output[i + 1] += p.high;
    for (size_t j = i + 2; overflow; j++) {
      output[j]++;
      overflow = (output[j] == 0);
    }
    if (minexactwords <= n - i) {
      // check if addition overflows
      if (p.low + w - 1 >= p.low) {
        // we are good at output[n], ..., output[i+1], so we have
        // n - (i+1) + 1 = n - i good words
        return n - i;
      }
      if (minexactwords < n - i) {
        // then the only way we could overflow is if the following is true
        if (output[i + 1] != 0xFFFFFFFFFFFFFFFF) {
          // we are good at output[n], ..., output[i+2], so we have
          // n - (i+2) + 1 = n - i - 1 good words

          return n - i - 1;
        }
      }
    }
  }
  return n + 1;
}

// Assembly:
subq $0x78, %rsp
movq %rdi, 0x68(%rsp)
movq %rsi, 0x60(%rsp)
movq %rdx, 0x58(%rsp)
movq %rcx, 0x50(%rsp)
movq %r8, 0x48(%rsp)
cmpq $0x0, 0x68(%rsp)
je 0x15dd
cmpq $0x0, 0x58(%rsp)
jne 0x161c
movq 0x50(%rsp), %rdi
movq 0x50(%rsp), %rsi
movq 0x58(%rsp), %rax
shlq $0x3, %rax
addq %rax, %rsi
addq $0x8, %rsi
movl $0x0, 0x44(%rsp)
leaq 0x44(%rsp), %rdx
callq 0x21e0
movq 0x58(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x70(%rsp)
jmp 0x1830
movq 0x68(%rsp), %rdi
movq 0x60(%rsp), %rax
movq 0x58(%rsp), %rcx
subq $0x1, %rcx
movq (%rax,%rcx,8), %rsi
callq 0x1280
movq %rax, 0x30(%rsp)
movq %rdx, 0x38(%rsp)
movq 0x30(%rsp), %rdx
movq 0x50(%rsp), %rax
movq 0x58(%rsp), %rcx
subq $0x1, %rcx
movq %rdx, (%rax,%rcx,8)
movq 0x38(%rsp), %rdx
movq 0x50(%rsp), %rax
movq 0x58(%rsp), %rcx
movq %rdx, (%rax,%rcx,8)
cmpq $0x1, 0x48(%rsp)
ja 0x1699
movq 0x30(%rsp), %rax
addq 0x68(%rsp), %rax
subq $0x1, %rax
cmpq 0x30(%rsp), %rax
jb 0x1697
movq $0x1, 0x70(%rsp)
jmp 0x1830
jmp 0x1699
movq 0x58(%rsp), %rax
subq $0x2, %rax
movq %rax, 0x28(%rsp)
cmpq $-0x1, 0x28(%rsp)
je 0x1822
movq 0x68(%rsp), %rdi
movq 0x60(%rsp), %rax
movq 0x28(%rsp), %rcx
movq (%rax,%rcx,8), %rsi
callq 0x1280
movq %rax, 0x18(%rsp)
movq %rdx, 0x20(%rsp)
movq 0x18(%rsp), %rax
movq %rax, 0x30(%rsp)
movq 0x20(%rsp), %rax
movq %rax, 0x38(%rsp)
movq 0x30(%rsp), %rdx
movq 0x50(%rsp), %rax
movq 0x28(%rsp), %rcx
movq %rdx, (%rax,%rcx,8)
movq 0x50(%rsp), %rax
movq 0x28(%rsp), %rcx
movq 0x8(%rax,%rcx,8), %rax
addq 0x38(%rsp), %rax
movq 0x50(%rsp), %rcx
movq 0x28(%rsp), %rdx
cmpq 0x8(%rcx,%rdx,8), %rax
setb %al
andb $0x1, %al
movb %al, 0x17(%rsp)
movq 0x38(%rsp), %rdx
movq 0x50(%rsp), %rax
movq 0x28(%rsp), %rcx
addq 0x8(%rax,%rcx,8), %rdx
movq %rdx, 0x8(%rax,%rcx,8)
movq 0x28(%rsp), %rax
addq $0x2, %rax
movq %rax, 0x8(%rsp)
testb $0x1, 0x17(%rsp)
je 0x1794
movq 0x50(%rsp), %rax
movq 0x8(%rsp), %rcx
movq (%rax,%rcx,8), %rdx
addq $0x1, %rdx
movq %rdx, (%rax,%rcx,8)
movq 0x50(%rsp), %rax
movq 0x8(%rsp), %rcx
cmpq $0x0, (%rax,%rcx,8)
sete %al
andb $0x1, %al
movb %al, 0x17(%rsp)
movq 0x8(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x8(%rsp)
jmp 0x174f
movq 0x48(%rsp), %rax
movq 0x58(%rsp), %rcx
subq 0x28(%rsp), %rcx
cmpq %rcx, %rax
ja 0x180d
movq 0x30(%rsp), %rax
addq 0x68(%rsp), %rax
subq $0x1, %rax
cmpq 0x30(%rsp), %rax
jb 0x17ce
movq 0x58(%rsp), %rax
subq 0x28(%rsp), %rax
movq %rax, 0x70(%rsp)
jmp 0x1830
movq 0x48(%rsp), %rax
movq 0x58(%rsp), %rcx
subq 0x28(%rsp), %rcx
cmpq %rcx, %rax
jae 0x180b
movq 0x50(%rsp), %rax
movq 0x28(%rsp), %rcx
cmpq $-0x1, 0x8(%rax,%rcx,8)
je 0x1809
movq 0x58(%rsp), %rax
subq 0x28(%rsp), %rax
subq $0x1, %rax
movq %rax, 0x70(%rsp)
jmp 0x1830
jmp 0x180b
jmp 0x180d
jmp 0x180f
movq 0x28(%rsp), %rax
addq $-0x1, %rax
movq %rax, 0x28(%rsp)
jmp 0x16a7
movq 0x58(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x70(%rsp)
movq 0x70(%rsp), %rax
addq $0x78, %rsp
retq
nopw (%rax,%rax)