// 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
}
}
}