Skip to content

Commit

Permalink
Merge branch 'rust-itertools:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
lpewewq authored Dec 13, 2024
2 parents 6489da4 + f80883b commit 3265f40
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ rand = "0.7"
criterion = { version = "0.4.0", features = ["html_reports"] }
paste = "1.0.0" # Used in test_std to instantiate generic tests
permutohedron = "0.2"
quickcheck = { version = "0.9", default_features = false }
quickcheck = { version = "0.9", default-features = false }

[features]
default = ["use_std"]
Expand Down
175 changes: 120 additions & 55 deletions src/combinations.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::array;
use core::borrow::BorrowMut;
use std::fmt;
use std::iter::FusedIterator;

Expand All @@ -6,45 +8,101 @@ use alloc::vec::Vec;

use crate::adaptors::checked_binomial;

/// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations)
pub type Combinations<I> = CombinationsGeneric<I, Vec<usize>>;
/// Iterator for const generic combinations returned by [`.array_combinations()`](crate::Itertools::array_combinations)
pub type ArrayCombinations<I, const K: usize> = CombinationsGeneric<I, [usize; K]>;

/// Create a new `Combinations` from a clonable iterator.
pub fn combinations<I: Iterator>(iter: I, k: usize) -> Combinations<I>
where
I::Item: Clone,
{
Combinations::new(iter, (0..k).collect())
}

/// Create a new `ArrayCombinations` from a clonable iterator.
pub fn array_combinations<I: Iterator, const K: usize>(iter: I) -> ArrayCombinations<I, K>
where
I::Item: Clone,
{
ArrayCombinations::new(iter, array::from_fn(|i| i))
}

/// An iterator to iterate through all the `k`-length combinations in an iterator.
///
/// See [`.combinations()`](crate::Itertools::combinations) for more information.
/// See [`.combinations()`](crate::Itertools::combinations) and [`.array_combinations()`](crate::Itertools::array_combinations) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Combinations<I: Iterator> {
indices: Vec<usize>,
pub struct CombinationsGeneric<I: Iterator, Idx> {
indices: Idx,
pool: LazyBuffer<I>,
first: bool,
}

impl<I> Clone for Combinations<I>
/// A type holding indices of elements in a pool or buffer of items from an inner iterator
/// and used to pick out different combinations in a generic way.
pub trait PoolIndex<T>: BorrowMut<[usize]> {
type Item;

fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Self::Item
where
T: Clone;

fn len(&self) -> usize {
self.borrow().len()
}
}

impl<T> PoolIndex<T> for Vec<usize> {
type Item = Vec<T>;

fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Vec<T>
where
T: Clone,
{
pool.get_at(self)
}
}

impl<T, const K: usize> PoolIndex<T> for [usize; K] {
type Item = [T; K];

fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> [T; K]
where
T: Clone,
{
pool.get_array(*self)
}
}

impl<I, Idx> Clone for CombinationsGeneric<I, Idx>
where
I: Clone + Iterator,
I: Iterator + Clone,
I::Item: Clone,
Idx: Clone,
{
clone_fields!(indices, pool, first);
}

impl<I> fmt::Debug for Combinations<I>
impl<I, Idx> fmt::Debug for CombinationsGeneric<I, Idx>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
Idx: fmt::Debug,
{
debug_fmt_fields!(Combinations, indices, pool, first);
}

