dsp_process/
compose.rs

1use crate::{SplitInplace, SplitProcess};
2use core::marker::PhantomData;
3
4//////////// SPLIT COMPOSE ////////////
5
6/// Chain of two different large filters
7///
8/// `X->Y->Y`
9impl<X: Copy, Y: Copy, C0, C1, S0, S1> SplitProcess<X, Y, (S0, S1)> for (C0, C1)
10where
11    C0: SplitProcess<X, Y, S0>,
12    C1: SplitInplace<Y, S1>,
13{
14    fn process(&self, state: &mut (S0, S1), x: X) -> Y {
15        self.1
16            .process(&mut state.1, self.0.process(&mut state.0, x))
17    }
18
19    fn block(&self, state: &mut (S0, S1), x: &[X], y: &mut [Y]) {
20        self.0.block(&mut state.0, x, y);
21        self.1.inplace(&mut state.1, y);
22    }
23}
24
25impl<X: Copy, C0, C1, S0, S1> SplitInplace<X, (S0, S1)> for (C0, C1)
26where
27    C0: SplitInplace<X, S0>,
28    C1: SplitInplace<X, S1>,
29{
30    fn inplace(&self, state: &mut (S0, S1), xy: &mut [X]) {
31        self.0.inplace(&mut state.0, xy);
32        self.1.inplace(&mut state.1, xy);
33    }
34}
35
36/// Chain of multiple large filters of the same type
37///
38///
39/// `X->X->X...`
40///
41/// * Clice can be empty
42impl<X: Copy, C, S> SplitProcess<X, X, [S]> for [C]
43where
44    C: SplitInplace<X, S>,
45{
46    fn process(&self, state: &mut [S], x: X) -> X {
47        debug_assert_eq!(self.len(), state.len());
48        self.iter()
49            .zip(state.iter_mut())
50            .fold(x, |x, (c, s)| c.process(s, x))
51    }
52
53    fn block(&self, state: &mut [S], x: &[X], y: &mut [X]) {
54        debug_assert_eq!(self.len(), state.len());
55        if let Some(((c0, c), (s0, s))) = self.split_first().zip(state.split_first_mut()) {
56            c0.block(s0, x, y);
57            for (c, s) in c.iter().zip(s) {
58                c.inplace(s, y);
59            }
60        } else {
61            y.copy_from_slice(x);
62        }
63    }
64}
65
66impl<X: Copy, C, S> SplitInplace<X, [S]> for [C]
67where
68    C: SplitInplace<X, S>,
69{
70    fn inplace(&self, state: &mut [S], xy: &mut [X]) {
71        debug_assert_eq!(self.len(), state.len());
72        for (c, s) in self.iter().zip(state.iter_mut()) {
73            c.inplace(s, xy);
74        }
75    }
76}
77
78/// A chain of multiple large filters of the same type
79///
80/// `X->Y->Y...`
81impl<X: Copy, Y: Copy, C, S, const N: usize> SplitProcess<X, Y, [S; N]> for [C; N]
82where
83    C: SplitProcess<X, Y, S> + SplitInplace<Y, S>,
84{
85    fn process(&self, state: &mut [S; N], x: X) -> Y {
86        const { assert!(N > 0) }
87        let Some(((c0, c), (s0, s))) = self.split_first().zip(state.split_first_mut()) else {
88            unreachable!()
89        };
90        c.iter()
91            .zip(s.iter_mut())
92            .fold(c0.process(s0, x), |x, (c, s)| c.process(s, x))
93    }
94
95    fn block(&self, state: &mut [S; N], x: &[X], y: &mut [Y]) {
96        const { assert!(N > 0) }
97        let Some(((c0, c), (s0, s))) = self.split_first().zip(state.split_first_mut()) else {
98            unreachable!()
99        };
100        c0.block(s0, x, y);
101        for (c, s) in c.iter().zip(s.iter_mut()) {
102            c.inplace(s, y)
103        }
104    }
105}
106
107impl<X: Copy, C, S, const N: usize> SplitInplace<X, [S; N]> for [C; N]
108where
109    C: SplitInplace<X, S>,
110{
111    fn inplace(&self, state: &mut [S; N], xy: &mut [X]) {
112        self.as_ref().inplace(state.as_mut(), xy)
113    }
114}
115
116//////////// SPLIT MINOR ////////////
117
118/// Processor-minor, data-major
119///
120/// The various Process tooling implementations for `Minor`
121/// place the data loop as the outer-most loop (processor-minor, data-major).
122/// This is optimal for processors with small or no state and configuration.
123///
124/// Chain of large processors are implemented through native
125/// tuples and slices/arrays or [`Major`].
126/// Those optimize well if the sizes obey configuration ~ state > data.
127/// If they do not, use `Minor`.
128///
129/// Note that the major implementations only override the behavior
130/// for `block()` and `inplace()`. `process()` is unaffected and the same for all.
131#[derive(Clone, Copy, Debug, Default)]
132#[repr(transparent)]
133pub struct Minor<C: ?Sized, U> {
134    /// An intermediate data type
135    _intermediate: PhantomData<U>,
136    /// The inner configurations
137    pub inner: C,
138}
139
140impl<C, U> Minor<C, U> {
141    /// Create a new chain
142    pub const fn new(inner: C) -> Self {
143        Self {
144            inner,
145            _intermediate: PhantomData,
146        }
147    }
148}
149
150impl<X: Copy, U: Copy, Y, C0, C1, S0, S1> SplitProcess<X, Y, (S0, S1)> for Minor<(C0, C1), U>
151where
152    C0: SplitProcess<X, U, S0>,
153    C1: SplitProcess<U, Y, S1>,
154{
155    fn process(&self, state: &mut (S0, S1), x: X) -> Y {
156        self.inner
157            .1
158            .process(&mut state.1, self.inner.0.process(&mut state.0, x))
159    }
160}
161
162/// A chain of multiple small filters of the same type
163impl<X: Copy, C, S> SplitProcess<X, X, [S]> for Minor<[C], X>
164where
165    C: SplitProcess<X, X, S>,
166{
167    fn process(&self, state: &mut [S], x: X) -> X {
168        debug_assert_eq!(self.inner.len(), state.len());
169        self.inner
170            .iter()
171            .zip(state.iter_mut())
172            .fold(x, |x, (c, s)| c.process(s, x))
173    }
174}
175
176/// A chain of multiple small filters of the same type
177impl<X: Copy, Y: Copy, C, S, const N: usize> SplitProcess<X, Y, [S; N]> for Minor<[C; N], Y>
178where
179    C: SplitProcess<X, Y, S> + SplitProcess<Y, Y, S>,
180{
181    fn process(&self, state: &mut [S; N], x: X) -> Y {
182        const { assert!(N > 0) }
183        let Some(((c0, c), (s0, s))) = self.inner.split_first().zip(state.split_first_mut()) else {
184            unreachable!()
185        };
186        c.iter()
187            .zip(s.iter_mut())
188            .fold(c0.process(s0, x), |x, (c, s)| c.process(s, x))
189    }
190}
191
192impl<X: Copy, U, C, S> SplitInplace<X, S> for Minor<C, U> where Self: SplitProcess<X, X, S> {}
193
194//////////// SPLIT PARALLEL ////////////
195
196/// Fan out parallel input to parallel processors
197#[derive(Clone, Copy, Debug, Default)]
198pub struct Parallel<P>(pub P);
199
200impl<X0: Copy, X1: Copy, Y0, Y1, C0, C1, S0, S1> SplitProcess<(X0, X1), (Y0, Y1), (S0, S1)>
201    for Parallel<(C0, C1)>
202where
203    C0: SplitProcess<X0, Y0, S0>,
204    C1: SplitProcess<X1, Y1, S1>,
205{
206    fn process(&self, state: &mut (S0, S1), x: (X0, X1)) -> (Y0, Y1) {
207        (
208            self.0.0.process(&mut state.0, x.0),
209            self.0.1.process(&mut state.1, x.1),
210        )
211    }
212}
213
214impl<X: Copy, Y, C0, C1, S0, S1> SplitProcess<[X; 2], [Y; 2], (S0, S1)> for Parallel<(C0, C1)>
215where
216    C0: SplitProcess<X, Y, S0>,
217    C1: SplitProcess<X, Y, S1>,
218{
219    fn process(&self, state: &mut (S0, S1), x: [X; 2]) -> [Y; 2] {
220        [
221            self.0.0.process(&mut state.0, x[0]),
222            self.0.1.process(&mut state.1, x[1]),
223        ]
224    }
225}
226
227impl<X: Copy, Y: Copy + Default, C, S, const N: usize> SplitProcess<[X; N], [Y; N], [S; N]>
228    for Parallel<[C; N]>
229where
230    C: SplitProcess<X, Y, S>,
231{
232    fn process(&self, state: &mut [S; N], x: [X; N]) -> [Y; N] {
233        let mut y = [Y::default(); N];
234        for ((c, s), (x, y)) in self
235            .0
236            .iter()
237            .zip(state.iter_mut())
238            .zip(x.into_iter().zip(y.iter_mut()))
239        {
240            *y = c.process(s, x);
241        }
242        y
243    }
244}
245
246impl<X: Copy, C, S> SplitInplace<X, S> for Parallel<C> where Self: SplitProcess<X, X, S> {}
247
248//////////// TRANSPOSE ////////////
249
250/// Data block transposition wrapper
251///
252/// Like [`Parallel`] but reinterpreting data as transpose `[[X; N]] <-> [[X]; N]`
253/// such that `block()` and `inplace()` are lowered.
254#[derive(Clone, Copy, Debug, Default)]
255pub struct Transpose<C>(pub C);
256
257impl<X: Copy, Y, C0, C1, S0, S1> SplitProcess<[X; 2], [Y; 2], (S0, S1)> for Transpose<(C0, C1)>
258where
259    C0: SplitProcess<X, Y, S0>,
260    C1: SplitProcess<X, Y, S1>,
261{
262    fn process(&self, state: &mut (S0, S1), x: [X; 2]) -> [Y; 2] {
263        [
264            self.0.0.process(&mut state.0, x[0]),
265            self.0.1.process(&mut state.1, x[1]),
266        ]
267    }
268
269    fn block(&self, state: &mut (S0, S1), x: &[[X; 2]], y: &mut [[Y; 2]]) {
270        debug_assert_eq!(x.len(), y.len());
271        let n = x.len();
272        let (x0, x1) = x.as_flattened().split_at(n);
273        let (y0, y1) = y.as_flattened_mut().split_at_mut(n);
274        self.0.0.block(&mut state.0, x0, y0);
275        self.0.1.block(&mut state.1, x1, y1);
276    }
277}
278
279impl<X: Copy, C0, C1, S0, S1> SplitInplace<[X; 2], (S0, S1)> for Transpose<(C0, C1)>
280where
281    C0: SplitInplace<X, S0>,
282    C1: SplitInplace<X, S1>,
283{
284    fn inplace(&self, state: &mut (S0, S1), xy: &mut [[X; 2]]) {
285        let n = xy.len();
286        let (xy0, xy1) = xy.as_flattened_mut().split_at_mut(n);
287        self.0.0.inplace(&mut state.0, xy0);
288        self.0.1.inplace(&mut state.1, xy1);
289    }
290}
291
292impl<X: Copy, Y: Copy + Default, C, S, const N: usize> SplitProcess<[X; N], [Y; N], [S; N]>
293    for Transpose<[C; N]>
294where
295    C: SplitProcess<X, Y, S>,
296{
297    fn process(&self, state: &mut [S; N], x: [X; N]) -> [Y; N] {
298        let mut y = [Y::default(); N];
299        for ((c, s), (x, y)) in self
300            .0
301            .iter()
302            .zip(state.iter_mut())
303            .zip(x.into_iter().zip(y.iter_mut()))
304        {
305            *y = c.process(s, x);
306        }
307        y
308    }
309
310    fn block(&self, state: &mut [S; N], x: &[[X; N]], y: &mut [[Y; N]]) {
311        debug_assert_eq!(x.len(), y.len());
312        let n = x.len();
313        for ((c, s), (x, y)) in self.0.iter().zip(state.iter_mut()).zip(
314            x.as_flattened()
315                .chunks_exact(n)
316                .zip(y.as_flattened_mut().chunks_exact_mut(n)),
317        ) {
318            c.block(s, x, y)
319        }
320    }
321}
322
323impl<X: Copy + Default, C, S, const N: usize> SplitInplace<[X; N], [S; N]> for Transpose<[C; N]>
324where
325    C: SplitInplace<X, S>,
326{
327    fn inplace(&self, state: &mut [S; N], xy: &mut [[X; N]]) {
328        let n = xy.len();
329        for ((c, s), xy) in self
330            .0
331            .iter()
332            .zip(state.iter_mut())
333            .zip(xy.as_flattened_mut().chunks_exact_mut(n))
334        {
335            c.inplace(s, xy)
336        }
337    }
338}
339
340//////////// CHANNELS ////////////
341
342/// Multiple channels to be processed with the same configuration
343#[derive(Clone, Copy, Debug, Default)]
344pub struct Channels<C>(pub C);
345
346/// Process data from multiple channels with a common configuration
347///
348/// Note that block() and inplace() reinterpret the data as [`Transpose`]: __not__ as `[[X; N]]` but as `[[X]; N]`.
349/// Use `x.as_flattened().chunks_exact(x.len())`/`x.as_chunks<N>().0` etc. to match that.
350impl<X: Copy, Y: Copy + Default, C, S, const N: usize> SplitProcess<[X; N], [Y; N], [S; N]>
351    for Channels<C>
352where
353    C: SplitProcess<X, Y, S>,
354{
355    fn process(&self, state: &mut [S; N], x: [X; N]) -> [Y; N] {
356        let mut y = [Y::default(); N];
357        for ((x, y), state) in x.into_iter().zip(y.iter_mut()).zip(state.iter_mut()) {
358            *y = self.0.process(state, x);
359        }
360        y
361    }
362
363    fn block(&self, state: &mut [S; N], x: &[[X; N]], y: &mut [[Y; N]]) {
364        debug_assert_eq!(x.len(), y.len());
365        let n = x.len();
366        for ((x, y), state) in x
367            .as_flattened()
368            .chunks_exact(n)
369            .zip(y.as_flattened_mut().chunks_exact_mut(n))
370            .zip(state.iter_mut())
371        {
372            self.0.block(state, x, y)
373        }
374    }
375}
376
377impl<X: Copy + Default, C, S, const N: usize> SplitInplace<[X; N], [S; N]> for Channels<C>
378where
379    C: SplitInplace<X, S>,
380{
381    fn inplace(&self, state: &mut [S; N], xy: &mut [[X; N]]) {
382        let n = xy.len();
383        for (xy, state) in xy
384            .as_flattened_mut()
385            .chunks_exact_mut(n)
386            .zip(state.iter_mut())
387        {
388            self.0.inplace(state, xy)
389        }
390    }
391}
392
393//////////// SPLIT MAJOR ////////////
394
395/// Chain of major processors with intermediate buffer supporting block() processing
396/// from individual block()s
397///
398/// Prefer default composition for X->X->X, arrays/slices where inplace is possible
399#[derive(Debug, Clone, Copy, Default)]
400pub struct Major<P: ?Sized, U> {
401    /// Intermediate buffer
402    _buf: PhantomData<U>,
403    /// The inner processors
404    pub inner: P,
405}
406impl<P, U> Major<P, U> {
407    /// Create a new chain of processors
408    pub const fn new(inner: P) -> Self {
409        Self {
410            inner,
411            _buf: PhantomData,
412        }
413    }
414}
415
416impl<X: Copy, U: Copy + Default, Y, C0, C1, S0, S1, const N: usize> SplitProcess<X, Y, (S0, S1)>
417    for Major<(C0, C1), [U; N]>
418where
419    C0: SplitProcess<X, U, S0>,
420    C1: SplitProcess<U, Y, S1>,
421{
422    fn process(&self, state: &mut (S0, S1), x: X) -> Y {
423        self.inner
424            .1
425            .process(&mut state.1, self.inner.0.process(&mut state.0, x))
426    }
427
428    fn block(&self, state: &mut (S0, S1), x: &[X], y: &mut [Y]) {
429        let mut u = [U::default(); N];
430        let (x, xr) = x.as_chunks::<N>();
431        let (y, yr) = y.as_chunks_mut::<N>();
432        for (x, y) in x.iter().zip(y) {
433            self.inner.0.block(&mut state.0, x, &mut u);
434            self.inner.1.block(&mut state.1, &u, y);
435        }
436        debug_assert_eq!(xr.len(), yr.len());
437        let ur = &mut u[..xr.len()];
438        self.inner.0.block(&mut state.0, xr, ur);
439        self.inner.1.block(&mut state.1, ur, yr);
440    }
441}
442
443impl<X: Copy, U: Copy + Default, C0, C1, S0, S1, const N: usize> SplitInplace<X, (S0, S1)>
444    for Major<(C0, C1), [U; N]>
445where
446    C0: SplitProcess<X, U, S0>,
447    C1: SplitProcess<U, X, S1>,
448{
449    fn inplace(&self, state: &mut (S0, S1), xy: &mut [X]) {
450        let mut u = [U::default(); N];
451        let (xy, xyr) = xy.as_chunks_mut::<N>();
452        for xy in xy {
453            self.inner.0.block(&mut state.0, xy, &mut u);
454            self.inner.1.block(&mut state.1, &u, xy);
455        }
456        let ur = &mut u[..xyr.len()];
457        self.inner.0.block(&mut state.0, xyr, ur);
458        self.inner.1.block(&mut state.1, ur, xyr);
459    }
460}