///|
pub(all) struct InverseRange {
lower : Double
upper : Double
} derive(Debug)
///|
/// Preimage under linear interpolation, restricted to the training domain.
pub fn inverse(model : Model, target : Double) -> Result[InverseRange, String] {
match validate_model(model) {
Err(e) => return Err(e)
Ok(_) => ()
}
if !finite(target) {
return Err("nonfinite inverse target")
}
let first = model.knots[0]
let last = model.knots[model.knots.length() - 1]
if !before(first.y, target, model.direction) ||
!before(target, last.y, model.direction) {
return Err("target outside fitted range")
}
let mut lower : Double? = None
let mut upper : Double? = None
for i = 0; i < model.knots.length(); i = i + 1 {
let k = model.knots[i]
if k.y == target {
if lower is None {
lower = Some(k.x)
}
upper = Some(k.x)
}
if i > 0 {
let previous = model.knots[i - 1]
if previous.y != k.y &&
before(previous.y, target, model.direction) &&
before(target, k.y, model.direction) {
let denominator = k.y - previous.y
let numerator = target - previous.y
if !finite(denominator) || !finite(numerator) {
return Err("inverse span overflow")
}
let ratio = numerator / denominator
let x = previous.x * (1.0 - ratio) + k.x * ratio
if !finite(x) {
return Err("inverse coordinate overflow")
}
if lower is None {
lower = Some(x)
}
upper = Some(x)
}
}
}
match (lower, upper) {
(Some(lower), Some(upper)) => Ok({ lower, upper, })
_ => Err("no inverse in training domain")
}
}
///|
pub fn threshold_domain(
model : Model,
target : Double,
above? : Bool = true,
) -> Result[InverseRange, String] {
let cut = match inverse(model, target) {
Err(e) => return Err(e)
Ok(v) => v
}
let ends = match domain(model) {
Err(e) => return Err(e)
Ok(v) => v
}
match (model.direction, above) {
(Increasing, true) | (Decreasing, false) =>
Ok({ lower: cut.lower, upper: ends.1, })
_ => Ok({ lower: ends.0, upper: cut.upper, })
}
}
///|
#deprecated
pub extend InverseRange with @debug.Debug::{to_repr}