///|
// Local names keep their strings and entry counts under a bijective reorder.
// A nonincreasing index-width sum keeps their payload and length prefixes from
// growing. Bound row lookup; large metadata maps retain the existing order.
fn dae2_local_order_names_nonincreasing(
  names : @lib.NameSec?,
  function_abs : Int32,
  before : Array[Int32],
  after : Array[Int32],
) -> Bool {
  let rows = match names {
    Some(names) =>
      match names.local_names {
        Some(@lib.IndirectNameMap(rows)) => rows
        None => return true
      }
    None => return true
  }
  if rows.length() > 256 {
    return false
  }
  for row in rows {
    let @lib.IndirectNameAssoc(index, @lib.NameMap(entries)) = row
    if index.reinterpret_as_int() != function_abs {
      continue
    }
    let mut delta = 0L
    for entry in entries {
      let @lib.NameAssoc(raw, _) = entry
      let slot = raw.reinterpret_as_int()
      if slot >= 0 && slot < before.length() && before[slot] >= 0 {
        delta += dae2_local_index_width(after[slot]).to_int64() -
          dae2_local_index_width(before[slot]).to_int64()
      }
    }
    if delta > 0L {
      return false
    }
  }
  true
}

///|
// Reuse the complete cleanup census and its required remap. Equal types keep
// the local declaration byte-for-byte unchanged. Parameters are fixed, ties
// keep the old order, and both body and name payloads must not grow.
fn dae2_order_compacted_local_indices(
  indices : Array[Int32],
  reads : Array[Int32],
  writes : Array[Int32],
  params : Int32,
  retained : Array[@lib.ValType],
  names : @lib.NameSec?,
  function_abs : Int32,
) -> Bool {
  if retained.is_empty() ||
    dae2_local_index_width(params) ==
    dae2_local_index_width(params + retained.length() - 1) {
    return false
  }
  let ty = retained[0]
  for other in retained {
    if other != ty {
      return false
    }
  }
  let order : Array[Int32] = []
  let mut before_cost = 0L
  for old in params..= params {
      order.push(old)
      before_cost += (reads[old].to_int64() + writes[old].to_int64()) *
        dae2_local_index_width(indices[old]).to_int64()
    }
  }
  order.sort_by(fn(left, right) {
    let left_count = reads[left].to_int64() + writes[left].to_int64()
    let right_count = reads[right].to_int64() + writes[right].to_int64()
    if left_count == right_count {
      left.compare(right)
    } else {
      right_count.compare(left_count)
    }
  })
  let proposed = indices.copy()
  let mut after_cost = 0L
  for rank in 0..= before_cost ||
    !dae2_local_order_names_nonincreasing(
      names, function_abs, indices, proposed,
    ) {
    return false
  }
  for old in params..