// File: phillip-h[P]fractal-gen/lodepng.cpp
// Function: lodepng_huffman_code_lengths(unsigned int*, unsigned int const*, unsigned long, unsigned int)
unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
                                      size_t numcodes, unsigned maxbitlen)
{
  unsigned error = 0;
  unsigned i;
  size_t numpresent = 0; /*number of symbols with non-zero frequency*/
  BPMNode* leaves; /*the symbols, only those with > 0 frequency*/

  if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
  if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/

  leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves));
  if(!leaves) return 83; /*alloc fail*/

  for(i = 0; i != numcodes; ++i)
  {
    if(frequencies[i] > 0)
    {
      leaves[numpresent].weight = (int)frequencies[i];
      leaves[numpresent].index = i;
      ++numpresent;
    }
  }

  for(i = 0; i != numcodes; ++i) lengths[i] = 0;

  /*ensure at least two present symbols. There should be at least one symbol
  according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To
  make these work as well ensure there are at least two symbols. The
  Package-Merge code below also doesn't work correctly if there's only one
  symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/
  if(numpresent == 0)
  {
    lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/
  }
  else if(numpresent == 1)
  {
    lengths[leaves[0].index] = 1;
    lengths[leaves[0].index == 0 ? 1 : 0] = 1;
  }
  else
  {
    BPMLists lists;
    BPMNode* node;

    bpmnode_sort(leaves, numpresent);

    lists.listsize = maxbitlen;
    lists.memsize = 2 * maxbitlen * (maxbitlen + 1);
    lists.nextfree = 0;
    lists.numfree = lists.memsize;
    lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory));
    lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*));
    lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
    lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
    if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/

    if(!error)
    {
      for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i];

      bpmnode_create(&lists, leaves[0].weight, 1, 0);
      bpmnode_create(&lists, leaves[1].weight, 2, 0);

      for(i = 0; i != lists.listsize; ++i)
      {
        lists.chains0[i] = &lists.memory[0];
        lists.chains1[i] = &lists.memory[1];
      }

      /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/
      for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i);

      for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail)
      {
        for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index];
      }
    }

    lodepng_free(lists.memory);
    lodepng_free(lists.freelist);
    lodepng_free(lists.chains0);
    lodepng_free(lists.chains1);
  }

  lodepng_free(leaves);
  return error;
}

// Assembly:
subq $0x88, %rsp
movq %rdi, 0x78(%rsp)
movq %rsi, 0x70(%rsp)
movq %rdx, 0x68(%rsp)
movl %ecx, 0x64(%rsp)
movl $0x0, 0x60(%rsp)
movq $0x0, 0x50(%rsp)
cmpq $0x0, 0x68(%rsp)
jne 0x6e23
movl $0x50, 0x84(%rsp)
jmp 0x71ec
movl 0x64(%rsp), %ecx
movl $0x1, %eax
shll %cl, %eax
movl %eax, %eax
cmpq 0x68(%rsp), %rax
jae 0x6e47
movl $0x50, 0x84(%rsp)
jmp 0x71ec
imulq $0x18, 0x68(%rsp), %rdi
callq 0x6cd0
movq %rax, 0x48(%rsp)
cmpq $0x0, 0x48(%rsp)
jne 0x6e6f
movl $0x53, 0x84(%rsp)
jmp 0x71ec
movl $0x0, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
cmpq 0x68(%rsp), %rax
je 0x6edf
movq 0x70(%rsp), %rax
movl 0x5c(%rsp), %ecx
cmpl $0x0, (%rax,%rcx,4)
jbe 0x6ed0
movq 0x70(%rsp), %rax
movl 0x5c(%rsp), %ecx
movl (%rax,%rcx,4), %ecx
movq 0x48(%rsp), %rax
imulq $0x18, 0x50(%rsp), %rdx
addq %rdx, %rax
movl %ecx, (%rax)
movl 0x5c(%rsp), %ecx
movq 0x48(%rsp), %rax
imulq $0x18, 0x50(%rsp), %rdx
addq %rdx, %rax
movl %ecx, 0x4(%rax)
movq 0x50(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x50(%rsp)
jmp 0x6ed2
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x6e77
movl $0x0, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
cmpq 0x68(%rsp), %rax
je 0x6f0f
movq 0x78(%rsp), %rax
movl 0x5c(%rsp), %ecx
movl $0x0, (%rax,%rcx,4)
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x6ee7
cmpq $0x0, 0x50(%rsp)
jne 0x6f33
movq 0x78(%rsp), %rax
movl $0x1, 0x4(%rax)
movq 0x78(%rsp), %rax
movl $0x1, (%rax)
jmp 0x71d7
cmpq $0x1, 0x50(%rsp)
jne 0x6f78
movq 0x78(%rsp), %rax
movq 0x48(%rsp), %rcx
movl 0x4(%rcx), %ecx
movl $0x1, (%rax,%rcx,4)
movq 0x78(%rsp), %rax
movq 0x48(%rsp), %rcx
movl 0x4(%rcx), %esi
xorl %ecx, %ecx
movl $0x1, %edx
cmpl $0x0, %esi
cmovel %edx, %ecx
movslq %ecx, %rcx
movl $0x1, (%rax,%rcx,4)
jmp 0x71d5
movq 0x48(%rsp), %rdi
movq 0x50(%rsp), %rsi
callq 0x7200
movl 0x64(%rsp), %eax
movl %eax, 0x30(%rsp)
movl 0x64(%rsp), %eax
shll %eax
movl 0x64(%rsp), %ecx
addl $0x1, %ecx
imull %ecx, %eax
movl %eax, 0x10(%rsp)
movl $0x0, 0x24(%rsp)
movl 0x10(%rsp), %eax
movl %eax, 0x20(%rsp)
movl 0x10(%rsp), %eax
imulq $0x18, %rax, %rdi
callq 0x6cd0
movq %rax, 0x18(%rsp)
movl 0x10(%rsp), %eax
movl %eax, %edi
shlq $0x3, %rdi
callq 0x6cd0
movq %rax, 0x28(%rsp)
movl 0x30(%rsp), %eax
movl %eax, %edi
shlq $0x3, %rdi
callq 0x6cd0
movq %rax, 0x38(%rsp)
movl 0x30(%rsp), %eax
movl %eax, %edi
shlq $0x3, %rdi
callq 0x6cd0
movq %rax, 0x40(%rsp)
cmpq $0x0, 0x18(%rsp)
je 0x7021
cmpq $0x0, 0x28(%rsp)
je 0x7021
cmpq $0x0, 0x38(%rsp)
je 0x7021
cmpq $0x0, 0x40(%rsp)
jne 0x7029
movl $0x53, 0x60(%rsp)
cmpl $0x0, 0x60(%rsp)
jne 0x71ad
movl $0x0, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
cmpl 0x10(%rsp), %eax
je 0x7070
movq 0x18(%rsp), %rdx
movl 0x5c(%rsp), %eax
imulq $0x18, %rax, %rax
addq %rax, %rdx
movq 0x28(%rsp), %rax
movl 0x5c(%rsp), %ecx
movq %rdx, (%rax,%rcx,8)
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x703c
movq 0x48(%rsp), %rax
movl (%rax), %esi
leaq 0x10(%rsp), %rdi
movl $0x1, %edx
xorl %eax, %eax
movl %eax, %ecx
callq 0x74b0
movq 0x48(%rsp), %rax
movl 0x18(%rax), %esi
leaq 0x10(%rsp), %rdi
movl $0x2, %edx
xorl %eax, %eax
movl %eax, %ecx
callq 0x74b0
movl $0x0, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
cmpl 0x30(%rsp), %eax
je 0x70ec
movq 0x18(%rsp), %rdx
movq 0x38(%rsp), %rax
movl 0x5c(%rsp), %ecx
movq %rdx, (%rax,%rcx,8)
movq 0x18(%rsp), %rdx
addq $0x18, %rdx
movq 0x40(%rsp), %rax
movl 0x5c(%rsp), %ecx
movq %rdx, (%rax,%rcx,8)
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x70ad
movl $0x2, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
movq 0x50(%rsp), %rcx
shlq %rcx
subq $0x2, %rcx
cmpq %rcx, %rax
je 0x7136
movq 0x48(%rsp), %rsi
movq 0x50(%rsp), %rdx
movl 0x64(%rsp), %ecx
subl $0x1, %ecx
movl 0x5c(%rsp), %r8d
leaq 0x10(%rsp), %rdi
callq 0x7690
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x70f4
movq 0x40(%rsp), %rax
movl 0x64(%rsp), %ecx
subl $0x1, %ecx
movl %ecx, %ecx
movq (%rax,%rcx,8), %rax
movq %rax, 0x8(%rsp)
cmpq $0x0, 0x8(%rsp)
je 0x71ab
movl $0x0, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
movq 0x8(%rsp), %rcx
cmpl 0x4(%rcx), %eax
je 0x7199
movq 0x78(%rsp), %rax
movq 0x48(%rsp), %rcx
movl 0x5c(%rsp), %edx
imulq $0x18, %rdx, %rdx
addq %rdx, %rcx
movl 0x4(%rcx), %ecx
movl (%rax,%rcx,4), %edx
addl $0x1, %edx
movl %edx, (%rax,%rcx,4)
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x715d
jmp 0x719b
movq 0x8(%rsp), %rax
movq 0x8(%rax), %rax
movq %rax, 0x8(%rsp)
jmp 0x714d
jmp 0x71ad
movq 0x18(%rsp), %rdi
callq 0x78b0
movq 0x28(%rsp), %rdi
callq 0x78b0
movq 0x38(%rsp), %rdi
callq 0x78b0
movq 0x40(%rsp), %rdi
callq 0x78b0
jmp 0x71d7
movq 0x48(%rsp), %rdi
callq 0x78b0
movl 0x60(%rsp), %eax
movl %eax, 0x84(%rsp)
movl 0x84(%rsp), %eax
addq $0x88, %rsp
retq
nopl (%rax,%rax)