Skip to content

Commit

Permalink
Simplify Powerset implementation (uses default Iterator::size_hint())
Browse files Browse the repository at this point in the history
  • Loading branch information
willcrozi committed Sep 26, 2020
1 parent d9db41e commit 69357be
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 171 deletions.
54 changes: 19 additions & 35 deletions src/combinations.rs
Expand Up @@ -31,15 +31,15 @@ impl<I> fmt::Debug for Combinations<I>
pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
where I: Iterator
{
let mut pool = LazyBuffer::new(iter);
pool.prefill(k);

Combinations::from_pool(pool, k)
Combinations::new(iter, k)
}

impl<I: Iterator> Combinations<I> {
#[inline]
pub(crate) fn from_pool(pool: LazyBuffer<I>, k: usize) -> Self {
pub(crate) fn new(iter: I, k: usize) -> Self {
let mut pool = LazyBuffer::new(iter);
pool.prefill(k);

Combinations {
indices: (0..k).collect(),
pool,
Expand All @@ -52,45 +52,29 @@ impl<I: Iterator> Combinations<I> {
pub fn k(&self) -> usize { self.indices.len() }

/// Returns the (current) length of the pool from which combination elements are
/// selected. This value can change between invocations of `next()` and `init()`.
#[inline]
pub(crate) fn n(&self) -> usize { self.pool.len() }

/// Returns a reference to the source iterator.
/// selected. This value can change between invocations of `next()` and `reset()`.
#[inline]
pub(crate) fn src(&self) -> &I { &self.pool.it }
pub fn n(&self) -> usize { self.pool.len() }

/// Resets this `Combinations` back to an initial state for combinations of length
/// `k` over the same pool data source. If `k` is larger than the previous value
/// an attempt is made to prefill the pool so that it holds `k` elements.
///
/// Returns `true` if the underlying pool is large enough to provide combinations
/// of length `k`, otherwise `false`.
pub(crate) fn reset(&mut self, k: usize) -> bool {
let prev_k = self.indices.len();
let mut filled = true;

// Shrink/grow indices vector, filling in new values if required.
let reset_limit: usize;
if k < prev_k {
self.indices.truncate(k);
reset_limit = k;
} else {
filled = self.pool.prefill(k);
pub(crate) fn reset(&mut self, k: usize) {
self.first = true;

for i in prev_k..k {
self.indices.push(i);
if k < self.indices.len() {
self.indices.truncate(k);
for i in 0..k {
self.indices[i] = i;
}
reset_limit = prev_k;
}

// Reset old indices.
for i in 0..reset_limit {
self.indices[i] = i;
} else {
for i in 0..self.indices.len() {
self.indices[i] = i;
}
self.indices.extend(self.indices.len()..k);
self.pool.prefill(k);
}

self.first = true;
filled
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/lazy_buffer.rs
Expand Up @@ -13,11 +13,10 @@ where
I: Iterator,
{
pub fn new(it: I) -> LazyBuffer<I> {
LazyBuffer::from_parts(it, false, Vec::new())
}

pub(crate) fn from_parts(it: I, done: bool, buffer: Vec<I::Item>) -> LazyBuffer<I> {
LazyBuffer { it, done, buffer }
LazyBuffer {
it,
done: false,
buffer: Vec::new() }
}

pub fn len(&self) -> usize {
Expand Down
119 changes: 13 additions & 106 deletions src/powerset.rs
@@ -1,56 +1,37 @@
use std::{fmt, mem};
use std::usize;
use std::fmt;
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,
combs: Combinations<I>,
}

impl<I> Clone for Powerset<I>
where I: Clone + Iterator,
I::Item: Clone,
{
clone_fields!(inner, pos);
clone_fields!(combs);
}

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

/// 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_,
Powerset { combs: Combinations::new(src, 0) }
}

impl<I> Iterator for Powerset<I>
Expand All @@ -61,89 +42,15 @@ impl<I> Iterator for Powerset<I>
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
if let Some(elt) = self.combs.next() {
Some(elt)
} else {
None
if self.combs.k() < self.combs.n() || self.combs.k() == 0 {
self.combs.reset(self.combs.k() + 1);
self.combs.next()
} else {
None
}
}
}
}
25 changes: 0 additions & 25 deletions src/size_hint.rs
Expand Up @@ -74,31 +74,6 @@ pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint {
(low, hi)
}

/// Raise two correctly by a **SizeHint** exponent.
#[inline]
#[allow(dead_code)]
pub fn two_exp(exp: SizeHint) -> SizeHint {
let (mut low, mut hi) = exp;


// saturating 'raise two by exponent'
let shl_range = 1_usize.leading_zeros() as usize;
low = if low <= shl_range {
1 << low
} else {
usize::MAX
};

// checked 'raise two by exponent'
hi = match hi {
Some(hi) if hi <= shl_range => Some(1 << hi),
_ => None
};

(low, hi)
}


/// Return the maximum
#[inline]
pub fn max(a: SizeHint, b: SizeHint) -> SizeHint {
Expand Down

0 comments on commit 69357be

Please sign in to comment.