Skip to content

Commit 2a03a03

Browse files
committed
Adds .permutations(k) adaptor
1 parent a6e9713 commit 2a03a03

9 files changed

+576
-53
lines changed

benches/bench1.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use test::{black_box};
77
use itertools::Itertools;
88

99
use itertools::free::cloned;
10+
use itertools::Permutations;
1011

1112
use std::iter::repeat;
1213
use std::cmp;
13-
use std::ops::Add;
14+
use std::ops::{Add, Range};
1415

1516
mod extra;
1617

@@ -762,3 +763,44 @@ fn all_equal_default(b: &mut test::Bencher) {
762763

763764
b.iter(|| xs.iter().dedup().nth(1).is_none())
764765
}
766+
767+
const PERM_COUNT: usize = 6;
768+
769+
#[bench]
770+
fn permutations_iter(b: &mut test::Bencher) {
771+
struct NewIterator(Range<usize>);
772+
773+
impl Iterator for NewIterator {
774+
type Item = usize;
775+
776+
fn next(&mut self) -> Option<Self::Item> {
777+
self.0.next()
778+
}
779+
}
780+
781+
b.iter(|| {
782+
for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {
783+
784+
}
785+
})
786+
}
787+
788+
#[bench]
789+
fn permutations_range(b: &mut test::Bencher) {
790+
b.iter(|| {
791+
for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {
792+
793+
}
794+
})
795+
}
796+
797+
#[bench]
798+
fn permutations_slice(b: &mut test::Bencher) {
799+
let v = (0..PERM_COUNT).collect_vec();
800+
801+
b.iter(|| {
802+
for _ in v.as_slice().iter().permutations(PERM_COUNT) {
803+
804+
}
805+
})
806+
}

src/combinations.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::fmt;
22

33
use super::lazy_buffer::LazyBuffer;
44

