From e27b3eeff7bb14d1bf763832c50ed5acd9ce8afa Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 20:34:08 +0100 Subject: [PATCH 01/18] Treat k==0 less special --- src/permutations.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 91389a73a..14db32662 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -66,18 +66,19 @@ where fn next(&mut self) -> Option { let Self { vals, state } = self; match state { - PermutationState::Start { k: 0 } => { - *state = PermutationState::End; - Some(Vec::new()) - } &mut PermutationState::Start { k } => { - vals.prefill(k); - if vals.len() != k { + if k == 0 { *state = PermutationState::End; - return None; + Some(Vec::new()) + } else { + vals.prefill(k); + if vals.len() != k { + *state = PermutationState::End; + return None; + } + *state = PermutationState::Buffered { k, min_n: k }; + Some(vals[0..k].to_vec()) } - *state = PermutationState::Buffered { k, min_n: k }; - Some(vals[0..k].to_vec()) } PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { From 8ae44e06efc81757120daba69491c86fcf5e4568 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 20:38:18 +0100 Subject: [PATCH 02/18] Treat k==0 less special (2) --- src/permutations.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 14db32662..9cdc94219 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -69,7 +69,6 @@ where &mut PermutationState::Start { k } => { if k == 0 { *state = PermutationState::End; - Some(Vec::new()) } else { vals.prefill(k); if vals.len() != k { @@ -77,8 +76,8 @@ where return None; } *state = PermutationState::Buffered { k, min_n: k }; - Some(vals[0..k].to_vec()) } + Some(vals[0..k].to_vec()) } PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { From 9b3d6e2b9b7c83323778fe7af741ebf7721a9dec Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 21:15:16 +0100 Subject: [PATCH 03/18] Introduce MaybeConstSize --- src/combinations.rs | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 54a027551..8a1a73c75 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -39,22 +39,39 @@ pub struct CombinationsGeneric { first: bool, } +pub trait MaybeConstUsize { + /*TODO const*/fn value(self) -> usize; +} + +pub struct ConstUsize; +impl MaybeConstUsize for ConstUsize { + fn value(self) -> usize { + N + } +} + +impl MaybeConstUsize for usize { + fn value(self) -> usize { + self + } +} + /// 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: BorrowMut<[usize]> { type Item; + type Length: MaybeConstUsize; fn extract_item>(&self, pool: &LazyBuffer) -> Self::Item where T: Clone; - fn len(&self) -> usize { - self.borrow().len() - } + fn len(&self) -> Self::Length; } impl PoolIndex for Vec { type Item = Vec; + type Length = usize; fn extract_item>(&self, pool: &LazyBuffer) -> Vec where @@ -62,10 +79,15 @@ impl PoolIndex for Vec { { pool.get_at(self) } + + fn len(&self) -> Self::Length { + self.len() + } } impl PoolIndex for [usize; K] { type Item = [T; K]; + type Length = ConstUsize; fn extract_item>(&self, pool: &LazyBuffer) -> [T; K] where @@ -73,6 +95,10 @@ impl PoolIndex for [usize; K] { { pool.get_array(*self) } + + fn len(&self) -> Self::Length { + ConstUsize:: + } } impl Clone for CombinationsGeneric @@ -105,7 +131,7 @@ impl> CombinationsGeneric { /// Returns the length of a combination produced by this iterator. #[inline] - pub fn k(&self) -> usize { + pub fn k(&self) -> Idx::Length { self.indices.len() } @@ -136,8 +162,8 @@ impl> CombinationsGeneric { /// Initialises the iterator by filling a buffer with elements from the /// iterator. Returns true if there are no combinations, false otherwise. fn init(&mut self) -> bool { - self.pool.prefill(self.k()); - let done = self.k() > self.n(); + self.pool.prefill(self.k().value()); + let done = self.k().value() > self.n(); if !done { self.first = false; } From 5003927dad72beae244dc8151cab35fd73d5e25d Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 21:31:39 +0100 Subject: [PATCH 04/18] PoolIndex -> PoolIndex --- src/combinations.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 8a1a73c75..5fdfbe676 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -58,24 +58,24 @@ impl MaybeConstUsize for usize { /// 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: BorrowMut<[usize]> { - type Item; +pub trait PoolIndex: BorrowMut<[usize]> { + type Item; type Length: MaybeConstUsize; - fn extract_item>(&self, pool: &LazyBuffer) -> Self::Item + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item where - T: Clone; + I::Item: Clone; fn len(&self) -> Self::Length; } -impl PoolIndex for Vec { - type Item = Vec; +impl PoolIndex for Vec { + type Item = Vec; type Length = usize; - fn extract_item>(&self, pool: &LazyBuffer) -> Vec + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item where - T: Clone, + I::Item: Clone { pool.get_at(self) } @@ -85,13 +85,13 @@ impl PoolIndex for Vec { } } -impl PoolIndex for [usize; K] { - type Item = [T; K]; +impl PoolIndex for [usize; K] { + type Item = [T; K]; type Length = ConstUsize; - fn extract_item>(&self, pool: &LazyBuffer) -> [T; K] + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item where - T: Clone, + I::Item: Clone { pool.get_array(*self) } @@ -119,7 +119,7 @@ where debug_fmt_fields!(Combinations, indices, pool, first); } -impl> CombinationsGeneric { +impl CombinationsGeneric { /// Constructor with arguments the inner iterator and the initial state for the indices. fn new(iter: I, indices: Idx) -> Self { Self { @@ -236,9 +236,9 @@ impl Iterator for CombinationsGeneric where I: Iterator, I::Item: Clone, - Idx: PoolIndex, + Idx: PoolIndex, { - type Item = Idx::Item; + type Item = Idx::Item; fn next(&mut self) -> Option { let done = if self.first { self.init() @@ -274,7 +274,7 @@ impl FusedIterator for CombinationsGeneric where I: Iterator, I::Item: Clone, - Idx: PoolIndex, + Idx: PoolIndex, { } From 582647022ac05c1ae43f1bca2cd6391469f0c5da Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 22:04:32 +0100 Subject: [PATCH 05/18] MaybeConstSize (2) --- src/combinations.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/combinations.rs b/src/combinations.rs index 5fdfbe676..22470d471 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -39,10 +39,11 @@ pub struct CombinationsGeneric { first: bool, } -pub trait MaybeConstUsize { +pub trait MaybeConstUsize : Clone + Copy + std::fmt::Debug { /*TODO const*/fn value(self) -> usize; } +#[derive(Clone, Copy, Debug)] pub struct ConstUsize; impl MaybeConstUsize for ConstUsize { fn value(self) -> usize { From aa90cbbf15feaf02ba83a25bbb79e09f114665c9 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 22:05:13 +0100 Subject: [PATCH 06/18] Express some things via PoolIndex/MaybeConstUsize --- src/permutations.rs | 61 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 9cdc94219..d58ede3a0 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -6,32 +6,36 @@ use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; use crate::size_hint::{self, SizeHint}; +use crate::combinations::{MaybeConstUsize, PoolIndex}; + +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct PermutationsGeneric { + vals: LazyBuffer, + state: PermutationState, +} /// An iterator adaptor that iterates through all the `k`-permutations of the /// elements from an iterator. /// /// See [`.permutations()`](crate::Itertools::permutations) for /// more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Permutations { - vals: LazyBuffer, - state: PermutationState, -} +pub type Permutations = PermutationsGeneric>; -impl Clone for Permutations +impl Clone for PermutationsGeneric where I: Clone + Iterator, I::Item: Clone, + Idx: Clone + PoolIndex, { clone_fields!(vals, state); } #[derive(Clone, Debug)] -enum PermutationState { +enum PermutationState { /// No permutation generated yet. - Start { k: usize }, + Start { k: Idx::Length }, /// Values from the iterator are not fully loaded yet so `n` is still unknown. - Buffered { k: usize, min_n: usize }, + Buffered { k: Idx::Length, min_n: usize }, /// All values from the iterator are known so `n` is known. Loaded { indices: Box<[usize]>, @@ -41,12 +45,13 @@ enum PermutationState { End, } -impl fmt::Debug for Permutations +impl fmt::Debug for PermutationsGeneric where I: Iterator + fmt::Debug, I::Item: fmt::Debug, + Idx: fmt::Debug, { - debug_fmt_fields!(Permutations, vals, state); + debug_fmt_fields!(PermutationsGeneric, vals, state); } pub fn permutations(iter: I, k: usize) -> Permutations { @@ -56,7 +61,7 @@ pub fn permutations(iter: I, k: usize) -> Permutations { } } -impl Iterator for Permutations +impl Iterator for PermutationsGeneric where I: Iterator, I::Item: Clone, @@ -67,21 +72,21 @@ where let Self { vals, state } = self; match state { &mut PermutationState::Start { k } => { - if k == 0 { + if k.value() == 0 { *state = PermutationState::End; } else { - vals.prefill(k); - if vals.len() != k { + vals.prefill(k.value()); + if vals.len() != k.value() { *state = PermutationState::End; return None; } - *state = PermutationState::Buffered { k, min_n: k }; + *state = PermutationState::Buffered { k, min_n: k.value() }; } - Some(vals[0..k].to_vec()) + Some(vals[0..k.value()].to_vec()) } PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { - let item = (0..*k - 1) + let item = (0..k.value() - 1) .chain(once(*min_n)) .map(|i| vals[i].clone()) .collect(); @@ -89,9 +94,9 @@ where Some(item) } else { let n = *min_n; - let prev_iteration_count = n - *k + 1; + let prev_iteration_count = n - k.value() + 1; let mut indices: Box<[_]> = (0..n).collect(); - let mut cycles: Box<[_]> = (n - k..n).rev().collect(); + let mut cycles: Box<[_]> = (n - k.value()..n).rev().collect(); // Advance the state to the correct point. for _ in 0..prev_iteration_count { if advance(&mut indices, &mut cycles) { @@ -99,7 +104,7 @@ where return None; } } - let item = vals.get_at(&indices[0..*k]); + let item = vals.get_at(&indices[0..k.value()]); *state = PermutationState::Loaded { indices, cycles }; Some(item) } @@ -130,7 +135,7 @@ where } } -impl FusedIterator for Permutations +impl FusedIterator for PermutationsGeneric where I: Iterator, I::Item: Clone, @@ -155,20 +160,20 @@ fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { true } -impl PermutationState { +impl PermutationState { fn size_hint_for(&self, n: usize) -> SizeHint { // At the beginning, there are `n!/(n-k)!` items to come. - let at_start = |n, k| { - debug_assert!(n >= k); - let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)); + let at_start = |n, k: Idx::Length| { + debug_assert!(n >= k.value()); + let total = (n - k.value() + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)); (total.unwrap_or(usize::MAX), total) }; match *self { - Self::Start { k } if n < k => (0, Some(0)), + Self::Start { k } if n < k.value() => (0, Some(0)), Self::Start { k } => at_start(n, k), Self::Buffered { k, min_n } => { // Same as `Start` minus the previously generated items. - size_hint::sub_scalar(at_start(n, k), min_n - k + 1) + size_hint::sub_scalar(at_start(n, k), min_n - k.value() + 1) } Self::Loaded { ref indices, From 98b79de070d1898e3aa41727d346e2f7d682836c Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Jan 2025 22:13:22 +0100 Subject: [PATCH 07/18] Try to use PoolIndex for permutations --- src/combinations.rs | 10 ++++++++++ src/permutations.rs | 32 ++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 22470d471..4cc216588 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -67,6 +67,8 @@ pub trait PoolIndex: BorrowMut<[usize]> { where I::Item: Clone; + fn from_fnT>(k: Self::Length, f: F) -> Self::Item; + fn len(&self) -> Self::Length; } @@ -80,6 +82,10 @@ impl PoolIndex for Vec { { pool.get_at(self) } + + fn from_fnT>(k: Self::Length, f: F) -> Self::Item { + (0..k).map(f).collect() + } fn len(&self) -> Self::Length { self.len() @@ -97,6 +103,10 @@ impl PoolIndex for [usize; K] { pool.get_array(*self) } + fn from_fnT>(_k: Self::Length, f: F) -> Self::Item { + std::array::from_fn(f) + } + fn len(&self) -> Self::Length { ConstUsize:: } diff --git a/src/permutations.rs b/src/permutations.rs index d58ede3a0..9ae58d2c8 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -1,7 +1,6 @@ use alloc::boxed::Box; use alloc::vec::Vec; use std::fmt; -use std::iter::once; use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; @@ -39,7 +38,8 @@ enum PermutationState { /// All values from the iterator are known so `n` is known. Loaded { indices: Box<[usize]>, - cycles: Box<[usize]>, + cycles: Box<[usize]>, // TODO Should be Idx::Item + k: Idx::Length, // TODO Should be inferred from cycles }, /// No permutation left to generate. End, @@ -66,7 +66,7 @@ where I: Iterator, I::Item: Clone, { - type Item = Vec; + type Item = Idx::Item; fn next(&mut self) -> Option { let Self { vals, state } = self; @@ -82,14 +82,18 @@ where } *state = PermutationState::Buffered { k, min_n: k.value() }; } - Some(vals[0..k.value()].to_vec()) + Some(Idx::from_fn(k, |i| vals[i].clone())) } - PermutationState::Buffered { ref k, min_n } => { + PermutationState::Buffered { k, min_n } => { if vals.get_next() { - let item = (0..k.value() - 1) - .chain(once(*min_n)) - .map(|i| vals[i].clone()) - .collect(); + // TODO This is ugly. Maybe working on indices is better? + let item = Idx::from_fn(*k, |i| { + vals[if i==k.value()-1 { + *min_n + } else { + i + }].clone() + }); *min_n += 1; Some(item) } else { @@ -104,18 +108,17 @@ where return None; } } - let item = vals.get_at(&indices[0..k.value()]); - *state = PermutationState::Loaded { indices, cycles }; + let item = Idx::from_fn(*k, |i| vals[indices[i]].clone()); + *state = PermutationState::Loaded { indices, cycles, k:*k }; Some(item) } } - PermutationState::Loaded { indices, cycles } => { + PermutationState::Loaded { indices, cycles, k} => { if advance(indices, cycles) { *state = PermutationState::End; return None; } - let k = cycles.len(); - Some(vals.get_at(&indices[0..k])) + Some(Idx::from_fn(*k, |i| vals[indices[i]].clone())) } PermutationState::End => None, } @@ -178,6 +181,7 @@ impl PermutationState { Self::Loaded { ref indices, ref cycles, + k: _, } => { let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { acc.checked_mul(indices.len() - i) From 79f52b2e4fa9a74a3d12ce09396e1b260170ad78 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 12:31:49 +0100 Subject: [PATCH 08/18] Move MaybeConstUsize, PoolIndex to lazy_buffer --- src/combinations.rs | 77 ++------------------------------------------- src/lazy_buffer.rs | 75 +++++++++++++++++++++++++++++++++++++++++++ src/permutations.rs | 24 +++++++------- 3 files changed, 90 insertions(+), 86 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 4cc216588..5af9cd47a 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -1,12 +1,12 @@ use core::array; -use core::borrow::BorrowMut; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::LazyBuffer; +use super::lazy_buffer::{LazyBuffer, PoolIndex}; use alloc::vec::Vec; use crate::adaptors::checked_binomial; +use crate::lazy_buffer::MaybeConstUsize as _; /// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations) pub type Combinations = CombinationsGeneric>; @@ -39,79 +39,6 @@ pub struct CombinationsGeneric { first: bool, } -pub trait MaybeConstUsize : Clone + Copy + std::fmt::Debug { - /*TODO const*/fn value(self) -> usize; -} - -#[derive(Clone, Copy, Debug)] -pub struct ConstUsize; -impl MaybeConstUsize for ConstUsize { - fn value(self) -> usize { - N - } -} - -impl MaybeConstUsize for usize { - fn value(self) -> usize { - self - } -} - -/// 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: BorrowMut<[usize]> { - type Item; - type Length: MaybeConstUsize; - - fn extract_item(&self, pool: &LazyBuffer) -> Self::Item - where - I::Item: Clone; - - fn from_fnT>(k: Self::Length, f: F) -> Self::Item; - - fn len(&self) -> Self::Length; -} - -impl PoolIndex for Vec { - type Item = Vec; - type Length = usize; - - fn extract_item(&self, pool: &LazyBuffer) -> Self::Item - where - I::Item: Clone - { - pool.get_at(self) - } - - fn from_fnT>(k: Self::Length, f: F) -> Self::Item { - (0..k).map(f).collect() - } - - fn len(&self) -> Self::Length { - self.len() - } -} - -impl PoolIndex for [usize; K] { - type Item = [T; K]; - type Length = ConstUsize; - - fn extract_item(&self, pool: &LazyBuffer) -> Self::Item - where - I::Item: Clone - { - pool.get_array(*self) - } - - fn from_fnT>(_k: Self::Length, f: F) -> Self::Item { - std::array::from_fn(f) - } - - fn len(&self) -> Self::Length { - ConstUsize:: - } -} - impl Clone for CombinationsGeneric where I: Iterator + Clone, diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index fafa5f726..80f74f56f 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -1,4 +1,5 @@ use alloc::vec::Vec; +use core::borrow::BorrowMut; use std::iter::Fuse; use std::ops::Index; @@ -77,3 +78,77 @@ where self.buffer.index(index) } } + +pub trait MaybeConstUsize: Clone + Copy + std::fmt::Debug { + /*TODO const*/ + fn value(self) -> usize; +} + +#[derive(Clone, Copy, Debug)] +pub struct ConstUsize; +impl MaybeConstUsize for ConstUsize { + fn value(self) -> usize { + N + } +} + +impl MaybeConstUsize for usize { + fn value(self) -> usize { + self + } +} + +/// 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: BorrowMut<[usize]> { + type Item; + type Length: MaybeConstUsize; + + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item + where + I::Item: Clone; + + fn from_fn T>(k: Self::Length, f: F) -> Self::Item; + + fn len(&self) -> Self::Length; +} + +impl PoolIndex for Vec { + type Item = Vec; + type Length = usize; + + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item + where + I::Item: Clone, + { + pool.get_at(self) + } + + fn from_fn T>(k: Self::Length, f: F) -> Self::Item { + (0..k).map(f).collect() + } + + fn len(&self) -> Self::Length { + self.len() + } +} + +impl PoolIndex for [usize; K] { + type Item = [T; K]; + type Length = ConstUsize; + + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item + where + I::Item: Clone, + { + pool.get_array(*self) + } + + fn from_fn T>(_k: Self::Length, f: F) -> Self::Item { + std::array::from_fn(f) + } + + fn len(&self) -> Self::Length { + ConstUsize:: + } +} diff --git a/src/permutations.rs b/src/permutations.rs index 9ae58d2c8..708f76a16 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -3,9 +3,8 @@ use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::LazyBuffer; +use super::lazy_buffer::{LazyBuffer, MaybeConstUsize as _, PoolIndex}; use crate::size_hint::{self, SizeHint}; -use crate::combinations::{MaybeConstUsize, PoolIndex}; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PermutationsGeneric { @@ -39,7 +38,7 @@ enum PermutationState { Loaded { indices: Box<[usize]>, cycles: Box<[usize]>, // TODO Should be Idx::Item - k: Idx::Length, // TODO Should be inferred from cycles + k: Idx::Length, // TODO Should be inferred from cycles }, /// No permutation left to generate. End, @@ -80,7 +79,10 @@ where *state = PermutationState::End; return None; } - *state = PermutationState::Buffered { k, min_n: k.value() }; + *state = PermutationState::Buffered { + k, + min_n: k.value(), + }; } Some(Idx::from_fn(k, |i| vals[i].clone())) } @@ -88,11 +90,7 @@ where if vals.get_next() { // TODO This is ugly. Maybe working on indices is better? let item = Idx::from_fn(*k, |i| { - vals[if i==k.value()-1 { - *min_n - } else { - i - }].clone() + vals[if i == k.value() - 1 { *min_n } else { i }].clone() }); *min_n += 1; Some(item) @@ -109,11 +107,15 @@ where } } let item = Idx::from_fn(*k, |i| vals[indices[i]].clone()); - *state = PermutationState::Loaded { indices, cycles, k:*k }; + *state = PermutationState::Loaded { + indices, + cycles, + k: *k, + }; Some(item) } } - PermutationState::Loaded { indices, cycles, k} => { + PermutationState::Loaded { indices, cycles, k } => { if advance(indices, cycles) { *state = PermutationState::End; return None; From 85a84bd12f5059be348e752690a0aa30f95bdd50 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 12:33:42 +0100 Subject: [PATCH 09/18] Rename PoolIndex to ArrayOrVecHelper --- src/combinations.rs | 8 ++++---- src/lazy_buffer.rs | 6 +++--- src/permutations.rs | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 5af9cd47a..32000afb7 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -2,7 +2,7 @@ use core::array; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::{LazyBuffer, PoolIndex}; +use super::lazy_buffer::{LazyBuffer, ArrayOrVecHelper}; use alloc::vec::Vec; use crate::adaptors::checked_binomial; @@ -57,7 +57,7 @@ where debug_fmt_fields!(Combinations, indices, pool, first); } -impl CombinationsGeneric { +impl CombinationsGeneric { /// Constructor with arguments the inner iterator and the initial state for the indices. fn new(iter: I, indices: Idx) -> Self { Self { @@ -174,7 +174,7 @@ impl Iterator for CombinationsGeneric where I: Iterator, I::Item: Clone, - Idx: PoolIndex, + Idx: ArrayOrVecHelper, { type Item = Idx::Item; fn next(&mut self) -> Option { @@ -212,7 +212,7 @@ impl FusedIterator for CombinationsGeneric where I: Iterator, I::Item: Clone, - Idx: PoolIndex, + Idx: ArrayOrVecHelper, { } diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index 80f74f56f..2f688ad60 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -100,7 +100,7 @@ impl MaybeConstUsize for usize { /// 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: BorrowMut<[usize]> { +pub trait ArrayOrVecHelper: BorrowMut<[usize]> { type Item; type Length: MaybeConstUsize; @@ -113,7 +113,7 @@ pub trait PoolIndex: BorrowMut<[usize]> { fn len(&self) -> Self::Length; } -impl PoolIndex for Vec { +impl ArrayOrVecHelper for Vec { type Item = Vec; type Length = usize; @@ -133,7 +133,7 @@ impl PoolIndex for Vec { } } -impl PoolIndex for [usize; K] { +impl ArrayOrVecHelper for [usize; K] { type Item = [T; K]; type Length = ConstUsize; diff --git a/src/permutations.rs b/src/permutations.rs index 708f76a16..1a3b69402 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -3,11 +3,11 @@ use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::{LazyBuffer, MaybeConstUsize as _, PoolIndex}; +use super::lazy_buffer::{ArrayOrVecHelper, LazyBuffer, MaybeConstUsize as _}; use crate::size_hint::{self, SizeHint}; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct PermutationsGeneric { +pub struct PermutationsGeneric { vals: LazyBuffer, state: PermutationState, } @@ -23,13 +23,13 @@ impl Clone for PermutationsGeneric where I: Clone + Iterator, I::Item: Clone, - Idx: Clone + PoolIndex, + Idx: Clone + ArrayOrVecHelper, { clone_fields!(vals, state); } #[derive(Clone, Debug)] -enum PermutationState { +enum PermutationState { /// No permutation generated yet. Start { k: Idx::Length }, /// Values from the iterator are not fully loaded yet so `n` is still unknown. @@ -44,7 +44,7 @@ enum PermutationState { End, } -impl fmt::Debug for PermutationsGeneric +impl fmt::Debug for PermutationsGeneric where I: Iterator + fmt::Debug, I::Item: fmt::Debug, @@ -60,7 +60,7 @@ pub fn permutations(iter: I, k: usize) -> Permutations { } } -impl Iterator for PermutationsGeneric +impl Iterator for PermutationsGeneric where I: Iterator, I::Item: Clone, @@ -140,7 +140,7 @@ where } } -impl FusedIterator for PermutationsGeneric +impl FusedIterator for PermutationsGeneric where I: Iterator, I::Item: Clone, @@ -165,7 +165,7 @@ fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { true } -impl PermutationState { +impl PermutationState { fn size_hint_for(&self, n: usize) -> SizeHint { // At the beginning, there are `n!/(n-k)!` items to come. let at_start = |n, k: Idx::Length| { From 6056b738cd023ed8e782da68e7521ffdd07740dd Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 12:46:12 +0100 Subject: [PATCH 10/18] Rename ArrayOrVecHelper::from_fn to item_from_fn --- src/lazy_buffer.rs | 10 ++++++---- src/permutations.rs | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index 2f688ad60..5a72e50cf 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -108,7 +108,9 @@ pub trait ArrayOrVecHelper: BorrowMut<[usize]> { where I::Item: Clone; - fn from_fn T>(k: Self::Length, f: F) -> Self::Item; + // TODO if only ever used to index into LazyBuffer, specialize to + // extract_from_fn(Self::Length, Fn(usize) -> usize, &LazyBuffer) -> Item? + fn item_from_fn T>(len: Self::Length, f: F) -> Self::Item; fn len(&self) -> Self::Length; } @@ -124,8 +126,8 @@ impl ArrayOrVecHelper for Vec { pool.get_at(self) } - fn from_fn T>(k: Self::Length, f: F) -> Self::Item { - (0..k).map(f).collect() + fn item_from_fn T>(len: Self::Length, f: F) -> Self::Item { + (0..len).map(f).collect() } fn len(&self) -> Self::Length { @@ -144,7 +146,7 @@ impl ArrayOrVecHelper for [usize; K] { pool.get_array(*self) } - fn from_fn T>(_k: Self::Length, f: F) -> Self::Item { + fn item_from_fn T>(_len: Self::Length, f: F) -> Self::Item { std::array::from_fn(f) } diff --git a/src/permutations.rs b/src/permutations.rs index 1a3b69402..d7fa98534 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -84,12 +84,12 @@ where min_n: k.value(), }; } - Some(Idx::from_fn(k, |i| vals[i].clone())) + Some(Idx::item_from_fn(k, |i| vals[i].clone())) } PermutationState::Buffered { k, min_n } => { if vals.get_next() { // TODO This is ugly. Maybe working on indices is better? - let item = Idx::from_fn(*k, |i| { + let item = Idx::item_from_fn(*k, |i| { vals[if i == k.value() - 1 { *min_n } else { i }].clone() }); *min_n += 1; @@ -106,7 +106,7 @@ where return None; } } - let item = Idx::from_fn(*k, |i| vals[indices[i]].clone()); + let item = Idx::item_from_fn(*k, |i| vals[indices[i]].clone()); *state = PermutationState::Loaded { indices, cycles, @@ -120,7 +120,7 @@ where *state = PermutationState::End; return None; } - Some(Idx::from_fn(*k, |i| vals[indices[i]].clone())) + Some(Idx::item_from_fn(*k, |i| vals[indices[i]].clone())) } PermutationState::End => None, } From 99de00397357190e9b2cd3b2f8fa379690d9e788 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 12:57:30 +0100 Subject: [PATCH 11/18] Use Idx type directly in PermutationState --- src/lazy_buffer.rs | 17 +++++++++++++ src/permutations.rs | 60 +++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index 5a72e50cf..f86292f74 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -113,6 +113,15 @@ pub trait ArrayOrVecHelper: BorrowMut<[usize]> { fn item_from_fn T>(len: Self::Length, f: F) -> Self::Item; fn len(&self) -> Self::Length; + + fn from_fn usize>(k: Self::Length, f: F) -> Self; + + fn start(len: Self::Length) -> Self + where + Self: Sized, + { + Self::from_fn(len, |i| i) + } } impl ArrayOrVecHelper for Vec { @@ -133,6 +142,10 @@ impl ArrayOrVecHelper for Vec { fn len(&self) -> Self::Length { self.len() } + + fn from_fn usize>(k: Self::Length, f: F) -> Self { + (0..k).map(f).collect() + } } impl ArrayOrVecHelper for [usize; K] { @@ -153,4 +166,8 @@ impl ArrayOrVecHelper for [usize; K] { fn len(&self) -> Self::Length { ConstUsize:: } + + fn from_fn usize>(_len: Self::Length, f: F) -> Self { + std::array::from_fn(f) + } } diff --git a/src/permutations.rs b/src/permutations.rs index d7fa98534..f3fa24102 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -33,13 +33,9 @@ enum PermutationState { /// No permutation generated yet. Start { k: Idx::Length }, /// Values from the iterator are not fully loaded yet so `n` is still unknown. - Buffered { k: Idx::Length, min_n: usize }, + Buffered { indices: Idx, min_n: usize }, /// All values from the iterator are known so `n` is known. - Loaded { - indices: Box<[usize]>, - cycles: Box<[usize]>, // TODO Should be Idx::Item - k: Idx::Length, // TODO Should be inferred from cycles - }, + Loaded { indices: Box<[usize]>, cycles: Idx }, /// No permutation left to generate. End, } @@ -80,47 +76,43 @@ where return None; } *state = PermutationState::Buffered { - k, + indices: Idx::start(k), min_n: k.value(), }; } Some(Idx::item_from_fn(k, |i| vals[i].clone())) } - PermutationState::Buffered { k, min_n } => { + PermutationState::Buffered { indices, min_n } => { + let k = indices.len(); if vals.get_next() { - // TODO This is ugly. Maybe working on indices is better? - let item = Idx::item_from_fn(*k, |i| { - vals[if i == k.value() - 1 { *min_n } else { i }].clone() - }); + indices.borrow_mut()[k.value() - 1] += 1; *min_n += 1; - Some(item) + Some(indices.extract_item(vals)) } else { let n = *min_n; let prev_iteration_count = n - k.value() + 1; let mut indices: Box<[_]> = (0..n).collect(); - let mut cycles: Box<[_]> = (n - k.value()..n).rev().collect(); + let mut cycles = Idx::from_fn(k, |i| n - 1 - i); // Advance the state to the correct point. for _ in 0..prev_iteration_count { - if advance(&mut indices, &mut cycles) { + if advance(&mut indices, cycles.borrow_mut()) { *state = PermutationState::End; return None; } } - let item = Idx::item_from_fn(*k, |i| vals[indices[i]].clone()); - *state = PermutationState::Loaded { - indices, - cycles, - k: *k, - }; + let item = Idx::item_from_fn(k, |i| vals[indices[i]].clone()); + *state = PermutationState::Loaded { indices, cycles }; Some(item) } } - PermutationState::Loaded { indices, cycles, k } => { - if advance(indices, cycles) { + PermutationState::Loaded { indices, cycles } => { + if advance(indices, cycles.borrow_mut()) { *state = PermutationState::End; return None; } - Some(Idx::item_from_fn(*k, |i| vals[indices[i]].clone())) + Some(Idx::item_from_fn(cycles.len(), |i| { + vals[indices[i]].clone() + })) } PermutationState::End => None, } @@ -173,22 +165,26 @@ impl PermutationState { let total = (n - k.value() + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)); (total.unwrap_or(usize::MAX), total) }; - match *self { + match self { Self::Start { k } if n < k.value() => (0, Some(0)), - Self::Start { k } => at_start(n, k), - Self::Buffered { k, min_n } => { + Self::Start { k } => at_start(n, *k), + Self::Buffered { indices, min_n } => { + let k = indices.len(); // Same as `Start` minus the previously generated items. size_hint::sub_scalar(at_start(n, k), min_n - k.value() + 1) } Self::Loaded { ref indices, ref cycles, - k: _, } => { - let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { - acc.checked_mul(indices.len() - i) - .and_then(|count| count.checked_add(c)) - }); + let count = cycles + .borrow() + .iter() + .enumerate() + .try_fold(0usize, |acc, (i, &c)| { + acc.checked_mul(indices.len() - i) + .and_then(|count| count.checked_add(c)) + }); (count.unwrap_or(usize::MAX), count) } Self::End => (0, Some(0)), From d8de4b84f46e39b6bc4f2edd01f1ed72e0467907 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 13:03:19 +0100 Subject: [PATCH 12/18] Use Box<[usize]> instead of Vec in Permutations --- src/lazy_buffer.rs | 26 ++++++++++++++++++++++++++ src/permutations.rs | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index f86292f74..e511e23b3 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -1,5 +1,7 @@ +use alloc::boxed::Box; use alloc::vec::Vec; use core::borrow::BorrowMut; +use core::ops::Deref as _; use std::iter::Fuse; use std::ops::Index; @@ -148,6 +150,30 @@ impl ArrayOrVecHelper for Vec { } } +impl ArrayOrVecHelper for Box<[usize]> { + type Item = Vec; + type Length = usize; + + fn extract_item(&self, pool: &LazyBuffer) -> Self::Item + where + I::Item: Clone, + { + pool.get_at(self) + } + + fn item_from_fn T>(len: Self::Length, f: F) -> Self::Item { + (0..len).map(f).collect() + } + + fn len(&self) -> Self::Length { + self.deref().len() + } + + fn from_fn usize>(k: Self::Length, f: F) -> Self { + (0..k).map(f).collect() + } +} + impl ArrayOrVecHelper for [usize; K] { type Item = [T; K]; type Length = ConstUsize; diff --git a/src/permutations.rs b/src/permutations.rs index f3fa24102..7703556a6 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -1,5 +1,4 @@ use alloc::boxed::Box; -use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; @@ -17,7 +16,7 @@ pub struct PermutationsGeneric { /// /// See [`.permutations()`](crate::Itertools::permutations) for /// more information. -pub type Permutations = PermutationsGeneric>; +pub type Permutations = PermutationsGeneric>; impl Clone for PermutationsGeneric where From 57fff6e9ec2c706da7ae37e546e957366ab9fb99 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 13:11:37 +0100 Subject: [PATCH 13/18] Use ArrayOrVecHelper::start in {Array,}Combinations --- src/combinations.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 32000afb7..01dd0246a 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -1,12 +1,10 @@ -use core::array; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::{LazyBuffer, ArrayOrVecHelper}; +use super::lazy_buffer::{ArrayOrVecHelper, ConstUsize, LazyBuffer, MaybeConstUsize as _}; use alloc::vec::Vec; use crate::adaptors::checked_binomial; -use crate::lazy_buffer::MaybeConstUsize as _; /// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations) pub type Combinations = CombinationsGeneric>; @@ -18,7 +16,7 @@ pub fn combinations(iter: I, k: usize) -> Combinations where I::Item: Clone, { - Combinations::new(iter, (0..k).collect()) + Combinations::new(iter, ArrayOrVecHelper::start(k)) } /// Create a new `ArrayCombinations` from a clonable iterator. @@ -26,7 +24,7 @@ pub fn array_combinations(iter: I) -> ArrayCombinat where I::Item: Clone, { - ArrayCombinations::new(iter, array::from_fn(|i| i)) + ArrayCombinations::new(iter, ArrayOrVecHelper::start(ConstUsize)) } /// An iterator to iterate through all the `k`-length combinations in an iterator. From 0b96072371205948e0bdf67669537338b4534382 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 13:16:44 +0100 Subject: [PATCH 14/18] Update ArrayOrVecHelper doc comments --- src/lazy_buffer.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index e511e23b3..26c897398 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -100,8 +100,8 @@ impl MaybeConstUsize for usize { } } -/// 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. +/// A type holding indices, mostly used to pick out different combinations of elements from +/// a pool or buffer of items from an inner iterator in a generic way. pub trait ArrayOrVecHelper: BorrowMut<[usize]> { type Item; type Length: MaybeConstUsize; @@ -118,6 +118,7 @@ pub trait ArrayOrVecHelper: BorrowMut<[usize]> { fn from_fn usize>(k: Self::Length, f: F) -> Self; + /// Create an array/vec/... of indices from 0 to `len - 1`. fn start(len: Self::Length) -> Self where Self: Sized, From 406eac290dc133d78b7b2c1473cd012fe65b0010 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 16:19:45 +0100 Subject: [PATCH 15/18] Avoid `use ... as _` --- src/combinations.rs | 2 +- src/lazy_buffer.rs | 2 +- src/permutations.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 01dd0246a..01693e58c 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -1,7 +1,7 @@ use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::{ArrayOrVecHelper, ConstUsize, LazyBuffer, MaybeConstUsize as _}; +use super::lazy_buffer::{ArrayOrVecHelper, ConstUsize, LazyBuffer, MaybeConstUsize}; use alloc::vec::Vec; use crate::adaptors::checked_binomial; diff --git a/src/lazy_buffer.rs b/src/lazy_buffer.rs index 26c897398..bb064f23b 100644 --- a/src/lazy_buffer.rs +++ b/src/lazy_buffer.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; use alloc::vec::Vec; use core::borrow::BorrowMut; -use core::ops::Deref as _; +use core::ops::Deref; use std::iter::Fuse; use std::ops::Index; diff --git a/src/permutations.rs b/src/permutations.rs index 7703556a6..e0e705668 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -2,7 +2,7 @@ use alloc::boxed::Box; use std::fmt; use std::iter::FusedIterator; -use super::lazy_buffer::{ArrayOrVecHelper, LazyBuffer, MaybeConstUsize as _}; +use super::lazy_buffer::{ArrayOrVecHelper, LazyBuffer, MaybeConstUsize}; use crate::size_hint::{self, SizeHint}; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] From 134878544e68652b87db653582ca5fd56c311117 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 13 Jan 2025 16:20:17 +0100 Subject: [PATCH 16/18] Specify generic in `ArrayCombinations::new` --- src/combinations.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combinations.rs b/src/combinations.rs index 01693e58c..b7c5bb275 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -24,7 +24,7 @@ pub fn array_combinations(iter: I) -> ArrayCombinat where I::Item: Clone, { - ArrayCombinations::new(iter, ArrayOrVecHelper::start(ConstUsize)) + ArrayCombinations::new(iter, ArrayOrVecHelper::start(ConstUsize::)) } /// An iterator to iterate through all the `k`-length combinations in an iterator. From 3dd45f552c047335177153f3f0b7e2c47bff29b8 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Thu, 16 Jan 2025 15:16:57 +0100 Subject: [PATCH 17/18] Add `debug_assert` in advancing `PermutationState::Buffered` --- src/permutations.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/permutations.rs b/src/permutations.rs index e0e705668..69f36437d 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -85,6 +85,11 @@ where let k = indices.len(); if vals.get_next() { indices.borrow_mut()[k.value() - 1] += 1; + debug_assert!(indices + .borrow() + .iter() + .copied() + .eq((0..k.value() - 1).chain([*min_n]))); *min_n += 1; Some(indices.extract_item(vals)) } else { From 5a7c5221dfa76b4348a7867a1343c3cb99c03c5c Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Thu, 16 Jan 2025 15:24:17 +0100 Subject: [PATCH 18/18] Use `last_mut()?` instead of indexing --- src/permutations.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/permutations.rs b/src/permutations.rs index 69f36437d..682d97f70 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -84,7 +84,9 @@ where PermutationState::Buffered { indices, min_n } => { let k = indices.len(); if vals.get_next() { - indices.borrow_mut()[k.value() - 1] += 1; + // This could be an unwrap instead of ?, indices.len() should be + // k.value() > 0 to be in this case + *indices.borrow_mut().last_mut()? += 1; debug_assert!(indices .borrow() .iter()