///|
/// Fixed-capacity rolling mean over finite, nonnegative frame energy.
/// A pairwise tree is rebuilt along the replaced leaf's path on every push.
priv struct EnergyWindow {
  capacity : Int32
  leaf_base : Int32
  sums : Array[Double]
  mut cursor : Int32
  mut filled : Int32
}

///|
/// Creates an empty rolling window with a positive capacity.
fn EnergyWindow::new(capacity : Int32) -> EnergyWindow {
  let mut leaf_base = 1
  while leaf_base < capacity {
    leaf_base = leaf_base * 2
  }
  let sums : Array[Double] = []
  for _ in 0..<(leaf_base * 2) {
    sums.push(0.0)
  }
  { capacity, leaf_base, sums, cursor: 0, filled: 0, }
}

///|
/// Inserts one energy value and returns a mean only once the window is complete.
fn EnergyWindow::push(self : EnergyWindow, value : Double) -> Double? {
  if self.filled < self.capacity {
    self.filled = self.filled + 1
  }
  let mut node = self.leaf_base + self.cursor
  self.sums[node] = value
  while node > 1 {
    node = node / 2
    // Never subtract an expired value from an already rounded large total.
    // Quiet leaves survive independently and reappear as loud leaves expire.
    self.sums[node] = self.sums[node * 2] + self.sums[node * 2 + 1]
  }
  self.cursor = (self.cursor + 1) % self.capacity
  if self.filled == self.capacity {
    Some(self.sums[1] / self.capacity.to_double())
  } else {
    None
  }
}

///|
/// Clears every tree node, including unused padding, without changing the span.
fn EnergyWindow::clear(self : EnergyWindow) -> Unit {
  for index in 0..