From 8459f932efa014e2ef2fa551eede576672b4a136 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet <44676486+Philippe-Cholet@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:11:26 +0200 Subject: [PATCH 1/4] `CombinationsWithReplacement::increment_indices` Steal how `next` increment indices, a bit improved. It will soon be used in `nth` (and more later). --- src/combinations_with_replacement.rs | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/combinations_with_replacement.rs b/src/combinations_with_replacement.rs index 4043678c1..9cb658169 100644 --- a/src/combinations_with_replacement.rs +++ b/src/combinations_with_replacement.rs @@ -46,6 +46,46 @@ where } } +impl CombinationsWithReplacement +where + I: Iterator, + I::Item: Clone, +{ + /// Increments indices representing the combination to advance to the next + /// (in lexicographic order by increasing sequence) combination. + /// + /// Returns true if we've run out of combinations, false otherwise. + fn increment_indices(&mut self) -> bool { + // Check if we need to consume more from the iterator + // This will run while we increment our first index digit + self.pool.get_next(); + + // Work out where we need to update our indices + let mut increment = None; + for (i, indices_int) in self.indices.iter().enumerate().rev() { + if *indices_int < self.pool.len() - 1 { + increment = Some((i, indices_int + 1)); + break; + } + } + match increment { + // If we can update the indices further + Some((increment_from, increment_value)) => { + // We need to update the rightmost non-max value + // and all those to the right + for i in &mut self.indices[increment_from..] { + *i = increment_value; + } + // TODO: once MSRV >= 1.50, use `fill` instead: + // self.indices[increment_from..].fill(increment_value); + false + } + // Otherwise, we're done + None => true, + } + } +} + impl Iterator for CombinationsWithReplacement where I: Iterator, From 271b55fe01449e973d2acf726747332f10097882 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet <44676486+Philippe-Cholet@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:13:25 +0200 Subject: [PATCH 2/4] Update `CombinationsWithReplacement::next` (1) Use the new `increment_indices`. This is done in a different commit because the git difference was difficult to read. --- src/combinations_with_replacement.rs | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/combinations_with_replacement.rs b/src/combinations_with_replacement.rs index 9cb658169..1e2126c1a 100644 --- a/src/combinations_with_replacement.rs +++ b/src/combinations_with_replacement.rs @@ -92,6 +92,7 @@ where I::Item: Clone, { type Item = Vec; + fn next(&mut self) -> Option { // If this is the first iteration, return early if self.first { @@ -105,32 +106,11 @@ where }; } - // Check if we need to consume more from the iterator - // This will run while we increment our first index digit - self.pool.get_next(); - - // Work out where we need to update our indices - let mut increment: Option<(usize, usize)> = None; - for (i, indices_int) in self.indices.iter().enumerate().rev() { - if *indices_int < self.pool.len() - 1 { - increment = Some((i, indices_int + 1)); - break; - } + if self.increment_indices() { + return None; } - match increment { - // If we can update the indices further - Some((increment_from, increment_value)) => { - // We need to update the rightmost non-max value - // and all those to the right - for indices_index in increment_from..self.indices.len() { - self.indices[indices_index] = increment_value; - } - Some(self.pool.get_at(&self.indices)) - } - // Otherwise, we're done - None => None, - } + Some(self.pool.get_at(&self.indices)) } fn size_hint(&self) -> (usize, Option) { From a04e0bc7c623289c9398842893638ce4600e15e3 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet <44676486+Philippe-Cholet@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:15:10 +0200 Subject: [PATCH 3/4] Update `CombinationsWithReplacement::next` (2) Use `pool.get_at` once! That way, `next` and `nth` will ressemble each other. --- src/combinations_with_replacement.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/combinations_with_replacement.rs b/src/combinations_with_replacement.rs index 1e2126c1a..7acfee898 100644 --- a/src/combinations_with_replacement.rs +++ b/src/combinations_with_replacement.rs @@ -94,22 +94,15 @@ where type Item = Vec; fn next(&mut self) -> Option { - // If this is the first iteration, return early if self.first { // In empty edge cases, stop iterating immediately - return if !(self.indices.is_empty() || self.pool.get_next()) { - None - // Otherwise, yield the initial state - } else { - self.first = false; - Some(self.pool.get_at(&self.indices)) - }; - } - - if self.increment_indices() { + if !(self.indices.is_empty() || self.pool.get_next()) { + return None; + } + self.first = false; + } else if self.increment_indices() { return None; } - Some(self.pool.get_at(&self.indices)) } From a250651ecc8fa6c7d492e5022dd7e70ae6bfb6f0 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet <44676486+Philippe-Cholet@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:15:56 +0200 Subject: [PATCH 4/4] Specialize `CombinationsWithReplacement::nth` Similar to `next` except we increment indices n times before generating the vector item. --- src/combinations_with_replacement.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/combinations_with_replacement.rs b/src/combinations_with_replacement.rs index 7acfee898..f363f9ba2 100644 --- a/src/combinations_with_replacement.rs +++ b/src/combinations_with_replacement.rs @@ -106,6 +106,24 @@ where Some(self.pool.get_at(&self.indices)) } + fn nth(&mut self, n: usize) -> Option { + if self.first { + // In empty edge cases, stop iterating immediately + if !(self.indices.is_empty() || self.pool.get_next()) { + return None; + } + self.first = false; + } else if self.increment_indices() { + return None; + } + for _ in 0..n { + if self.increment_indices() { + return None; + } + } + Some(self.pool.get_at(&self.indices)) + } + fn size_hint(&self) -> (usize, Option) { let (mut low, mut upp) = self.pool.size_hint(); low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);