//! 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. //! //! **NOTE:** Due to performance limitations, these tests are not run with miri! //! They cannot be relied upon to discover soundness issues.
use itertools::free::{
cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq,
}; use itertools::Itertools; use itertools::{iproduct, izip, multizip, EitherOrBoth}; use quickcheck as qc; use std::cmp::{max, min, Ordering}; use std::collections::{HashMap, HashSet}; use std::default::Default; use std::num::Wrapping; use std::ops::Range;
use quickcheck::TestResult; use rand::seq::SliceRandom; use rand::Rng;
/// 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 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();
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))
}
#[should_panic] fn zip_eq_panics(a: Vec<u8>, b: Vec<u8>) -> TestResult { if a.len() == b.len() { return TestResult::discard(); }
zip_eq(a.iter(), b.iter()).for_each(|_| {});
TestResult::passed() // won't come here
}
fn equal_positions(a: Vec<i32>) -> bool { let with_pos = a.iter().positions(|v| v % 2 == 0); let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i);
itertools::equal(with_pos.clone(), without.clone())
&& itertools::equal(with_pos.rev(), without.rev())
} 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();
assert_eq!(expected, actual);
}
}
quickcheck! { fn correct_peek_nth(mut a: Vec<u16>) -> () { letmut it = peek_nth(a.clone()); for start_pos in0..a.len() + 2 { for real_idx in start_pos..a.len() + 2 { let peek_idx = real_idx - start_pos;
assert_eq!(it.peek_nth(peek_idx), a.get(real_idx));
assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx));
}
assert_eq!(it.next(), a.get(start_pos).copied());
}
}
fn peek_nth_mut_replace(a: Vec<u16>, b: Vec<u16>) -> () { letmut it = peek_nth(a.iter()); for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) {
*it.peek_nth_mut(i).unwrap() = m;
} for (i, m) in a.iter().enumerate() {
assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m));
}
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
}
fn peek_nth_next_if(a: Vec<u8>) -> () { letmut it = peek_nth(a.clone()); for (idx, mut value) in a.iter().copied().enumerate() { let should_be_none = it.next_if(|x| x != &value);
assert_eq!(should_be_none, None); if value % 5 == 0 { // Sometimes, peek up to 3 further. let n = value as usize % 3; let nth = it.peek_nth(n);
assert_eq!(nth, a.get(idx + n));
} elseif value % 5 == 1 { // Sometimes, peek next element mutably. iflet Some(v) = it.peek_mut() {
*v = v.wrapping_sub(1); let should_be_none = it.next_if_eq(&value);
assert_eq!(should_be_none, None);
value = value.wrapping_sub(1);
}
} let eq = it.next_if_eq(&value);
assert_eq!(eq, Some(value));
}
}
}
let tup1 = |(_, b)| b; for &(ord, consume_now) in &order { let iter = &mut [&mut chunks1, &'color:red'>mut chunks2][ord as usize]; match iter.next() {
Some((_, gr)) => if consume_now { for og in old_chunks.drain(..) {
elts.extend(og);
}
elts.extend(gr);
} else {
old_chunks.push(gr);
},
None => break,
}
} for og in old_chunks.drain(..) {
elts.extend(og);
} for gr in chunks1.map(&tup1) { elts.extend(gr); } for gr in chunks2.map(&tup1) { elts.extend(gr); }
itertools::assert_equal(&data, elts); true
}
}
quickcheck! { fn chunk_clone_equal(a: Vec<u8>, size: u8) -> () { letmut size = size; if size == 0 {
size += 1;
} let it = a.chunks(size as usize);
itertools::assert_equal(it.clone(), it);
}
}
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
}
}
// tuple iterators
quickcheck! { fn equal_circular_tuple_windows_1(a: Vec<u8>) -> bool { let x = a.iter().map(|e| (e,) ); let y = a.iter().circular_tuple_windows::<(_,)>();
itertools::assert_equal(x,y); true
}
fn equal_circular_tuple_windows_2(a: Vec<u8>) -> bool { let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
)); let y = a.iter().circular_tuple_windows::<(_, _)>();
itertools::assert_equal(x,y); true
}
fn equal_circular_tuple_windows_3(a: Vec<u8>) -> bool { let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
&a[(start_idx + 2) % a.len()],
)); let y = a.iter().circular_tuple_windows::<(_, _, _)>();
itertools::assert_equal(x,y); true
}
fn equal_circular_tuple_windows_4(a: Vec<u8>) -> bool { let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
&a[(start_idx + 2) % a.len()],
&a[(start_idx + 3) % a.len()],
)); let y = a.iter().circular_tuple_windows::<(_, _, _, _)>();
itertools::assert_equal(x,y); true
}
fn equal_cloned_circular_tuple_windows(a: Vec<u8>) -> bool { let x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); let y = x.clone();
itertools::assert_equal(x,y); true
}
fn equal_cloned_circular_tuple_windows_noninitial(a: Vec<u8>) -> bool { letmut x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); let _ = x.next(); let y = x.clone();
itertools::assert_equal(x,y); true
}
fn equal_cloned_circular_tuple_windows_complete(a: Vec<u8>) -> bool { letmut x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); for _ in x.by_ref() {} let y = x.clone();
itertools::assert_equal(x,y); true
}
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)
}
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<Self> for Val { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Val { fn cmp(&self, other: &Self) -> 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<_>>();
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)
.fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| {
assert!(val % modulo == key); let acc = acc + val;
Accumulator { acc }
});
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())).map(|(key, acc)| (key,Accumulator { acc }))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &Accumulator { acc: 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_fold_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)
.fold(0u64, |acc, &key, val| {
assert!(val % modulo == key);
acc + val
});
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_reduce_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)
.reduce(|acc, &key, val| {
assert!(val % modulo == key);
acc + val
});
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().reduce(|acc, val| acc + val).unwrap()))
.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_collect_modulo_key(a: Vec<u8>, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup_grouping_map = a.iter().copied().into_grouping_map_by(|i| i % modulo).collect::<Vec<_>>(); let lookup_group_map = a.iter().copied().map(|i| (i % modulo, i)).into_group_map();
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);
fn tail(v: Vec<i32>, n: u8) -> bool { let n = n as usize; let result = &v[v.len().saturating_sub(n)..];
itertools::equal(v.iter().tail(n), result)
&& itertools::equal(v.iter().filter(|_| true).tail(n), result)
}
}
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.