shape_runtime/simd_rolling/
diff.rs1use wide::f64x4;
4
5use super::SIMD_THRESHOLD;
6
7#[cfg(feature = "simd")]
11#[inline]
12pub fn diff(data: &[f64]) -> Vec<f64> {
13 diff_simd(data)
14}
15
16#[cfg(not(feature = "simd"))]
17#[inline]
18pub fn diff(data: &[f64]) -> Vec<f64> {
19 diff_scalar(data)
20}
21
22#[cfg(feature = "simd")]
24#[inline]
25pub fn pct_change(data: &[f64]) -> Vec<f64> {
26 pct_change_simd(data)
27}
28
29#[cfg(not(feature = "simd"))]
30#[inline]
31pub fn pct_change(data: &[f64]) -> Vec<f64> {
32 pct_change_scalar(data)
33}
34
35#[cfg(not(feature = "simd"))]
38fn diff_scalar(data: &[f64]) -> Vec<f64> {
39 let n = data.len();
40 if n < 2 {
41 return vec![f64::NAN; n];
42 }
43
44 let mut result = vec![f64::NAN; n];
45 for i in 1..n {
46 result[i] = data[i] - data[i - 1];
47 }
48 result
49}
50
51#[cfg(not(feature = "simd"))]
52fn pct_change_scalar(data: &[f64]) -> Vec<f64> {
53 let n = data.len();
54 if n < 2 {
55 return vec![f64::NAN; n];
56 }
57
58 let mut result = vec![f64::NAN; n];
59 for i in 1..n {
60 if data[i - 1] != 0.0 {
61 result[i] = (data[i] - data[i - 1]) / data[i - 1];
62 } else {
63 result[i] = f64::NAN;
64 }
65 }
66 result
67}
68
69fn diff_simd(data: &[f64]) -> Vec<f64> {
75 let n = data.len();
76 if n < 2 {
77 return vec![f64::NAN; n];
78 }
79
80 let mut result = vec![f64::NAN; n];
81
82 if n < SIMD_THRESHOLD {
84 for i in 1..n {
85 result[i] = data[i] - data[i - 1];
86 }
87 return result;
88 }
89
90 let chunks = (n - 1) / 4;
92
93 for chunk in 0..chunks {
94 let i = 1 + chunk * 4;
95
96 let curr = f64x4::new([data[i], data[i + 1], data[i + 2], data[i + 3]]);
97 let prev = f64x4::new([data[i - 1], data[i], data[i + 1], data[i + 2]]);
98 let diff = curr - prev;
99
100 let arr = diff.to_array();
101 result[i..i + 4].copy_from_slice(&arr);
102 }
103
104 for i in (1 + chunks * 4)..n {
106 result[i] = data[i] - data[i - 1];
107 }
108
109 result
110}
111
112fn pct_change_simd(data: &[f64]) -> Vec<f64> {
118 let n = data.len();
119 if n < 2 {
120 return vec![f64::NAN; n];
121 }
122
123 let mut result = vec![f64::NAN; n];
124
125 if n < SIMD_THRESHOLD {
126 for i in 1..n {
127 result[i] = (data[i] - data[i - 1]) / data[i - 1];
128 }
129 return result;
130 }
131
132 let chunks = (n - 1) / 4;
133
134 for chunk in 0..chunks {
135 let i = 1 + chunk * 4;
136
137 let curr = f64x4::new([data[i], data[i + 1], data[i + 2], data[i + 3]]);
138 let prev = f64x4::new([data[i - 1], data[i], data[i + 1], data[i + 2]]);
139 let diff = curr - prev;
140 let pct = diff / prev;
141
142 let arr = pct.to_array();
143 result[i..i + 4].copy_from_slice(&arr);
144 }
145
146 for i in (1 + chunks * 4)..n {
148 result[i] = (data[i] - data[i - 1]) / data[i - 1];
149 }
150
151 result
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_diff_simd() {
160 let data = vec![100.0, 102.0, 101.0, 105.0, 103.0];
161 let result = diff(&data);
162
163 assert!(result[0].is_nan());
164 assert_eq!(result[1], 2.0);
165 assert_eq!(result[2], -1.0);
166 assert_eq!(result[3], 4.0);
167 assert_eq!(result[4], -2.0);
168 }
169
170 #[test]
171 fn test_pct_change_simd() {
172 let data = vec![100.0, 110.0, 99.0, 105.0];
173 let result = pct_change(&data);
174
175 assert!(result[0].is_nan());
176 assert!((result[1] - 0.10).abs() < 1e-10); assert!((result[2] - (-0.1)).abs() < 1e-10); assert!((result[3] - 0.06060606).abs() < 1e-6); }
180}