diff --git a/asparit/src/core/iterator.rs b/asparit/src/core/iterator.rs index 119fefd..d515362 100644 --- a/asparit/src/core/iterator.rs +++ b/asparit/src/core/iterator.rs @@ -18,7 +18,9 @@ use crate::{ map::Map, map_init::MapInit, map_with::MapWith, + product::Product, reduce::Reduce, + sum::Sum, try_fold::{TryFold, TryFoldWith}, try_for_each::{TryForEach, TryForEachInit, TryForEachWith}, try_reduce::TryReduce, @@ -925,6 +927,70 @@ pub trait ParallelIterator<'a>: Sized + Send { TryFoldWith::new(self, init, operation) } + /// Sums up the items in the iterator. + /// + /// Note that the order in items will be reduced is not specified, + /// so if the `+` operator is not truly [associative] \(as is the + /// case for floating point numbers), then the results are not + /// fully deterministic. + /// + /// [associative]: https://en.wikipedia.org/wiki/Associative_property + /// + /// Basically equivalent to `self.reduce(|| 0, |a, b| a + b)`, + /// except that the type of `0` and the `+` operation may vary + /// depending on the type of value being produced. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// + /// let a = [1, 5, 7]; + /// + /// let sum: i32 = a.par_iter().sum(); + /// + /// assert_eq!(sum, 13); + /// ``` + fn sum(self) -> Sum + where + S: std::iter::Sum + std::iter::Sum + Send, + { + Sum::new(self) + } + + /// Multiplies all the items in the iterator. + /// + /// Note that the order in items will be reduced is not specified, + /// so if the `*` operator is not truly [associative] \(as is the + /// case for floating point numbers), then the results are not + /// fully deterministic. + /// + /// [associative]: https://en.wikipedia.org/wiki/Associative_property + /// + /// Basically equivalent to `self.reduce(|| 1, |a, b| a * b)`, + /// except that the type of `1` and the `*` operation may vary + /// depending on the type of value being produced. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// + /// fn factorial(n: u32) -> u32 { + /// (1..n+1).into_par_iter().product() + /// } + /// + /// assert_eq!(factorial(0), 1); + /// assert_eq!(factorial(1), 1); + /// assert_eq!(factorial(5), 120); + /// ``` + fn product

(self) -> Product + where + P: std::iter::Product + std::iter::Product

+ Send, + { + Product::new(self) + } + /// Creates a fresh collection containing all the elements produced /// by this parallel iterator. /// diff --git a/asparit/src/inner/mod.rs b/asparit/src/inner/mod.rs index 75e6862..27da6ed 100644 --- a/asparit/src/inner/mod.rs +++ b/asparit/src/inner/mod.rs @@ -11,7 +11,9 @@ pub mod map; pub mod map_init; pub mod map_with; pub mod noop; +pub mod product; pub mod reduce; +pub mod sum; pub mod try_fold; pub mod try_for_each; pub mod try_reduce; @@ -51,7 +53,6 @@ mod tests { move || j.fetch_add(1, Ordering::Relaxed), |init, item| -> Result<(), ()> { println!("{:?} - {:?}", init, item); - Ok(()) }, ) diff --git a/asparit/src/inner/product.rs b/asparit/src/inner/product.rs new file mode 100644 index 0000000..230aa5d --- /dev/null +++ b/asparit/src/inner/product.rs @@ -0,0 +1,121 @@ +use std::iter::{empty, once}; +use std::marker::PhantomData; + +use crate::{core::Driver, Consumer, Executor, Folder, ParallelIterator, Reducer}; + +/* Product */ + +pub struct Product { + iterator: X, + marker: PhantomData

, +} + +impl Product { + pub fn new(iterator: X) -> Self { + Self { + iterator, + marker: PhantomData, + } + } +} + +impl<'a, X, P> Driver<'a, P> for Product +where + X: ParallelIterator<'a>, + P: std::iter::Product + std::iter::Product + Send + 'a, +{ + fn exec_with(self, executor: E) -> E::Result + where + E: Executor<'a, P>, + { + let iterator = self.iterator; + let consumer = ProductConsumer(PhantomData); + + iterator.drive(executor, consumer) + } +} + +/* ProductConsumer */ + +pub struct ProductConsumer

(PhantomData

); + +impl Consumer for ProductConsumer

+where + P: std::iter::Product + std::iter::Product + Send, +{ + type Folder = ProductFolder

; + type Reducer = ProductReducer

; + type Result = P; + + fn split(self) -> (Self, Self, Self::Reducer) { + let left = self; + let right = ProductConsumer(PhantomData); + + (left, right, ProductReducer(PhantomData)) + } + + fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { + let left = self; + let right = ProductConsumer(PhantomData); + + (left, right, ProductReducer(PhantomData)) + } + + fn into_folder(self) -> Self::Folder { + ProductFolder { + product: empty::().product(), + } + } +} + +/* ProductFolder */ + +pub struct ProductFolder

{ + product: P, +} + +impl Folder for ProductFolder

+where + P: std::iter::Product + std::iter::Product + Send, +{ + type Result = P; + + fn consume(mut self, item: I) -> Self { + self.product = mul(self.product, once(item).product()); + + self + } + + fn consume_iter(mut self, iter: X) -> Self + where + X: IntoIterator, + { + self.product = mul(self.product, iter.into_iter().product()); + + self + } + + fn complete(self) -> Self::Result { + self.product + } +} + +/* ProductReducer */ + +pub struct ProductReducer

(PhantomData

); + +impl

Reducer

for ProductReducer

+where + P: std::iter::Product + Send, +{ + fn reduce(self, left: P, right: P) -> P { + mul(left, right) + } +} + +fn mul(left: T, right: T) -> T +where + T: std::iter::Product, +{ + once(left).chain(once(right)).product() +} diff --git a/asparit/src/inner/sum.rs b/asparit/src/inner/sum.rs new file mode 100644 index 0000000..aa4e9b0 --- /dev/null +++ b/asparit/src/inner/sum.rs @@ -0,0 +1,121 @@ +use std::iter::{empty, once}; +use std::marker::PhantomData; + +use crate::{core::Driver, Consumer, Executor, Folder, ParallelIterator, Reducer}; + +/* Sum */ + +pub struct Sum { + iterator: X, + marker: PhantomData, +} + +impl Sum { + pub fn new(iterator: X) -> Self { + Self { + iterator, + marker: PhantomData, + } + } +} + +impl<'a, X, S> Driver<'a, S> for Sum +where + X: ParallelIterator<'a>, + S: std::iter::Sum + std::iter::Sum + Send + 'a, +{ + fn exec_with(self, executor: E) -> E::Result + where + E: Executor<'a, S>, + { + let iterator = self.iterator; + let consumer = SumConsumer(PhantomData); + + iterator.drive(executor, consumer) + } +} + +/* SumConsumer */ + +pub struct SumConsumer(PhantomData); + +impl Consumer for SumConsumer +where + S: std::iter::Sum + std::iter::Sum + Send, +{ + type Folder = SumFolder; + type Reducer = SumReducer; + type Result = S; + + fn split(self) -> (Self, Self, Self::Reducer) { + let left = self; + let right = SumConsumer(PhantomData); + + (left, right, SumReducer(PhantomData)) + } + + fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { + let left = self; + let right = SumConsumer(PhantomData); + + (left, right, SumReducer(PhantomData)) + } + + fn into_folder(self) -> Self::Folder { + SumFolder { + sum: empty::().sum(), + } + } +} + +/* SumFolder */ + +pub struct SumFolder { + sum: S, +} + +impl Folder for SumFolder +where + S: std::iter::Sum + std::iter::Sum + Send, +{ + type Result = S; + + fn consume(mut self, item: I) -> Self { + self.sum = add(self.sum, once(item).sum()); + + self + } + + fn consume_iter(mut self, iter: X) -> Self + where + X: IntoIterator, + { + self.sum = add(self.sum, iter.into_iter().sum()); + + self + } + + fn complete(self) -> Self::Result { + self.sum + } +} + +/* SumReducer */ + +pub struct SumReducer(PhantomData); + +impl Reducer for SumReducer +where + S: std::iter::Sum + Send, +{ + fn reduce(self, left: S, right: S) -> S { + add(left, right) + } +} + +fn add(left: T, right: T) -> T +where + T: std::iter::Sum, +{ + once(left).chain(once(right)).sum() +}