From 69357bebd63b24d5875facf8c77923ea2b8310c8 Mon Sep 17 00:00:00 2001 From: Will Crozier Date: Sat, 26 Sep 2020 15:44:01 +0100 Subject: [PATCH] Simplify Powerset implementation (uses default Iterator::size_hint()) --- src/combinations.rs | 54 +++++++------------- src/lazy_buffer.rs | 9 ++-- src/powerset.rs | 119 +++++--------------------------------------- src/size_hint.rs | 25 ---------- 4 files changed, 36 insertions(+), 171 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 85f69804d..22ad205da 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -31,15 +31,15 @@ impl fmt::Debug for Combinations pub fn combinations(iter: I, k: usize) -> Combinations where I: Iterator { - let mut pool = LazyBuffer::new(iter); - pool.prefill(k); - - Combinations::from_pool(pool, k) + Combinations::new(iter, k) } impl Combinations { #[inline] - pub(crate) fn from_pool(pool: LazyBuffer, 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, @@ -52,45 +52,29 @@ impl Combinations { 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 } } diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index f7d735a29..c93568811 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -13,11 +13,10 @@ where I: Iterator, { pub fn new(it: I) -> LazyBuffer { - LazyBuffer::from_parts(it, false, Vec::new()) - } - - pub(crate) fn from_parts(it: I, done: bool, buffer: Vec) -> LazyBuffer { - LazyBuffer { it, done, buffer } + LazyBuffer { + it, + done: false, + buffer: Vec::new() } } pub fn len(&self) -> usize { diff --git a/src/powerset.rs b/src/powerset.rs index f83bfa045..6cc317654 100644 --- a/src/powerset.rs +++ b/src/powerset.rs @@ -1,10 +1,7 @@ -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. /// @@ -12,23 +9,21 @@ use super::size_hint; /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Powerset { - inner: Inner, - // Position from start of iteration, used for first element and size-hint. - pos: usize, + combs: Combinations, } impl Clone for Powerset where I: Clone + Iterator, I::Item: Clone, { - clone_fields!(inner, pos); + clone_fields!(combs); } impl fmt::Debug for Powerset 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. @@ -36,21 +31,7 @@ pub fn powerset(src: I) -> Powerset 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 { - // Buffering mode, source iterator still yielding elements. - Buffer(I, Vec), - // Combination mode, yielding combinations of buffered elements. - Combs(Combinations), - // Temporary state for in-place switching of Buffer->Combs. - Empty_, + Powerset { combs: Combinations::new(src, 0) } } impl Iterator for Powerset @@ -61,89 +42,15 @@ impl Iterator for Powerset type Item = Vec; fn next(&mut self) -> Option> { - 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) { - // 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 Powerset - 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> { - // 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 + } } } } diff --git a/src/size_hint.rs b/src/size_hint.rs index d969fd3b5..9d9b8b8a1 100644 --- a/src/size_hint.rs +++ b/src/size_hint.rs @@ -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 {