///|
/// From extra bits, generate base values and reverse index map
/// Returns (b: base values array of 31, r: reverse map)
fn freb(
  eb : FixedArray[Byte],
  start : Int,
) -> (FixedArray[Int], FixedArray[Int]) {
  let b : FixedArray[Int] = FixedArray::make(31, 0)
  let mut s = start
  for i in 0..<31 {
    // In JS: b[i] = start += 1 << eb[i - 1]
    // eb[-1] is undefined in JS, so 1 << undefined = 1
    let shift = if i > 0 { eb[i - 1].to_int() } else { 0 }
    s = s + (1 << shift)
    b[i] = s
  }
  // numbers here are at max 18 bits
  let r : FixedArray[Int] = FixedArray::make(b[30], 0)
  for i in 1..<30 {
    for j in b[i].. FixedArray[Int] {
  let s = cd.length()
  // count codes of each length
  let l : FixedArray[Int] = FixedArray::make(mb, 0)
  for i in 0..> rvb] = sv
          v += 1
        }
      }
    }
    co2
  } else {
    let co2 = FixedArray::make(s, 0)
    for i in 0..> (15 - cd[i].to_int())
        le[cd[i].to_int() - 1] = le[cd[i].to_int() - 1] + 1
      }
    }
    co2
  }
  co
}

///|
/// Recursively get max depth and assign code lengths using implicit array tree
fn ln_node(
  node_idx : Int,
  s_arr : FixedArray[Int],
  l_arr : FixedArray[Int],
  r_arr : FixedArray[Int],
  lengths : FixedArray[Int],
  d : Int,
) -> Int {
  if s_arr[node_idx] == -1 {
    let left = l_arr[node_idx]
    let right = r_arr[node_idx]
    let left_max = if left != -1 {
      ln_node(left, s_arr, l_arr, r_arr, lengths, d + 1)
    } else {
      d
    }
    let right_max = if right != -1 {
      ln_node(right, s_arr, l_arr, r_arr, lengths, d + 1)
    } else {
      d
    }
    if left_max > right_max {
      left_max
    } else {
      right_max
    }
  } else {
    lengths[s_arr[node_idx]] = d
    d
  }
}

///|
let h_tree_s_pool : Array[FixedArray[Int]] = []

///|
let h_tree_f_pool : Array[FixedArray[Int]] = []

///|
let h_tree_l_pool : Array[FixedArray[Int]] = []

///|
let h_tree_r_pool : Array[FixedArray[Int]] = []

///|
/// Create code lengths from a frequency table
/// d: frequency table, mb: max allowed bits
/// Returns (t: code lengths, l: max bits used)
fn h_tree(d : FixedArray[Int], mb : Int) -> (FixedArray[Byte], Int) {
  let et : FixedArray[Byte] = FixedArray::make(0, b'\x00')
  let s_arr = match h_tree_s_pool.pop() {
    Some(arr) => arr
    None => FixedArray::make(600, 0)
  }
  let f_arr = match h_tree_f_pool.pop() {
    Some(arr) => arr
    None => FixedArray::make(600, 0)
  }
  let l_arr = match h_tree_l_pool.pop() {
    Some(arr) => arr
    None => FixedArray::make(600, -1)
  }
  let r_arr = match h_tree_r_pool.pop() {
    Some(arr) => arr
    None => FixedArray::make(600, -1)
  }
  let nodes : Array[Int] = []
  let mut next_node_idx = 0
  for i in 0.. max_sym {
      max_sym = s_arr[t2[i]]
    }
  }
  // Compute code lengths
  let tr : FixedArray[Int] = FixedArray::make(max_sym + 1, 0)
  let mut mbt = ln_node(nodes[i1 - 1], s_arr, l_arr, r_arr, tr, 0)
  if mbt > mb {
    // Limit code lengths to mb
    let mut i = 0
    let mut dt = 0
    let lft = mbt - mb
    let cst = 1 << lft
    // Sort by code length desc, then frequency asc
    t2.sort_by(fn(a, b) {
      if tr[s_arr[b]] != tr[s_arr[a]] {
        tr[s_arr[b]] - tr[s_arr[a]]
      } else {
        f_arr[a] - f_arr[b]
      }
    })
    while i < s {
      let i2v = s_arr[t2[i]]
      if tr[i2v] > mb {
        dt += cst - (1 << (mbt - tr[i2v]))
        tr[i2v] = mb
        i += 1
      } else {
        break
      }
    }
    dt = dt >> lft
    while dt > 0 {
      let i2v = s_arr[t2[i]]
      if tr[i2v] < mb {
        dt -= 1 << (mb - tr[i2v] - 1)
        tr[i2v] = tr[i2v] + 1
      } else {
        i += 1
      }
    }
    // i >= 0 check
    while i > 0 && dt < 0 {
      i -= 1
      let i2v = s_arr[t2[i]]
      if tr[i2v] == mb {
        tr[i2v] = tr[i2v] - 1
        dt += 1
      }
    }
    mbt = mb
  }
  h_tree_s_pool.push(s_arr)
  h_tree_f_pool.push(f_arr)
  h_tree_l_pool.push(l_arr)
  h_tree_r_pool.push(r_arr)
  // Convert to byte array
  let result : FixedArray[Byte] = FixedArray::make(max_sym + 1, b'\x00')
  for i in 0..<=max_sym {
    result[i] = tr[i].to_byte()
  }
  (result, mbt)
}

///|
/// Generate length codes from a code length array
/// Returns (codes array, count of meaningful entries)
fn lc_gen(c : FixedArray[Byte]) -> (FixedArray[Int], Int) {
  let mut s = c.length()
  // Find last nonzero
  while s > 0 && c[s - 1] == b'\x00' {
    s -= 1
  }
  if s == 0 {
    // Return a single zero-length code entry (represents 1 symbol with length 0)
    return (FixedArray::make(1, 0), 1)
  }
  let cl : FixedArray[Int] = FixedArray::make(s * 3, 0)
  let mut cli = 0
  let mut cln = c[0].to_int()
  let mut cls = 1
  for i in 1..<=s {
    if i < s && c[i].to_int() == cln {
      cls += 1
    } else {
      if cln == 0 && cls > 2 {
        while cls > 138 {
          cl[cli] = 32754
          cli += 1
          cls -= 138
        }
        if cls > 2 {
          if cls > 10 {
            cl[cli] = ((cls - 11) << 5) | 28690
          } else {
            cl[cli] = ((cls - 3) << 5) | 12305
          }
          cli += 1
          cls = 0
        }
      } else if cls > 3 {
        cl[cli] = cln
        cli += 1
        cls -= 1
        while cls > 6 {
          cl[cli] = 8304
          cli += 1
          cls -= 6
        }
        if cls > 2 {
          cl[cli] = ((cls - 3) << 5) | 8208
          cli += 1
          cls = 0
        }
      }
      while cls > 0 {
        cl[cli] = cln
        cli += 1
        cls -= 1
      }
      cls = 1
      if i < s {
        cln = c[i].to_int()
      }
    }
  }
  // Return slice of used portion
  let result : FixedArray[Int] = FixedArray::make(cli, 0)
  for i in 0.. Int {
  let mut l = 0
  for i in 0..