///|
/// Three classical paired-observation estimators, without automatic trimming.
pub(all) enum Average {
Volume
Simple
Regression
} derive(Eq, Debug)
///|
pub extend Average with @debug.Debug::{to_repr}
///|
/// One development period's transparent fitted statistics.
pub(all) struct FactorStat {
age : Int32
factor : Double
pairs : Int32
skipped_zero : Int32
numerator : Double
denominator : Double
} derive(Debug)
///|
pub extend FactorStat with @debug.Debug::{to_repr}
///|
fn estimate_pair_column(
triangle : Triangle,
age : Int32,
average : Average,
excluded : Array[(Int32, Int32)],
recent : Int32,
) -> FactorStat raise RunoffError {
let n = triangle.order()
let mut numerator = 0.0
let mut denominator = 0.0
let mut pairs = 0
let mut skipped_zero = 0
let mut start = 0
if recent > 0 && n - age - 1 > recent {
start = n - age - 1 - recent
}
for i in start..<(n - age - 1) {
if excluded.contains((i, age)) {
continue
}
let x = triangle.rows[i][age]
let y = triangle.rows[i][age + 1]
if x == 0.0 {
skipped_zero = skipped_zero + 1
continue
}
let (top, bottom) = match average {
Volume => (y, x)
Simple => (checked(y / x, "individual ratio overflow"), 1.0)
Regression =>
(
checked(x * y, "regression product overflow"),
checked(x * x, "regression square overflow"),
)
}
numerator = checked(numerator + top, "factor numerator overflow")
denominator = checked(denominator + bottom, "factor denominator overflow")
pairs = pairs + 1
}
if pairs == 0 || denominator <= 0.0 {
raise Selection("no usable positive-base pair for development period")
}
let factor = checked(numerator / denominator, "factor division overflow")
if factor < 1.0 {
raise Numerical("development factor below one")
}
{ age, factor, pairs, skipped_zero, numerator, denominator, }
}
///|
/// Fit all observed development periods using all available pairs.
pub fn development_factors(
triangle : Triangle,
average~ : Average = Volume,
) -> Array[FactorStat] raise RunoffError {
let out : Array[FactorStat] = []
for age in 0..<(triangle.order() - 1) {
out.push(estimate_pair_column(triangle, age, average, [], 0))
}
out
}
///|
pub extend Average with Eq::{not_equal, equal}