solvers/
parallel.rs

1//! Parallel utilities with feature-gated implementations
2//!
3//! Provides parallel abstractions that work across native (rayon) and WASM
4//! (wasm-bindgen-rayon) environments, with sequential fallbacks.
5
6/// Check if parallel processing is available
7#[cfg(any(feature = "native", feature = "wasm"))]
8pub fn is_parallel_available() -> bool {
9    true
10}
11
12/// Check if parallel processing is available
13#[cfg(not(any(feature = "native", feature = "wasm")))]
14pub fn is_parallel_available() -> bool {
15    false
16}
17
18/// Parallel map over a slice
19#[cfg(any(feature = "native", feature = "wasm"))]
20pub fn parallel_map<T, U, F>(data: &[T], f: F) -> Vec<U>
21where
22    T: Sync,
23    U: Send,
24    F: Fn(&T) -> U + Sync + Send,
25{
26    use rayon::prelude::*;
27    data.par_iter().map(f).collect()
28}
29
30/// Sequential map (fallback when parallel is not available)
31#[cfg(not(any(feature = "native", feature = "wasm")))]
32pub fn parallel_map<T, U, F>(data: &[T], f: F) -> Vec<U>
33where
34    F: Fn(&T) -> U,
35{
36    data.iter().map(f).collect()
37}
38
39/// Parallel map with index
40#[cfg(any(feature = "native", feature = "wasm"))]
41pub fn parallel_map_indexed<U, F>(count: usize, f: F) -> Vec<U>
42where
43    U: Send,
44    F: Fn(usize) -> U + Sync + Send,
45{
46    use rayon::prelude::*;
47    (0..count).into_par_iter().map(f).collect()
48}
49
50/// Sequential map with index (fallback)
51#[cfg(not(any(feature = "native", feature = "wasm")))]
52pub fn parallel_map_indexed<U, F>(count: usize, f: F) -> Vec<U>
53where
54    F: Fn(usize) -> U,
55{
56    (0..count).map(f).collect()
57}
58
59/// Parallel for_each over a slice
60#[cfg(any(feature = "native", feature = "wasm"))]
61pub fn parallel_for_each<T, F>(data: &[T], f: F)
62where
63    T: Sync,
64    F: Fn(&T) + Sync + Send,
65{
66    use rayon::prelude::*;
67    data.par_iter().for_each(f);
68}
69
70/// Sequential for_each (fallback)
71#[cfg(not(any(feature = "native", feature = "wasm")))]
72pub fn parallel_for_each<T, F>(data: &[T], f: F)
73where
74    F: Fn(&T),
75{
76    data.iter().for_each(f);
77}
78
79/// Parallel enumerate and map
80#[cfg(any(feature = "native", feature = "wasm"))]
81pub fn parallel_enumerate_map<T, U, F>(data: &[T], f: F) -> Vec<U>
82where
83    T: Sync,
84    U: Send,
85    F: Fn(usize, &T) -> U + Sync + Send,
86{
87    use rayon::prelude::*;
88    data.par_iter().enumerate().map(|(i, x)| f(i, x)).collect()
89}
90
91/// Sequential enumerate and map (fallback)
92#[cfg(not(any(feature = "native", feature = "wasm")))]
93pub fn parallel_enumerate_map<T, U, F>(data: &[T], f: F) -> Vec<U>
94where
95    F: Fn(usize, &T) -> U,
96{
97    data.iter().enumerate().map(|(i, x)| f(i, x)).collect()
98}
99
100/// Parallel filter_map
101#[cfg(any(feature = "native", feature = "wasm"))]
102pub fn parallel_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
103where
104    T: Sync,
105    U: Send,
106    F: Fn(&T) -> Option<U> + Sync + Send,
107{
108    use rayon::prelude::*;
109    data.par_iter().filter_map(f).collect()
110}
111
112/// Sequential filter_map (fallback)
113#[cfg(not(any(feature = "native", feature = "wasm")))]
114pub fn parallel_filter_map<T, U, F>(data: &[T], f: F) -> Vec<U>
115where
116    F: Fn(&T) -> Option<U>,
117{
118    data.iter().filter_map(f).collect()
119}
120
121/// Parallel flat_map
122#[cfg(any(feature = "native", feature = "wasm"))]
123pub fn parallel_flat_map<T, U, I, F>(data: &[T], f: F) -> Vec<U>
124where
125    T: Sync,
126    U: Send,
127    I: rayon::iter::IntoParallelIterator<Item = U>,
128    F: Fn(&T) -> I + Sync + Send,
129{
130    use rayon::prelude::*;
131    data.par_iter().flat_map(f).collect()
132}
133
134/// Sequential flat_map (fallback)
135#[cfg(not(any(feature = "native", feature = "wasm")))]
136pub fn parallel_flat_map<T, U, I, F>(data: &[T], f: F) -> Vec<U>
137where
138    I: IntoIterator<Item = U>,
139    F: Fn(&T) -> I,
140{
141    data.iter().flat_map(f).collect()
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_parallel_map() {
150        let data = vec![1, 2, 3, 4, 5];
151        let result = parallel_map(&data, |x| x * 2);
152        assert_eq!(result, vec![2, 4, 6, 8, 10]);
153    }
154
155    #[test]
156    fn test_parallel_map_indexed() {
157        let result = parallel_map_indexed(5, |i| i * 2);
158        assert_eq!(result, vec![0, 2, 4, 6, 8]);
159    }
160
161    #[test]
162    fn test_parallel_enumerate_map() {
163        let data = vec![10, 20, 30];
164        let result = parallel_enumerate_map(&data, |i, x| (i, *x));
165        assert_eq!(result, vec![(0, 10), (1, 20), (2, 30)]);
166    }
167}