///|
// Iterative Kosaraju condensation keeps deep and cyclic scopes off the call stack.
fn ranks(
  members : ArrayView[Int32],
  links : ArrayView[(Int32, Int32)],
  budget : Budget,
) -> Map[Int32, Int32] {
  let outgoing : Map[Int32, Array[Int32]] = Map([])
  let incoming : Map[Int32, Array[Int32]] = Map([])
  for (a, b) in links {
    if !budget.charge(1) {
      return Map([])
    }
    match outgoing.get(a) {
      Some(values) => values.push(b)
      None => outgoing[a] = [b]
    }
    match incoming.get(b) {
      Some(values) => values.push(a)
      None => incoming[b] = [a]
    }
  }
  let seen = Map([])
  let order = []
  for start in members {
    let stack = [(start, false)]
    while stack.pop() is Some((node, leave)) {
      if !budget.charge(1) {
        return Map([])
      }
      if leave {
        order.push(node)
        continue
      }
      if seen.contains(node) {
        continue
      }
      seen[node] = true
      stack.push((node, true))
      let neighbors = outgoing.get(node).unwrap_or([])
      for i in 0.. [])
  let indegree = Array::make(count.length(), 0)
  let rank = Array::make(count.length(), 0)
  for (a, b) in links {
    if !budget.charge(1) {
      return Map([])
    }
    let a = components[a]
    let b = components[b]
    if a != b && !edges.contains((a, b)) {
      edges[(a, b)] = true
      next[a].push(b)
      indegree[b] += 1
    }
  }
  let queue = []
  for i in 0..