///|
// 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.. Map[Int32, Int32] {
  let pairs = []
  let outgoing : Map[Int32, Array[(Int32, Int32)]] = Map([])
  for (a, b, length) in links {
    if !budget.charge(1) {
      return Map([])
    }
    if length < 1 || length > 1000000 {
      budget.error(
        "swimlane/layout-limit", "Connection rank length exceeds its safe bound",
      )
      return Map([])
    }
    pairs.push((a, b))
    match outgoing.get(a) {
      Some(items) => items.push((b, length))
      None => outgoing[a] = [(b, length)]
    }
  }
  let base = ranks(members, pairs, budget)
  let result = Map([])
  for (id, rank) in base {
    result[id] = rank
  }
  let order = members.to_owned()
  order.sort_by((a, b) => {
    let c = base.get(a).unwrap_or(0).compare(base.get(b).unwrap_or(0))
    if c == 0 {
      a.compare(b)
    } else {
      c
    }
  })
  for a in order {
    for (b, length) in outgoing.get(a).unwrap_or([]) {
      if !budget.charge(1) {
        return Map([])
      }
      // Edges inside a condensed cycle have the same base rank. They use return
      // channels, rather than growing ranks forever.
      if base.get(b).unwrap_or(0) > base.get(a).unwrap_or(0) {
        if result[a] > 1000000 - length {
          budget.error(
            "swimlane/layout-limit", "Rank extent exceeds its safe bound",
          )
          return Map([])
        }
        result[b] = Int32::max(result[b], result[a] + length)
      }
    }
  }
  result
}