///|
priv struct Owner {
mut active : Bool
}
///|
/// Advances all registered variables together, exposing one new frontier per round.
pub struct Iteration {
priv owner : Owner
priv stages : Array[() -> Bool]
priv reports : Array[() -> VariableStats]
priv mut rounds : Int32
}
///|
pub struct VariableStats {
name : String
stable : Int32
recent : Int32
pending : Int32
batches : Int32
}
///|
pub fn Iteration::Iteration() -> Iteration {
{ owner: { active: true, }, stages: [], reports: [], rounds: 0, }
}
///|
pub fn Iteration::changed(self : Iteration) -> Bool {
guard self.owner.active else { abort("iteration is disposed") }
let mut changed = false
for advance in self.stages {
// Do not short circuit: every variable must advance in the same round.
if advance() {
changed = true
}
}
self.rounds += 1
changed
}
///|
pub fn Iteration::round(self : Iteration) -> Int32 {
self.rounds
}
///|
pub fn Iteration::stats(self : Iteration) -> Array[VariableStats] {
self.reports.map(fn(report) { report() })
}
///|
/// Unregister all stages. Existing variable handles can no longer be used.
pub fn Iteration::dispose(self : Iteration) -> Unit {
self.owner.active = false
self.stages.clear()
self.reports.clear()
}
///|
pub fn[T : Compare] Iteration::variable(
self : Iteration,
name : String,
distinct~ : Bool = true,
) -> Variable[T] {
guard self.owner.active else { abort("iteration is disposed") }
let state : Variable[T] = {
owner: self.owner,
name,
distinct,
stable: [],
recent: Relation([]),
pending: [],
}
// Capture the variable, never the iteration: no reference-counted ownership cycle.
self.stages.push(fn() { state.advance() })
self.reports.push(fn() { state.stats() })
state
}