///|
pub(all) struct OriginEstimate {
  origin : Int32
  age : Int32
  latest : Double
  ultimate : Double
  reserve : Double
} derive(Debug)

///|
pub extend OriginEstimate with @debug.Debug::{to_repr}

///|
/// Full estimate carries the selected pattern for reproducible cash flows.
pub struct Estimate {
  priv origins : Array[OriginEstimate]
  priv pattern : Pattern
  priv estimator : String
}

///|
fn from_ultimates(
  triangle : Triangle,
  pattern : Pattern,
  ultimates : Array[Double],
  estimator : String,
) -> Estimate raise RunoffError {
  compatible(triangle, pattern)
  if ultimates.length() != triangle.order() {
    raise Shape("ultimate count differs from order")
  }
  let origins : Array[OriginEstimate] = []
  for i in 0.. Estimate raise RunoffError {
  compatible(triangle, pattern)
  let out : Array[Double] = []
  for i in 0.. Array[OriginEstimate] {
  self.origins.copy()
}

///|
pub fn Estimate::estimator(self : Estimate) -> String {
  self.estimator
}

///|
pub fn Estimate::total_latest(self : Estimate) -> Double raise RunoffError {
  let mut total = 0.0
  for row in self.origins {
    total = checked(total + row.latest, "latest total overflow")
  }
  total
}

///|
pub fn Estimate::total_ultimate(self : Estimate) -> Double raise RunoffError {
  let mut total = 0.0
  for row in self.origins {
    total = checked(total + row.ultimate, "ultimate total overflow")
  }
  total
}

///|
pub fn Estimate::total_reserve(self : Estimate) -> Double raise RunoffError {
  let mut total = 0.0
  for row in self.origins {
    total = checked(total + row.reserve, "reserve total overflow")
  }
  total
}

///|
fn compatible(triangle : Triangle, pattern : Pattern) -> Unit raise RunoffError {
  if triangle.order() != pattern.order() {
    raise Shape("pattern and triangle order differ")
  }
}

///|
pub fn Estimate::pattern(self : Estimate) -> Pattern {
  self.pattern
}