-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
An iterator adapter that produces vectors containing all subsets of input iterator elements.
- Loading branch information
Showing
7 changed files
with
321 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
use std::{fmt, mem}; | ||
use std::usize; | ||
use alloc::vec::Vec; | ||
|
||
use super::combinations::Combinations; | ||
use super::lazy_buffer::LazyBuffer; | ||
use super::size_hint; | ||
|
||
/// An iterator over the powerset of all elements from its source iterator. | ||
/// | ||
/// See [`.powerset()`](../trait.Itertools.html#method.powerset) for more | ||
/// information. | ||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] | ||
pub struct Powerset<I: Iterator> { | ||
inner: Inner<I>, | ||
// Position from start of iteration, used for first element and size-hint. | ||
pos: usize, | ||
} | ||
|
||
impl<I> Clone for Powerset<I> | ||
where I: Clone + Iterator, | ||
I::Item: Clone, | ||
{ | ||
clone_fields!(inner, pos); | ||
} | ||
|
||
impl<I> fmt::Debug for Powerset<I> | ||
where I: Iterator + fmt::Debug, | ||
I::Item: fmt::Debug, | ||
{ | ||
debug_fmt_fields!(Powerset, inner, pos); | ||
} | ||
|
||
/// Create a new `Powerset` from a cloneable iterator. | ||
pub fn powerset<I>(src: I) -> Powerset<I> | ||
where I: Iterator, | ||
I::Item: Clone, | ||
{ | ||
Powerset { | ||
inner: Inner::Buffer(src, alloc::vec![]), | ||
pos: 0, | ||
} | ||
} | ||
|
||
// Represents the inner state of a `Powerset` iterator. | ||
#[derive(Clone, Debug)] | ||
enum Inner<I: Iterator> { | ||
// Buffering mode, source iterator still yielding elements. | ||
Buffer(I, Vec<I::Item>), | ||
// Combination mode, yielding combinations of buffered elements. | ||
Combs(Combinations<I>), | ||
// Temporary state for in-place switching of Buffer->Combs. | ||
Empty_, | ||
} | ||
|
||
impl<I> Iterator for Powerset<I> | ||
where | ||
I: Iterator, | ||
I::Item: Clone, | ||
{ | ||
type Item = Vec<I::Item>; | ||
|
||
fn next(&mut self) -> Option<Vec<I::Item>> { | ||
let result = match &mut self.inner { | ||
Inner::Buffer(src, buf) => { | ||
// First element is always the empty set. | ||
if self.pos == 0 { | ||
Some(alloc::vec![]) | ||
} else if let Some(elt) = src.next() { | ||
// Return source item as subset with length of one. | ||
buf.push(elt.clone()); | ||
Some(alloc::vec![elt]) | ||
} else { | ||
// Source iterator is exhausted. | ||
if buf.len() > 1 { | ||
// Begin yielding combinations of buffered elements. | ||
self.upgrade_and_get_next() | ||
} else { | ||
return None; | ||
} | ||
} | ||
}, | ||
// Iterating through subset combinations. | ||
Inner::Combs(combs) => { | ||
if let Some(elt) = combs.next() { | ||
Some(elt) | ||
} else { | ||
// Try increasing combination length if not already maxed out. | ||
let maxed = combs.k() >= combs.n(); | ||
if !maxed && combs.reset(combs.k() + 1) { | ||
combs.next() | ||
} else { | ||
return None; | ||
} | ||
} | ||
}, | ||
Inner::Empty_ => { unreachable!() }, | ||
}; | ||
|
||
debug_assert!(result.is_some()); | ||
self.pos = self.pos.saturating_add(1); | ||
result | ||
} | ||
|
||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
// Total bounds for source iterator (or return 'empty' hint if we're done). | ||
let src_total = match &self.inner { | ||
Inner::Buffer(src, buf) => size_hint::add_scalar(src.size_hint(), buf.len()), | ||
Inner::Combs(combs) => size_hint::add_scalar(combs.src().size_hint(), combs.n()), | ||
Inner::Empty_ => unreachable!(), | ||
}; | ||
|
||
// Total bounds for self ( length(powerset(set) == 2 ^ length(set) ) | ||
let self_total = size_hint::two_exp(src_total); | ||
|
||
if self.pos < usize::MAX { | ||
// Subtract count of already yielded elements from total. | ||
size_hint::sub_scalar(self_total, self.pos) | ||
} else { | ||
// Fallback: self.pos is saturated and no longer reliable. | ||
(0, self_total.1) | ||
} | ||
} | ||
} | ||
|
||
impl<I> Powerset<I> | ||
where I: Iterator, | ||
I::Item: Clone, | ||
{ | ||
// Upgrades the inner state to Inner::Combinations and returns the first | ||
// combination, should only be called when self.inner value is Inner::Buffer. | ||
#[inline] | ||
fn upgrade_and_get_next(&mut self) -> Option<Vec<I::Item>> { | ||
// Switch out inner enum with dummy value, reusing its contents. | ||
let inner = mem::replace(&mut self.inner, Inner::Empty_); | ||
|
||
if let Inner::Buffer(src, buf) = inner { | ||
let lazy_buf = LazyBuffer::from_parts(src, true, buf); | ||
let mut combs = Combinations::from_pool(lazy_buf, 2); | ||
|
||
let result = combs.next(); | ||
self.inner = Inner::Combs(combs); | ||
|
||
result | ||
} else { | ||
None | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.