1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
use crate::{
ChunkSize, IterationOrder, NumThreads, ParCollectInto, Params, RunnerWithPool, Sum,
runner::{DefaultRunner, ParallelRunner},
using::using_variants::Using,
};
use crate::{ParThreadPool, default_fns::*};
use core::cmp::Ordering;
use orx_concurrent_iter::ConcurrentIter;
/// Parallel iterator which allows mutable access to a variable of type `U` within its iterator methods.
///
/// Note that one variable will be created per thread used by the parallel computation.
pub trait ParIterUsing<U, R = DefaultRunner>: Sized + Send + Sync
where
R: ParallelRunner,
U: Using,
{
/// Element type of the parallel iterator.
type Item;
/// Returns a reference to the input concurrent iterator.
fn con_iter(&self) -> &impl ConcurrentIter;
/// Parameters of the parallel iterator.
///
/// See [crate::ParIter::params] for details.
fn params(&self) -> Params;
// params transformations
/// Sets the number of threads to be used in the parallel execution.
/// Integers can be used as the argument with the following mapping:
///
/// * `0` -> `NumThreads::Auto`
/// * `1` -> `NumThreads::sequential()`
/// * `n > 0` -> `NumThreads::Max(n)`
///
/// /// Parameters of the parallel iterator.
///
/// See [crate::ParIter::num_threads] for details.
fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self;
/// Sets the number of elements to be pulled from the concurrent iterator during the
/// parallel execution. When integers are used as argument, the following mapping applies:
///
/// * `0` -> `ChunkSize::Auto`
/// * `n > 0` -> `ChunkSize::Exact(n)`
///
/// Please use the default enum constructor for creating `ChunkSize::Min` variant.
///
/// See [crate::ParIter::chunk_size] for details.
fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self;
/// Sets the iteration order of the parallel computation.
///
/// See [crate::ParIter::iteration_order] for details.
fn iteration_order(self, collect: IterationOrder) -> Self;
/// Rather than the [`DefaultRunner`], uses the parallel runner `Q` which implements [`ParallelRunner`].
///
/// See [`ParIter::with_runner`] for details.
///
/// [`DefaultRunner`]: crate::DefaultRunner
/// [`ParIter::with_runner`]: crate::ParIter::with_runner
fn with_runner<Q: ParallelRunner>(
self,
orchestrator: Q,
) -> impl ParIterUsing<U, Q, Item = Self::Item>;
/// Rather than [`DefaultPool`], uses the parallel runner with the given `pool` implementing
/// [`ParThreadPool`].
///
/// See [`ParIter::with_pool`] for details.
///
/// [`DefaultPool`]: crate::DefaultPool
/// [`ParIter::with_pool`]: crate::ParIter::with_pool
fn with_pool<P: ParThreadPool>(
self,
pool: P,
) -> impl ParIterUsing<U, RunnerWithPool<P, R::Executor>, Item = Self::Item>
where
Self: Sized,
{
let runner = RunnerWithPool::from(pool).with_executor::<R::Executor>();
self.with_runner(runner)
}
// computation transformations
/// Takes a closure `map` and creates a parallel iterator which calls that closure on each element.
///
/// Unlike [crate::ParIter::map], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn map<Out, Map>(self, map: Map) -> impl ParIterUsing<U, R, Item = Out>
where
Map: Fn(&mut U::Item, Self::Item) -> Out + Sync + Clone;
/// Creates an iterator which uses a closure `filter` to determine if an element should be yielded.
///
/// Unlike [crate::ParIter::filter], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn filter<Filter>(self, filter: Filter) -> impl ParIterUsing<U, R, Item = Self::Item>
where
Filter: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone;
/// Creates an iterator that works like map, but flattens nested structure.
///
/// Unlike [crate::ParIter::flat_map], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn flat_map<IOut, FlatMap>(
self,
flat_map: FlatMap,
) -> impl ParIterUsing<U, R, Item = IOut::Item>
where
IOut: IntoIterator,
FlatMap: Fn(&mut U::Item, Self::Item) -> IOut + Sync + Clone;
/// Creates an iterator that both filters and maps.
///
/// The returned iterator yields only the values for which the supplied closure `filter_map` returns `Some(value)`.
///
/// `filter_map` can be used to make chains of `filter` and `map` more concise.
/// The example below shows how a `map().filter().map()` can be shortened to a single call to `filter_map`.
///
/// Unlike [crate::ParIter::filter_map], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn filter_map<Out, FilterMap>(
self,
filter_map: FilterMap,
) -> impl ParIterUsing<U, R, Item = Out>
where
FilterMap: Fn(&mut U::Item, Self::Item) -> Option<Out> + Sync + Clone;
/// Does something with each element of an iterator, passing the value on.
///
/// Unlike [crate::ParIter::inspect], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn inspect<Operation>(self, operation: Operation) -> impl ParIterUsing<U, R, Item = Self::Item>
where
Operation: Fn(&mut U::Item, &Self::Item) + Sync + Clone,
{
let map = move |u: &mut U::Item, x: Self::Item| {
operation(u, &x);
x
};
self.map(map)
}
// special item transformations
/// Creates an iterator which copies all of its elements.
///
/// Unlike [crate::ParIter::copied], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn copied<'a, T>(self) -> impl ParIterUsing<U, R, Item = T>
where
T: 'a + Copy,
Self: ParIterUsing<U, R, Item = &'a T>,
{
self.map(u_map_copy)
}
/// Creates an iterator which clones all of its elements.
///
/// Unlike [crate::ParIter::cloned], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn cloned<'a, T>(self) -> impl ParIterUsing<U, R, Item = T>
where
T: 'a + Clone,
Self: ParIterUsing<U, R, Item = &'a T>,
{
self.map(u_map_clone)
}
/// Creates an iterator that flattens nested structure.
///
/// Unlike [crate::ParIter::flatten], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn flatten(self) -> impl ParIterUsing<U, R, Item = <Self::Item as IntoIterator>::Item>
where
Self::Item: IntoIterator,
{
let map = |_: &mut U::Item, e: Self::Item| e.into_iter();
self.flat_map(map)
}
// collect
/// Collects all the items from an iterator into a collection.
///
/// Unlike [crate::ParIter::collect_into], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn collect_into<C>(self, output: C) -> C
where
C: ParCollectInto<Self::Item>;
/// Transforms an iterator into a collection.
///
/// Unlike [crate::ParIter::collect], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn collect<C>(self) -> C
where
C: ParCollectInto<Self::Item>,
{
let output = C::empty(self.con_iter().try_get_len());
self.collect_into(output)
}
// reduce
/// Reduces the elements to a single one, by repeatedly applying a reducing operation.
///
/// See the details here: [crate::ParIter::reduce].
fn reduce<Reduce>(self, reduce: Reduce) -> Option<Self::Item>
where
Self::Item: Send,
Reduce: Fn(&mut U::Item, Self::Item, Self::Item) -> Self::Item + Sync;
/// Tests if every element of the iterator matches a predicate.
///
/// Unlike [crate::ParIter::all], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn all<Predicate>(self, predicate: Predicate) -> bool
where
Self::Item: Send,
Predicate: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone,
{
let violates = |u: &mut U::Item, x: &Self::Item| !predicate(u, x);
self.find(violates).is_none()
}
/// Tests if any element of the iterator matches a predicate.
///
/// Unlike [crate::ParIter::any], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn any<Predicate>(self, predicate: Predicate) -> bool
where
Self::Item: Send,
Predicate: Fn(&mut U::Item, &Self::Item) -> bool + Sync + Clone,
{
self.find(predicate).is_some()
}
/// Consumes the iterator, counting the number of iterations and returning it.
///
/// See the details here: [crate::ParIter::count].
fn count(self) -> usize {
self.map(u_map_count).reduce(u_reduce_sum).unwrap_or(0)
}
/// Calls a closure on each element of an iterator.
///
/// Unlike [crate::ParIter::for_each], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn for_each<Operation>(self, operation: Operation)
where
Operation: Fn(&mut U::Item, Self::Item) + Sync,
{
let map = |u: &mut U::Item, x| operation(u, x);
let _ = self.map(map).reduce(u_reduce_unit);
}
/// Returns the maximum element of an iterator.
///
/// See the details here: [crate::ParIter::max].
fn max(self) -> Option<Self::Item>
where
Self::Item: Ord + Send,
{
self.reduce(|_, a, b| Ord::max(a, b))
}
/// Returns the element that gives the maximum value with respect to the specified `compare` function.
///
/// See the details here: [crate::ParIter::max_by].
fn max_by<Compare>(self, compare: Compare) -> Option<Self::Item>
where
Self::Item: Send,
Compare: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,
{
let reduce = |_: &mut U::Item, x, y| match compare(&x, &y) {
Ordering::Greater | Ordering::Equal => x,
Ordering::Less => y,
};
self.reduce(reduce)
}
/// Returns the element that gives the maximum value from the specified function.
///
/// See the details here: [crate::ParIter::max_by_key].
fn max_by_key<Key, GetKey>(self, key: GetKey) -> Option<Self::Item>
where
Self::Item: Send,
Key: Ord,
GetKey: Fn(&Self::Item) -> Key + Sync,
{
let reduce = |_: &mut U::Item, x, y| match key(&x).cmp(&key(&y)) {
Ordering::Greater | Ordering::Equal => x,
Ordering::Less => y,
};
self.reduce(reduce)
}
/// Returns the minimum element of an iterator.
///
/// See the details here: [crate::ParIter::min].
fn min(self) -> Option<Self::Item>
where
Self::Item: Ord + Send,
{
self.reduce(|_, a, b| Ord::min(a, b))
}
/// Returns the element that gives the minimum value with respect to the specified `compare` function.
///
/// See the details here: [crate::ParIter::min_by].
fn min_by<Compare>(self, compare: Compare) -> Option<Self::Item>
where
Self::Item: Send,
Compare: Fn(&Self::Item, &Self::Item) -> Ordering + Sync,
{
let reduce = |_: &mut U::Item, x, y| match compare(&x, &y) {
Ordering::Less | Ordering::Equal => x,
Ordering::Greater => y,
};
self.reduce(reduce)
}
/// Returns the element that gives the minimum value from the specified function.
///
/// See the details here: [crate::ParIter::min_by_key].
fn min_by_key<Key, GetKey>(self, get_key: GetKey) -> Option<Self::Item>
where
Self::Item: Send,
Key: Ord,
GetKey: Fn(&Self::Item) -> Key + Sync,
{
let reduce = |_: &mut U::Item, x, y| match get_key(&x).cmp(&get_key(&y)) {
Ordering::Less | Ordering::Equal => x,
Ordering::Greater => y,
};
self.reduce(reduce)
}
/// Sums the elements of an iterator.
///
/// See the details here: [crate::ParIter::sum].
fn sum<Out>(self) -> Out
where
Self::Item: Sum<Out>,
Out: Send,
{
self.map(Self::Item::u_map)
.reduce(Self::Item::u_reduce)
.unwrap_or(Self::Item::zero())
}
// early exit
/// Returns the first (or any) element of the iterator; returns None if it is empty.
///
/// * first element is returned if default iteration order `IterationOrder::Ordered` is used,
/// * any element is returned if `IterationOrder::Arbitrary` is set.
///
/// See the details here: [crate::ParIter::first].
fn first(self) -> Option<Self::Item>
where
Self::Item: Send;
/// Searches for an element of an iterator that satisfies a `predicate`.
///
/// Unlike [crate::ParIter::find], the closure allows access to mutable reference of the used variable.
///
/// Please see [`crate::ParIter::using`] transformation for details and examples.
///
/// Further documentation can be found here: [`using.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/using.md).
fn find<Predicate>(self, predicate: Predicate) -> Option<Self::Item>
where
Self::Item: Send,
Predicate: Fn(&mut U::Item, &Self::Item) -> bool + Sync,
{
self.filter(&predicate).first()
}
}