orx_parallel/infallible/par.rs
1use crate::infallible::fun::{FnCloned, FnCopied};
2use crate::infallible::xap::FlattenOf;
3use crate::infallible::{FilMapOf, FilOf, FlatMapOf, InsOf, MapOf, MappedOf, ParIter};
4use crate::infallible::{Xap, xap_variants::Id};
5use crate::infallible_use::{ParUseIter, xap_variants::IdUse};
6use crate::option::ParOptionIter;
7use crate::result::ParResultIter;
8use crate::sizes::{One, Size};
9use crate::use_var::{UseSlice, UseVec};
10use crate::{ChunkSize, IterationOrder, NumThreads, ParExtend};
11use crate::{ParOption, ParResult, ParUse, Sum};
12use crate::{infallible::par_core::ParCore, runner::ParRunner};
13use alloc::vec::Vec;
14use core::cmp::Ordering;
15use orx_concurrent_iter::ExactSizeConcurrentIter;
16
17/// Infallible parallel iterator.
18///
19/// `Par` is the central trait for describing parallel computations as iterator
20/// pipelines. It mirrors common sequential iterator operations (`map`,
21/// `filter`, `flat_map`, `collect`, `reduce`, ...) while allowing runtime
22/// configuration of execution details such as number of threads, chunk size,
23/// iteration order, and runner/pool selection.
24///
25/// Related traits:
26/// - [`ParUse`](crate::ParUse) for worker-local mutable state,
27/// - [`ParOption`](crate::ParOption) for `Option`-based fallibility,
28/// - [`ParResult`](crate::ParResult) for `Result`-based fallibility.
29///
30/// # Examples
31///
32/// ```
33/// use orx_parallel::*;
34///
35/// let sum_of_even_squares: usize = (1..11)
36/// .into_par()
37/// .map(|x| x * x)
38/// .filter(|x| x % 2 == 0)
39/// .sum();
40///
41/// assert_eq!(sum_of_even_squares, 220);
42/// ```
43pub trait Par: Sized + ParCore {
44 // configuration
45
46 /// Replaces the current parallel runner with `runner`.
47 ///
48 /// This allows per-computation control over execution strategy.
49 ///
50 /// Please see [`Runner`] for parallel runners implemented in this crate.
51 ///
52 /// [`Runner`]: crate::Runner
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use orx_parallel::*;
58 ///
59 /// let baseline: usize = (0..1000).into_par().sum();
60 ///
61 /// let par = (0..1000).par();
62 ///
63 /// let par = par.runner(Runner::fixed());
64 ///
65 /// let configured: usize = par.sum();
66 /// assert_eq!(baseline, configured);
67 /// ```
68 fn runner<Q: ParRunner>(
69 self,
70 runner: Q,
71 ) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>;
72
73 /// Wraps the current parallel runner with a diagnostics-enabled runner.
74 ///
75 /// The returned iterator behaves the same, but additionally reports runtime
76 /// diagnostics at the end of the computation.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// # #[cfg(feature = "std")]
82 /// # fn main() {
83 /// use orx_parallel::*;
84 ///
85 /// let par = (1..10_001).par().num_threads(4);
86 ///
87 /// #[cfg(feature = "std")]
88 /// let par = par.runner_with_diagnostics();
89 ///
90 /// let sum = par.sum();
91 /// assert_eq!(sum, 50005000);
92 /// # }
93 /// ```
94 ///
95 /// This will print a summary report which currently looks like the following:
96 ///
97 /// ```console
98 /// │ # Parallel Executor Diagnostics
99 /// │
100 /// │ Available threads : 4
101 /// │ Used threads : 4
102 /// │ Wall time : 1.15 ms
103 /// │
104 /// │ ## Summary Table
105 /// │ thread num_chunks num_tasks min_chunk avg_chunk max_chunk util%
106 /// │ ------ ---------- ---------- --------- --------- --------- -------
107 /// │ 0 35 27335 781 781 781 100.0%
108 /// │ 1 32 24992 781 781 781 91.5%
109 /// │ 2 30 23430 781 781 781 85.9%
110 /// │ 3 28 21868 781 781 781 77.8%
111 /// │
112 /// │ ## Workload Balance
113 /// │ max/min task ratio : 1.25x (1.00 = perfect balance)
114 /// │ coeff. of variation : 8.3% (lower is better)
115 /// │
116 /// │ ## Thread Active Timeline (each block ≈ 0.02 ms)
117 /// │ [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
118 /// │ [ 1] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
119 /// │ [ 2] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
120 /// │ [ 3] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
121 /// │
122 /// │ ## Thread Task Distribution (bar length ∝ tasks processed)
123 /// │ [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (27335)
124 /// │ [ 1] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (24992)
125 /// │ [ 2] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (23430)
126 /// │ [ 3] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ (21868)
127 /// ```
128 #[cfg(feature = "std")]
129 fn runner_with_diagnostics(
130 self,
131 ) -> impl Par<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>;
132
133 /// Sets the maximum number of worker threads for this computation.
134 ///
135 /// This method configures the **computation layer** of the thread count decision.
136 /// The actual number of threads used is determined by combining:
137 ///
138 /// 1. **Pool constraint** (from `pool()` method or default pool)
139 /// - Already includes `ORX_NUM_THREADS` environment variable constraint
140 /// 2. **Computation constraint** (this method)
141 /// - Your per-computation thread preference
142 /// 3. **Input size constraint**
143 /// - Cannot spawn more threads than input elements
144 ///
145 /// The actual thread count is the **minimum** of all these constraints.
146 ///
147 /// # Parameter Interpretation
148 ///
149 /// Integer values map as follows:
150 /// - `0` => `NumThreads::Auto` (use all available threads, spawn only as needed)
151 /// - `n > 0` => `NumThreads::Max(n)` (cap at `n` threads)
152 ///
153 /// # Thread Count Decision Logic
154 ///
155 /// ```text
156 /// available = pool.max_num_threads() // Pool maximum (includes env variable)
157 ///
158 /// requested = match num_threads {
159 /// 0 | Auto => input_size.max(1), // Limited by input size
160 /// Max(n) => min(input_size, n), // Limited by input size and this param
161 /// };
162 ///
163 /// actual_threads = min(requested, available)
164 /// ```
165 ///
166 /// # Examples
167 ///
168 /// ```ignore
169 /// use orx_parallel::*;
170 ///
171 /// // Sequential execution
172 /// let sum: usize = (1..11).into_par().num_threads(1).sum();
173 /// assert_eq!(sum, 55);
174 ///
175 /// // Cap at 4 threads
176 /// let sum: usize = (1..1001).into_par().num_threads(4).sum();
177 ///
178 /// // Auto: uses available threads (respects ORX_NUM_THREADS)
179 /// let sum: usize = (1..11).into_par().num_threads(0).sum();
180 /// ```
181 ///
182 /// # See Also
183 ///
184 /// - [`NumThreads`](crate::NumThreads) - Type for thread configuration
185 /// - [`thread_usage.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/thread_usage.md) - Complete threading guide
186 fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self;
187
188 /// Sets chunk size used when pulling items from the concurrent input.
189 ///
190 /// Integer values map as follows:
191 /// - `0` => automatic (default)
192 /// - `n > 0` => exact chunk size `n`
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// use orx_parallel::*;
198 ///
199 /// let values: Vec<_> = (0..32)
200 /// .into_par()
201 /// .chunk_size(8)
202 /// .map(|x| x + 1)
203 /// .collect();
204 ///
205 /// assert_eq!(values.len(), 32);
206 /// assert_eq!(values[0], 1);
207 /// assert_eq!(values[31], 32);
208 /// ```
209 ///
210 /// # Rules of Thumb
211 ///
212 /// * Automatic chunk size (default) is efficient in general.
213 /// Parallel runner aims to find best chunk sizes to balance between minimizing parallelization overhead
214 /// and maximizing resource utilization.
215 /// * While tuning a specific computation, we aim to find the smallest chunk size that is large enough
216 /// to mitigate the impact of parallelization overhead.
217 /// * If the individual tasks are large enough, parallelization overhead becomes insignificant making
218 /// `chunk_size = 1` the optimal choice.
219 fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self;
220
221 /// Sets iteration order semantics for operations sensitive to ordering.
222 ///
223 /// `Ordered` (default) preserves positional meaning (for example, `first` returns the
224 /// earliest matching element in input order). `Arbitrary` allows any matching
225 /// element that is reached first in parallel execution.
226 ///
227 /// # Examples
228 ///
229 /// ```
230 /// use orx_parallel::*;
231 ///
232 /// let ordered = (1..10_000)
233 /// .into_par()
234 /// .iteration_order(IterationOrder::Ordered)
235 /// .find(|x| x % 3421 == 0);
236 /// assert_eq!(ordered, Some(3421));
237 ///
238 /// let any = (1..10_000)
239 /// .into_par()
240 /// .iteration_order(IterationOrder::Arbitrary)
241 /// .find(|x| x % 3421 == 0)
242 /// .unwrap();
243 /// assert!([3421, 6842].contains(&any));
244 /// ```
245 fn iteration_order(self, collect: IterationOrder) -> Self;
246
247 // kind transformations
248
249 /// Converts `Par<Item = Option<T>>` into `ParOption<Item = T>`.
250 ///
251 /// The resulting fallible iterator **short-circuits** to `None` if any element is `None`.
252 ///
253 /// Similar to pattern using the `?` operator, fallible iterators allow us to work with
254 /// the **success path**.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use orx_parallel::*;
260 ///
261 /// let ok: Option<Vec<_>> = ["1", "2", "3"]
262 /// .into_par()
263 /// .map(|s| s.parse::<i32>().ok())
264 /// .into_optional()
265 /// .map(|x| x * 2)
266 /// .filter(|x| *x > 3)
267 /// .collect();
268 /// assert_eq!(ok, Some(vec![4, 6]));
269 ///
270 /// let fail: Option<Vec<_>> = ["1", "x", "3"]
271 /// .into_par()
272 /// .map(|s| s.parse::<i32>().ok())
273 /// .into_optional()
274 /// .map(|x| x * 2)
275 /// .filter(|x| *x > 3)
276 /// .collect();
277 /// assert_eq!(fail, None);
278 /// ```
279 ///
280 /// Notice that `x` is of type `i32`, rather than `Option<i32>`, which allows for concise
281 /// expressions.
282 ///
283 /// Without fallible iterators, the above result could be obtained by the following version,
284 /// which is not only more verbose, but also lacks the short-circuiting mechanism.
285 ///
286 /// ```
287 /// use orx_parallel::*;
288 ///
289 /// let ok: Option<Vec<_>> = ["1", "2", "3"]
290 /// .into_par()
291 /// .map(|s| s.parse::<i32>().ok())
292 /// .map(|x| x.map(|x| x * 2))
293 /// .filter(|x| x.as_ref().map(|x| *x > 3).unwrap_or(true))
294 /// .collect::<Vec<_>>()
295 /// .into_iter()
296 /// .collect();
297 /// assert_eq!(ok, Some(vec![4, 6]));
298 /// ```
299 fn into_optional<T>(
300 self,
301 ) -> impl ParOption<
302 Elem = T,
303 Xap1 = Self::Xap,
304 M = T,
305 Xap2 = Id<T>,
306 Input = Self::Input,
307 Size = <<Self::Xap as Xap>::Size as Size>::IntoPair,
308 >
309 where
310 Self::Xap: Xap<O = Option<T>>,
311 {
312 let (iter, xap, exe, params) = self.destruct();
313
314 ParOptionIter::new(iter, xap, Id::new(), exe, params)
315 }
316
317 /// Converts `Par<Item = Result<T, E>>` into `ParResult<Item = T, Error = E>`.
318 ///
319 /// The resulting fallible iterator **short-circuits** and returns the first
320 /// observed error.
321 ///
322 /// Similar to pattern using the `?` operator, fallible iterators allow us to
323 /// work with the **success path**.
324 ///
325 /// # Examples
326 ///
327 /// ```
328 /// use orx_parallel::*;
329 ///
330 /// let ok: Result<Vec<_>, _> = ["1", "2", "3"]
331 /// .into_par()
332 /// .map(|s| s.parse::<i32>())
333 /// .into_fallible()
334 /// .map(|x| x * 2)
335 /// .filter(|x| *x > 3)
336 /// .collect();
337 /// assert_eq!(ok, Ok(vec![4, 6]));
338 ///
339 /// let fail: Result<Vec<_>, _> = ["1", "x", "3"]
340 /// .into_par()
341 /// .map(|s| s.parse::<i32>())
342 /// .into_fallible()
343 /// .map(|x| x * 2)
344 /// .filter(|x| *x > 3)
345 /// .collect();
346 /// assert!(fail.is_err());
347 /// ```
348 ///
349 /// Notice that `x` is of type `i32`, rather than `Result<i32, _>`, which
350 /// allows for concise expressions.
351 ///
352 /// Without fallible iterators, the above result could be obtained by the
353 /// following version, which is not only more verbose, but also lacks the
354 /// short-circuiting mechanism.
355 ///
356 /// ```
357 /// use orx_parallel::*;
358 ///
359 /// let ok: Result<Vec<_>, _> = ["1", "2", "3"]
360 /// .into_par()
361 /// .map(|s| s.parse::<i32>())
362 /// .map(|x| x.map(|x| x * 2))
363 /// .filter(|x| x.as_ref().map(|x| *x > 3).unwrap_or(true))
364 /// .collect::<Vec<_>>()
365 /// .into_iter()
366 /// .collect();
367 /// assert_eq!(ok, Ok(vec![4, 6]));
368 /// ```
369 fn into_fallible<T, E>(
370 self,
371 ) -> impl ParResult<
372 Elem = T,
373 Error = E,
374 Xap1 = Self::Xap,
375 M = T,
376 Xap2 = Id<T>,
377 Input = Self::Input,
378 Size = <<Self::Xap as Xap>::Size as Size>::IntoPair,
379 >
380 where
381 Self::Xap: Xap<O = Result<T, E>>,
382 {
383 let (iter, xap, exe, params) = self.destruct();
384 ParResultIter::new(iter, xap, Id::new(), exe, params)
385 }
386
387 /// Creates one mutable `Use` value per participating worker.
388 ///
389 /// The initializer `f` is called with the worker's thread index, and the
390 /// returned value is then passed as `&mut Use` to downstream [`ParUse`]
391 /// operations such as `map`, `filter`, `flat_map`, `reduce`, and `for_each`.
392 ///
393 /// This is useful for thread-local scratch buffers, counters, or other
394 /// mutable state that should not be shared across workers.
395 ///
396 /// # Examples
397 ///
398 /// Reusing one buffer per worker avoids allocating a fresh String
399 /// for every parsed item.
400 ///
401 /// ```
402 /// use orx_parallel::*;
403 ///
404 /// let values: Vec<_> = (1..4)
405 /// .into_par()
406 /// .num_threads(1)
407 /// .use_new(|_| String::new())
408 /// .map(|buffer, x| {
409 /// buffer.clear();
410 /// buffer.push_str(&x.to_string());
411 /// buffer.parse::<usize>().unwrap() * 10
412 /// })
413 /// .collect();
414 ///
415 /// assert_eq!(values, vec![10, 20, 30]);
416 /// ```
417 ///
418 /// Some pipelines need fast worker-local randomness, for example for
419 /// sampling, randomized search, or simulation.
420 /// Seeding one RNG per worker with thread_idx creates independent
421 /// thread-local random streams without shared mutable state.
422 ///
423 /// ```
424 /// use orx_parallel::*;
425 /// use rand::{Rng, RngExt, SeedableRng};
426 /// use rand_chacha::ChaCha8Rng;
427 ///
428 /// let values: Vec<_> = (0..8)
429 /// .into_par()
430 /// .num_threads(2)
431 /// .use_new(|thread_idx| ChaCha8Rng::seed_from_u64(thread_idx as u64 + 1))
432 /// .map(|rng, _| rng.random_range(0..100usize))
433 /// .collect();
434 ///
435 /// assert_eq!(values.len(), 8);
436 /// assert!(values.into_iter().all(|x| x < 100));
437 /// ```
438 fn use_new<U, F>(
439 self,
440 f: F,
441 ) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
442 where
443 U: Send,
444 F: Fn(usize) -> U + Sync,
445 {
446 let (iter, xap, exe, params) = self.destruct();
447 let xap = IdUse::new(xap);
448 let using = UseVec::new(f);
449 ParUseIter::new(using, iter, xap, exe, params)
450 }
451
452 /// Uses an externally owned [`UseVec`] as worker-local mutable state.
453 ///
454 /// Unlike [`Par::use_new`], the state container is provided by the caller,
455 /// which allows reading back per-worker values after the computation.
456 ///
457 /// This is practical when we need thread-local accumulation with a final
458 /// merge step, such as per-thread partial sums or local metrics.
459 ///
460 /// Note that the resulting `UseVec` length equals the number of worker
461 /// threads that actually participated in the computation. Exactly one
462 /// element is created per participating thread.
463 ///
464 /// # Examples
465 ///
466 /// ```
467 /// use orx_parallel::*;
468 ///
469 /// let n = 10_000usize;
470 /// let mut use_vec = UseVec::new(|_| 0usize);
471 ///
472 /// (0..n)
473 /// .into_par()
474 /// .map(|x| 2 * x)
475 /// .use_vec(&mut use_vec)
476 /// .for_each(|thread_sum, x| *thread_sum += x);
477 ///
478 /// let partial_sums = use_vec.into_vec();
479 /// let total: usize = partial_sums.into_iter().sum();
480 ///
481 /// assert_eq!(total, (n - 1) * n);
482 /// ```
483 ///
484 /// The following example demonstrates an expensive per-thread state:
485 /// a pre-allocated scratch buffer.
486 ///
487 /// ```
488 /// use core::fmt::Write;
489 /// use core::sync::atomic::{AtomicUsize, Ordering};
490 /// use orx_parallel::*;
491 ///
492 /// let created = AtomicUsize::new(0);
493 /// let mut use_vec = UseVec::new(|_| {
494 /// created.fetch_add(1, Ordering::Relaxed);
495 /// String::with_capacity(4096)
496 /// });
497 ///
498 /// let out: Vec<_> = (0..64)
499 /// .into_par()
500 /// .num_threads(4)
501 /// .use_vec(&mut use_vec)
502 /// .map(|buffer, x| {
503 /// buffer.clear();
504 /// write!(buffer, "{x}").unwrap();
505 /// buffer.parse::<usize>().unwrap()
506 /// })
507 /// .collect();
508 ///
509 /// assert_eq!(out, (0..64).collect::<Vec<_>>());
510 ///
511 /// let buffers = use_vec.into_vec();
512 /// assert_eq!(created.load(Ordering::Relaxed), buffers.len());
513 /// assert!(buffers.len() <= 4);
514 /// ```
515 fn use_vec<U, F>(
516 self,
517 use_vec: &mut UseVec<U, F>,
518 ) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
519 where
520 U: Send,
521 F: Fn(usize) -> U + Sync,
522 {
523 let (iter, xap, exe, params) = self.destruct();
524 let xap = IdUse::new(xap);
525 ParUseIter::new(use_vec, iter, xap, exe, params)
526 }
527
528 /// Uses a caller-provided mutable slice as worker-local mutable state.
529 ///
530 /// This is similar to [`Par::use_vec`], but the state storage is a
531 /// borrowed slice instead of an owned `UseVec`. Therefore, no per-thread
532 /// state objects are created by this method; existing slice elements are
533 /// reused as thread-local state.
534 ///
535 /// The number of worker threads that can participate in the computation is
536 /// limited by `slice.len()`.
537 ///
538 /// # Examples
539 ///
540 /// ```
541 /// use orx_parallel::*;
542 ///
543 /// let n = 10_000usize;
544 /// let mut thread_sums = vec![0usize; 4];
545 ///
546 /// (0..n)
547 /// .into_par()
548 /// .map(|x| 2 * x)
549 /// .use_slice(&mut thread_sums) // participating workers are limited to 4
550 /// .for_each(|thread_sum, x| *thread_sum += x);
551 ///
552 /// let total: usize = thread_sums.into_iter().sum();
553 /// assert_eq!(total, (n - 1) * n);
554 /// ```
555 ///
556 /// # Panics
557 ///
558 /// Panics if `slice` is empty.
559 fn use_slice<'a, U>(
560 self,
561 slice: &'a mut [U],
562 ) -> impl ParUse<Item = Self::Item, Use = U, Xap = IdUse<Self::Xap, U>, Input = Self::Input>
563 where
564 U: Send + 'a,
565 {
566 assert!(
567 !slice.is_empty(),
568 "Number of parallel threads is limited to slice.len(); and hence, slice cannot be empty."
569 );
570 let (iter, xap, exe, params) = self.destruct();
571 let xap = IdUse::new(xap);
572 let using = UseSlice::new(slice);
573 ParUseIter::new(using, iter, xap, exe, params)
574 }
575
576 /// Copies elements of a reference iterator.
577 ///
578 /// Equivalent to `.map(|&x| x)`.
579 ///
580 /// # Examples
581 ///
582 /// ```
583 /// use orx_parallel::*;
584 ///
585 /// let data = vec![1, 2, 3];
586 /// let copied: Vec<_> = data.par().copied().collect();
587 ///
588 /// assert_eq!(copied, vec![1, 2, 3]);
589 /// ```
590 fn copied<'a, O>(
591 self,
592 ) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCopied<'a, O>>, Input = Self::Input>
593 where
594 Self: Par<Item = &'a O>,
595 O: Copy + 'a,
596 {
597 let (iter, xap, exe, params) = self.destruct();
598 ParIter::new(iter, xap.mapped(FnCopied::new()), exe, params)
599 }
600
601 /// Clones elements of a reference iterator.
602 ///
603 /// Equivalent to `.map(|x| x.clone())`.
604 ///
605 /// # Examples
606 ///
607 /// ```
608 /// use orx_parallel::*;
609 ///
610 /// let data = vec!["a".to_string(), "b".to_string()];
611 /// let cloned: Vec<_> = data.par().cloned().collect();
612 ///
613 /// assert_eq!(cloned, vec!["a".to_string(), "b".to_string()]);
614 /// ```
615 fn cloned<'a, O>(
616 self,
617 ) -> impl Par<Item = O, Xap = MappedOf<Self::Xap, FnCloned<'a, O>>, Input = Self::Input>
618 where
619 Self: Par<Item = &'a O>,
620 O: Clone + 'a,
621 {
622 let (iter, xap, exe, params) = self.destruct();
623 ParIter::new(iter, xap.mapped(FnCloned::new()), exe, params)
624 }
625
626 // transformations
627
628 /// Maps each element with closure `h`.
629 ///
630 /// # Examples
631 ///
632 /// ```
633 /// use orx_parallel::*;
634 ///
635 /// let doubled: Vec<_> = (1..4).into_par().map(|x| 2 * x).collect();
636 /// assert_eq!(doubled, vec![2, 4, 6]);
637 /// ```
638 fn map<Q, H>(
639 self,
640 h: H,
641 ) -> impl Par<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input>
642 where
643 H: Fn(Self::Item) -> Q + Copy + Send;
644
645 /// Runs `h` on each element and forwards the item unchanged.
646 ///
647 /// Useful for logging or debugging pipelines.
648 ///
649 /// # Examples
650 ///
651 /// ```
652 /// use orx_parallel::*;
653 ///
654 /// let out: Vec<_> = (1..5)
655 /// .into_par()
656 /// .inspect(|x| {
657 /// println!("observed {x}");
658 /// })
659 /// .collect();
660 ///
661 /// assert_eq!(out, vec![1, 2, 3, 4]);
662 /// ```
663 fn inspect<H>(
664 self,
665 h: H,
666 ) -> impl Par<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input>
667 where
668 H: Fn(&Self::Item) + Copy + Send;
669
670 /// Keeps only elements satisfying predicate `h`.
671 ///
672 /// # Examples
673 ///
674 /// ```
675 /// use orx_parallel::*;
676 ///
677 /// let odds: Vec<_> = (1..7).into_par().filter(|x| x % 2 == 1).collect();
678 /// assert_eq!(odds, vec![1, 3, 5]);
679 /// ```
680 fn filter<H>(
681 self,
682 h: H,
683 ) -> impl Par<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input>
684 where
685 H: Fn(&Self::Item) -> bool + Copy + Send;
686
687 /// Maps and filters in a single pass.
688 ///
689 /// Returns mapped values for elements where `h` returns `Some(_)`.
690 ///
691 /// # Examples
692 ///
693 /// ```
694 /// use orx_parallel::*;
695 ///
696 /// let numbers: Vec<_> = ["1", "x", "5"]
697 /// .into_par()
698 /// .filter_map(|s| s.parse::<usize>().ok())
699 /// .collect();
700 ///
701 /// assert_eq!(numbers, vec![1, 5]);
702 /// ```
703 fn filter_map<Q, H>(
704 self,
705 h: H,
706 ) -> impl Par<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input>
707 where
708 H: Fn(Self::Item) -> Option<Q> + Copy + Send;
709
710 /// Maps each element to an iterator and flattens one level.
711 ///
712 /// # Examples
713 ///
714 /// ```
715 /// use orx_parallel::*;
716 ///
717 /// let out: Vec<_> = (1..4).into_par().flat_map(|x| [x, x + 10]).collect();
718 /// assert_eq!(out, vec![1, 11, 2, 12, 3, 13]);
719 /// ```
720 fn flat_map<V, H>(
721 self,
722 h: H,
723 ) -> impl Par<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input>
724 where
725 V: IntoIterator,
726 H: Fn(Self::Item) -> V + Copy + Send;
727
728 /// Flattens one level of nested iterables.
729 ///
730 /// # Examples
731 ///
732 /// ```
733 /// use orx_parallel::*;
734 ///
735 /// let nested = vec![vec![1, 2], vec![3, 4]];
736 /// let flat: Vec<_> = nested.into_par().flatten().collect();
737 ///
738 /// assert_eq!(flat, vec![1, 2, 3, 4]);
739 /// ```
740 fn flatten(
741 self,
742 ) -> impl Par<
743 Item = <Self::Item as IntoIterator>::Item,
744 Xap = FlattenOf<Self::Xap>,
745 Input = Self::Input,
746 >
747 where
748 Self::Item: IntoIterator;
749
750 // get
751
752 /// Returns a lower and optional upper bound on the number of output items.
753 ///
754 /// The bounds follow the usual [`Iterator::size_hint`] convention. For an
755 /// exact-size input and a one-to-one transformation, both bounds are exact.
756 /// Transformations such as `filter` may reduce the lower bound while keeping
757 /// the input length as the upper bound.
758 ///
759 /// # Examples
760 ///
761 /// ```
762 /// use orx_parallel::*;
763 ///
764 /// let mapped = (0..4).into_par().map(|x| x * 2);
765 /// assert_eq!(mapped.size_hint(), (4, Some(4)));
766 ///
767 /// let filtered = (0..4).into_par().filter(|x| x % 2 == 0);
768 /// assert_eq!(filtered.size_hint(), (0, Some(4)));
769 /// ```
770 fn size_hint(&self) -> (usize, Option<usize>);
771
772 /// Returns the exact number of output items.
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use orx_parallel::*;
778 ///
779 /// assert_eq!((0..10).into_par().len(), 10);
780 /// assert_eq!((0..10).into_par().map(|x| x + 2).len(), 10);
781 /// ```
782 fn len(&self) -> usize
783 where
784 Self::Input: ExactSizeConcurrentIter,
785 Self::Xap: Xap<Size = One>,
786 {
787 self.size_hint().0
788 }
789
790 /// Returns `true` when the parallel iterator has no output items.
791 ///
792 /// # Examples
793 ///
794 /// ```
795 /// use orx_parallel::*;
796 ///
797 /// assert!((0..0).into_par().is_empty());
798 /// assert!(!(0..1).into_par().is_empty());
799 /// ```
800 fn is_empty(&self) -> bool
801 where
802 Self::Input: ExactSizeConcurrentIter,
803 Self::Xap: Xap<Size = One>,
804 {
805 self.len() == 0
806 }
807
808 // compute
809
810 /// Returns the first item according to iteration order, or `None` if empty.
811 ///
812 /// With `IterationOrder::Ordered` (default), this is the earliest matching item by
813 /// input position. With `IterationOrder::Arbitrary`, this may be any
814 /// matching item reached first in parallel execution.
815 ///
816 /// This operation is short-circuiting: once a first candidate is determined,
817 /// remaining work is cancelled.
818 ///
819 /// # Examples
820 ///
821 /// ```
822 /// use orx_parallel::*;
823 ///
824 /// assert_eq!(Vec::<usize>::new().into_par().first(), None);
825 /// assert_eq!((1..4).into_par().first(), Some(1));
826 /// ```
827 fn first(self) -> Option<Self::Item>
828 where
829 Self::Item: Send;
830
831 /// Reduces items into one value using associative reducer `f`.
832 ///
833 /// Returns `None` for an empty iterator.
834 ///
835 /// # Examples
836 ///
837 /// ```
838 /// use orx_parallel::*;
839 ///
840 /// let reduced = (1..6).into_par().reduce(|a, b| a + b);
841 /// assert_eq!(reduced, Some(15));
842 /// ```
843 fn reduce<F>(self, f: F) -> Option<Self::Item>
844 where
845 F: Fn(Self::Item, Self::Item) -> Self::Item + Send + Copy,
846 Self::Item: Send;
847
848 /// Collects all items into `dst`.
849 ///
850 /// # Examples
851 ///
852 /// ```
853 /// use orx_parallel::*;
854 ///
855 /// let mut dst = vec![10];
856 /// (0..3).into_par().collect_into(&mut dst);
857 /// assert_eq!(dst, vec![10, 0, 1, 2]);
858 /// ```
859 fn collect_into<P>(self, dst: &mut P)
860 where
861 P: ParExtend<Self::Item>,
862 Self::Item: Send;
863
864 /// Collects all items into a new collection.
865 ///
866 /// # Examples
867 ///
868 /// ```
869 /// use orx_parallel::*;
870 ///
871 /// let out: Vec<_> = (1..4).into_par().map(|x| x * 2).collect();
872 /// assert_eq!(out, vec![2, 4, 6]);
873 /// ```
874 fn collect<P>(self) -> P
875 where
876 P: ParExtend<Self::Item> + Default,
877 Self::Item: Send,
878 {
879 let mut dst = P::default();
880 self.collect_into(&mut dst);
881 dst
882 }
883
884 // compute - derived
885
886 /// Returns `true` if all items satisfy predicate `f`.
887 ///
888 /// Empty iterators return `true`.
889 ///
890 /// This operation is short-circuiting: evaluation stops as soon as one item
891 /// fails the predicate.
892 ///
893 /// # Examples
894 ///
895 /// ```
896 /// use orx_parallel::*;
897 ///
898 /// assert!((1..5).into_par().all(|x| x > &0));
899 /// assert!(!(1..5).into_par().all(|x| x % 2 == 0));
900 /// ```
901 fn all<F>(self, f: F) -> bool
902 where
903 F: Fn(&Self::Item) -> bool + Sync,
904 {
905 self.map(|x| f(&x)).find(|x| !*x).is_none()
906 }
907
908 /// Returns `true` if any item satisfies predicate `f`.
909 ///
910 /// Empty iterators return `false`.
911 ///
912 /// This operation is short-circuiting: evaluation stops as soon as one item
913 /// satisfies the predicate.
914 ///
915 /// # Examples
916 ///
917 /// ```
918 /// use orx_parallel::*;
919 ///
920 /// assert!((1..5).into_par().any(|x| x % 2 == 0));
921 /// assert!(!(1..5).into_par().any(|x| x > &10));
922 /// ```
923 fn any<F>(self, f: F) -> bool
924 where
925 F: Fn(&Self::Item) -> bool + Sync,
926 {
927 self.map(|x| f(&x)).find(|x| *x).is_some()
928 }
929
930 /// Counts elements.
931 ///
932 /// # Examples
933 ///
934 /// ```
935 /// use orx_parallel::*;
936 ///
937 /// let n = (1..11).into_par().filter(|x| x % 3 == 0).count();
938 /// assert_eq!(n, 3);
939 /// ```
940 fn count(self) -> usize {
941 self.map(|_| 1).reduce(|a, b| a + b).unwrap_or(0)
942 }
943
944 /// Finds first ([`Ordered`], default) or any ([`Arbitrary`]) item satisfying predicate `f`.
945 ///
946 /// This is equivalent to `self.filter(f).first()`.
947 ///
948 /// This operation is short-circuiting: once a matching item is found,
949 /// remaining work is cancelled.
950 ///
951 /// [`Ordered`]: crate::IterationOrder::Ordered
952 /// [`Arbitrary`]: crate::IterationOrder::Arbitrary
953 ///
954 /// # Examples
955 ///
956 /// ```
957 /// use orx_parallel::*;
958 ///
959 /// let found = (1..101).into_par().find(|x| x % 17 == 0);
960 /// assert_eq!(found, Some(17));
961 /// ```
962 fn find<F>(self, f: F) -> Option<Self::Item>
963 where
964 Self::Item: Send,
965 F: Fn(&Self::Item) -> bool + Sync,
966 {
967 self.filter(&f).first()
968 }
969
970 /// Folds elements into per-thread accumulators and returns them.
971 ///
972 /// The output contains one accumulator for each participating worker.
973 ///
974 /// # Examples
975 ///
976 /// ```
977 /// use orx_parallel::*;
978 ///
979 /// let num_threads = 2;
980 ///
981 /// let partials: Vec<usize> = (1..6)
982 /// .into_par()
983 /// .num_threads(num_threads)
984 /// .fold(|| 0usize, |acc, x| *acc += x);
985 ///
986 /// assert!(partials.len() <= num_threads);
987 ///
988 /// assert_eq!(partials.iter().sum::<usize>(), 15);
989 /// ```
990 fn fold<B, I, F>(self, init: I, f: F) -> Vec<B>
991 where
992 B: Send,
993 I: Fn() -> B + Sync,
994 F: Fn(&mut B, Self::Item) + Copy + Send,
995 {
996 let mut use_vec = UseVec::new(|_| init());
997 let par_use = self.use_vec(&mut use_vec);
998 par_use.for_each(move |u: &mut B, x| f(u, x));
999 use_vec.into_vec()
1000 }
1001
1002 /// Executes `f` for each item.
1003 ///
1004 /// # Examples
1005 ///
1006 /// ```
1007 /// use core::sync::atomic::{AtomicUsize, Ordering};
1008 /// use orx_parallel::*;
1009 ///
1010 /// let total = AtomicUsize::new(0);
1011 ///
1012 /// (1..5)
1013 /// .into_par()
1014 /// .for_each(|x| {
1015 /// total.fetch_add(x, Ordering::Relaxed);
1016 /// });
1017 ///
1018 /// assert_eq!(total.load(Ordering::Relaxed), 10);
1019 /// ```
1020 fn for_each<F>(self, f: F)
1021 where
1022 F: Fn(Self::Item) + Send + Copy,
1023 {
1024 let _ = self.map(f).reduce(|_, _| {});
1025 }
1026
1027 /// Returns maximum element, or `None` if empty.
1028 ///
1029 /// # Examples
1030 ///
1031 /// ```
1032 /// use orx_parallel::*;
1033 ///
1034 /// assert_eq!((1..5).into_par().max(), Some(4));
1035 /// assert_eq!(Vec::<usize>::new().into_par().max(), None);
1036 /// ```
1037 fn max(self) -> Option<Self::Item>
1038 where
1039 Self::Item: Ord + Send,
1040 {
1041 self.reduce(Ord::max)
1042 }
1043
1044 /// Returns element considered maximum by comparator `f`.
1045 ///
1046 /// # Examples
1047 ///
1048 /// ```
1049 /// use orx_parallel::*;
1050 ///
1051 /// let x = vec![-3_i32, 0, 1, 5, -10]
1052 /// .into_par()
1053 /// .max_by(|a, b| a.cmp(b));
1054 /// assert_eq!(x, Some(5));
1055 /// ```
1056 fn max_by<F>(self, f: F) -> Option<Self::Item>
1057 where
1058 Self::Item: Send,
1059 F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,
1060 {
1061 let reduce = |x, y| match f(&x, &y) {
1062 Ordering::Greater | Ordering::Equal => x,
1063 Ordering::Less => y,
1064 };
1065 self.reduce(reduce)
1066 }
1067
1068 /// Returns element with maximum key value.
1069 ///
1070 /// # Examples
1071 ///
1072 /// ```
1073 /// use orx_parallel::*;
1074 ///
1075 /// let x = vec![-3_i32, 0, 1, 5, -10]
1076 /// .into_par()
1077 /// .max_by_key(|x| x.abs());
1078 /// assert_eq!(x, Some(-10));
1079 /// ```
1080 fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
1081 where
1082 Self::Item: Send,
1083 B: Ord,
1084 F: Fn(&Self::Item) -> B + Sync,
1085 {
1086 let reduce = |x, y| match f(&x).cmp(&f(&y)) {
1087 Ordering::Greater | Ordering::Equal => x,
1088 Ordering::Less => y,
1089 };
1090 self.reduce(reduce)
1091 }
1092
1093 /// Returns minimum element, or `None` if empty.
1094 ///
1095 /// # Examples
1096 ///
1097 /// ```
1098 /// use orx_parallel::*;
1099 ///
1100 /// assert_eq!((1..5).into_par().min(), Some(1));
1101 /// assert_eq!(Vec::<usize>::new().into_par().min(), None);
1102 /// ```
1103 fn min(self) -> Option<Self::Item>
1104 where
1105 Self::Item: Ord + Send,
1106 {
1107 self.reduce(Ord::min)
1108 }
1109
1110 /// Returns element considered minimum by comparator `f`.
1111 ///
1112 /// # Examples
1113 ///
1114 /// ```
1115 /// use orx_parallel::*;
1116 ///
1117 /// let x = vec![-3_i32, 0, 1, 5, -10]
1118 /// .into_par()
1119 /// .min_by(|a, b| a.cmp(b));
1120 /// assert_eq!(x, Some(-10));
1121 /// ```
1122 fn min_by<F>(self, f: F) -> Option<Self::Item>
1123 where
1124 Self::Item: Send,
1125 F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,
1126 {
1127 let reduce = |x, y| match f(&x, &y) {
1128 Ordering::Less | Ordering::Equal => x,
1129 Ordering::Greater => y,
1130 };
1131 self.reduce(reduce)
1132 }
1133
1134 /// Returns element with minimum key value.
1135 ///
1136 /// # Examples
1137 ///
1138 /// ```
1139 /// use orx_parallel::*;
1140 ///
1141 /// let x = vec![-3_i32, 0, 1, 5, -10]
1142 /// .into_par()
1143 /// .min_by_key(|x| x.abs());
1144 /// assert_eq!(x, Some(0));
1145 /// ```
1146 fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
1147 where
1148 Self::Item: Send,
1149 B: Ord,
1150 F: Fn(&Self::Item) -> B + Sync,
1151 {
1152 let reduce = |x, y| match f(&x).cmp(&f(&y)) {
1153 Ordering::Less | Ordering::Equal => x,
1154 Ordering::Greater => y,
1155 };
1156 self.reduce(reduce)
1157 }
1158
1159 /// Sums elements using [`Sum`] implementation of the item type.
1160 ///
1161 /// Empty iterators return additive identity (`zero`).
1162 ///
1163 /// # Examples
1164 ///
1165 /// ```
1166 /// use orx_parallel::*;
1167 ///
1168 /// let sum: usize = (1..5).into_par().sum();
1169 /// assert_eq!(sum, 10);
1170 /// ```
1171 fn sum<S>(self) -> S
1172 where
1173 Self::Item: Sum<S>,
1174 S: Send,
1175 {
1176 self.map(Self::Item::owned)
1177 .reduce(Self::Item::add)
1178 .unwrap_or(Self::Item::zero())
1179 }
1180}