// File: tomfunkhouser[P]gaps/pkgs/png/trees.c
// Function: build_tree
local void build_tree(s, desc)
    deflate_state *s;
    tree_desc *desc; /* the tree descriptor */
{
    ct_data *tree         = desc->dyn_tree;
    const ct_data *stree  = desc->stat_desc->static_tree;
    int elems             = desc->stat_desc->elems;
    int n, m;          /* iterate over heap elements */
    int max_code = -1; /* largest code with non zero frequency */
    int node;          /* new node being created */

    /* Construct the initial heap, with least frequent element in
     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
     * heap[0] is not used.
     */
    s->heap_len = 0, s->heap_max = HEAP_SIZE;

    for (n = 0; n < elems; n++) {
        if (tree[n].Freq != 0) {
            s->heap[++(s->heap_len)] = max_code = n;
            s->depth[n] = 0;
        } else {
            tree[n].Len = 0;
        }
    }

    /* The pkzip format requires that at least one distance code exists,
     * and that at least one bit should be sent even if there is only one
     * possible code. So to avoid special checks later on we force at least
     * two codes of non zero frequency.
     */
    while (s->heap_len < 2) {
        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
        tree[node].Freq = 1;
        s->depth[node] = 0;
        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
        /* node is 0 or 1 so it does not have extra bits */
    }
    desc->max_code = max_code;

    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
     * establish sub-heaps of increasing lengths:
     */
    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);

    /* Construct the Huffman tree by repeatedly combining the least two
     * frequent nodes.
     */
    node = elems;              /* next internal node of the tree */
    do {
        pqremove(s, tree, n);  /* n = node of least frequency */
        m = s->heap[SMALLEST]; /* m = node of next least frequency */

        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
        s->heap[--(s->heap_max)] = m;

        /* Create a new node father of n and m */
        tree[node].Freq = tree[n].Freq + tree[m].Freq;
        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
                                s->depth[n] : s->depth[m]) + 1);
        tree[n].Dad = tree[m].Dad = (ush)node;
#ifdef DUMP_BL_TREE
        if (tree == s->bl_tree) {
            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
        }
#endif
        /* and insert the new node in the heap */
        s->heap[SMALLEST] = node++;
        pqdownheap(s, tree, SMALLEST);

    } while (s->heap_len >= 2);

    s->heap[--(s->heap_max)] = s->heap[SMALLEST];

    /* At this point, the fields freq and dad are set. We can now
     * generate the bit lengths.
     */
    gen_bitlen(s, (tree_desc *)desc);

    /* The field len is now set, we can generate the bit codes */
    gen_codes ((ct_data *)tree, max_code, s->bl_count);
}

