1use std::ops::Range;
10
11use dspk_core::MemoryOrder;
12use rayon::prelude::*;
13
14pub const EMPTY: [f32; 2] = [f32::INFINITY, f32::NEG_INFINITY];
16
17const LANES: usize = 8;
19
20#[inline]
22pub fn fold(samples: &[f32], acc: [f32; 2]) -> [f32; 2] {
23 let mut lo = [acc[0]; LANES];
24 let mut hi = [acc[1]; LANES];
25 let mut chunks = samples.chunks_exact(LANES);
26 for c in &mut chunks {
27 for i in 0..LANES {
28 lo[i] = if c[i] < lo[i] { c[i] } else { lo[i] };
29 hi[i] = if c[i] > hi[i] { c[i] } else { hi[i] };
30 }
31 }
32 let mut out = acc;
33 for i in 0..LANES {
34 out = merge(out, [lo[i], hi[i]]);
35 }
36 for &v in chunks.remainder() {
37 out = merge(out, [v, v]);
38 }
39 out
40}
41
42#[inline]
44pub fn merge(a: [f32; 2], b: [f32; 2]) -> [f32; 2] {
45 [if b[0] < a[0] { b[0] } else { a[0] }, if b[1] > a[1] { b[1] } else { a[1] }]
46}
47
48pub fn finish(acc: &mut [[f32; 2]]) {
50 for v in acc.iter_mut().filter(|v| v[0] > v[1]) {
51 *v = [f32::NAN, f32::NAN];
52 }
53}
54
55pub fn mean_range(env: &[[f32; 2]], rows: usize, width: usize) -> Vec<f32> {
58 (0..width)
59 .map(|x| {
60 let (sum, n) = (0..rows).map(|r| env[r * width + x]).filter(|v| v[0].is_finite() && v[1].is_finite()).fold((0.0f64, 0usize), |(s, n), v| (s + (v[1] - v[0]) as f64, n + 1));
61 if n == 0 { f32::NAN } else { (sum / n as f64) as f32 }
62 })
63 .collect()
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum Columns {
69 Even { start: u64, len: u64, width: usize },
72 Buckets { origin: u64, size: u64, count: usize },
74}
75
76impl Columns {
77 pub fn count(&self) -> usize {
78 match *self {
79 Columns::Even { width, .. } => width,
80 Columns::Buckets { count, .. } => count,
81 }
82 }
83
84 pub fn start(&self, x: usize) -> u64 {
86 match *self {
87 Columns::Even { start, len, width } => start + x as u64 * len / width as u64,
88 Columns::Buckets { origin, size, .. } => origin + x as u64 * size,
89 }
90 }
91
92 pub fn end(&self) -> u64 {
94 match *self {
95 Columns::Even { start, len, .. } => start + len,
96 Columns::Buckets { origin, size, count } => origin + count as u64 * size,
97 }
98 }
99
100 pub fn column_of(&self, s: u64) -> usize {
102 match *self {
103 Columns::Even { start, len, width } => {
104 if s < start || len == 0 {
105 0
106 } else {
107 (((s - start + 1) * width as u64 - 1) / len).min(width as u64 - 1) as usize
108 }
109 }
110 Columns::Buckets { origin, size, count } => (s.saturating_sub(origin) / size).min(count as u64 - 1) as usize,
111 }
112 }
113
114 fn next_start(&self, x: usize) -> u64 {
116 if x + 1 < self.count() { self.start(x + 1) } else { u64::MAX }
117 }
118}
119
120#[derive(Debug, Clone, Copy)]
122pub struct Block<'a> {
123 pub data: &'a [f32],
124 pub order: MemoryOrder,
126 pub channels: usize,
127 pub samples: usize,
128 pub first: u64,
130}
131
132pub fn fold_block(block: &Block, range: Range<usize>, columns: Columns, acc: &mut [[f32; 2]]) {
135 let count = columns.count();
136 if block.channels == 0 || count == 0 || range.is_empty() {
137 return;
138 }
139 debug_assert!(acc.len() >= block.channels * count && range.end <= block.samples);
140 let first = block.first + range.start as u64;
141 match block.order {
142 MemoryOrder::ChannelMajor => {
143 acc.par_chunks_mut(count).zip(block.data.par_chunks(block.samples)).take(block.channels).for_each(|(acc, row)| {
144 fold_row(&row[range.clone()], first, columns, acc);
145 });
146 }
147 MemoryOrder::TimeMajor => {
148 let frames = &block.data[range.start * block.channels..range.end * block.channels];
149 fold_frames(frames, block.channels, first, columns, acc);
150 }
151 }
152}
153
154pub fn fold_row(row: &[f32], first: u64, columns: Columns, acc: &mut [[f32; 2]]) {
156 let end = first + row.len() as u64;
157 let (mut s, mut x) = (first, columns.column_of(first));
158 while s < end {
159 let next = columns.next_start(x).min(end);
160 if next > s {
161 acc[x] = fold(&row[(s - first) as usize..(next - first) as usize], acc[x]);
162 s = next;
163 }
164 x += 1;
165 }
166}
167
168fn fold_frames(frames: &[f32], channels: usize, first: u64, columns: Columns, acc: &mut [[f32; 2]]) {
172 let n = frames.len() / channels;
173 let end = first + n as u64;
174 let (x0, x1) = (columns.column_of(first), columns.column_of(end - 1) + 1);
175 let tasks = rayon::current_num_threads().min(x1 - x0).max(1);
176 let parts: Vec<(usize, Vec<f32>, Vec<f32>)> = (0..tasks)
177 .into_par_iter()
178 .map(|k| {
179 let (xa, xb) = (x0 + k * (x1 - x0) / tasks, x0 + (k + 1) * (x1 - x0) / tasks);
180 let s0 = if xa == x0 { first } else { columns.start(xa) };
181 let s1 = if xb == x1 { end } else { columns.start(xb) };
182 let mut lo = vec![f32::INFINITY; (xb - xa) * channels];
183 let mut hi = vec![f32::NEG_INFINITY; (xb - xa) * channels];
184 let (mut x, mut next) = (xa, columns.next_start(xa));
185 for s in s0..s1 {
186 while s >= next {
187 x += 1;
188 next = columns.next_start(x);
189 }
190 let frame = &frames[(s - first) as usize * channels..][..channels];
191 let l = &mut lo[(x - xa) * channels..][..channels];
192 let h = &mut hi[(x - xa) * channels..][..channels];
193 for ((&v, l), h) in frame.iter().zip(l.iter_mut()).zip(h.iter_mut()) {
194 *l = if v < *l { v } else { *l };
195 *h = if v > *h { v } else { *h };
196 }
197 }
198 (xa, lo, hi)
199 })
200 .collect();
201 let count = columns.count();
202 for (xa, lo, hi) in parts {
203 for (i, (l, h)) in lo.chunks_exact(channels).zip(hi.chunks_exact(channels)).enumerate() {
204 for c in 0..channels {
205 let a = &mut acc[c * count + xa + i];
206 *a = merge(*a, [l[c], h[c]]);
207 }
208 }
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_mean_range() {
218 let nan = [f32::NAN, f32::NAN];
220 let env = [[0.0, 2.0], [1.0, 2.0], nan, [0.0, 4.0], nan, nan];
221 let m = mean_range(&env, 2, 3);
222 assert_eq!(m[0], 3.0);
223 assert_eq!(m[1], 1.0);
224 assert!(m[2].is_nan());
225 }
226
227 fn brute(row: &[f32]) -> [f32; 2] {
228 row.iter().fold(EMPTY, |[a, b], &v| [a.min(v), b.max(v)])
229 }
230
231 #[test]
232 fn test_fold_matches_brute_force_and_skips_nan() {
233 let v: Vec<f32> = (0..1001).map(|i| ((i * 7919) % 2003) as f32 - 1000.0).collect();
234 for len in [0, 1, 7, 8, 9, 100, 1001] {
235 assert_eq!(fold(&v[..len], EMPTY), brute(&v[..len]), "len {len}");
236 }
237 assert_eq!(fold(&[3.0, f32::NAN, -2.0], EMPTY), [-2.0, 3.0]);
238 }
239
240 #[test]
241 fn test_columns_map_samples_both_ways() {
242 let even = Columns::Even { start: 100, len: 1000, width: 7 };
243 for s in 100..1100u64 {
244 let x = even.column_of(s);
245 assert!(even.start(x) <= s && s < even.start(x + 1), "sample {s} in column {x}");
246 }
247 let buckets = Columns::Buckets { origin: 64, size: 10, count: 5 };
248 assert_eq!((buckets.column_of(64), buckets.column_of(73), buckets.column_of(74), buckets.column_of(500)), (0, 0, 1, 4));
249 }
250
251 #[test]
253 fn test_fold_block_matches_brute_force_in_both_orders() {
254 let (channels, samples) = (5usize, 3001usize);
255 let value = |c: usize, t: usize| ((t * 7919 + c * 104_729) % 2003) as f32 - 1000.0;
256 let cm: Vec<f32> = (0..channels).flat_map(|c| (0..samples).map(move |t| value(c, t))).collect();
257 let tm: Vec<f32> = (0..samples).flat_map(|t| (0..channels).map(move |c| value(c, t))).collect();
258 for columns in [Columns::Even { start: 1000, len: samples as u64, width: 97 }, Columns::Buckets { origin: 1000, size: 64, count: samples.div_ceil(64) }] {
259 let count = columns.count();
260 let mut want = vec![EMPTY; channels * count];
261 for c in 0..channels {
262 for x in 0..count {
263 let (a, b) = ((columns.start(x) - 1000) as usize, ((if x + 1 < count { columns.start(x + 1) } else { 1000 + samples as u64 }) - 1000) as usize);
264 want[c * count + x] = brute(&cm[c * samples + a..c * samples + b.min(samples)]);
265 }
266 }
267 for (data, order) in [(&cm, MemoryOrder::ChannelMajor), (&tm, MemoryOrder::TimeMajor)] {
268 let block = Block { data, order, channels, samples, first: 1000 };
269 let mut acc = vec![EMPTY; channels * count];
270 for range in [0..1234, 1234..1300, 1300..samples] {
271 fold_block(&block, range, columns, &mut acc);
272 }
273 assert_eq!(acc, want, "{order:?} {columns:?}");
274 }
275 }
276 }
277}