From cbe87cbf145d7ce6bee9437488b988371c8d1f78 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 3 Oct 2023 14:14:33 +0200 Subject: [PATCH 1/6] Benchmark `TupleCombinations::fold` --- benches/bench1.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index 664191ab4..84caf88bc 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -855,6 +855,17 @@ fn with_position_fold(c: &mut Criterion) { }); } +fn tuple_combinations_fold(c: &mut Criterion) { + let v = black_box((0..64).collect_vec()); + c.bench_function("tuple_combinations fold", move |b| { + b.iter(|| { + v.iter() + .tuple_combinations() + .fold(0, |acc, (a, b, c, d)| acc + *a * *c - *b * *d) + }) + }); +} + criterion_group!( benches, slice_iter, @@ -903,6 +914,7 @@ criterion_group!( permutations_range, permutations_slice, with_position_fold, + tuple_combinations_fold, while_some, ); criterion_main!(benches); From 318ae0afc095713508aa7cfe5fb140cedbf2062c Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 3 Oct 2023 11:21:12 +0200 Subject: [PATCH 2/6] `TupleCombinations::fold` --- src/adaptors/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 18c864ad1..a47cb6ca6 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -650,6 +650,13 @@ where fn count(self) -> usize { self.iter.count() } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } } impl FusedIterator for TupleCombinations From b743e2a95f7ec9f3402e86ecfe3debadd21b1a95 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 3 Oct 2023 11:20:34 +0200 Subject: [PATCH 3/6] `Tuple1Combinations::fold` --- src/adaptors/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index a47cb6ca6..6d9013f0c 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -691,6 +691,13 @@ impl Iterator for Tuple1Combination { fn count(self) -> usize { self.iter.count() } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.map(|x| (x,)).fold(init, f) + } } impl HasCombination for (I::Item,) { From 21004edcd17d1663fd396fc9190a1788968ca193 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 3 Oct 2023 11:39:53 +0200 Subject: [PATCH 4/6] `Tuple*Combinations::fold` (macro) `I::Item` and `A` are the same so the "where condition" is not really changed, but it is apparently needed here. We fold `c` then for each item of `iter`, we fold the cloned `iter`. The `while let` loop should probably be converted to `fold` somehow later (if possible) but the core logic is done and it is already really faster. --- src/adaptors/mod.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 6d9013f0c..802256542 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -731,7 +731,7 @@ macro_rules! impl_tuple_combination { impl Iterator for $C where I: Iterator + Clone, - I::Item: Clone + A: Clone, { type Item = (A, $(ignore_ident!($X, A)),*); @@ -761,6 +761,26 @@ macro_rules! impl_tuple_combination { let n = self.iter.count(); checked_binomial(n, K).unwrap() + self.c.count() } + + fn fold(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + let Self { c, item, mut iter } = self; + init = c + .map(|($($X),*,)| { + let z = item.clone().unwrap(); + (z, $($X),*) + }) + .fold(init, &mut f); + while let Some(z) = iter.next() { + let c: $P = iter.clone().into(); + init = c + .map(|($($X),*,)| (z.clone(), $($X),*)) + .fold(init, &mut f); + } + init + } } impl HasCombination for (A, $(ignore_ident!($X, A)),*) From 9154da62e3248271702d32a059788e916393b7ff Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Fri, 27 Oct 2023 08:51:09 +0200 Subject: [PATCH 5/6] `Tuple*Combination::fold`: avoid `unwrap` --- src/adaptors/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 802256542..b3d307a2a 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -767,12 +767,11 @@ macro_rules! impl_tuple_combination { F: FnMut(B, Self::Item) -> B, { let Self { c, item, mut iter } = self; - init = c - .map(|($($X),*,)| { - let z = item.clone().unwrap(); - (z, $($X),*) - }) - .fold(init, &mut f); + if let Some(z) = item.as_ref() { + init = c + .map(|($($X),*,)| (z.clone(), $($X),*)) + .fold(init, &mut f); + } while let Some(z) = iter.next() { let c: $P = iter.clone().into(); init = c From b289e77edc55738e67f1643bd92437658d7221d6 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Fri, 27 Oct 2023 09:21:07 +0200 Subject: [PATCH 6/6] Simplify commas in `impl_tuple_combination` macro --- src/adaptors/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index b3d307a2a..3aa49d224 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -736,14 +736,14 @@ macro_rules! impl_tuple_combination { type Item = (A, $(ignore_ident!($X, A)),*); fn next(&mut self) -> Option { - if let Some(($($X),*,)) = self.c.next() { + if let Some(($($X,)*)) = self.c.next() { let z = self.item.clone().unwrap(); Some((z, $($X),*)) } else { self.item = self.iter.next(); self.item.clone().and_then(|z| { self.c = self.iter.clone().into(); - self.c.next().map(|($($X),*,)| (z, $($X),*)) + self.c.next().map(|($($X,)*)| (z, $($X),*)) }) } } @@ -769,13 +769,13 @@ macro_rules! impl_tuple_combination { let Self { c, item, mut iter } = self; if let Some(z) = item.as_ref() { init = c - .map(|($($X),*,)| (z.clone(), $($X),*)) + .map(|($($X,)*)| (z.clone(), $($X),*)) .fold(init, &mut f); } while let Some(z) = iter.next() { let c: $P = iter.clone().into(); init = c - .map(|($($X),*,)| (z.clone(), $($X),*)) + .map(|($($X,)*)| (z.clone(), $($X),*)) .fold(init, &mut f); } init