/// Create a new `Combinations` from a clonable iterator.
pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
where
I: Iterator,
{
Combinations {
indices: (0..k).collect(),
pool: LazyBuffer::new(iter),
first: true,
impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsGeneric<I, Idx> {
/// Constructor with arguments the inner iterator and the initial state for the indices.
fn new(iter: I, indices: Idx) -> Self {
Self {
indices,
pool: LazyBuffer::new(iter),
first: true,
}
}
}

impl<I: Iterator> Combinations<I> {
/// Returns the length of a combination produced by this iterator.
#[inline]
pub fn k(&self) -> usize {
Expand All @@ -64,35 +122,15 @@ impl<I: Iterator> Combinations<I> {
&self.pool
}

/// 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 current length
/// of the data pool an attempt is made to prefill the pool so that it holds `k`
/// elements.
pub(crate) fn reset(&mut self, k: usize) {
self.first = true;

if k < self.indices.len() {
self.indices.truncate(k);
for i in 0..k {
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);
}
}

/// Return the length of the inner iterator and the count of remaining combinations.
pub(crate) fn n_and_count(self) -> (usize, usize) {
let Self {
indices,
pool,
first,
} = self;
let n = pool.count();
(n, remaining_for(n, first, &indices).unwrap())
(n, remaining_for(n, first, indices.borrow()).unwrap())
}

/// Initialises the iterator by filling a buffer with elements from the
Expand All @@ -113,19 +151,21 @@ impl<I: Iterator> Combinations<I> {
///
/// Returns true if we've run out of combinations, false otherwise.
fn increment_indices(&mut self) -> bool {
if self.indices.is_empty() {
// Borrow once instead of noise each time it's indexed
let indices = self.indices.borrow_mut();

if indices.is_empty() {
return true; // Done
}

// Scan from the end, looking for an index to increment
let mut i: usize = self.indices.len() - 1;
let mut i: usize = indices.len() - 1;

// Check if we need to consume more from the iterator
if self.indices[i] == self.pool.len() - 1 {
if indices[i] == self.pool.len() - 1 {
self.pool.get_next(); // may change pool size
}

while self.indices[i] == i + self.pool.len() - self.indices.len() {
while indices[i] == i + self.pool.len() - indices.len() {
if i > 0 {
i -= 1;
} else {
Expand All @@ -135,18 +175,18 @@ impl<I: Iterator> Combinations<I> {
}

// Increment index, and reset the ones to its right
self.indices[i] += 1;
for j in i + 1..self.indices.len() {
self.indices[j] = self.indices[j - 1] + 1;
indices[i] += 1;
for j in i + 1..indices.len() {
indices[j] = indices[j - 1] + 1;
}

// If we've made it this far, we haven't run out of combos
false
}

/// Returns the n-th item or the number of successful steps.
pub(crate) fn try_nth(&mut self, n: usize) -> Result<<Self as Iterator>::Item, usize>
where
I: Iterator,
I::Item: Clone,
{
let done = if self.first {
Expand All @@ -162,16 +202,17 @@ impl<I: Iterator> Combinations<I> {
return Err(i + 1);
}
}
Ok(self.pool.get_at(&self.indices))
Ok(self.indices.extract_item(&self.pool))
}
}

impl<I> Iterator for Combinations<I>
impl<I, Idx> Iterator for CombinationsGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
type Item = Vec<I::Item>;
type Item = Idx::Item;
fn next(&mut self) -> Option<Self::Item> {
let done = if self.first {
self.init()
Expand All @@ -183,7 +224,7 @@ where
return None;
}

Some(self.pool.get_at(&self.indices))
Some(self.indices.extract_item(&self.pool))
}

fn nth(&mut self, n: usize) -> Option<Self::Item> {
Expand All @@ -192,8 +233,8 @@ where

fn size_hint(&self) -> (usize, Option<usize>) {
let (mut low, mut upp) = self.pool.size_hint();
low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX);
upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow()));
(low, upp)
}

Expand All @@ -203,13 +244,37 @@ where
}
}

impl<I> FusedIterator for Combinations<I>
impl<I, Idx> FusedIterator for CombinationsGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
}

impl<I: Iterator> Combinations<I> {
/// 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 current length
/// of the data pool an attempt is made to prefill the pool so that it holds `k`
/// elements.
pub(crate) fn reset(&mut self, k: usize) {
self.first = true;

if k < self.indices.len() {
self.indices.truncate(k);
for i in 0..k {
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);
}
}
}

/// For a given size `n`, return the count of remaining combinations or None if it would overflow.
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
let k = indices.len();
Expand Down
45 changes: 13 additions & 32 deletions src/cons_tuples_impl.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,39 @@
use crate::adaptors::map::{MapSpecialCase, MapSpecialCaseFn};

macro_rules! impl_cons_iter(
($_A:ident, $_B:ident, ) => (); // stop

($A:ident, $($B:ident,)*) => (
impl_cons_iter!($($B,)*);
#[allow(non_snake_case)]
impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
where Iter: Iterator<Item = (($($B,)*), X)>,
{
type Item = ($($B,)* X, );
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
fn fold<Acc, Fold>(self, accum: Acc, mut f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, )))
impl<$($B),*, X> MapSpecialCaseFn<(($($B,)*), X)> for ConsTuplesFn {
type Out = ($($B,)* X, );
fn call(&mut self, (($($B,)*), X): (($($B,)*), X)) -> Self::Out {
($($B,)* X, )
}
}
);
);

impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,);

#[derive(Debug, Clone)]
pub struct ConsTuplesFn;

/// An iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct ConsTuples<I, J>
where
I: Iterator<Item = J>,
{
iter: I,
}

impl<I, J> Clone for ConsTuples<I, J>
where
I: Clone + Iterator<Item = J>,
{
clone_fields!(iter);
}
pub type ConsTuples<I> = MapSpecialCase<I, ConsTuplesFn>;

/// Create an iterator that maps for example iterators of
/// `((A, B), C)` to `(A, B, C)`.
pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I::IntoIter, J>
pub fn cons_tuples<I>(iterable: I) -> ConsTuples<I::IntoIter>
where
I: IntoIterator<Item = J>,
I: IntoIterator,
{
ConsTuples {
iter: iterable.into_iter(),
f: ConsTuplesFn,
}
}
2 changes: 1 addition & 1 deletion src/diff.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! "Diff"ing iterators for caching elements to sequential collections without requiring the new
//! elements' iterator to be `Clone`.
//!
//! - [`Diff`] (produced by the [`diff_with`] function)
//! [`Diff`] (produced by the [`diff_with`] function)
//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
//! a lock-step comparison.
Expand Down
4 changes: 4 additions & 0 deletions src/lazy_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ where
pub fn get_at(&self, indices: &[usize]) -> Vec<I::Item> {
indices.iter().map(|i| self.buffer[*i].clone()).collect()
}

pub fn get_array<const K: usize>(&self, indices: [usize; K]) -> [I::Item; K] {
indices.map(|i| self.buffer[i].clone())
}
}

impl<I, J> Index<J> for LazyBuffer<I>
Expand Down
Loading

0 comments on commit 3265f40

Please sign in to comment.