diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/macros_hygiene.rs | 13 | ||||
-rw-r--r-- | tests/quick.rs | 428 | ||||
-rw-r--r-- | tests/specializations.rs | 129 | ||||
-rw-r--r-- | tests/test_std.rs | 260 | ||||
-rw-r--r-- | tests/zip.rs | 16 |
5 files changed, 753 insertions, 93 deletions
diff --git a/tests/macros_hygiene.rs b/tests/macros_hygiene.rs new file mode 100644 index 0000000..d111124 --- /dev/null +++ b/tests/macros_hygiene.rs @@ -0,0 +1,13 @@ +#[test] +fn iproduct_hygiene() { + let _ = itertools::iproduct!(0..6); + let _ = itertools::iproduct!(0..6, 0..9); + let _ = itertools::iproduct!(0..6, 0..9, 0..12); +} + +#[test] +fn izip_hygiene() { + let _ = itertools::izip!(0..6); + let _ = itertools::izip!(0..6, 0..9); + let _ = itertools::izip!(0..6, 0..9, 0..12); +} diff --git a/tests/quick.rs b/tests/quick.rs index 683ba7a..e5bee17 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -5,9 +5,10 @@ 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::HashSet; +use std::collections::{HashMap, HashSet}; use itertools::Itertools; use itertools::{ multizip, @@ -19,6 +20,7 @@ use itertools::free::{ cloned, enumerate, multipeek, + peek_nth, put_back, put_back_n, rciter, @@ -487,6 +489,15 @@ quickcheck! { exact_size(it) } + fn size_peek_nth(a: Iter<u16, Exact>, s: u8) -> bool { + let mut it = peek_nth(a); + // peek a few times + for n in 0..s { + it.peek_nth(n as usize); + } + exact_size(it) + } + fn equal_merge(a: Vec<i16>, b: Vec<i16>) -> bool { let mut sa = a.clone(); let mut sb = b.clone(); @@ -744,6 +755,29 @@ quickcheck! { } quickcheck! { + fn dedup_via_coalesce(a: Vec<i32>) -> bool { + let mut b = a.clone(); + b.dedup(); + itertools::equal( + &b, + a + .iter() + .coalesce(|x, y| { + if x==y { + Ok(x) + } else { + Err((x, y)) + } + }) + .fold(vec![], |mut v, n| { + v.push(n); + v + }) + ) + } +} + +quickcheck! { fn equal_dedup(a: Vec<i32>) -> bool { let mut b = a.clone(); b.dedup(); @@ -875,6 +909,13 @@ quickcheck! { } quickcheck! { + fn size_powerset(it: Iter<u8, Exact>) -> bool { + // Powerset cardinality gets large very quickly, limit input to keep test fast. + correct_size_hint(it.take(12).powerset()) + } +} + +quickcheck! { fn size_unique(it: Iter<i8>) -> bool { correct_size_hint(it.unique()) } @@ -1155,3 +1196,388 @@ quickcheck! { } } } + +quickcheck! { + fn consistent_grouping_map_with_by(a: Vec<u8>, modulo: u8) -> () { + let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 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<_>>(); + + assert_eq!(lookup_grouping_map, lookup_grouping_map_by); + } + + fn correct_grouping_map_by_aggregate_modulo_key(a: Vec<u8>, modulo: u8) -> () { + let modulo = if modulo < 2 { 2 } else { modulo } as u64; // Avoid `% 0` + let lookup = a.iter() + .map(|&b| b as u64) // Avoid overflows + .into_grouping_map_by(|i| i % modulo) + .aggregate(|acc, &key, val| { + assert!(val % modulo == key); + if val % (modulo - 1) == 0 { + None + } else { + Some(acc.unwrap_or(0) + val) + } + }); + + let group_map_lookup = a.iter() + .map(|&b| b as u64) + .map(|i| (i % modulo, i)) + .into_group_map() + .into_iter() + .filter_map(|(key, vals)| { + vals.into_iter().fold(None, |acc, val| { + if val % (modulo - 1) == 0 { + None + } else { + Some(acc.unwrap_or(0) + val) + } + }).map(|new_val| (key, new_val)) + }) + .collect::<HashMap<_,_>>(); + assert_eq!(lookup, group_map_lookup); + + for m in 0..modulo { + assert_eq!( + lookup.get(&m).copied(), + a.iter() + .map(|&b| b as u64) + .filter(|&val| val % modulo == m) + .fold(None, |acc, val| { + if val % (modulo - 1) == 0 { + None + } else { + Some(acc.unwrap_or(0) + val) + } + }) + ); + } + } + + 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().fold(0u64, |acc, val| acc + val))) + .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_fold_first_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_first(|acc, &key, val| { + assert!(val % modulo == key); + acc + val + }); + + // TODO: Swap `fold1` with stdlib's `fold_first` when it's stabilized + 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().fold1(|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(); + + assert_eq!(lookup_grouping_map, lookup_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); + + assert_eq!(lookup[&0], MinMaxResult::MinMax(0, 10)); + } +} + +quickcheck! { + #[test] + fn counts(nums: Vec<isize>) -> TestResult { + let counts = nums.iter().counts(); + for (&item, &count) in counts.iter() { + if count <= 0 { + return TestResult::failed(); + } + if count != nums.iter().filter(|&x| x == item).count() { + return TestResult::failed(); + } + } + for item in nums.iter() { + if !counts.contains_key(item) { + return TestResult::failed(); + } + } + TestResult::passed() + } +} + +quickcheck! { + fn test_double_ended_zip_2(a: Vec<u8>, b: Vec<u8>) -> TestResult { + let mut x = + multizip((a.clone().into_iter(), b.clone().into_iter())) + .collect_vec(); + x.reverse(); + + let y = + multizip((a.into_iter(), b.into_iter())) + .rfold(Vec::new(), |mut vec, e| { vec.push(e); vec }); + + TestResult::from_bool(itertools::equal(x, y)) + } + + fn test_double_ended_zip_3(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult { + let mut x = + multizip((a.clone().into_iter(), b.clone().into_iter(), c.clone().into_iter())) + .collect_vec(); + x.reverse(); + + let y = + multizip((a.into_iter(), b.into_iter(), c.into_iter())) + .rfold(Vec::new(), |mut vec, e| { vec.push(e); vec }); + + TestResult::from_bool(itertools::equal(x, y)) + } +} diff --git a/tests/specializations.rs b/tests/specializations.rs index ef51bbe..bc337c2 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -1,6 +1,5 @@ -use itertools::{EitherOrBoth, Itertools}; +use itertools::Itertools; use std::fmt::Debug; -use std::ops::BitXor; use quickcheck::quickcheck; struct Unspecialized<I>(I); @@ -11,20 +10,14 @@ where type Item = I::Item; #[inline(always)] - fn next(&mut self) -> Option<I::Item> { + fn next(&mut self) -> Option<Self::Item> { self.0.next() } - - #[inline(always)] - fn size_hint(&self) -> (usize, Option<usize>) { - self.0.size_hint() - } } fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F) where V: Eq + Debug, - IterItem: 'a, Iter: Iterator<Item = IterItem> + Clone + 'a, F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V, { @@ -34,27 +27,43 @@ where ) } -fn check_specialized_count_last_nth_sizeh<'a, IterItem, Iter>( +fn test_specializations<IterItem, Iter>( it: &Iter, - known_expected_size: Option<usize>, ) where - IterItem: 'a + Eq + Debug, - Iter: Iterator<Item = IterItem> + Clone + 'a, + IterItem: Eq + Debug + Clone, + Iter: Iterator<Item = IterItem> + Clone, { - let size = it.clone().count(); - if let Some(expected_size) = known_expected_size { - assert_eq!(size, expected_size); - } check_specialized(it, |i| i.count()); check_specialized(it, |i| i.last()); + check_specialized(it, |i| i.collect::<Vec<_>>()); + check_specialized(it, |i| { + let mut parameters_from_fold = vec![]; + let fold_result = i.fold(vec![], |mut acc, v: IterItem| { + parameters_from_fold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_fold, fold_result) + }); + check_specialized(it, |mut i| { + let mut parameters_from_all = vec![]; + let first = i.next(); + let all_result = i.all(|x| { + parameters_from_all.push(x.clone()); + Some(x)==first + }); + (parameters_from_all, all_result) + }); + let size = it.clone().count(); for n in 0..size + 2 { check_specialized(it, |mut i| i.nth(n)); } + // size_hint is a bit harder to check let mut it_sh = it.clone(); for n in 0..size + 2 { let len = it_sh.clone().count(); let (min, max) = it_sh.size_hint(); - assert_eq!((size - n.min(size)), len); + assert_eq!(size - n.min(size), len); assert!(min <= len); if let Some(max) = max { assert!(len <= max); @@ -63,87 +72,29 @@ fn check_specialized_count_last_nth_sizeh<'a, IterItem, Iter>( } } -fn check_specialized_fold_xor<'a, IterItem, Iter>(it: &Iter) -where - IterItem: 'a - + BitXor - + Eq - + Debug - + BitXor<<IterItem as BitXor>::Output, Output = <IterItem as BitXor>::Output> - + Clone, - <IterItem as BitXor>::Output: - BitXor<Output = <IterItem as BitXor>::Output> + Eq + Debug + Clone, - Iter: Iterator<Item = IterItem> + Clone + 'a, -{ - check_specialized(it, |mut i| { - let first = i.next().map(|f| f.clone() ^ (f.clone() ^ f)); - i.fold(first, |acc, v: IterItem| acc.map(move |a| v ^ a)) - }); -} - -fn put_back_test(test_vec: Vec<i32>, known_expected_size: Option<usize>) { - { - // Lexical lifetimes support - let pb = itertools::put_back(test_vec.iter()); - check_specialized_count_last_nth_sizeh(&pb, known_expected_size); - check_specialized_fold_xor(&pb); - } - - let mut pb = itertools::put_back(test_vec.into_iter()); - pb.put_back(1); - check_specialized_count_last_nth_sizeh(&pb, known_expected_size.map(|x| x + 1)); - check_specialized_fold_xor(&pb) -} - -#[test] -fn put_back() { - put_back_test(vec![7, 4, 1], Some(3)); -} - quickcheck! { fn put_back_qc(test_vec: Vec<i32>) -> () { - put_back_test(test_vec, None) + test_specializations(&itertools::put_back(test_vec.iter())); + let mut pb = itertools::put_back(test_vec.into_iter()); + pb.put_back(1); + test_specializations(&pb); } } -fn merge_join_by_test(i1: Vec<usize>, i2: Vec<usize>, known_expected_size: Option<usize>) { - let i1 = i1.into_iter(); - let i2 = i2.into_iter(); - let mjb = i1.clone().merge_join_by(i2.clone(), std::cmp::Ord::cmp); - check_specialized_count_last_nth_sizeh(&mjb, known_expected_size); - // Rust 1.24 compatibility: - fn eob_left_z(eob: EitherOrBoth<usize, usize>) -> usize { - eob.left().unwrap_or(0) - } - fn eob_right_z(eob: EitherOrBoth<usize, usize>) -> usize { - eob.left().unwrap_or(0) - } - fn eob_both_z(eob: EitherOrBoth<usize, usize>) -> usize { - let (a, b) = eob.both().unwrap_or((0, 0)); - assert_eq!(a, b); - a +quickcheck! { + fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () { + test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp)); } - check_specialized_fold_xor(&mjb.clone().map(eob_left_z)); - check_specialized_fold_xor(&mjb.clone().map(eob_right_z)); - check_specialized_fold_xor(&mjb.clone().map(eob_both_z)); - - // And the other way around - let mjb = i2.merge_join_by(i1, std::cmp::Ord::cmp); - check_specialized_count_last_nth_sizeh(&mjb, known_expected_size); - check_specialized_fold_xor(&mjb.clone().map(eob_left_z)); - check_specialized_fold_xor(&mjb.clone().map(eob_right_z)); - check_specialized_fold_xor(&mjb.clone().map(eob_both_z)); } -#[test] -fn merge_join_by() { - let i1 = vec![1, 3, 5, 7, 8, 9]; - let i2 = vec![0, 3, 4, 5]; - merge_join_by_test(i1, i2, Some(8)); +quickcheck! { + fn map_into(v: Vec<u8>) -> () { + test_specializations(&v.into_iter().map_into::<u32>()); + } } quickcheck! { - fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () { - merge_join_by_test(i1, i2, None) + fn map_ok(v: Vec<Result<u8, char>>) -> () { + test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); } } diff --git a/tests/test_std.rs b/tests/test_std.rs index ba07784..d1ff815 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -1,8 +1,15 @@ +use paste; use permutohedron; +use quickcheck as qc; +use rand::{distributions::{Distribution, Standard}, Rng, SeedableRng, rngs::StdRng}; +use rand::{seq::SliceRandom, thread_rng}; +use std::{cmp::min, fmt::Debug, marker::PhantomData}; use itertools as it; use crate::it::Itertools; +use crate::it::ExactlyOneError; use crate::it::multizip; use crate::it::multipeek; +use crate::it::peek_nth; use crate::it::free::rciter; use crate::it::free::put_back_n; use crate::it::FoldWhile; @@ -58,6 +65,9 @@ fn unique_by() { let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; let ys = ["aaa", "bbbbb", "ccc"]; it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string())); + it::assert_equal(ys.iter(), xs.iter().rev().unique_by(|x| x[..2].to_string()).rev()); + let ys_rev = ["cccc", "aaaaa", "bbbb"]; + it::assert_equal(ys_rev.iter(), xs.iter().unique_by(|x| x[..2].to_string()).rev()); } #[test] @@ -65,9 +75,16 @@ fn unique() { let xs = [0, 1, 2, 3, 2, 1, 3]; let ys = [0, 1, 2, 3]; it::assert_equal(ys.iter(), xs.iter().unique()); + it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); + let ys_rev = [3, 1, 2, 0]; + it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); + let xs = [0, 1]; let ys = [0, 1]; it::assert_equal(ys.iter(), xs.iter().unique()); + it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); + let ys_rev = [1, 0]; + it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); } #[test] @@ -99,6 +116,26 @@ fn dedup() { } #[test] +fn coalesce() { + let data = vec![-1., -2., -3., 3., 1., 0., -1.]; + let it = data.iter().cloned().coalesce(|x, y| + if (x >= 0.) == (y >= 0.) { + Ok(x + y) + } else { + Err((x, y)) + } + ); + itertools::assert_equal(it.clone(), vec![-6., 4., -1.]); + assert_eq!( + it.fold(vec![], |mut v, n| { + v.push(n); + v + }), + vec![-6., 4., -1.] + ); +} + +#[test] fn dedup_by() { let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; @@ -115,6 +152,33 @@ fn dedup_by() { } #[test] +fn dedup_with_count() { + let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); + + let xs: [i32; 5] = [0, 0, 0, 0, 0]; + let ys: [(usize, &i32); 1] = [(5, &0)]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); +} + + +#[test] +fn dedup_by_with_count() { + let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; + let ys = [(1, &(0, 0)), (3, &(0, 1)), (1, &(0, 2)), (1, &(3, 1)), (2, &(0, 3))]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.1==y.1)); + + let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; + let ys = [( 5, &(0, 1))]; + + it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.0==y.0)); +} + +#[test] fn all_equal() { assert!("".chars().all_equal()); assert!("A".chars().all_equal()); @@ -192,7 +256,7 @@ fn trait_pointers() { I: 'r + Iterator<Item=X> { type Item = X; - fn next(&mut self) -> Option<X> + fn next(&mut self) -> Option<Self::Item> { self.0.next() } @@ -285,6 +349,26 @@ fn join() { } #[test] +fn sorted_unstable_by() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| { + a.cmp(&b) + }); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse()); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn sorted_unstable_by_key() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_unstable_by_key(|&x| -x); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] fn sorted_by() { let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| { a.cmp(&b) @@ -295,6 +379,88 @@ fn sorted_by() { it::assert_equal(v, vec![4, 3, 2, 1, 0]); } +qc::quickcheck! { + fn k_smallest_range(n: u64, m: u16, k: u16) -> () { + // u16 is used to constrain k and m to 0..2¹⁶, + // otherwise the test could use too much memory. + let (k, m) = (k as u64, m as u64); + + // Generate a random permutation of n..n+m + let i = { + let mut v: Vec<u64> = (n..n.saturating_add(m)).collect(); + v.shuffle(&mut thread_rng()); + v.into_iter() + }; + + // Check that taking the k smallest elements yields n..n+min(k, m) + it::assert_equal( + i.k_smallest(k as usize), + n..n.saturating_add(min(k, m)) + ); + } +} + +#[derive(Clone, Debug)] +struct RandIter<T: 'static + Clone + Send, R: 'static + Clone + Rng + SeedableRng + Send = StdRng> { + idx: usize, + len: usize, + rng: R, + _t: PhantomData<T> +} + +impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R> +where Standard: Distribution<T> { + type Item = T; + fn next(&mut self) -> Option<T> { + if self.idx == self.len { + None + } else { + self.idx += 1; + Some(self.rng.gen()) + } + } +} + +impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> { + fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { + RandIter { + idx: 0, + len: g.size(), + rng: R::seed_from_u64(g.next_u64()), + _t : PhantomData{}, + } + } +} + +// Check that taking the k smallest is the same as +// sorting then taking the k first elements +fn k_smallest_sort<I>(i: I, k: u16) -> () +where + I: Iterator + Clone, + I::Item: Ord + Debug, +{ + let j = i.clone(); + let k = k as usize; + it::assert_equal( + i.k_smallest(k), + j.sorted().take(k) + ) +} + +macro_rules! generic_test { + ($f:ident, $($t:ty),+) => { + $(paste::item! { + qc::quickcheck! { + fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () { + $f(i, k) + } + } + })+ + }; +} + +generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64); + #[test] fn sorted_by_key() { let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x); @@ -328,7 +494,6 @@ fn test_multipeek() { assert_eq!(mp.next(), Some(5)); assert_eq!(mp.next(), None); assert_eq!(mp.peek(), None); - } #[test] @@ -372,6 +537,70 @@ fn test_multipeek_peeking_next() { } #[test] +fn test_peek_nth() { + let nums = vec![1u8,2,3,4,5]; + + let iter = peek_nth(nums.iter().map(|&x| x)); + assert_eq!(nums, iter.collect::<Vec<_>>()); + + let mut iter = peek_nth(nums.iter().map(|&x| x)); + + assert_eq!(iter.peek_nth(0), Some(&1)); + assert_eq!(iter.peek_nth(0), Some(&1)); + assert_eq!(iter.next(), Some(1)); + + assert_eq!(iter.peek_nth(0), Some(&2)); + assert_eq!(iter.peek_nth(1), Some(&3)); + assert_eq!(iter.next(), Some(2)); + + assert_eq!(iter.peek_nth(0), Some(&3)); + assert_eq!(iter.peek_nth(1), Some(&4)); + assert_eq!(iter.peek_nth(2), Some(&5)); + assert_eq!(iter.peek_nth(3), None); + + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(4)); + + assert_eq!(iter.peek_nth(0), Some(&5)); + assert_eq!(iter.peek_nth(1), None); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), None); + + assert_eq!(iter.peek_nth(0), None); + assert_eq!(iter.peek_nth(1), None); +} + +#[test] +fn test_peek_nth_peeking_next() { + use it::PeekingNext; + let nums = vec![1u8,2,3,4,5,6,7]; + let mut iter = peek_nth(nums.iter().map(|&x| x)); + + assert_eq!(iter.peeking_next(|&x| x != 0), Some(1)); + assert_eq!(iter.next(), Some(2)); + + assert_eq!(iter.peek_nth(0), Some(&3)); + assert_eq!(iter.peek_nth(1), Some(&4)); + assert_eq!(iter.peeking_next(|&x| x == 3), Some(3)); + assert_eq!(iter.peek(), Some(&4)); + + assert_eq!(iter.peeking_next(|&x| x != 4), None); + assert_eq!(iter.peeking_next(|&x| x == 4), Some(4)); + assert_eq!(iter.peek_nth(0), Some(&5)); + assert_eq!(iter.peek_nth(1), Some(&6)); + + assert_eq!(iter.peeking_next(|&x| x != 5), None); + assert_eq!(iter.peek(), Some(&5)); + + assert_eq!(iter.peeking_next(|&x| x == 5), Some(5)); + assert_eq!(iter.peeking_next(|&x| x == 6), Some(6)); + assert_eq!(iter.peek_nth(0), Some(&7)); + assert_eq!(iter.peek_nth(1), None); + assert_eq!(iter.next(), Some(7)); + assert_eq!(iter.peek(), None); +} + +#[test] fn pad_using() { it::assert_equal((0..0).pad_using(1, |_| 1), 1..2); @@ -642,6 +871,23 @@ fn combinations_with_replacement() { } #[test] +fn powerset() { + it::assert_equal((0..0).powerset(), vec![vec![]]); + it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]); + it::assert_equal((0..2).powerset(), vec![vec![], vec![0], vec![1], vec![0, 1]]); + it::assert_equal((0..3).powerset(), vec![ + vec![], + vec![0], vec![1], vec![2], + vec![0, 1], vec![0, 2], vec![1, 2], + vec![0, 1, 2] + ]); + + assert_eq!((0..4).powerset().count(), 1 << 4); + assert_eq!((0..8).powerset().count(), 1 << 8); + assert_eq!((0..16).powerset().count(), 1 << 16); +} + +#[test] fn diff_mismatch() { let a = vec![1, 2, 3, 4]; let b = vec![1.0, 5.0, 3.0, 4.0]; @@ -791,3 +1037,13 @@ fn tree_fold1() { assert_eq!(actual, expected); } } + +#[test] +fn exactly_one_question_mark_syntax_works() { + exactly_one_question_mark_return().unwrap_err(); +} + +fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>> { + [].iter().exactly_one()?; + Ok(()) +} diff --git a/tests/zip.rs b/tests/zip.rs index b1af52c..5801b42 100644 --- a/tests/zip.rs +++ b/tests/zip.rs @@ -1,6 +1,7 @@ use itertools::Itertools; use itertools::EitherOrBoth::{Both, Left, Right}; use itertools::free::zip_eq; +use itertools::multizip; #[test] fn zip_longest_fused() { @@ -40,6 +41,20 @@ fn test_double_ended_zip_longest() { assert_eq!(it.next(), None); } +#[test] +fn test_double_ended_zip() { + let xs = [1, 2, 3, 4, 5, 6]; + let ys = [1, 2, 3, 7]; + let a = xs.iter().map(|&x| x); + let b = ys.iter().map(|&x| x); + let mut it = multizip((a, b)); + assert_eq!(it.next_back(), Some((4, 7))); + assert_eq!(it.next_back(), Some((3, 3))); + assert_eq!(it.next_back(), Some((2, 2))); + assert_eq!(it.next_back(), Some((1, 1))); + assert_eq!(it.next_back(), None); +} + #[should_panic] #[test] @@ -60,4 +75,3 @@ fn zip_eq_panic2() zip_eq(&a, &b).count(); } - |