// Assembly:
subq $0x48, %rsp
movq %rdi, 0x40(%rsp)
movq %rsi, 0x38(%rsp)
movq 0x38(%rsp), %rax
movq (%rax), %rax
movq %rax, 0x30(%rsp)
movq 0x38(%rsp), %rax
movq 0x10(%rax), %rax
movq (%rax), %rax
movq %rax, 0x28(%rsp)
movq 0x38(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x14(%rax), %eax
movl %eax, 0x24(%rsp)
movl $0xffffffff, 0x18(%rsp) # imm = 0xFFFFFFFF
movq 0x40(%rsp), %rax
movl $0x0, 0x14a4(%rax)
movq 0x40(%rsp), %rax
movl $0x23d, 0x14a8(%rax)    # imm = 0x23D
movl $0x0, 0x20(%rsp)
movl 0x20(%rsp), %eax
cmpl 0x24(%rsp), %eax
jge 0x11fd56
movq 0x30(%rsp), %rax
movslq 0x20(%rsp), %rcx
movzwl (%rax,%rcx,4), %eax
cmpl $0x0, %eax
je 0x11fd36
movl 0x20(%rsp), %edx
movl %edx, 0x18(%rsp)
movq 0x40(%rsp), %rax
movq 0x40(%rsp), %rsi
movl 0x14a4(%rsi), %ecx
addl $0x1, %ecx
movl %ecx, 0x14a4(%rsi)
movslq %ecx, %rcx
movl %edx, 0xbb0(%rax,%rcx,4)
movq 0x40(%rsp), %rax
movslq 0x20(%rsp), %rcx
movb $0x0, 0x14ac(%rax,%rcx)
jmp 0x11fd47
movq 0x30(%rsp), %rax
movslq 0x20(%rsp), %rcx
movw $0x0, 0x2(%rax,%rcx,4)
jmp 0x11fd49
movl 0x20(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x20(%rsp)
jmp 0x11fcda
jmp 0x11fd58
movq 0x40(%rsp), %rax
cmpl $0x2, 0x14a4(%rax)
jge 0x11fe22
cmpl $0x2, 0x18(%rsp)
jge 0x11fd82
movl 0x18(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x18(%rsp)
movl %eax, 0x10(%rsp)
jmp 0x11fd8a
xorl %eax, %eax
movl %eax, 0x10(%rsp)
jmp 0x11fd8a
movl 0x10(%rsp), %eax
movq 0x40(%rsp), %rcx
movq 0x40(%rsp), %rsi
movl 0x14a4(%rsi), %edx
addl $0x1, %edx
movl %edx, 0x14a4(%rsi)
movslq %edx, %rdx
movl %eax, 0xbb0(%rcx,%rdx,4)
movl %eax, 0x14(%rsp)
movq 0x30(%rsp), %rax
movslq 0x14(%rsp), %rcx
movw $0x1, (%rax,%rcx,4)
movq 0x40(%rsp), %rax
movslq 0x14(%rsp), %rcx
movb $0x0, 0x14ac(%rax,%rcx)
movq 0x40(%rsp), %rax
movq 0x1708(%rax), %rcx
addq $-0x1, %rcx
movq %rcx, 0x1708(%rax)
cmpq $0x0, 0x28(%rsp)
je 0x11fe1d
movq 0x28(%rsp), %rax
movslq 0x14(%rsp), %rcx
movzwl 0x2(%rax,%rcx,4), %eax
movl %eax, %edx
movq 0x40(%rsp), %rax
movq 0x1710(%rax), %rcx
subq %rdx, %rcx
movq %rcx, 0x1710(%rax)
jmp 0x11fd58
movl 0x18(%rsp), %ecx
movq 0x38(%rsp), %rax
movl %ecx, 0x8(%rax)
movq 0x40(%rsp), %rax
movl 0x14a4(%rax), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, 0x20(%rsp)
cmpl $0x1, 0x20(%rsp)
jl 0x11fe6c
movq 0x40(%rsp), %rdi
movq 0x30(%rsp), %rsi
movl 0x20(%rsp), %edx
callq 0x121300
movl 0x20(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x20(%rsp)
jmp 0x11fe45
movl 0x24(%rsp), %eax
movl %eax, 0x14(%rsp)
movq 0x40(%rsp), %rax
movl 0xbb4(%rax), %eax
movl %eax, 0x20(%rsp)
movq 0x40(%rsp), %rax
movq 0x40(%rsp), %rdx
movl 0x14a4(%rdx), %ecx
movl %ecx, %esi
addl $-0x1, %esi
movl %esi, 0x14a4(%rdx)
movslq %ecx, %rcx
movl 0xbb0(%rax,%rcx,4), %ecx
movq 0x40(%rsp), %rax
movl %ecx, 0xbb4(%rax)
movq 0x40(%rsp), %rdi
movq 0x30(%rsp), %rsi
movl $0x1, %edx
callq 0x121300
movq 0x40(%rsp), %rax
movl 0xbb4(%rax), %eax
movl %eax, 0x1c(%rsp)
movl 0x20(%rsp), %edx
movq 0x40(%rsp), %rax
movq 0x40(%rsp), %rsi
movl 0x14a8(%rsi), %ecx
addl $-0x1, %ecx
movl %ecx, 0x14a8(%rsi)
movslq %ecx, %rcx
movl %edx, 0xbb0(%rax,%rcx,4)
movl 0x1c(%rsp), %edx
movq 0x40(%rsp), %rax
movq 0x40(%rsp), %rsi
movl 0x14a8(%rsi), %ecx
addl $-0x1, %ecx
movl %ecx, 0x14a8(%rsi)
movslq %ecx, %rcx
movl %edx, 0xbb0(%rax,%rcx,4)
movq 0x30(%rsp), %rax
movslq 0x20(%rsp), %rcx
movzwl (%rax,%rcx,4), %eax
movq 0x30(%rsp), %rcx
movslq 0x1c(%rsp), %rdx
movzwl (%rcx,%rdx,4), %ecx
addl %ecx, %eax
movw %ax, %dx
movq 0x30(%rsp), %rax
movslq 0x14(%rsp), %rcx
movw %dx, (%rax,%rcx,4)
movq 0x40(%rsp), %rax
movslq 0x20(%rsp), %rcx
movzbl 0x14ac(%rax,%rcx), %eax
movq 0x40(%rsp), %rcx
movslq 0x1c(%rsp), %rdx
movzbl 0x14ac(%rcx,%rdx), %ecx
cmpl %ecx, %eax
jl 0x11ff93
movq 0x40(%rsp), %rax
movslq 0x20(%rsp), %rcx
movzbl 0x14ac(%rax,%rcx), %eax
movl %eax, 0xc(%rsp)
jmp 0x11ffa9
movq 0x40(%rsp), %rax
movslq 0x1c(%rsp), %rcx
movzbl 0x14ac(%rax,%rcx), %eax
movl %eax, 0xc(%rsp)
movl 0xc(%rsp), %eax
addl $0x1, %eax
movb %al, %dl
movq 0x40(%rsp), %rax
movslq 0x14(%rsp), %rcx
movb %dl, 0x14ac(%rax,%rcx)
movl 0x14(%rsp), %eax
movw %ax, %dx
movq 0x30(%rsp), %rax
movslq 0x1c(%rsp), %rcx
movw %dx, 0x2(%rax,%rcx,4)
movq 0x30(%rsp), %rax
movslq 0x20(%rsp), %rcx
movw %dx, 0x2(%rax,%rcx,4)
movl 0x14(%rsp), %ecx
movl %ecx, %eax
addl $0x1, %eax
movl %eax, 0x14(%rsp)
movq 0x40(%rsp), %rax
movl %ecx, 0xbb4(%rax)
movq 0x40(%rsp), %rdi
movq 0x30(%rsp), %rsi
movl $0x1, %edx
callq 0x121300
movq 0x40(%rsp), %rax
cmpl $0x2, 0x14a4(%rax)
jge 0x11fe74
movq 0x40(%rsp), %rax
movl 0xbb4(%rax), %edx
movq 0x40(%rsp), %rax
movq 0x40(%rsp), %rsi
movl 0x14a8(%rsi), %ecx
addl $-0x1, %ecx
movl %ecx, 0x14a8(%rsi)
movslq %ecx, %rcx
movl %edx, 0xbb0(%rax,%rcx,4)
movq 0x40(%rsp), %rdi
movq 0x38(%rsp), %rsi
callq 0x121510
movq 0x30(%rsp), %rdi
movl 0x18(%rsp), %esi
movq 0x40(%rsp), %rdx
addq $0xb90, %rdx            # imm = 0xB90
callq 0x1218e0
addq $0x48, %rsp
retq
nopw %cs:(%rax,%rax)