5-
/// An iterator to iterate through all the `n`-length combinations in an iterator.
5+
/// An iterator to iterate through all the `k`-length combinations in an iterator.
66
///
77
/// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information.
88
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
99
pub struct Combinations<I: Iterator> {
10-
n: usize,
10+
k: usize,
1111
indices: Vec<usize>,
1212
pool: LazyBuffer<I>,
1313
first: bool,
@@ -17,27 +17,27 @@ impl<I> fmt::Debug for Combinations<I>
1717
where I: Iterator + fmt::Debug,
1818
I::Item: fmt::Debug,
1919
{
20-
debug_fmt_fields!(Combinations, n, indices, pool, first);
20+
debug_fmt_fields!(Combinations, k, indices, pool, first);
2121
}
2222

2323
/// Create a new `Combinations` from a clonable iterator.
24-
pub fn combinations<I>(iter: I, n: usize) -> Combinations<I>
24+
pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
2525
where I: Iterator
2626
{
27-
let mut indices: Vec<usize> = Vec::with_capacity(n);
28-
for i in 0..n {
27+
let mut indices: Vec<usize> = Vec::with_capacity(k);
28+
for i in 0..k {
2929
indices.push(i);
3030
}
3131
let mut pool: LazyBuffer<I> = LazyBuffer::new(iter);
3232

33-
for _ in 0..n {
33+
for _ in 0..k {
3434
if !pool.get_next() {
3535
break;
3636
}
3737
}
3838

3939
Combinations {
40-
n: n,
40+
k: k,
4141
indices: indices,
4242
pool: pool,
4343
first: true,
@@ -52,18 +52,18 @@ impl<I> Iterator for Combinations<I>
5252
fn next(&mut self) -> Option<Self::Item> {
5353
let mut pool_len = self.pool.len();
5454
if self.pool.is_done() {
55-
if pool_len == 0 || self.n > pool_len {
55+
if pool_len == 0 || self.k > pool_len {
5656
return None;
5757
}
5858
}
5959

6060
if self.first {
6161
self.first = false;
62-
} else if self.n == 0 {
62+
} else if self.k == 0 {
6363
return None;
6464
} else {
6565
// Scan from the end, looking for an index to increment
66-
let mut i: usize = self.n - 1;
66+
let mut i: usize = self.k - 1;
6767

6868
// Check if we need to consume more from the iterator
6969
if self.indices[i] == pool_len - 1 && !self.pool.is_done() {
@@ -72,7 +72,7 @@ impl<I> Iterator for Combinations<I>
7272
}
7373
}
7474

75-
while self.indices[i] == i + pool_len - self.n {
75+
while self.indices[i] == i + pool_len - self.k {
7676
if i > 0 {
7777
i -= 1;
7878
} else {
@@ -84,14 +84,14 @@ impl<I> Iterator for Combinations<I>
8484
// Increment index, and reset the ones to its right
8585
self.indices[i] += 1;
8686
let mut j = i + 1;
87-
while j < self.n {
87+
while j < self.k {
8888
self.indices[j] = self.indices[j - 1] + 1;
8989
j += 1;
9090
}
9191
}
9292

9393
// Create result vector based on the indices
94-
let mut result = Vec::with_capacity(self.n);
94+
let mut result = Vec::with_capacity(self.k);
9595
for i in self.indices.iter() {
9696
result.push(self.pool[*i].clone());
9797
}

src/combinations_with_replacement.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ where
1111
I: Iterator,
1212
I::Item: Clone,
1313
{
14-
n: usize,
14+
k: usize,
1515
indices: Vec<usize>,
1616
// The current known max index value. This increases as pool grows.
1717
max_index: usize,
@@ -24,7 +24,7 @@ where
2424
I: Iterator + fmt::Debug,
2525
I::Item: fmt::Debug + Clone,
2626
{
27-
debug_fmt_fields!(Combinations, n, indices, max_index, pool, first);
27+
debug_fmt_fields!(Combinations, k, indices, max_index, pool, first);
2828
}
2929

3030
impl<I> CombinationsWithReplacement<I>
@@ -39,16 +39,16 @@ where
3939
}
4040

4141
/// Create a new `CombinationsWithReplacement` from a clonable iterator.
42-
pub fn combinations_with_replacement<I>(iter: I, n: usize) -> CombinationsWithReplacement<I>
42+
pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
4343
where
4444
I: Iterator,
4545
I::Item: Clone,
4646
{
47-
let indices: Vec<usize> = vec![0; n];
47+
let indices: Vec<usize> = vec![0; k];
4848
let pool: LazyBuffer<I> = LazyBuffer::new(iter);
4949

5050
CombinationsWithReplacement {
51-
n,
51+
k,
5252
indices,
5353
max_index: 0,
5454
pool: pool,
@@ -66,7 +66,7 @@ where
6666
// If this is the first iteration, return early
6767
if self.first {
6868
// In empty edge cases, stop iterating immediately
69-
return if self.n == 0 || self.pool.is_done() {
69+
return if self.k == 0 || self.pool.is_done() {
7070
None
7171
// Otherwise, yield the initial state
7272
} else {

src/lazy_buffer.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ops::Index;
22

33
#[derive(Debug, Clone)]
44
pub struct LazyBuffer<I: Iterator> {
5-
it: I,
5+
pub it: I,
66
done: bool,
77
buffer: Vec<I::Item>,
88
}
@@ -54,14 +54,15 @@ where
5454
}
5555
}
5656

57-
impl<I> Index<usize> for LazyBuffer<I>
57+
impl<I, J> Index<J> for LazyBuffer<I>
5858
where
5959
I: Iterator,
6060
I::Item: Sized,
61+
Vec<I::Item>: Index<J>
6162
{
62-
type Output = I::Item;
63+
type Output = <Vec<I::Item> as Index<J>>::Output;
6364

64-
fn index<'b>(&'b self, _index: usize) -> &'b I::Item {
65+
fn index(&self, _index: J) -> &Self::Output {
6566
self.buffer.index(_index)
6667
}
6768
}

src/lib.rs

+66-15
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ pub mod structs {
117117
pub use multipeek_impl::MultiPeek;
118118
pub use pad_tail::PadUsing;
119119
pub use peeking_take_while::PeekingTakeWhile;
120+
#[cfg(feature = "use_std")]
121+
pub use permutations::Permutations;
120122
pub use process_results_impl::ProcessResults;
121123
#[cfg(feature = "use_std")]
122124
pub use put_back_n_impl::PutBackN;
@@ -182,6 +184,8 @@ mod minmax;
182184
mod multipeek_impl;
183185
mod pad_tail;
184186
mod peeking_take_while;
187+
#[cfg(feature = "use_std")]
188+
mod permutations;
185189
mod process_results_impl;
186190
#[cfg(feature = "use_std")]
187191
mod put_back_n_impl;
@@ -1135,7 +1139,7 @@ pub trait Itertools : Iterator {
11351139
adaptors::tuple_combinations(self)
11361140
}
11371141

1138-
/// Return an iterator adaptor that iterates over the `n`-length combinations of
1142+
/// Return an iterator adaptor that iterates over the `k`-length combinations of
11391143
/// the elements from an iterator.
11401144
///
11411145
/// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
@@ -1150,10 +1154,11 @@ pub trait Itertools : Iterator {
11501154
/// vec![1, 2, 4],
11511155
/// vec![1, 3, 4],
11521156
/// vec![2, 3, 4],
1153-
/// ]);
1157+
/// ]);
11541158
/// ```
11551159
///
11561160
/// Note: Combinations does not take into account the equality of the iterated values.
1161+
///
11571162
/// ```
11581163
/// use itertools::Itertools;
11591164
///
@@ -1164,16 +1169,15 @@ pub trait Itertools : Iterator {
11641169
/// vec![2, 2],
11651170
/// ]);
11661171
/// ```
1167-
///
11681172
#[cfg(feature = "use_std")]
1169-
fn combinations(self, n: usize) -> Combinations<Self>
1173+
fn combinations(self, k: usize) -> Combinations<Self>
11701174
where Self: Sized,
11711175
Self::Item: Clone
11721176
{
1173-
combinations::combinations(self, n)
1177+
combinations::combinations(self, k)
11741178
}
11751179

1176-
/// Return an iterator that iterates over the `n`-length combinations of
1180+
/// Return an iterator that iterates over the `k`-length combinations of
11771181
/// the elements from an iterator, with replacement.
11781182
///
11791183
/// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
@@ -1190,15 +1194,60 @@ pub trait Itertools : Iterator {
11901194
/// vec![2, 2],
11911195
/// vec![2, 3],
11921196
/// vec![3, 3],
1193-
/// ]);
1197+
/// ]);
11941198
/// ```
11951199
#[cfg(feature = "use_std")]
1196-
fn combinations_with_replacement(self, n: usize) -> CombinationsWithReplacement<Self>
1200+
fn combinations_with_replacement(self, k: usize) -> CombinationsWithReplacement<Self>
11971201
where
11981202
Self: Sized,
11991203
Self::Item: Clone,
12001204
{
1201-
combinations_with_replacement::combinations_with_replacement(self, n)
1205+
combinations_with_replacement::combinations_with_replacement(self, k)
1206+
}
1207+
1208+
/// Return an iterator adaptor that iterates over all k-permutations of the
1209+
/// elements from an iterator.
1210+
///
1211+
/// Iterator element type is `Vec<Self::Item>` with length `k`. The iterator
1212+
/// produces a new Vec per iteration, and clones the iterator elements.
1213+
///
1214+
/// If `k` is greater than the length of the input iterator, the resultant
1215+
/// iterator adaptor will be empty.
1216+
///
1217+
/// ```
1218+
/// use itertools::Itertools;
1219+
///
1220+
/// let perms = (5..8).permutations(2);
1221+
/// itertools::assert_equal(perms, vec![
1222+
/// vec![5, 6],
1223+
/// vec![5, 7],
1224+
/// vec![6, 5],
1225+
/// vec![6, 7],
1226+
/// vec![7, 5],
1227+
/// vec![7, 6],
1228+
/// ]);
1229+
/// ```
1230+
///
1231+
/// Note: Permutations does not take into account the equality of the iterated values.
1232+
///
1233+
/// ```
1234+
/// use itertools::Itertools;
1235+
///
1236+
/// let it = vec![2, 2].into_iter().permutations(2);
1237+
/// itertools::assert_equal(it, vec![
1238+
/// vec![2, 2], // Note: these are the same
1239+
/// vec![2, 2], // Note: these are the same
1240+
/// ]);
1241+
/// ```
1242+
///
1243+
/// Note: The source iterator is collected lazily, and will not be
1244+
/// re-iterated if the permutations adaptor is completed and re-iterated.
1245+
#[cfg(feature = "use_std")]
1246+
fn permutations(self, k: usize) -> Permutations<Self>
1247+
where Self: Sized,
1248+
Self::Item: Clone
1249+
{
1250+
permutations::permutations(self, k)
12021251
}
12031252

12041253
/// Return an iterator adaptor that pads the sequence to a minimum length of
@@ -1997,19 +2046,21 @@ pub trait Itertools : Iterator {
19972046
/// assert_eq!(successes, [1, 2]);
19982047
/// assert_eq!(failures, [false, true]);
19992048
/// ```
2000-
fn partition_map<A, B, F, L, R>(self, mut predicate: F) -> (A, B)
2049+
fn partition_map<A, B, F, L, R>(self, predicate: F) -> (A, B)
20012050
where Self: Sized,
2002-
F: FnMut(Self::Item) -> Either<L, R>,
2051+
F: Fn(Self::Item) -> Either<L, R>,
20032052
A: Default + Extend<L>,
20042053
B: Default + Extend<R>,
20052054
{
20062055
let mut left = A::default();
20072056
let mut right = B::default();
20082057

2009-
self.for_each(|val| match predicate(val) {
2010-
Either::Left(v) => left.extend(Some(v)),
2011-
Either::Right(v) => right.extend(Some(v)),
2012-
});
2058+
for val in self {
2059+
match predicate(val) {
2060+
Either::Left(v) => left.extend(Some(v)),
2061+
Either::Right(v) => right.extend(Some(v)),
2062+
}
2063+
}
20132064

20142065
(left, right)
20152066
}

0 commit comments

Comments
 (0)