// Phase 2: Gate resolution.
//
// A Gate stores nothing. When active it pulls along its incoming edges, sums
// the total, and distributes it across its outgoing edges either by weight
// ratio (Deterministic, integer largest-remainder split) or by weighted random
// per unit (Probabilistic, seeded). Amounts that exceed a destination's
// capacity are dropped (gates do not hold resources).

///|
/// Integer split of `total` across `weights` using the largest-remainder method.
fn split_det(total : Int, weights : Array[Double]) -> Array[Int] {
  let k = weights.length()
  let result = Array::make(k, 0)
  if k == 0 || total <= 0 {
    return result
  }
  let mut sumw = 0.0
  for w in weights {
    sumw = sumw + w
  }
  if sumw <= 0.0 {
    // Degenerate weights: distribute as evenly as possible, front-loaded.
    let mut rem = total
    let mut i = 0
    while rem > 0 {
      result[i % k] = result[i % k] + 1
      rem = rem - 1
      i = i + 1
    }
    return result
  }
  let frac = Array::make(k, 0.0)
  let mut assigned = 0
  for i in 0.. 0 {
    let mut bi = 0
    let mut bf = -1.0
    for i in 0.. bf {
        bf = frac[i]
        bi = i
      }
    }
    result[bi] = result[bi] + 1
    frac[bi] = -1.0
    rem = rem - 1
  }
  result
}

///|
/// Distribute `total` units one at a time, each routed to a weighted-random edge.
fn split_prob(
  state : SimState,
  total : Int,
  weights : Array[Double],
) -> Array[Int] {
  let k = weights.length()
  let result = Array::make(k, 0)
  if k == 0 || total <= 0 {
    return result
  }
  let mut sumw = 0.0
  for w in weights {
    sumw = sumw + w
  }
  if sumw <= 0.0 {
    for _ in 0.. Unit {
  let edges = diagram.resources
  for gi in 0..
          effective_rate(diagram, state, ei, resolve_rate(e.rate, state, 0))
        _ =>
          effective_rate(
            diagram,
            state,
            ei,
            resolve_rate(e.rate, state, work[e.from]),
          )
      }
      let avail = match from.kind {
        Source => want
        _ => work[e.from]
      }
      let mut amt = if want < avail { want } else { avail }
      if amt < 0 {
        amt = 0
      }
      match from.kind {
        Source => ()
        _ => work[e.from] = work[e.from] - amt
      }
      total = total + amt
      flows[ei] = amt
    }
    // Collect outgoing edges and their weights.
    let out_idx : Array[Int] = []
    let weights : Array[Double] = []
    for ei in 0.. split_det(total, weights)
      Probabilistic => split_prob(state, total, weights)
    }
    // Place shares at destinations (respecting capacity; excess is dropped).
    for k in 0.. {
          let room = cap - work[edges[ei].to]
          if place > room {
            place = room
          }
        }
        None => ()
      }
      if place < 0 {
        place = 0
      }
      match to.kind {
        Drain | Source => ()
        _ => work[edges[ei].to] = work[edges[ei].to] + place
      }
      flows[ei] = place
    }
  }
}