// File: logicx24[P]RayTracer/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 i, j;
  size_t sum = 0, numpresent = 0;
  unsigned error = 0;
  Coin* coins; /*the coins of the currently calculated row*/
  Coin* prev_row; /*the previous row of coins*/
  size_t numcoins;
  size_t coinmem;

  if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/

  for(i = 0; i != numcodes; ++i)
  {
    if(frequencies[i] > 0)
    {
      ++numpresent;
      sum += frequencies[i];
    }
  }

  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. To 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)
  {
    for(i = 0; i != numcodes; ++i)
    {
      if(frequencies[i])
      {
        lengths[i] = 1;
        lengths[i == 0 ? 1 : 0] = 1;
        break;
      }
    }
  }
  else
  {
    /*Package-Merge algorithm represented by coin collector's problem
    For every symbol, maxbitlen coins will be created*/

    coinmem = numpresent * 2; /*max amount of coins needed with the current algo*/
    coins = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem);
    prev_row = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem);
    if(!coins || !prev_row)
    {
      lodepng_free(coins);
      lodepng_free(prev_row);
      return 83; /*alloc fail*/
    }
    init_coins(coins, coinmem);
    init_coins(prev_row, coinmem);

    /*first row, lowest denominator*/
    error = append_symbol_coins(coins, frequencies, numcodes, sum);
    numcoins = numpresent;
    qsort(coins, numcoins, sizeof(Coin), coin_compare);
    if(!error)
    {
      unsigned numprev = 0;
      for(j = 1; j <= maxbitlen && !error; ++j) /*each of the remaining rows*/
      {
        unsigned tempnum;
        Coin* tempcoins;
        /*swap prev_row and coins, and their amounts*/
        tempcoins = prev_row; prev_row = coins; coins = tempcoins;
        tempnum = numprev; numprev = numcoins; numcoins = tempnum;

        cleanup_coins(coins, numcoins);
        init_coins(coins, numcoins);

        numcoins = 0;

        /*fill in the merged coins of the previous row*/
        for(i = 0; i + 1 < numprev; i += 2)
        {
          /*merge prev_row[i] and prev_row[i + 1] into new coin*/
          Coin* coin = &coins[numcoins++];
          coin_copy(coin, &prev_row[i]);
          add_coins(coin, &prev_row[i + 1]);
        }
        /*fill in all the original symbols again*/
        if(j < maxbitlen)
        {
          error = append_symbol_coins(coins + numcoins, frequencies, numcodes, sum);
          numcoins += numpresent;
        }
        qsort(coins, numcoins, sizeof(Coin), coin_compare);
      }
    }

    if(!error)
    {
      /*calculate the lengths of each symbol, as the amount of times a coin of each symbol is used*/
      for(i = 0; i + 1 < numpresent; ++i)
      {
        Coin* coin = &coins[i];
        for(j = 0; j < coin->symbols.size; ++j) ++lengths[coin->symbols.data[j]];
      }
    }

    cleanup_coins(coins, coinmem);
    lodepng_free(coins);
    cleanup_coins(prev_row, coinmem);
    lodepng_free(prev_row);
  }

  return error;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x90, %rsp
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
movl %ecx, -0x24(%rbp)
movq $0x0, -0x38(%rbp)
movq $0x0, -0x40(%rbp)
movl $0x0, -0x44(%rbp)
cmpq $0x0, -0x20(%rbp)
jne 0x12054
movl $0x50, -0x4(%rbp)
jmp 0x1240e
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpq -0x20(%rbp), %rax
je 0x1209c
movq -0x18(%rbp), %rax
movl -0x28(%rbp), %ecx
cmpl $0x0, (%rax,%rcx,4)
jbe 0x1208f
movq -0x40(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x40(%rbp)
movq -0x18(%rbp), %rax
movl -0x28(%rbp), %ecx
movl (%rax,%rcx,4), %eax
addq -0x38(%rbp), %rax
movq %rax, -0x38(%rbp)
jmp 0x12091
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x1205b
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpq -0x20(%rbp), %rax
je 0x120c5
movq -0x10(%rbp), %rax
movl -0x28(%rbp), %ecx
movl $0x0, (%rax,%rcx,4)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x120a3
cmpq $0x0, -0x40(%rbp)
jne 0x120e6
movq -0x10(%rbp), %rax
movl $0x1, 0x4(%rax)
movq -0x10(%rbp), %rax
movl $0x1, (%rax)
jmp 0x12408
cmpq $0x1, -0x40(%rbp)
jne 0x1214a
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpq -0x20(%rbp), %rax
je 0x12145
movq -0x18(%rbp), %rax
movl -0x28(%rbp), %ecx
cmpl $0x0, (%rax,%rcx,4)
je 0x12138
movq -0x10(%rbp), %rax
movl -0x28(%rbp), %ecx
movl $0x1, (%rax,%rcx,4)
movq -0x10(%rbp), %rax
movl -0x28(%rbp), %esi
xorl %ecx, %ecx
movl $0x1, %edx
cmpl $0x0, %esi
cmovel %edx, %ecx
movslq %ecx, %rcx
movl $0x1, (%rax,%rcx,4)
jmp 0x12145
jmp 0x1213a
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x120f4
jmp 0x12406
movq -0x40(%rbp), %rax
shlq %rax
movq %rax, -0x68(%rbp)
movq -0x68(%rbp), %rdi
shlq $0x5, %rdi
callq 0x11f80
movq %rax, -0x50(%rbp)
movq -0x68(%rbp), %rdi
shlq $0x5, %rdi
callq 0x11f80
movq %rax, -0x58(%rbp)
cmpq $0x0, -0x50(%rbp)
je 0x12185
cmpq $0x0, -0x58(%rbp)
jne 0x121a3
movq -0x50(%rbp), %rdi
callq 0x12420
movq -0x58(%rbp), %rdi
callq 0x12420
movl $0x53, -0x4(%rbp)
jmp 0x1240e
movq -0x50(%rbp), %rdi
movq -0x68(%rbp), %rsi
callq 0x12440
movq -0x58(%rbp), %rdi
movq -0x68(%rbp), %rsi
callq 0x12440
movq -0x50(%rbp), %rdi
movq -0x18(%rbp), %rsi
movq -0x20(%rbp), %rax
movl %eax, %edx
movq -0x38(%rbp), %rcx
callq 0x12490
movl %eax, -0x44(%rbp)
movq -0x40(%rbp), %rax
movq %rax, -0x60(%rbp)
movq -0x50(%rbp), %rdi
movq -0x60(%rbp), %rsi
movl $0x20, %edx
leaq 0x38d(%rip), %rcx       # 0x12580
callq 0x3560
cmpl $0x0, -0x44(%rbp)
jne 0x1235c
movl $0x0, -0x6c(%rbp)
movl $0x1, -0x2c(%rbp)
movl -0x2c(%rbp), %ecx
xorl %eax, %eax
cmpl -0x24(%rbp), %ecx
movb %al, -0x89(%rbp)
ja 0x1222f
cmpl $0x0, -0x44(%rbp)
setne %al
xorb $-0x1, %al
movb %al, -0x89(%rbp)
movb -0x89(%rbp), %al
testb $0x1, %al
jne 0x1223e
jmp 0x1235a
movq -0x58(%rbp), %rax
movq %rax, -0x78(%rbp)
movq -0x50(%rbp), %rax
movq %rax, -0x58(%rbp)
movq -0x78(%rbp), %rax
movq %rax, -0x50(%rbp)
movl -0x6c(%rbp), %eax
movl %eax, -0x70(%rbp)
movq -0x60(%rbp), %rax
movl %eax, -0x6c(%rbp)
movl -0x70(%rbp), %eax
movq %rax, -0x60(%rbp)
movq -0x50(%rbp), %rdi
movq -0x60(%rbp), %rsi
callq 0x125e0
movq -0x50(%rbp), %rdi
movq -0x60(%rbp), %rsi
callq 0x12440
movq $0x0, -0x60(%rbp)
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
addl $0x1, %eax
cmpl -0x6c(%rbp), %eax
jae 0x122fa
movq -0x50(%rbp), %rax
movq -0x60(%rbp), %rcx
movq %rcx, %rdx
addq $0x1, %rdx
movq %rdx, -0x60(%rbp)
shlq $0x5, %rcx
addq %rcx, %rax
movq %rax, -0x80(%rbp)
movq -0x80(%rbp), %rdi
movq -0x58(%rbp), %rsi
movl -0x28(%rbp), %eax
shlq $0x5, %rax
addq %rax, %rsi
callq 0x12630
movq -0x80(%rbp), %rdi
movq -0x58(%rbp), %rsi
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, %eax
shlq $0x5, %rax
addq %rax, %rsi
callq 0x12670
movl -0x28(%rbp), %eax
addl $0x2, %eax
movl %eax, -0x28(%rbp)
jmp 0x12293
movl -0x2c(%rbp), %eax
cmpl -0x24(%rbp), %eax
jae 0x12333
movq -0x50(%rbp), %rdi
movq -0x60(%rbp), %rax
shlq $0x5, %rax
addq %rax, %rdi
movq -0x18(%rbp), %rsi
movq -0x20(%rbp), %rax
movl %eax, %edx
movq -0x38(%rbp), %rcx
callq 0x12490
movl %eax, -0x44(%rbp)
movq -0x40(%rbp), %rax
addq -0x60(%rbp), %rax
movq %rax, -0x60(%rbp)
movq -0x50(%rbp), %rdi
movq -0x60(%rbp), %rsi
movl $0x20, %edx
leaq 0x239(%rip), %rcx       # 0x12580
callq 0x3560
movl -0x2c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x2c(%rbp)
jmp 0x12210
jmp 0x1235c
cmpl $0x0, -0x44(%rbp)
jne 0x123da
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, %eax
cmpq -0x40(%rbp), %rax
jae 0x123d8
movq -0x50(%rbp), %rax
movl -0x28(%rbp), %ecx
shlq $0x5, %rcx
addq %rcx, %rax
movq %rax, -0x88(%rbp)
movl $0x0, -0x2c(%rbp)
movl -0x2c(%rbp), %eax
movq -0x88(%rbp), %rcx
cmpq 0x8(%rcx), %rax
jae 0x123cb
movq -0x10(%rbp), %rax
movq -0x88(%rbp), %rcx
movq (%rcx), %rcx
movl -0x2c(%rbp), %edx
movl (%rcx,%rdx,4), %ecx
movl (%rax,%rcx,4), %edx
addl $0x1, %edx
movl %edx, (%rax,%rcx,4)
movl -0x2c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x2c(%rbp)
jmp 0x12393
jmp 0x123cd
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x12369
jmp 0x123da
movq -0x50(%rbp), %rdi
movq -0x68(%rbp), %rsi
callq 0x125e0
movq -0x50(%rbp), %rdi
callq 0x12420
movq -0x58(%rbp), %rdi
movq -0x68(%rbp), %rsi
callq 0x125e0
movq -0x58(%rbp), %rdi
callq 0x12420
jmp 0x12408
movl -0x44(%rbp), %eax
movl %eax, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0x90, %rsp
popq %rbp
retq
nopw (%rax,%rax)