diff --git a/.gitignore b/.gitignore index ca98cd96e..ed768f33e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target/ -Cargo.lock +Cargo.lock \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index c6518ff60..ef336c823 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,8 @@ pub mod structs { pub use peeking_take_while::PeekingTakeWhile; pub use process_results_impl::ProcessResults; #[cfg(feature = "use_std")] + pub use product_combination::ProductCombination; + #[cfg(feature = "use_std")] pub use put_back_n_impl::PutBackN; #[cfg(feature = "use_std")] pub use rciter_impl::RcIter; @@ -145,6 +147,8 @@ mod pad_tail; mod peeking_take_while; mod process_results_impl; #[cfg(feature = "use_std")] +mod product_combination; +#[cfg(feature = "use_std")] mod put_back_n_impl; #[cfg(feature = "use_std")] mod rciter_impl; @@ -1983,6 +1987,38 @@ pub trait Itertools : Iterator { |x, y, _, _| Ordering::Less == compare(x, y) ) } + + /// Returns an iterator adaptor that iterators over all product combinations of the elements from the iterator. + /// + /// Every iteration produces a new `Vec` containing cloned elements from the iterator. The iterator will never halt as there are infinitely many product combinations. + /// + ///``` + ///use itertools::Itertools; + /// + ///let list = vec![1, 2, 3]; + /// + ///let mut iter = list.iter().product_combination(); + /// + ///assert_eq!(iter.next().unwrap(), vec![&1]); + ///assert_eq!(iter.next().unwrap(), vec![&2]); + ///assert_eq!(iter.next().unwrap(), vec![&3]); + ///assert_eq!(iter.next().unwrap(), vec![&1, &1]); + ///assert_eq!(iter.next().unwrap(), vec![&2, &1]); + ///assert_eq!(iter.next().unwrap(), vec![&3, &1]); + ///assert_eq!(iter.next().unwrap(), vec![&1, &2]); + ///assert_eq!(iter.next().unwrap(), vec![&2, &2]); + ///assert_eq!(iter.next().unwrap(), vec![&3, &2]); + ///assert_eq!(iter.next().unwrap(), vec![&1, &3]); + ///assert_eq!(iter.next().unwrap(), vec![&2, &3]); + ///assert_eq!(iter.next().unwrap(), vec![&3, &3]); + ///assert_eq!(iter.next().unwrap(), vec![&1, &1, &1]); + ///``` + #[cfg(feature = "use_std")] + fn product_combination(self) -> product_combination::ProductCombination + where Self: Sized + Clone + { + product_combination::product_combination(self) + } } impl Itertools for T where T: Iterator { } diff --git a/src/product_combination.rs b/src/product_combination.rs new file mode 100644 index 000000000..f5120f63d --- /dev/null +++ b/src/product_combination.rs @@ -0,0 +1,80 @@ +/// An iterator to iterate through all product combinations of an iterator. +/// +/// See [`.product_combination()`](../trait.Itertools.html#method.product_combination) for more information. +pub struct ProductCombination { + data: I, + //Internal state is a vec of tuples containing an iterator and the last retrieved item from this iterator + internal_state: Vec<(I, I::Item)> +} + +/// Create a new `ProductionCombination` from a clonable iterator. +pub fn product_combination(iter: I) -> ProductCombination + where I: Iterator + Clone +{ + ProductCombination { + data: iter, + internal_state: Vec::new(), + } +} + +impl ProductCombination { + fn increment(&mut self) { + let mut pointer: usize = 0; + let mut tack_on = false; + loop { + match self.internal_state.get_mut(pointer) { + None => { + tack_on = true; + break; + }, + Some(ref mut pair) => { + let iter = &mut pair.0; + let item = &mut pair.1; + + let new_item = iter.next(); + match new_item { + //Reached the end of the iterator ... restart the iterator and advance the pointer + None => { + *iter = self.data.clone(); + match iter.next() { + None => { + panic!("0 sized iterator"); + }, + Some(i) => { + *item = i; + } + } + pointer += 1; + }, + Some(i) => { + *item = i; + break; + } + } + }, + } + } + if tack_on { + let mut new_iter = self.data.clone(); + match new_iter.next() { + None => { + panic!("0 sized iterator"); + }, + Some(new_item) => { + self.internal_state.push((new_iter, new_item)); + } + } + } + } +} + +impl Iterator for ProductCombination + where I: Iterator + Clone, I::Item: Clone +{ + type Item = Vec; + + fn next(&mut self) -> Option { + self.increment(); + Some(self.internal_state.iter().map(|ref pair| pair.1.clone()).collect()) + } +} \ No newline at end of file