///|
/// Construct from nonnegative incremental observations, checking accumulation.
pub fn Triangle::from_incremental(
  rows : Array[Array[Double]],
) -> Triangle raise RunoffError {
  let cumulative : Array[Array[Double]] = []
  for row in rows {
    let mut sum = 0.0
    let result : Array[Double] = []
    for x in row {
      nonnegative(x, "increments must be finite and nonnegative")
      sum = checked(sum + x, "increment accumulation overflow")
      result.push(sum)
    }
    cumulative.push(result)
  }
  Triangle::new(cumulative)
}

///|
/// Convert cumulative data to independent incremental rows.
pub fn Triangle::incremental(self : Triangle) -> Array[Array[Double]] {
  self.rows.map(fn(row) {
    let out : Array[Double] = []
    for j in 0.. Triangle raise RunoffError {
  if calendar < 1 || calendar >= self.order() {
    raise Shape("historical calendar must be 1..order-1")
  }
  let n = calendar + 1
  Triangle::new(
    Array::makei(n, fn(i) { Array::makei(n - i, fn(j) { self.rows[i][j] }) }),
  )
}

///|
/// Scale all observations. Zero is allowed; factor fitting may then fail.
pub fn Triangle::scale(
  self : Triangle,
  factor : Double,
) -> Triangle raise RunoffError {
  nonnegative(factor, "scale must be finite and nonnegative")
  let rows : Array[Array[Double]] = []
  for row in self.rows {
    let scaled : Array[Double] = []
    for x in row {
      scaled.push(checked(x * factor, "triangle scale overflow"))
    }
    rows.push(scaled)
  }
  Triangle::new(rows)
}

///|
/// Calendar-diagonal observed incremental sums, including early diagonals.
pub fn Triangle::calendar_totals(
  self : Triangle,
) -> Array[Double] raise RunoffError {
  let out = Array::make(self.order(), 0.0)
  for i, row in self.incremental() {
    for j, x in row {
      out[i + j] = checked(out[i + j] + x, "calendar total overflow")
    }
  }
  out
}

///|
/// Development-period observed incremental sums; each period has fewer origins.
pub fn Triangle::development_totals(
  self : Triangle,
) -> Array[Double] raise RunoffError {
  let out = Array::make(self.order(), 0.0)
  for row in self.incremental() {
    for j, x in row {
      out[j] = checked(out[j] + x, "development total overflow")
    }
  }
  out
}