//! The purpose of these tests is to cover corner cases of iterators //! and adaptors. //! //! In particular we test the tedious size_hint and exact size correctness.
use quickcheck as qc; use std::default::Default; use std::num::Wrapping; use std::ops::Range; use std::cmp::{max, min, Ordering}; use std::collections::{HashMap, HashSet}; use itertools::Itertools; use itertools::{
multizip,
EitherOrBoth,
iproduct,
izip,
}; use itertools::free::{
cloned,
enumerate,
multipeek,
peek_nth,
put_back,
put_back_n,
rciter,
zip,
zip_eq,
};
use rand::Rng; use rand::seq::SliceRandom; use quickcheck::TestResult;
/// Inexact size hint variant to simulate imprecise (but valid) size hints /// /// Will always decrease the lower bound and increase the upper bound /// of the size hint by set amounts. #[derive(Clone, Copy, Debug)] struct Inexact {
underestimate: usize,
overestimate: usize,
}
/// Our base iterator that we can impl Arbitrary for /// /// By default we'll return inexact bounds estimates for size_hint /// to make tests harder to pass. /// /// NOTE: Iter is tricky and is not fused, to help catch bugs. /// At the end it will return None once, then return Some(0), /// then return None again. #[derive(Clone, Debug)] struct Iter<T, SK: HintKind = Inexact> {
iterator: Range<T>, // fuse/done flag
fuse_flag: i32,
hint_kind: SK,
}
impl<T, HK> Iterator for Iter<T, HK> where Range<T>: Iterator,
<Range<T> as Iterator>::Item: Default,
HK: HintKind,
{ type Item = <Range<T> as Iterator>::Item;
fn next(&mutself) -> Option<Self::Item>
{ let elt = self.iterator.next(); if elt.is_none() { self.fuse_flag += 1; // check fuse flag ifself.fuse_flag == 2 { return Some(Default::default())
}
}
elt
}
fn correct_count<I, F>(get_it: F) -> bool where
I: Iterator,
F: Fn() -> I
{ letmut counts = vec![get_it().count()];
'outer: loop { letmut it = get_it();
for _ in0..(counts.len() - 1) { #[allow(clippy::manual_assert)] if it.next().is_none() {
panic!("Iterator shouldn't be finished, may not be deterministic");
}
}
if it.next().is_none() { break'outer;
}
counts.push(it.count());
}
let total_actual_count = counts.len() - 1;
for (i, returned_count) in counts.into_iter().enumerate() { let actual_count = total_actual_count - i; if actual_count != returned_count {
println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count);
returnfalse;
}
}
true
}
fn correct_size_hint<I: Iterator>(mut it: I) -> bool { // record size hint at each iteration let initial_hint = it.size_hint(); letmut hints = Vec::with_capacity(initial_hint.0 + 1);
hints.push(initial_hint); whilelet Some(_) = it.next() {
hints.push(it.size_hint())
}
letmut true_count = hints.len(); // start off +1 too much
// check all the size hints for &(low, hi) in &hints {
true_count -= 1; if low > true_count ||
(hi.is_some() && hi.unwrap() < true_count)
{
println!("True size: {:?}, size hint: {:?}", true_count, (low, hi)); //println!("All hints: {:?}", hints); returnfalse
}
} true
}
fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); if Some(low) != hi { returnfalse; } whilelet Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); if low != xlow + 1 { returnfalse; }
low = xlow;
hi = xhi; if Some(low) != hi { returnfalse; }
} let (low, hi) = it.size_hint();
low == 0 && hi == Some(0)
}
// Exact size for this case, without ExactSizeIterator fn exact_size_for_this<I: Iterator>(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); if Some(low) != hi { returnfalse; } whilelet Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); if low != xlow + 1 { returnfalse; }
low = xlow;
hi = xhi; if Some(low) != hi { returnfalse; }
} let (low, hi) = it.size_hint();
low == 0 && hi == Some(0)
}
fn size_multi_product(a: ShiftRange) -> bool {
correct_size_hint(a.multi_cartesian_product())
} fn correct_multi_product3(a: ShiftRange, take_manual: usize) -> () { // Fix no. of iterators at 3 let a = ShiftRange { iter_count: 3, ..a };
// test correctness of MultiProduct through regular iteration (take) // and through fold. letmut iters = a.clone(); let i0 = iters.next().unwrap(); let i1r = &iters.next().unwrap(); let i2r = &iters.next().unwrap(); let answer: Vec<_> = i0.flat_map(move |ei0| i1r.clone().flat_map(move |ei1| i2r.clone().map(move |ei2| vec![ei0, ei1, ei2]))).collect(); letmut multi_product = a.clone().multi_cartesian_product(); letmut actual = Vec::new();
#[allow(deprecated)] fn size_step(a: Iter<i16, Exact>, s: usize) -> bool { letmut s = s; if s == 0 {
s += 1; // never zero
} let filt = a.clone().dedup();
correct_size_hint(filt.step(s)) &&
exact_size(a.step(s))
}
#[allow(deprecated)] fn equal_step(a: Iter<i16>, s: usize) -> bool { letmut s = s; if s == 0 {
s += 1; // never zero
} letmut i = 0;
itertools::equal(a.clone().step(s), a.filter(|_| { let keep = i % s == 0;
i += 1;
keep
}))
}
#[allow(deprecated)] fn equal_step_vec(a: Vec<i16>, s: usize) -> bool { letmut s = s; if s == 0 {
s += 1; // never zero
} letmut i = 0;
itertools::equal(a.iter().step(s), a.iter().filter(|_| { let keep = i % s == 0;
i += 1;
keep
}))
}
fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool { letmut it = multipeek(a); // peek a few times for _ in0..s {
it.peek();
}
exact_size(it)
}
fn size_peek_nth(a: Iter<u16, Exact>, s: u8) -> bool { letmut it = peek_nth(a); // peek a few times for n in0..s {
it.peek_nth(n as usize);
}
exact_size(it)
}
// Any number of input iterators fn equal_kmerge_2(mut inputs: Vec<Vec<i16>>) -> bool { use itertools::free::kmerge; // sort the inputs for input in &mut inputs {
input.sort();
} letmut merged = inputs.concat();
merged.sort();
itertools::equal(merged.into_iter(), kmerge(inputs))
}
// Any number of input iterators fn equal_kmerge_by_ge(mut inputs: Vec<Vec<i16>>) -> bool { // sort the inputs for input in &mut inputs {
input.sort();
input.reverse();
} letmut merged = inputs.concat();
merged.sort();
merged.reverse();
itertools::equal(merged.into_iter(),
inputs.into_iter().kmerge_by(|x, y| x >= y))
}
// Any number of input iterators fn equal_kmerge_by_lt(mut inputs: Vec<Vec<i16>>) -> bool { // sort the inputs for input in &mut inputs {
input.sort();
} letmut merged = inputs.concat();
merged.sort();
itertools::equal(merged.into_iter(),
inputs.into_iter().kmerge_by(|x, y| x < y))
}
// Any number of input iterators fn equal_kmerge_by_le(mut inputs: Vec<Vec<i16>>) -> bool { // sort the inputs for input in &mut inputs {
input.sort();
} letmut merged = inputs.concat();
merged.sort();
itertools::equal(merged.into_iter(),
inputs.into_iter().kmerge_by(|x, y| x <= y))
} fn size_kmerge(a: Iter<i16>, b: Iter<i16>, c: Iter<i16>) -> bool { use itertools::free::kmerge;
correct_size_hint(kmerge(vec![a, b, c]))
} fn equal_zip_eq(a: Vec<i32>, b: Vec<i32>) -> bool { let len = std::cmp::min(a.len(), b.len()); let a = &a[..len]; let b = &b[..len];
itertools::equal(zip_eq(a, b), zip(a, b))
} fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool { let filt = a.clone().dedup(); let filt2 = b.clone().dedup();
correct_size_hint(filt.zip_longest(b.clone())) &&
correct_size_hint(a.clone().zip_longest(filt2)) &&
exact_size(a.zip_longest(b))
} fn size_2_zip_longest(a: Iter<i16>, b: Iter<i16>) -> bool { let it = a.clone().zip_longest(b.clone()); let jt = a.clone().zip_longest(b.clone());
itertools::equal(a,
it.filter_map(|elt| match elt {
EitherOrBoth::Both(x, _) => Some(x),
EitherOrBoth::Left(x) => Some(x),
_ => None,
}
))
&&
itertools::equal(b,
jt.filter_map(|elt| match elt {
EitherOrBoth::Both(_, y) => Some(y),
EitherOrBoth::Right(y) => Some(y),
_ => None,
}
))
} fn size_interleave(a: Iter<i16>, b: Iter<i16>) -> bool {
correct_size_hint(a.interleave(b))
} fn exact_interleave(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
exact_size_for_this(a.interleave(b))
} fn size_interleave_shortest(a: Iter<i16>, b: Iter<i16>) -> bool {
correct_size_hint(a.interleave_shortest(b))
} fn exact_interleave_shortest(a: Vec<()>, b: Vec<()>) -> bool {
exact_size_for_this(a.iter().interleave_shortest(&b))
} fn size_intersperse(a: Iter<i16>, x: i16) -> bool {
correct_size_hint(a.intersperse(x))
} fn equal_intersperse(a: Vec<i32>, x: i32) -> bool { letmut inter = false; letmut i = 0; for elt in a.iter().cloned().intersperse(x) { if inter { if elt != x { returnfalse }
} else { if elt != a[i] { returnfalse }
i += 1;
}
inter = !inter;
} true
}
fn equal_combinations_2(a: Vec<u8>) -> bool { letmut v = Vec::new(); for (i, x) in enumerate(&a) { for y in &a[i + 1..] {
v.push((x, y));
}
}
itertools::equal(a.iter().tuple_combinations::<(_, _)>(), v)
}
fn correct_permutations(vals: HashSet<i32>, k: usize) -> () { // Test permutations only on iterators of distinct integers, to prevent // false positives.
const MAX_N: usize = 5;
let n = min(vals.len(), MAX_N); let vals: HashSet<i32> = vals.into_iter().take(n).collect();
let perms = vals.iter().permutations(k);
letmut actual = HashSet::new();
for perm in perms {
assert_eq!(perm.len(), k);
let all_items_valid = perm.iter().all(|p| vals.contains(p));
assert!(all_items_valid, "perm contains value not from input: {:?}", perm);
// Check that all perm items are distinct let distinct_len = { let perm_set: HashSet<_> = perm.iter().collect();
perm_set.len()
};
assert_eq!(perm.len(), distinct_len);
// Check that the perm is new
assert!(actual.insert(perm.clone()), "perm already encountered: {:?}", perm);
}
}
fn permutations_lexic_order(a: usize, b: usize) -> () { let a = a % 6; let b = b % 6;
let n = max(a, b); let k = min (a, b);
let expected_first: Vec<usize> = (0..k).collect(); let expected_last: Vec<usize> = ((n - k)..n).rev().collect();
fn permutations_k0_yields_once(n: usize) -> () { let k = 0; let expected: Vec<Vec<usize>> = vec![vec![]]; let actual = (0..n).permutations(k).collect_vec();
fn count_unique(it: Vec<i8>, take_first: u8) -> () { let answer = { letmut v = it.clone();
v.sort(); v.dedup();
v.len()
}; letmut iter = cloned(&it).unique(); let first_count = (&mut iter).take(take_first as usize).count(); let rest_count = iter.count();
assert_eq!(answer, first_count + rest_count);
}
}
quickcheck! { fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool { let jt = it.clone(); let groups = it.group_by(|k| *k);
itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
}
}
quickcheck! { fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool { let groups = data.iter().group_by(|k| *k / 10); let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
res
}
}
quickcheck! { fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool { let grouper = data.iter().group_by(|k| *k / 10); let groups = grouper.into_iter().collect_vec(); let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
res
}
}
let tup1 = |(_, b)| b; for &(ord, consume_now) in &order { let iter = &mut [&mut groups1, &'color:red'>mut groups2][ord as usize]; match iter.next() {
Some((_, gr)) => if consume_now { for og in old_groups.drain(..) {
elts.extend(og);
}
elts.extend(gr);
} else {
old_groups.push(gr);
},
None => break,
}
} for og in old_groups.drain(..) {
elts.extend(og);
} for gr in groups1.map(&tup1) { elts.extend(gr); } for gr in groups2.map(&tup1) { elts.extend(gr); }
itertools::assert_equal(&data, elts); true
}
}
quickcheck! { fn equal_chunks_lazy(a: Vec<u8>, size: u8) -> bool { letmut size = size; if size == 0 {
size += 1;
} let chunks = a.iter().chunks(size as usize); let it = a.chunks(size as usize); for (a, b) in chunks.into_iter().zip(it) { if !itertools::equal(a, b) { returnfalse;
}
} true
}
}
quickcheck! { fn equal_tuple_windows_1(a: Vec<u8>) -> bool { let x = a.windows(1).map(|s| (&s[0], )); let y = a.iter().tuple_windows::<(_,)>();
itertools::equal(x, y)
}
fn equal_tuple_windows_2(a: Vec<u8>) -> bool { let x = a.windows(2).map(|s| (&s[0], &s[>1])); let y = a.iter().tuple_windows::<(_, _)>();
itertools::equal(x, y)
}
fn equal_tuple_windows_3(a: Vec<u8>) -> bool { let x = a.windows(3).map(|s| (&s[0], &s[>1], &s[2])); let y = a.iter().tuple_windows::<(_, _, _)>();
itertools::equal(x, y)
}
fn equal_tuple_windows_4(a: Vec<u8>) -> bool { let x = a.windows(4).map(|s| (&s[0], &s[>1], &s[2], &s[3])); let y = a.iter().tuple_windows::<(_, _, _, _)>();
itertools::equal(x, y)
}
fn equal_tuples_1(a: Vec<u8>) -> bool { let x = a.chunks(1).map(|s| (&s[0], )); let y = a.iter().tuples::<(_,)>();
itertools::equal(x, y)
}
fn equal_tuples_2(a: Vec<u8>) -> bool { let x = a.chunks(2).filter(|s| s.len() == 2).map(|s| (&s[0], &s[ style='color: green'>1])); let y = a.iter().tuples::<(_, _)>();
itertools::equal(x, y)
}
fn equal_tuples_3(a: Vec<u8>) -> bool { let x = a.chunks(3).filter(|s| s.len() == 3).map(|s| (&s[0], &s[ style='color: green'>1], &s[2])); let y = a.iter().tuples::<(_, _, _)>();
itertools::equal(x, y)
}
fn equal_tuples_4(a: Vec<u8>) -> bool { let x = a.chunks(4).filter(|s| s.len() == 4).map(|s| (&s[0], &s[ style='color: green'>1], &s[2], &s[3])); let y = a.iter().tuples::<(_, _, _, _)>();
itertools::equal(x, y)
}
for (&key, vals) in lookup.iter() {
assert!(vals.iter().all(|&val| val % modulo == key));
}
}
}
/// A peculiar type: Equality compares both tuple items, but ordering only the /// first item. This is so we can check the stability property easily. #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32);
impl PartialOrd<Val> for Val { fn partial_cmp(&self, other: &Val) -> Option<Ordering> { self.0.partial_cmp(&other.0)
}
}
impl Ord for Val { fn cmp(&self, other: &Val) -> Ordering { self.0.cmp(&other.0)
}
}
let lookup_grouping_map = a.iter().copied().map(|i| (i % modulo, i)).into_grouping_map().collect::<Vec<_>>(); let lookup_grouping_map_by = a.iter().copied().into_grouping_map_by(|i| i % modulo).collect::<Vec<_>>();
fn correct_grouping_map_by_max_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max();
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().max().unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &max) in lookup.iter() {
assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max());
}
}
fn correct_grouping_map_by_max_by_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max_by(|_, v1, v2| v1.cmp(v2));
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().max_by(|v1, v2| v1.cmp(v2)).unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &max) in lookup.iter() {
assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by(|v1, v2| v1.cmp(v2)));
}
}
fn correct_grouping_map_by_max_by_key_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max_by_key(|_, &val| val);
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().max_by_key(|&val| val).unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &max) in lookup.iter() {
assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by_key(|&val| val));
}
}
fn correct_grouping_map_by_min_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min();
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().min().unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &min) in lookup.iter() {
assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min());
}
}
fn correct_grouping_map_by_min_by_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min_by(|_, v1, v2| v1.cmp(v2));
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().min_by(|v1, v2| v1.cmp(v2)).unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &min) in lookup.iter() {
assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by(|v1, v2| v1.cmp(v2)));
}
}
fn correct_grouping_map_by_min_by_key_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min_by_key(|_, &val| val);
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().min_by_key(|&val| val).unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &min) in lookup.iter() {
assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by_key(|&val| val));
}
}
fn correct_grouping_map_by_minmax_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax();
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().minmax()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &minmax) in lookup.iter() {
assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax());
}
}
fn correct_grouping_map_by_minmax_by_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax_by(|_, v1, v2| v1.cmp(v2));
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().minmax_by(|v1, v2| v1.cmp(v2))))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &minmax) in lookup.iter() {
assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax_by(|v1, v2| v1.cmp(v2)));
}
}
fn correct_grouping_map_by_minmax_by_key_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax_by_key(|_, &val| val);
let group_map_lookup = a.iter().copied()
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().minmax_by_key(|&val| val)))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &minmax) in lookup.iter() {
assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax_by_key(|&val| val));
}
}
fn correct_grouping_map_by_sum_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.sum();
let group_map_lookup = a.iter().map(|&b| b as u64)
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().sum()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &sum) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
}
}
fn correct_grouping_map_by_product_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = Wrapping(if modulo == 0 { 1 } else { modulo } as u64); // Avoid `% 0` let lookup = a.iter().map(|&b| Wrapping(b as u64)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.product();
let group_map_lookup = a.iter().map(|&b| Wrapping(b as u64))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().product::<Wrapping<u64>>()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &prod) in lookup.iter() {
assert_eq!(
prod,
a.iter()
.map(|&b| Wrapping(b as u64))
.filter(|&val| val % modulo == key)
.product::<Wrapping<u64>>()
);
}
}
// This should check that if multiple elements are equally minimum or maximum // then `max`, `min` and `minmax` pick the first minimum and the last maximum. // This is to be consistent with `std::iter::max` and `std::iter::min`. fn correct_grouping_map_by_min_max_minmax_order_modulo_key() -> () { use itertools::MinMaxResult;
let lookup = (0..=10)
.into_grouping_map_by(|_| 0)
.max_by(|_, _, _| Ordering::Equal);
assert_eq!(lookup[&0], 10);
let lookup = (0..=10)
.into_grouping_map_by(|_| 0)
.min_by(|_, _, _| Ordering::Equal);
assert_eq!(lookup[&0], 0);
let lookup = (0..=10)
.into_grouping_map_by(|_| 0)
.minmax_by(|_, _, _| Ordering::Equal);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.