Skip to main content

flow_gate_core/transform/
linear.rs

1use crate::error::FlowGateError;
2use crate::traits::Transform;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct LinearTransform {
6    pub t: f64,
7    pub a: f64,
8    span: f64,
9    inv_span: f64,
10}
11
12impl LinearTransform {
13    pub fn new(t: f64, a: f64) -> Result<Self, FlowGateError> {
14        if !t.is_finite() || t <= 0.0 {
15            return Err(FlowGateError::InvalidTransformParam(
16                "Linear parameter T must be finite and > 0".to_string(),
17            ));
18        }
19        if !a.is_finite() {
20            return Err(FlowGateError::InvalidTransformParam(
21                "Linear parameter A must be finite".to_string(),
22            ));
23        }
24        let span = t + a;
25        if !span.is_finite() || span <= 0.0 {
26            return Err(FlowGateError::InvalidTransformParam(
27                "Linear span (T + A) must be finite and > 0".to_string(),
28            ));
29        }
30        if span.is_subnormal() {
31            return Err(FlowGateError::InvalidTransformParam(
32                "Linear span (T + A) is subnormal".to_string(),
33            ));
34        }
35        Ok(Self {
36            t,
37            a,
38            span,
39            inv_span: 1.0 / span,
40        })
41    }
42}
43
44impl Transform for LinearTransform {
45    fn apply(&self, value: f64) -> f64 {
46        if !value.is_finite() {
47            return f64::NAN;
48        }
49        (value + self.a) * self.inv_span
50    }
51
52    fn invert(&self, scaled: f64) -> f64 {
53        if !scaled.is_finite() {
54            return f64::NAN;
55        }
56        let value = scaled * self.span - self.a;
57        if value.is_finite() {
58            value
59        } else {
60            f64::NAN
61        }
62    }
63
64    fn transform_id(&self) -> &str {
65        "lin"
66    }
67}