///|
/// Immutable selected age-to-age factors and one final tail multiplier.
pub struct Pattern {
priv factors : Array[Double]
priv tail : Double
priv cdf : Array[Double]
}
///|
pub fn Pattern::new(
factors : Array[Double],
tail~ : Double = 1.0,
) -> Pattern raise RunoffError {
if factors.length() < 1 || factors.length() > 127 {
raise Shape("pattern requires 1..127 factors")
}
nonnegative(tail, "tail must be finite")
if tail < 1.0 {
raise Value("tail must be at least one")
}
let cdf = Array::make(factors.length() + 1, tail)
let mut age = factors.length() - 1
while age >= 0 {
nonnegative(factors[age], "factor must be finite")
if factors[age] < 1.0 {
raise Value("factor must be at least one")
}
cdf[age] = checked(
cdf[age + 1] * factors[age],
"cumulative factor overflow",
)
age = age - 1
}
{ factors: factors.copy(), tail, cdf, }
}
///|
pub fn Pattern::fit(
triangle : Triangle,
average~ : Average = Volume,
tail~ : Double = 1.0,
) -> Pattern raise RunoffError {
Pattern::new(
development_factors(triangle, average~).map(fn(s) { s.factor }),
tail~,
)
}
///|
pub fn Pattern::factors(self : Pattern) -> Array[Double] {
self.factors.copy()
}
///|
pub fn Pattern::tail(self : Pattern) -> Double {
self.tail
}
///|
pub fn Pattern::cumulative_factors(self : Pattern) -> Array[Double] {
self.cdf.copy()
}
///|
pub fn Pattern::order(self : Pattern) -> Int32 {
self.cdf.length()
}
///|
pub fn Pattern::reported_fraction(
self : Pattern,
age : Int32,
) -> Double raise RunoffError {
if age < 0 || age >= self.order() {
raise Shape("age outside pattern")
}
1.0 / self.cdf[age]
}