From bf0564f3f6a0a8a309fb26d9ee92e78a6f5baf71 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 30 Oct 2023 15:03:36 +0100 Subject: [PATCH 01/10] Rename `PermutationState` variants And small documentation of the variants. --- src/permutations.rs | 60 +++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index b6ff5c4fa..19cfa8aec 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -26,10 +26,14 @@ where #[derive(Clone, Debug)] enum PermutationState { - StartUnknownLen { k: usize }, - OngoingUnknownLen { k: usize, min_n: usize }, - Complete(CompleteState), - Empty, + /// No permutation generated yet. + Start { k: usize }, + /// Values from the iterator are not fully loaded yet so `n` is still unknown. + Buffered { k: usize, min_n: usize }, + /// All values from the iterator are known so `n` is known. + Loaded(CompleteState), + /// No permutation left to generate. + End, } #[derive(Clone, Debug)] @@ -57,7 +61,7 @@ pub fn permutations(iter: I, k: usize) -> Permutations { if k == 0 { // Special case, yields single empty vec; `n` is irrelevant - let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 }); + let state = PermutationState::Loaded(CompleteState::Start { n: 0, k: 0 }); return Permutations { vals, state }; } @@ -66,9 +70,9 @@ pub fn permutations(iter: I, k: usize) -> Permutations { let enough_vals = vals.len() == k; let state = if enough_vals { - PermutationState::StartUnknownLen { k } + PermutationState::Start { k } } else { - PermutationState::Empty + PermutationState::End }; Permutations { vals, state } @@ -88,12 +92,12 @@ where ref mut state, } = self; match *state { - PermutationState::StartUnknownLen { k } => { - *state = PermutationState::OngoingUnknownLen { k, min_n: k }; + PermutationState::Start { k } => { + *state = PermutationState::Buffered { k, min_n: k }; } - PermutationState::OngoingUnknownLen { k, min_n } => { + PermutationState::Buffered { k, min_n } => { if vals.get_next() { - *state = PermutationState::OngoingUnknownLen { + *state = PermutationState::Buffered { k, min_n: min_n + 1, }; @@ -107,13 +111,13 @@ where complete_state.advance(); } - *state = PermutationState::Complete(complete_state); + *state = PermutationState::Loaded(complete_state); } } - PermutationState::Complete(ref mut state) => { + PermutationState::Loaded(ref mut state) => { state.advance(); } - PermutationState::Empty => {} + PermutationState::End => {} }; } let &mut Permutations { @@ -121,23 +125,21 @@ where ref state, } = self; match *state { - PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"), - PermutationState::OngoingUnknownLen { k, min_n } => { + PermutationState::Start { .. } => panic!("unexpected iterator state"), + PermutationState::Buffered { k, min_n } => { let latest_idx = min_n - 1; let indices = (0..(k - 1)).chain(once(latest_idx)); Some(indices.map(|i| vals[i].clone()).collect()) } - PermutationState::Complete(CompleteState::Ongoing { + PermutationState::Loaded(CompleteState::Ongoing { ref indices, ref cycles, }) => { let k = cycles.len(); Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect()) } - PermutationState::Complete(CompleteState::Start { .. }) | PermutationState::Empty => { - None - } + PermutationState::Loaded(CompleteState::Start { .. }) | PermutationState::End => None, } } @@ -150,21 +152,21 @@ where let Permutations { vals, state } = self; match state { - PermutationState::StartUnknownLen { k } => { + PermutationState::Start { k } => { let n = vals.count(); let complete_state = CompleteState::Start { n, k }; from_complete(complete_state) } - PermutationState::OngoingUnknownLen { k, min_n } => { + PermutationState::Buffered { k, min_n } => { let prev_iteration_count = min_n - k + 1; let n = vals.count(); let complete_state = CompleteState::Start { n, k }; from_complete(complete_state) - prev_iteration_count } - PermutationState::Complete(state) => from_complete(state), - PermutationState::Empty => 0, + PermutationState::Loaded(state) => from_complete(state), + PermutationState::End => 0, } } @@ -179,16 +181,16 @@ where (low, upp) }; match self.state { - PermutationState::StartUnknownLen { k } => at_start(k), - PermutationState::OngoingUnknownLen { k, min_n } => { - // Same as `StartUnknownLen` minus the previously generated items. + PermutationState::Start { k } => at_start(k), + PermutationState::Buffered { k, min_n } => { + // Same as `Start` minus the previously generated items. size_hint::sub_scalar(at_start(k), min_n - k + 1) } - PermutationState::Complete(ref state) => match state.remaining() { + PermutationState::Loaded(ref state) => match state.remaining() { Some(count) => (count, Some(count)), None => (::std::usize::MAX, None), }, - PermutationState::Empty => (0, Some(0)), + PermutationState::End => (0, Some(0)), } } } From ff1b1e314045d5815e162e7df860962aa3d59d84 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 30 Oct 2023 15:20:15 +0100 Subject: [PATCH 02/10] `permutations`: simplify references --- src/permutations.rs | 69 ++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 19cfa8aec..4d4d667e4 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -87,24 +87,18 @@ where fn next(&mut self) -> Option { { - let &mut Permutations { - ref mut vals, - ref mut state, - } = self; - match *state { - PermutationState::Start { k } => { + let Self { vals, state } = self; + match state { + &mut PermutationState::Start { k } => { *state = PermutationState::Buffered { k, min_n: k }; } - PermutationState::Buffered { k, min_n } => { + PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { - *state = PermutationState::Buffered { - k, - min_n: min_n + 1, - }; + *min_n += 1; } else { - let n = min_n; - let prev_iteration_count = n - k + 1; - let mut complete_state = CompleteState::Start { n, k }; + let n = *min_n; + let prev_iteration_count = n - *k + 1; + let mut complete_state = CompleteState::Start { n, k: *k }; // Advance the complete-state iterator to the correct point for _ in 0..(prev_iteration_count + 1) { @@ -114,21 +108,18 @@ where *state = PermutationState::Loaded(complete_state); } } - PermutationState::Loaded(ref mut state) => { + PermutationState::Loaded(state) => { state.advance(); } PermutationState::End => {} }; } - let &mut Permutations { - ref vals, - ref state, - } = self; - match *state { + let Self { vals, state } = &self; + match state { PermutationState::Start { .. } => panic!("unexpected iterator state"), - PermutationState::Buffered { k, min_n } => { - let latest_idx = min_n - 1; - let indices = (0..(k - 1)).chain(once(latest_idx)); + PermutationState::Buffered { ref k, min_n } => { + let latest_idx = *min_n - 1; + let indices = (0..(*k - 1)).chain(once(latest_idx)); Some(indices.map(|i| vals[i].clone()).collect()) } @@ -197,17 +188,13 @@ where impl CompleteState { fn advance(&mut self) { - *self = match *self { - CompleteState::Start { n, k } => { + match self { + &mut CompleteState::Start { n, k } => { let indices = (0..n).collect(); let cycles = ((n - k)..n).rev().collect(); - - CompleteState::Ongoing { cycles, indices } + *self = CompleteState::Ongoing { cycles, indices }; } - CompleteState::Ongoing { - ref mut indices, - ref mut cycles, - } => { + CompleteState::Ongoing { indices, cycles } => { let n = indices.len(); let k = cycles.len(); @@ -225,28 +212,26 @@ impl CompleteState { return; } } - - CompleteState::Start { n, k } + *self = CompleteState::Start { n, k }; } } } /// Returns the count of remaining permutations, or None if it would overflow. fn remaining(&self) -> Option { - match *self { - CompleteState::Start { n, k } => { + match self { + &CompleteState::Start { n, k } => { if n < k { return Some(0); } (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)) } - CompleteState::Ongoing { - ref indices, - ref cycles, - } => cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { - acc.checked_mul(indices.len() - i) - .and_then(|count| count.checked_add(c)) - }), + CompleteState::Ongoing { indices, cycles } => { + cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { + acc.checked_mul(indices.len() - i) + .and_then(|count| count.checked_add(c)) + }) + } } } } From 839fa74c342e8f601fe80e4183c0b28ac8625474 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 30 Oct 2023 16:06:55 +0100 Subject: [PATCH 03/10] `permutations`: `advance` function --- src/permutations.rs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 4d4d667e4..76d47c68e 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -186,6 +186,24 @@ where } } +fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { + let n = indices.len(); + let k = cycles.len(); + // NOTE: if `cycles` are only zeros, then we reached the last permutation. + for i in (0..k).rev() { + if cycles[i] == 0 { + cycles[i] = n - i - 1; + indices[i..].rotate_left(1); + } else { + let swap_index = n - cycles[i]; + indices.swap(i, swap_index); + cycles[i] -= 1; + return false; + } + } + true +} + impl CompleteState { fn advance(&mut self) { match self { @@ -195,24 +213,12 @@ impl CompleteState { *self = CompleteState::Ongoing { cycles, indices }; } CompleteState::Ongoing { indices, cycles } => { - let n = indices.len(); - let k = cycles.len(); - - for i in (0..k).rev() { - if cycles[i] == 0 { - cycles[i] = n - i - 1; - - let to_push = indices.remove(i); - indices.push(to_push); - } else { - let swap_index = n - cycles[i]; - indices.swap(i, swap_index); - - cycles[i] -= 1; - return; - } + if advance(indices, cycles) { + *self = CompleteState::Start { + n: indices.len(), + k: cycles.len(), + }; } - *self = CompleteState::Start { n, k }; } } } From c6de3e2e65a9bd25abdd2ec23ead6de839fe4fe4 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 30 Oct 2023 17:29:13 +0100 Subject: [PATCH 04/10] `PermutationState::size_hint_for` --- src/permutations.rs | 86 ++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 76d47c68e..7e368b200 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -135,54 +135,16 @@ where } fn count(self) -> usize { - fn from_complete(complete_state: CompleteState) -> usize { - complete_state - .remaining() - .expect("Iterator count greater than usize::MAX") - } - - let Permutations { vals, state } = self; - match state { - PermutationState::Start { k } => { - let n = vals.count(); - let complete_state = CompleteState::Start { n, k }; - - from_complete(complete_state) - } - PermutationState::Buffered { k, min_n } => { - let prev_iteration_count = min_n - k + 1; - let n = vals.count(); - let complete_state = CompleteState::Start { n, k }; - - from_complete(complete_state) - prev_iteration_count - } - PermutationState::Loaded(state) => from_complete(state), - PermutationState::End => 0, - } + let Self { vals, state } = self; + let n = vals.count(); + state.size_hint_for(n).1.unwrap() } fn size_hint(&self) -> SizeHint { - let at_start = |k| { - // At the beginning, there are `n!/(n-k)!` items to come (see `remaining`) but `n` might be unknown. - let (mut low, mut upp) = self.vals.size_hint(); - low = CompleteState::Start { n: low, k } - .remaining() - .unwrap_or(usize::MAX); - upp = upp.and_then(|n| CompleteState::Start { n, k }.remaining()); - (low, upp) - }; - match self.state { - PermutationState::Start { k } => at_start(k), - PermutationState::Buffered { k, min_n } => { - // Same as `Start` minus the previously generated items. - size_hint::sub_scalar(at_start(k), min_n - k + 1) - } - PermutationState::Loaded(ref state) => match state.remaining() { - Some(count) => (count, Some(count)), - None => (::std::usize::MAX, None), - }, - PermutationState::End => (0, Some(0)), - } + let (mut low, mut upp) = self.vals.size_hint(); + low = self.state.size_hint_for(low).0; + upp = upp.and_then(|n| self.state.size_hint_for(n).1); + (low, upp) } } @@ -222,22 +184,34 @@ impl CompleteState { } } } +} - /// Returns the count of remaining permutations, or None if it would overflow. - fn remaining(&self) -> Option { - match self { - &CompleteState::Start { n, k } => { - if n < k { - return Some(0); - } - (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)) +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)); + (total.unwrap_or(usize::MAX), total) + }; + match *self { + 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) } - CompleteState::Ongoing { indices, cycles } => { - cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { + Self::Loaded(CompleteState::Start { n, k }) => at_start(n, k), + Self::Loaded(CompleteState::Ongoing { + ref indices, + ref cycles, + }) => { + let count = cycles.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 8ce6044b1e03032af95f4b80c3a6f23f8eec3d25 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 12:36:06 +0100 Subject: [PATCH 05/10] Merge `CompleteState` variants into `PermutationState` --- src/permutations.rs | 86 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 7e368b200..34bce3950 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -30,22 +30,18 @@ enum PermutationState { Start { k: usize }, /// Values from the iterator are not fully loaded yet so `n` is still unknown. Buffered { k: usize, min_n: usize }, - /// All values from the iterator are known so `n` is known. - Loaded(CompleteState), - /// No permutation left to generate. - End, -} - -#[derive(Clone, Debug)] -enum CompleteState { - Start { + // Temporary state that will be discarded soon! + LoadedStart { n: usize, k: usize, }, - Ongoing { + /// All values from the iterator are known so `n` is known. + LoadedOngoing { indices: Vec, cycles: Vec, }, + /// No permutation left to generate. + End, } impl fmt::Debug for Permutations @@ -61,7 +57,7 @@ pub fn permutations(iter: I, k: usize) -> Permutations { if k == 0 { // Special case, yields single empty vec; `n` is irrelevant - let state = PermutationState::Loaded(CompleteState::Start { n: 0, k: 0 }); + let state = PermutationState::LoadedStart { n: 0, k: 0 }; return Permutations { vals, state }; } @@ -98,18 +94,36 @@ where } else { let n = *min_n; let prev_iteration_count = n - *k + 1; - let mut complete_state = CompleteState::Start { n, k: *k }; - - // Advance the complete-state iterator to the correct point - for _ in 0..(prev_iteration_count + 1) { - complete_state.advance(); + let mut indices: Vec<_> = (0..n).collect(); + let mut cycles: Vec<_> = (n - k..n).rev().collect(); + let mut found_something = false; + // Advance the state to the correct point. + for _ in 0..prev_iteration_count { + if advance(&mut indices, &mut cycles) { + *state = PermutationState::LoadedStart { + n: indices.len(), + k: cycles.len(), + }; + found_something = true; + } + } + if !found_something { + *state = PermutationState::LoadedOngoing { indices, cycles }; } - - *state = PermutationState::Loaded(complete_state); } } - PermutationState::Loaded(state) => { - state.advance(); + &mut PermutationState::LoadedStart { n, k } => { + let indices = (0..n).collect(); + let cycles = (n - k..n).rev().collect(); + *state = PermutationState::LoadedOngoing { cycles, indices }; + } + PermutationState::LoadedOngoing { indices, cycles } => { + if advance(indices, cycles) { + *state = PermutationState::LoadedStart { + n: indices.len(), + k: cycles.len(), + }; + } } PermutationState::End => {} }; @@ -123,14 +137,14 @@ where Some(indices.map(|i| vals[i].clone()).collect()) } - PermutationState::Loaded(CompleteState::Ongoing { + PermutationState::LoadedOngoing { ref indices, ref cycles, - }) => { + } => { let k = cycles.len(); Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect()) } - PermutationState::Loaded(CompleteState::Start { .. }) | PermutationState::End => None, + PermutationState::LoadedStart { .. } | PermutationState::End => None, } } @@ -166,26 +180,6 @@ fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { true } -impl CompleteState { - fn advance(&mut self) { - match self { - &mut CompleteState::Start { n, k } => { - let indices = (0..n).collect(); - let cycles = ((n - k)..n).rev().collect(); - *self = CompleteState::Ongoing { cycles, indices }; - } - CompleteState::Ongoing { indices, cycles } => { - if advance(indices, cycles) { - *self = CompleteState::Start { - n: indices.len(), - k: cycles.len(), - }; - } - } - } - } -} - impl PermutationState { fn size_hint_for(&self, n: usize) -> SizeHint { // At the beginning, there are `n!/(n-k)!` items to come. @@ -200,11 +194,11 @@ impl PermutationState { // Same as `Start` minus the previously generated items. size_hint::sub_scalar(at_start(n, k), min_n - k + 1) } - Self::Loaded(CompleteState::Start { n, k }) => at_start(n, k), - Self::Loaded(CompleteState::Ongoing { + Self::LoadedStart { n, k } => at_start(n, k), + Self::LoadedOngoing { ref indices, ref cycles, - }) => { + } => { let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| { acc.checked_mul(indices.len() - i) .and_then(|count| count.checked_add(c)) From 518811a9600da6db204fed40535db24efcec22e8 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 12:38:45 +0100 Subject: [PATCH 06/10] Unindent block in `Permutations::next` --- src/permutations.rs | 82 ++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 34bce3950..b0d75627e 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -82,52 +82,50 @@ where type Item = Vec; fn next(&mut self) -> Option { - { - let Self { vals, state } = self; - match state { - &mut PermutationState::Start { k } => { - *state = PermutationState::Buffered { k, min_n: k }; - } - PermutationState::Buffered { ref k, min_n } => { - if vals.get_next() { - *min_n += 1; - } else { - let n = *min_n; - let prev_iteration_count = n - *k + 1; - let mut indices: Vec<_> = (0..n).collect(); - let mut cycles: Vec<_> = (n - k..n).rev().collect(); - let mut found_something = false; - // Advance the state to the correct point. - for _ in 0..prev_iteration_count { - if advance(&mut indices, &mut cycles) { - *state = PermutationState::LoadedStart { - n: indices.len(), - k: cycles.len(), - }; - found_something = true; - } - } - if !found_something { - *state = PermutationState::LoadedOngoing { indices, cycles }; + let Self { vals, state } = self; + match state { + &mut PermutationState::Start { k } => { + *state = PermutationState::Buffered { k, min_n: k }; + } + PermutationState::Buffered { ref k, min_n } => { + if vals.get_next() { + *min_n += 1; + } else { + let n = *min_n; + let prev_iteration_count = n - *k + 1; + let mut indices: Vec<_> = (0..n).collect(); + let mut cycles: Vec<_> = (n - k..n).rev().collect(); + let mut found_something = false; + // Advance the state to the correct point. + for _ in 0..prev_iteration_count { + if advance(&mut indices, &mut cycles) { + *state = PermutationState::LoadedStart { + n: indices.len(), + k: cycles.len(), + }; + found_something = true; } } - } - &mut PermutationState::LoadedStart { n, k } => { - let indices = (0..n).collect(); - let cycles = (n - k..n).rev().collect(); - *state = PermutationState::LoadedOngoing { cycles, indices }; - } - PermutationState::LoadedOngoing { indices, cycles } => { - if advance(indices, cycles) { - *state = PermutationState::LoadedStart { - n: indices.len(), - k: cycles.len(), - }; + if !found_something { + *state = PermutationState::LoadedOngoing { indices, cycles }; } } - PermutationState::End => {} - }; - } + } + &mut PermutationState::LoadedStart { n, k } => { + let indices = (0..n).collect(); + let cycles = (n - k..n).rev().collect(); + *state = PermutationState::LoadedOngoing { cycles, indices }; + } + PermutationState::LoadedOngoing { indices, cycles } => { + if advance(indices, cycles) { + *state = PermutationState::LoadedStart { + n: indices.len(), + k: cycles.len(), + }; + } + } + PermutationState::End => {} + }; let Self { vals, state } = &self; match state { PermutationState::Start { .. } => panic!("unexpected iterator state"), From 901e99b5f90e4c5634fdf73b97e4ed33bc0a266b Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 13:53:28 +0100 Subject: [PATCH 07/10] Inline second inspection of state --- src/permutations.rs | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index b0d75627e..2ccb0bd13 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -86,16 +86,21 @@ where match state { &mut PermutationState::Start { k } => { *state = PermutationState::Buffered { k, min_n: k }; + let latest_idx = k - 1; + let indices = (0..(k - 1)).chain(once(latest_idx)); + Some(indices.map(|i| vals[i].clone()).collect()) } PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { *min_n += 1; + let latest_idx = *min_n - 1; + let indices = (0..*k - 1).chain(once(latest_idx)); + Some(indices.map(|i| vals[i].clone()).collect()) } else { let n = *min_n; let prev_iteration_count = n - *k + 1; let mut indices: Vec<_> = (0..n).collect(); let mut cycles: Vec<_> = (n - k..n).rev().collect(); - let mut found_something = false; // Advance the state to the correct point. for _ in 0..prev_iteration_count { if advance(&mut indices, &mut cycles) { @@ -103,18 +108,20 @@ where n: indices.len(), k: cycles.len(), }; - found_something = true; + return None; } } - if !found_something { - *state = PermutationState::LoadedOngoing { indices, cycles }; - } + let item = indices[0..*k].iter().map(|&i| vals[i].clone()).collect(); + *state = PermutationState::LoadedOngoing { indices, cycles }; + Some(item) } } &mut PermutationState::LoadedStart { n, k } => { - let indices = (0..n).collect(); + let indices: Vec<_> = (0..n).collect(); let cycles = (n - k..n).rev().collect(); + let item = indices[0..k].iter().map(|&i| vals[i].clone()).collect(); *state = PermutationState::LoadedOngoing { cycles, indices }; + Some(item) } PermutationState::LoadedOngoing { indices, cycles } => { if advance(indices, cycles) { @@ -122,27 +129,12 @@ where n: indices.len(), k: cycles.len(), }; + return None; } - } - PermutationState::End => {} - }; - let Self { vals, state } = &self; - match state { - PermutationState::Start { .. } => panic!("unexpected iterator state"), - PermutationState::Buffered { ref k, min_n } => { - let latest_idx = *min_n - 1; - let indices = (0..(*k - 1)).chain(once(latest_idx)); - - Some(indices.map(|i| vals[i].clone()).collect()) - } - PermutationState::LoadedOngoing { - ref indices, - ref cycles, - } => { let k = cycles.len(); Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect()) } - PermutationState::LoadedStart { .. } | PermutationState::End => None, + PermutationState::End => None, } } From 05a815f45e0b2c51577aecb6d3576c9b5cd00539 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 13:59:04 +0100 Subject: [PATCH 08/10] Remove `PermutationState::LoadedStart` --- src/permutations.rs | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index 2ccb0bd13..b43acac8b 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -30,13 +30,8 @@ enum PermutationState { Start { k: usize }, /// Values from the iterator are not fully loaded yet so `n` is still unknown. Buffered { k: usize, min_n: usize }, - // Temporary state that will be discarded soon! - LoadedStart { - n: usize, - k: usize, - }, /// All values from the iterator are known so `n` is known. - LoadedOngoing { + Loaded { indices: Vec, cycles: Vec, }, @@ -55,13 +50,6 @@ where pub fn permutations(iter: I, k: usize) -> Permutations { let mut vals = LazyBuffer::new(iter); - if k == 0 { - // Special case, yields single empty vec; `n` is irrelevant - let state = PermutationState::LoadedStart { n: 0, k: 0 }; - - return Permutations { vals, state }; - } - vals.prefill(k); let enough_vals = vals.len() == k; @@ -84,6 +72,10 @@ 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 } => { *state = PermutationState::Buffered { k, min_n: k }; let latest_idx = k - 1; @@ -104,31 +96,18 @@ where // Advance the state to the correct point. for _ in 0..prev_iteration_count { if advance(&mut indices, &mut cycles) { - *state = PermutationState::LoadedStart { - n: indices.len(), - k: cycles.len(), - }; + *state = PermutationState::End; return None; } } let item = indices[0..*k].iter().map(|&i| vals[i].clone()).collect(); - *state = PermutationState::LoadedOngoing { indices, cycles }; + *state = PermutationState::Loaded { indices, cycles }; Some(item) } } - &mut PermutationState::LoadedStart { n, k } => { - let indices: Vec<_> = (0..n).collect(); - let cycles = (n - k..n).rev().collect(); - let item = indices[0..k].iter().map(|&i| vals[i].clone()).collect(); - *state = PermutationState::LoadedOngoing { cycles, indices }; - Some(item) - } - PermutationState::LoadedOngoing { indices, cycles } => { + PermutationState::Loaded { indices, cycles } => { if advance(indices, cycles) { - *state = PermutationState::LoadedStart { - n: indices.len(), - k: cycles.len(), - }; + *state = PermutationState::End; return None; } let k = cycles.len(); @@ -184,8 +163,7 @@ impl PermutationState { // Same as `Start` minus the previously generated items. size_hint::sub_scalar(at_start(n, k), min_n - k + 1) } - Self::LoadedStart { n, k } => at_start(n, k), - Self::LoadedOngoing { + Self::Loaded { ref indices, ref cycles, } => { From 8f0d0ae530625b5796de18ffb358f14653c93301 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 14:02:49 +0100 Subject: [PATCH 09/10] `Permutations::next`: simplify two vectors --- src/permutations.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/permutations.rs b/src/permutations.rs index b43acac8b..361d04bfe 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -78,16 +78,16 @@ where } &mut PermutationState::Start { k } => { *state = PermutationState::Buffered { k, min_n: k }; - let latest_idx = k - 1; - let indices = (0..(k - 1)).chain(once(latest_idx)); - Some(indices.map(|i| vals[i].clone()).collect()) + Some(vals[0..k].to_vec()) } PermutationState::Buffered { ref k, min_n } => { if vals.get_next() { + let item = (0..*k - 1) + .chain(once(*min_n)) + .map(|i| vals[i].clone()) + .collect(); *min_n += 1; - let latest_idx = *min_n - 1; - let indices = (0..*k - 1).chain(once(latest_idx)); - Some(indices.map(|i| vals[i].clone()).collect()) + Some(item) } else { let n = *min_n; let prev_iteration_count = n - *k + 1; From 8ab399565b23998ca98ce2a16ebe5e12a5020c74 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Wed, 1 Nov 2023 14:19:43 +0100 Subject: [PATCH 10/10] `impl FusedIterator for Permutations` --- src/permutations.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/permutations.rs b/src/permutations.rs index 361d04bfe..5dd3dbf9a 100644 --- a/src/permutations.rs +++ b/src/permutations.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use std::fmt; use std::iter::once; +use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; use crate::size_hint::{self, SizeHint}; @@ -131,6 +132,13 @@ where } } +impl FusedIterator for Permutations +where + I: Iterator, + I::Item: Clone, +{ +} + fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool { let n = indices.len(); let k = cycles.len();