flow_gate_core/transform/
linear.rs1use 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}