Skip to content

Commit

Permalink
powerset: implement purely using combinations.rs
Browse files Browse the repository at this point in the history
size_hint method now just returns (0, None) which is
pretty useless :-(
  • Loading branch information
willcrozi committed Sep 5, 2020
1 parent 69ba67b commit 673d706
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 83 deletions.
4 changes: 2 additions & 2 deletions src/combinations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ impl<I> Iterator for Combinations<I>
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
if self.pool.is_done() {
return None;
if self.pool.len() < self.indices.len() {
return None
}
self.first = false;
} else if self.indices.len() == 0 {
Expand Down
124 changes: 43 additions & 81 deletions src/powerset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,128 +4,90 @@ use std::vec::IntoIter;

use super::combinations::{self, Combinations};
use super::size_hint;
use crate::Itertools;
use std::fmt::Debug;

/// 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> {
src: I,
buf: Vec<I::Item>,
// Only used when once a source containing two or more items is exhausted.
combs: Option<SetCombinations<I>>,
// src: I,
combs: Combinations<I>,
// Used for detecting special cases and size_hint() calculation
pos: usize,
done: bool
}

type SetCombinations<I> = Combinations<IntoIter<<I as Iterator>::Item>>;
// type SetCombinations<I> = Combinations<IntoIter<<I as Iterator>::Item>>;

impl<I> fmt::Debug for Powerset<I>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
debug_fmt_fields!(Powerset, src, buf, combs, pos);
debug_fmt_fields!(Powerset, combs, pos, done);
}

/// Create a new `Powerset` from a cloneable iterator.
pub fn powerset<I>(src: I) -> Powerset<I>
where I: Iterator,
I::Item: Clone
{
let (src_low, _) = src.size_hint();
// let (src_low, _) = src.size_hint();

Powerset {
src,
buf: Vec::with_capacity(src_low),
combs: None,
// src,
combs: src.combinations(0),
pos: 0,
}
}

impl<I> Powerset<I>
where
I: Iterator,
I::Item: Clone
{
#[inline]
/// Creates and stores the next Combination, returning the first element or None if the
/// iterator is done.
fn next_from_new_combs(&mut self) -> Option<Vec<I::Item>> {
debug_assert!(self.buf.len() > 1);

let k = match self.combs {
Some(ref combs) => combs.k() + 1,
None => 2
};

let mut combs = combinations::combinations(self.buf.clone().into_iter(), k);
let result = combs.next();
self.combs = Some(combs);
result
done: false
}
}

impl<I> Iterator for Powerset<I>
where
I: Iterator,
I::Item: Clone
I::Item: Clone + Debug
{
type Item = Vec<I::Item>;

fn next(&mut self) -> Option<Vec<I::Item>> {
let mut new_combs = false;

let mut result = match *(&mut self.combs) {
None if self.pos == 0 => {
// First item, return the empty set
Some(Vec::new())
}
None => {
// Still draining from source iterator
if let Some(elt) = self.src.next() {
self.buf.push(elt.clone());
Some(vec!(elt))
} else {
new_combs = self.buf.len() >= 2;
None
}
}
Some(ref mut combs) if combs.k() < self.buf.len() => {
// Generating elements from Combinations
match combs.next() {
Some(elt) => Some(elt),
None => {
new_combs = self.buf.len() >= 2;
None
}
let result = if self.done {
None
} else {
let result = match self.combs.next() {
Some(elt) => {
self.pos += 1;
Some(elt)
},
None => {
self.combs.reset(self.combs.k() + 1);
self.combs.next()
}
}
Some(_) => {
// Iteration is done
None
}
};
self.done = result.is_none();
result
};

// Merge these lines into the relevant match arms above when NLLs are available
if new_combs { result = self.next_from_new_combs(); }
if result.is_some() { self.pos = self.pos.saturating_add(1); }

result
}

fn size_hint(&self) -> (usize, Option<usize>) {
// Aggregate bounds for 'unwalked' source iterator
let src_total = size_hint::add_scalar(self.src.size_hint(), self.buf.len());

// Aggregate bounds for self ( length(powerset(set) == 2 ^ length(set) )
let self_total = size_hint::two_raised_pow(src_total);

if self.pos < usize::MAX {
// Subtract count of walked elements from total
size_hint::sub_scalar(self_total, self.pos)
} else {
// self.pos is saturated, no longer reliable
(0, self_total.1)
}
// TODO try and improve on this
// Might be screwed here unless we keep hold of the src iterator etc.
(0, None)

// // Aggregate bounds for 'unwalked' source iterator
// let src_total = size_hint::add_scalar(self.src.size_hint(), self.buf.len());
//
// // Aggregate bounds for self ( length(powerset(set) == 2 ^ length(set) )
// let self_total = size_hint::two_raised_pow(src_total);
//
// if self.pos < usize::MAX {
// // Subtract count of walked elements from total
// size_hint::sub_scalar(self_total, self.pos)
// } else {
// // self.pos is saturated, no longer reliable
// (0, self_total.1)
// }
}
}

0 comments on commit 673d706

Please sign in to comment.