wickra_embed_core/indicators/
roc.rs1use crate::traits::Indicator;
4
5#[derive(Debug, Clone)]
26pub struct Roc<const N: usize> {
27 buf: [f64; N],
28 head: usize,
29 count: usize,
30 last: Option<f64>,
31}
32
33impl<const N: usize> Roc<N> {
34 #[must_use]
36 pub const fn new() -> Self {
37 Self {
38 buf: [0.0; N],
39 head: 0,
40 count: 0,
41 last: None,
42 }
43 }
44
45 #[must_use]
47 pub const fn value(&self) -> Option<f64> {
48 self.last
49 }
50}
51
52impl<const N: usize> Default for Roc<N> {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl<const N: usize> Indicator for Roc<N> {
59 type Input = f64;
60
61 fn update(&mut self, input: f64) -> Option<f64> {
62 if !input.is_finite() {
63 return self.last;
64 }
65 if self.count == N {
66 let prev = self.buf[self.head];
67 self.buf[self.head] = input;
68 self.head += 1;
69 if self.head == N {
70 self.head = 0;
71 }
72 let roc = if prev == 0.0 {
73 0.0
74 } else {
75 (input - prev) / prev * 100.0
76 };
77 self.last = Some(roc);
78 Some(roc)
79 } else {
80 self.buf[self.head] = input;
81 self.count += 1;
82 self.head += 1;
83 if self.head == N {
84 self.head = 0;
85 }
86 None
87 }
88 }
89
90 fn reset(&mut self) {
91 *self = Self::new();
92 }
93
94 fn warmup_period(&self) -> usize {
95 N + 1
96 }
97
98 fn is_ready(&self) -> bool {
99 self.last.is_some()
100 }
101
102 fn name(&self) -> &'static str {
103 "roc"
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::Roc;
110 use crate::traits::Indicator;
111
112 #[test]
113 fn known_value() {
114 let mut roc = Roc::<3>::new();
115 assert_eq!(roc.update(100.0), None);
116 assert_eq!(roc.update(105.0), None);
117 assert_eq!(roc.update(108.0), None);
118 assert_eq!(roc.update(110.0), Some(10.0));
119 assert_eq!(roc.warmup_period(), 4);
120 }
121
122 #[test]
123 fn constant_series_is_zero() {
124 let mut roc = Roc::<5>::new();
125 let mut last = None;
126 for _ in 0..20 {
127 last = roc.update(42.0);
128 }
129 assert_eq!(last, Some(0.0));
130 }
131
132 #[test]
133 fn non_finite_is_ignored() {
134 let mut roc = Roc::<2>::new();
135 roc.update(100.0);
136 roc.update(110.0);
137 let v = roc.update(120.0);
138 assert!(v.is_some());
139 assert_eq!(roc.update(f64::NAN), v);
140 }
141
142 #[test]
143 fn byte_parity_with_wickra_core() {
144 let mut ours = Roc::<12>::new();
145 let mut theirs = wickra_core::Roc::new(12).unwrap();
146 for i in 0..1000 {
147 let x = 100.0 + 30.0 * (f64::from(i) * 0.06).sin() + 0.03 * f64::from(i);
148 let a = ours.update(x);
149 let b = <wickra_core::Roc as wickra_core::Indicator>::update(&mut theirs, x);
150 assert_eq!(a.map(f64::to_bits), b.map(f64::to_bits), "index {i}");
151 }
152 }
153}