-
Notifications
You must be signed in to change notification settings - Fork 338
add array_permutations()
#1013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add array_permutations()
#1013
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,39 @@ | ||
| use alloc::boxed::Box; | ||
| use alloc::vec::Vec; | ||
| use core::marker::PhantomData; | ||
| use std::fmt; | ||
| use std::iter::once; | ||
| use std::iter::FusedIterator; | ||
|
|
||
| use super::lazy_buffer::LazyBuffer; | ||
| use crate::size_hint::{self, SizeHint}; | ||
|
|
||
| /// Iterator for `Vec` valued permutations returned by | ||
| /// [`.permutations()`](crate::Itertools::permutations) | ||
| pub type ArrayPermutations<I, const K: usize> = PermutationsGeneric<I, [<I as Iterator>::Item; K]>; | ||
| /// Iterator for const generic permutations returned by | ||
| /// [`.array_permutations()`](crate::Itertools::array_permutations) | ||
| pub type Permutations<I> = PermutationsGeneric<I, Vec<<I as Iterator>::Item>>; | ||
|
|
||
| /// An iterator adaptor that iterates through all the `k`-permutations of the | ||
| /// elements from an iterator. | ||
| /// | ||
| /// See [`.permutations()`](crate::Itertools::permutations) for | ||
| /// See [`.permutations()`](crate::Itertools::permutations) and | ||
| /// [`.array_permutations()`](crate::Itertools::array_permutations) for | ||
| /// more information. | ||
| #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] | ||
| pub struct Permutations<I: Iterator> { | ||
| pub struct PermutationsGeneric<I: Iterator, Item> { | ||
| vals: LazyBuffer<I>, | ||
| state: PermutationState, | ||
| _item: PhantomData<Item>, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the decision
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the current algorithm,
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, using a |
||
| } | ||
|
|
||
| impl<I> Clone for Permutations<I> | ||
| impl<I, Item> Clone for PermutationsGeneric<I, Item> | ||
| where | ||
| I: Clone + Iterator, | ||
| I::Item: Clone, | ||
| { | ||
| clone_fields!(vals, state); | ||
| clone_fields!(vals, state, _item); | ||
| } | ||
|
|
||
| #[derive(Clone, Debug)] | ||
|
|
@@ -41,34 +51,44 @@ enum PermutationState { | |
| End, | ||
| } | ||
|
|
||
| impl<I> fmt::Debug for Permutations<I> | ||
| impl<I, Item> fmt::Debug for PermutationsGeneric<I, Item> | ||
| where | ||
| I: Iterator + fmt::Debug, | ||
| I::Item: fmt::Debug, | ||
| { | ||
| debug_fmt_fields!(Permutations, vals, state); | ||
| } | ||
|
|
||
| pub fn array_permutations<I: Iterator, const K: usize>(iter: I) -> ArrayPermutations<I, K> { | ||
| PermutationsGeneric { | ||
| vals: LazyBuffer::new(iter), | ||
| state: PermutationState::Start { k: K }, | ||
| _item: PhantomData, | ||
| } | ||
| } | ||
|
|
||
| pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> { | ||
| Permutations { | ||
| PermutationsGeneric { | ||
| vals: LazyBuffer::new(iter), | ||
| state: PermutationState::Start { k }, | ||
| _item: PhantomData, | ||
| } | ||
| } | ||
|
|
||
| impl<I> Iterator for Permutations<I> | ||
| impl<I, Item> Iterator for PermutationsGeneric<I, Item> | ||
| where | ||
| I: Iterator, | ||
| I::Item: Clone, | ||
| Item: PermItem<I::Item>, | ||
| { | ||
| type Item = Vec<I::Item>; | ||
| type Item = Item; | ||
|
|
||
| fn next(&mut self) -> Option<Self::Item> { | ||
| let Self { vals, state } = self; | ||
| let Self { vals, state, _item } = self; | ||
| match state { | ||
| PermutationState::Start { k: 0 } => { | ||
| *state = PermutationState::End; | ||
| Some(Vec::new()) | ||
| Some(Item::extract_start(vals, 0, 0)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see calling
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that whatever code goes here needs to generically produce a impl PoolIndex for [usize; K]{
...
fn empty_item() -> Option<Self::Item> {
if K == 0 {
Some([])
} else {
None
}
}
}
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, this attempt won't work. However, we could introduce |
||
| } | ||
| &mut PermutationState::Start { k } => { | ||
| vals.prefill(k); | ||
|
|
@@ -77,14 +97,11 @@ where | |
| return None; | ||
| } | ||
| *state = PermutationState::Buffered { k, min_n: k }; | ||
| Some(vals[0..k].to_vec()) | ||
| Some(Item::extract_start(vals, k, k - 1)) | ||
| } | ||
| 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(); | ||
| let item = Item::extract_start(vals, *k, *min_n); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having a separate variant |
||
| *min_n += 1; | ||
| Some(item) | ||
| } else { | ||
|
|
@@ -99,7 +116,7 @@ where | |
| return None; | ||
| } | ||
| } | ||
| let item = vals.get_at(&indices[0..*k]); | ||
| let item = Item::extract_from_prefix(vals, &indices[0..*k]); | ||
| *state = PermutationState::Loaded { indices, cycles }; | ||
| Some(item) | ||
| } | ||
|
|
@@ -110,14 +127,14 @@ where | |
| return None; | ||
| } | ||
| let k = cycles.len(); | ||
| Some(vals.get_at(&indices[0..k])) | ||
| Some(Item::extract_from_prefix(vals, &indices[0..k])) | ||
| } | ||
| PermutationState::End => None, | ||
| } | ||
| } | ||
|
|
||
| fn count(self) -> usize { | ||
| let Self { vals, state } = self; | ||
| let Self { vals, state, _item } = self; | ||
| let n = vals.count(); | ||
| state.size_hint_for(n).1.unwrap() | ||
| } | ||
|
|
@@ -130,10 +147,11 @@ where | |
| } | ||
| } | ||
|
|
||
| impl<I> FusedIterator for Permutations<I> | ||
| impl<I, Item> FusedIterator for PermutationsGeneric<I, Item> | ||
| where | ||
| I: Iterator, | ||
| I::Item: Clone, | ||
| Item: PermItem<I::Item>, | ||
| { | ||
| } | ||
|
|
||
|
|
@@ -184,3 +202,39 @@ impl PermutationState { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /// A type that can be picked out from a pool or buffer of items from an inner iterator | ||
| /// and in a generic way given their indices. | ||
| pub trait PermItem<T> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this new trait really necessary? Couldn't we re-use/generalize/extend
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is what I started with but it seems that the operations required for |
||
| fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self; | ||
|
|
||
| fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self; | ||
| } | ||
|
|
||
| impl<T: Clone, const K: usize> PermItem<T> for [T; K] { | ||
| fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self { | ||
| assert_eq!(len, K); | ||
| buf.get_array_from_fn(|i| if i + 1 < len { i } else { last }) | ||
| } | ||
|
|
||
| fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self { | ||
| buf.get_array_from_fn(|i| indices[i]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this essentially
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that when this function is called, there does not exist an instance of |
||
| } | ||
| } | ||
|
|
||
| impl<T: Clone> PermItem<T> for Vec<T> { | ||
| fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self { | ||
| if len == 0 { | ||
| Vec::new() | ||
| } else { | ||
| (0..len - 1) | ||
| .chain(once(last)) | ||
| .map(|i| buf[i].clone()) | ||
| .collect() | ||
| } | ||
|
Comment on lines
+227
to
+234
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Special cases that might go away if we always worked on |
||
| } | ||
|
|
||
| fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self { | ||
| buf.get_at(indices) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the second generic parameter
Vec<<I as Iterator>::Item>seems odd whenPermutationsGenerichas[usize; K].[Array]CombinationshaveVec<usize>and[usize; K], respectively. Unless there's a good reason, we should adopt this pattern.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an error,
array_permutations()was only working onIterator<Item = usize>before. Fixed now.