From ae948d619629fc4282a4f4235186988ecdc13984 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Mon, 28 Aug 2023 21:34:50 +0900 Subject: [PATCH 01/22] test refactor --- src/lib.rs | 425 +--------------------------------------------- src/unit_tests.rs | 241 ++++++++++++++++++++++++++ tests/tests.rs | 186 ++++++++++++++++++++ 3 files changed, 429 insertions(+), 423 deletions(-) create mode 100644 src/unit_tests.rs create mode 100644 tests/tests.rs diff --git a/src/lib.rs b/src/lib.rs index c44b08b..c6d627b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,6 +284,7 @@ use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Index, IndexMut, Sub, SubAssign, }; +//mod unit_tests; type CounterMap = HashMap; @@ -334,6 +335,7 @@ where N: Zero, { /// Create a new, empty `Counter` + #[must_use] pub fn new() -> Counter { Counter { map: HashMap::new(), @@ -1308,426 +1310,3 @@ where } } } - -#[cfg(test)] -mod tests { - use super::*; - use maplit::hashmap; - use rand::Rng; - use std::collections::HashMap; - - #[test] - fn test_creation() { - let _: Counter = Counter::new(); - - let initializer = &[1]; - let counter = Counter::init(initializer); - - let mut expected = HashMap::new(); - static ONE: usize = 1; - expected.insert(&ONE, 1); - assert!(counter.map == expected); - } - - #[test] - fn test_update() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - counter.update("aeeeee".chars()); - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_add_update_iterable() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - counter += "aeeeee".chars(); - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_add_update_counter() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - let other = Counter::init("aeeeee".chars()); - counter += other; - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_subtract() { - let mut counter = Counter::init("abbccc".chars()); - counter.subtract("bbccddd".chars()); - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_sub_update_iterable() { - let mut counter = Counter::init("abbccc".chars()); - counter -= "bbccddd".chars(); - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_sub_update_counter() { - let mut counter = Counter::init("abbccc".chars()); - let other = Counter::init("bbccddd".chars()); - counter -= other; - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_composite_add_sub() { - let mut counts = Counter::<_>::init( - "able babble table babble rabble table able fable scrabble".split_whitespace(), - ); - // add or subtract an iterable of the same type - counts += "cain and abel fable table cable".split_whitespace(); - // or add or subtract from another Counter of the same type - let other_counts = Counter::init("scrabble cabbie fable babble".split_whitespace()); - let _diff = counts - other_counts; - } - - #[test] - fn test_most_common() { - let counter = Counter::init("abbccc".chars()); - let by_common = counter.most_common(); - let expected = vec![('c', 3), ('b', 2), ('a', 1)]; - assert!(by_common == expected); - } - - #[test] - fn test_most_common_tiebreaker() { - let counter = Counter::init("eaddbbccc".chars()); - let by_common = counter.most_common_tiebreaker(|&a, &b| a.cmp(&b)); - let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)]; - assert!(by_common == expected); - } - - #[test] - fn test_most_common_tiebreaker_reversed() { - let counter = Counter::init("eaddbbccc".chars()); - let by_common = counter.most_common_tiebreaker(|&a, &b| b.cmp(&a)); - let expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)]; - assert!(by_common == expected); - } - - // The main purpose of this test is to see that we can call `Counter::most_common_tiebreaker()` - // with a closure that is `FnMut` but not `Fn`. - #[test] - fn test_most_common_tiebreaker_fn_mut() { - let counter: Counter<_> = Counter::init("abracadabra".chars()); - // Count how many times the tiebreaker closure is called. - let mut num_ties = 0; - let sorted = counter.most_common_tiebreaker(|a, b| { - num_ties += 1; - a.cmp(b) - }); - let expected = vec![('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)]; - assert_eq!(sorted, expected); - // We should have called the tiebreaker twice: once to resolve the tie between `'b'` and - // `'r'` and once to resolve the tie between `'c'` and `'d'`. - assert_eq!(num_ties, 2); - } - - #[test] - fn test_most_common_ordered() { - let counter = Counter::init("eaddbbccc".chars()); - let by_common = counter.most_common_ordered(); - let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)]; - assert!(by_common == expected); - } - - #[test] - fn test_k_most_common_ordered() { - let counter: Counter<_> = "abracadabra".chars().collect(); - let all = counter.most_common_ordered(); - for k in 0..=counter.len() { - let topk = counter.k_most_common_ordered(k); - assert_eq!(&topk, &all[..k]); - } - } - - /// This test is fundamentally the same as `test_k_most_common_ordered`, but it operates on - /// a wider variety of data. In particular, it tests both longer, narrower, and wider - /// distributions of data than the other test does. - #[test] - fn test_k_most_common_ordered_heavy() { - let mut rng = rand::thread_rng(); - - for container_size in [5, 10, 25, 100, 256] { - for max_value_factor in [0.25, 0.5, 1.0, 1.25, 2.0, 10.0, 100.0] { - let max_value = ((container_size as f64) * max_value_factor) as u32; - let mut values = vec![0; container_size]; - for value in values.iter_mut() { - *value = rng.gen_range(0..=max_value); - } - - let counter: Counter<_> = values.into_iter().collect(); - let all = counter.most_common_ordered(); - for k in 0..=counter.len() { - let topk = counter.k_most_common_ordered(k); - assert_eq!(&topk, &all[..k]); - } - } - } - } - - #[test] - fn test_total() { - let counter = Counter::init("".chars()); - let total: usize = counter.total(); - assert_eq!(total, 0); - - let counter = Counter::init("eaddbbccc".chars()); - let total: usize = counter.total(); - assert_eq!(total, 9); - } - - #[test] - fn test_add() { - let d = Counter::<_>::init("abbccc".chars()); - let e = Counter::<_>::init("bccddd".chars()); - - let out = d + e; - let expected = Counter::init("abbbcccccddd".chars()); - assert!(out == expected); - } - - #[test] - fn test_sub() { - let d = Counter::<_>::init("abbccc".chars()); - let e = Counter::<_>::init("bccddd".chars()); - - let out = d - e; - let expected = Counter::init("abc".chars()); - assert!(out == expected); - } - - #[test] - fn test_intersection() { - let d = Counter::<_>::init("abbccc".chars()); - let e = Counter::<_>::init("bccddd".chars()); - - let out = d & e; - let expected = Counter::init("bcc".chars()); - assert!(out == expected); - } - - #[test] - fn test_union() { - let d = Counter::<_>::init("abbccc".chars()); - let e = Counter::<_>::init("bccddd".chars()); - - let out = d | e; - let expected = Counter::init("abbcccddd".chars()); - assert!(out == expected); - } - - #[test] - fn test_delete_key_from_backing_map() { - let mut counter = Counter::<_>::init("aa-bb-cc".chars()); - counter.remove(&'-'); - assert!(counter == Counter::init("aabbcc".chars())); - } - - #[test] - fn test_from_iter_simple() { - let counter = "abbccc".chars().collect::>(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_from_iter_tuple() { - let items = [('a', 1), ('b', 2), ('c', 3)]; - let counter = items.iter().cloned().collect::>(); - let expected: HashMap = items.iter().cloned().collect(); - assert_eq!(counter.map, expected); - } - - #[test] - fn test_from_iter_tuple_with_duplicates() { - let items = [('a', 1), ('b', 2), ('c', 3)]; - let counter = items - .iter() - .cycle() - .take(items.len() * 2) - .cloned() - .collect::>(); - let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); - assert_eq!(counter.map, expected); - } - - #[test] - fn test_extend_simple() { - let mut counter = "abbccc".chars().collect::>(); - counter.extend("bccddd".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 3, - 'c' => 5, - 'd' => 3, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_extend_tuple() { - let mut counter = "bccddd".chars().collect::>(); - let items = [('a', 1), ('b', 2), ('c', 3)]; - counter.extend(items.iter().cloned()); - let expected = hashmap! { - 'a' => 1, - 'b' => 3, - 'c' => 5, - 'd' => 3, - }; - assert_eq!(counter.map, expected); - } - - #[test] - fn test_extend_tuple_with_duplicates() { - let mut counter = "ccc".chars().collect::>(); - let items = [('a', 1), ('b', 2), ('c', 3)]; - counter.extend(items.iter().cycle().take(items.len() * 2 - 1).cloned()); - let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); - assert_eq!(counter.map, expected); - } - - #[test] - fn test_count_minimal_type() { - #[derive(Debug, Hash, PartialEq, Eq)] - struct Inty { - i: usize, - } - - impl Inty { - pub fn new(i: usize) -> Inty { - Inty { i } - } - } - - // - let intys = vec![ - Inty::new(8), - Inty::new(0), - Inty::new(0), - Inty::new(8), - Inty::new(6), - Inty::new(7), - Inty::new(5), - Inty::new(3), - Inty::new(0), - Inty::new(9), - ]; - - let inty_counts = Counter::init(intys); - // println!("{:?}", inty_counts.map); // test runner blanks this - // {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1, - // Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1} - assert!(inty_counts.map.get(&Inty { i: 8 }) == Some(&2)); - assert!(inty_counts.map.get(&Inty { i: 0 }) == Some(&3)); - assert!(inty_counts.map.get(&Inty { i: 6 }) == Some(&1)); - } - - #[test] - fn test_collect() { - let counter: Counter<_> = "abbccc".chars().collect(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_non_usize_count() { - let counter: Counter<_, i8> = "abbccc".chars().collect(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_superset_non_usize_count() { - let mut a: Counter<_, i8> = "abbcccc".chars().collect(); - let mut b: Counter<_, i8> = "abb".chars().collect(); - assert!(a.is_superset(&b)); - // Negative values are possible, a is no longer a superset - a[&'e'] = -1; - assert!(!a.is_superset(&b)); - // Adjust b to make a a superset again - b[&'e'] = -2; - assert!(a.is_superset(&b)); - } - - #[test] - fn test_subset_non_usize_count() { - let mut a: Counter<_, i8> = "abb".chars().collect(); - let mut b: Counter<_, i8> = "abbcccc".chars().collect(); - assert!(a.is_subset(&b)); - // Negative values are possible; a is no longer a subset - b[&'e'] = -1; - assert!(!a.is_subset(&b)); - // Adjust a to make it a subset again - a[&'e'] = -2; - assert!(a.is_subset(&b)); - } -} diff --git a/src/unit_tests.rs b/src/unit_tests.rs new file mode 100644 index 0000000..8f9b3f3 --- /dev/null +++ b/src/unit_tests.rs @@ -0,0 +1,241 @@ +#[cfg(test)] +mod tests { + use counter::Counter; + use maplit::hashmap; + use rand::Rng; + use std::collections::HashMap; + #[test] + fn test_creation() { + let _: Counter = Counter::new(); + + let initializer = &[1]; + let counter = Counter::init(initializer); + + let mut expected = HashMap::new(); + static ONE: usize = 1; + expected.insert(&ONE, 1); + assert!(counter.map == expected); + } + #[test] + fn test_update() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + counter.update("aeeeee".chars()); + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_add_update_iterable() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + counter += "aeeeee".chars(); + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_add_update_counter() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + let other = Counter::init("aeeeee".chars()); + counter += other; + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_subtract() { + let mut counter = Counter::init("abbccc".chars()); + counter.subtract("bbccddd".chars()); + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_sub_update_iterable() { + let mut counter = Counter::init("abbccc".chars()); + counter -= "bbccddd".chars(); + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_sub_update_counter() { + let mut counter = Counter::init("abbccc".chars()); + let other = Counter::init("bbccddd".chars()); + counter -= other; + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_from_iter_simple() { + let counter = "abbccc".chars().collect::>(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_from_iter_tuple() { + let items = [('a', 1), ('b', 2), ('c', 3)]; + let counter = items.iter().cloned().collect::>(); + let expected: HashMap = items.iter().cloned().collect(); + assert_eq!(counter.map, expected); + } + + #[test] + fn test_from_iter_tuple_with_duplicates() { + let items = [('a', 1), ('b', 2), ('c', 3)]; + let counter = items + .iter() + .cycle() + .take(items.len() * 2) + .cloned() + .collect::>(); + let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); + assert_eq!(counter.map, expected); + } + + #[test] + fn test_extend_simple() { + let mut counter = "abbccc".chars().collect::>(); + counter.extend("bccddd".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 3, + 'c' => 5, + 'd' => 3, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_extend_tuple() { + let mut counter = "bccddd".chars().collect::>(); + let items = [('a', 1), ('b', 2), ('c', 3)]; + counter.extend(items.iter().cloned()); + let expected = hashmap! { + 'a' => 1, + 'b' => 3, + 'c' => 5, + 'd' => 3, + }; + assert_eq!(counter.map, expected); + } + + #[test] + fn test_extend_tuple_with_duplicates() { + let mut counter = "ccc".chars().collect::>(); + let items = [('a', 1), ('b', 2), ('c', 3)]; + counter.extend(items.iter().cycle().take(items.len() * 2 - 1).cloned()); + let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); + assert_eq!(counter.map, expected); + } + + #[test] + fn test_count_minimal_type() { + #[derive(Debug, Hash, PartialEq, Eq)] + struct Inty { + i: usize, + } + + impl Inty { + pub fn new(i: usize) -> Inty { + Inty { i } + } + } + + // + let intys = vec![ + Inty::new(8), + Inty::new(0), + Inty::new(0), + Inty::new(8), + Inty::new(6), + Inty::new(7), + Inty::new(5), + Inty::new(3), + Inty::new(0), + Inty::new(9), + ]; + + let inty_counts = Counter::init(intys); + // println!("{:?}", inty_counts.map); // test runner blanks this + // {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1, + // Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1} + assert!(inty_counts.map.get(&Inty { i: 8 }) == Some(&2)); + assert!(inty_counts.map.get(&Inty { i: 0 }) == Some(&3)); + assert!(inty_counts.map.get(&Inty { i: 6 }) == Some(&1)); + } + + #[test] + fn test_collect() { + let counter: Counter<_> = "abbccc".chars().collect(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + } + + #[test] + fn test_non_usize_count() { + let counter: Counter<_, i8> = "abbccc".chars().collect(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + } + + +} diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..69dd2dc --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,186 @@ +#[cfg(test)] +mod tests { + use counter::Counter; + use rand::Rng; + + #[test] + fn test_composite_add_sub() { + let mut counts = Counter::<_>::init( + "able babble table babble rabble table able fable scrabble".split_whitespace(), + ); + // add or subtract an iterable of the same type + counts += "cain and abel fable table cable".split_whitespace(); + // or add or subtract from another Counter of the same type + let other_counts = Counter::init("scrabble cabbie fable babble".split_whitespace()); + let _diff = counts - other_counts; + } + + #[test] + fn test_most_common() { + let counter = Counter::init("abbccc".chars()); + let by_common = counter.most_common(); + let expected = vec![('c', 3), ('b', 2), ('a', 1)]; + assert!(by_common == expected); + } + + #[test] + fn test_most_common_tiebreaker() { + let counter = Counter::init("eaddbbccc".chars()); + let by_common = counter.most_common_tiebreaker(|&a, &b| a.cmp(&b)); + let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)]; + assert!(by_common == expected); + } + + #[test] + fn test_most_common_tiebreaker_reversed() { + let counter = Counter::init("eaddbbccc".chars()); + let by_common = counter.most_common_tiebreaker(|&a, &b| b.cmp(&a)); + let expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)]; + assert!(by_common == expected); + } + + // The main purpose of this test is to see that we can call `Counter::most_common_tiebreaker()` + // with a closure that is `FnMut` but not `Fn`. + #[test] + fn test_most_common_tiebreaker_fn_mut() { + let counter: Counter<_> = Counter::init("abracadabra".chars()); + // Count how many times the tiebreaker closure is called. + let mut num_ties = 0; + let sorted = counter.most_common_tiebreaker(|a, b| { + num_ties += 1; + a.cmp(b) + }); + let expected = vec![('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)]; + assert_eq!(sorted, expected); + // We should have called the tiebreaker twice: once to resolve the tie between `'b'` and + // `'r'` and once to resolve the tie between `'c'` and `'d'`. + assert_eq!(num_ties, 2); + } + + #[test] + fn test_most_common_ordered() { + let counter = Counter::init("eaddbbccc".chars()); + let by_common = counter.most_common_ordered(); + let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)]; + assert!(by_common == expected); + } + + #[test] + fn test_k_most_common_ordered() { + let counter: Counter<_> = "abracadabra".chars().collect(); + let all = counter.most_common_ordered(); + for k in 0..=counter.len() { + let topk = counter.k_most_common_ordered(k); + assert_eq!(&topk, &all[..k]); + } + } + + /// This test is fundamentally the same as `test_k_most_common_ordered`, but it operates on + /// a wider variety of data. In particular, it tests both longer, narrower, and wider + /// distributions of data than the other test does. + #[test] + fn test_k_most_common_ordered_heavy() { + let mut rng = rand::thread_rng(); + + for container_size in [5, 10, 25, 100, 256] { + for max_value_factor in [0.25, 0.5, 1.0, 1.25, 2.0, 10.0, 100.0] { + let max_value = ((container_size as f64) * max_value_factor) as u32; + let mut values = vec![0; container_size]; + for value in values.iter_mut() { + *value = rng.gen_range(0..=max_value); + } + + let counter: Counter<_> = values.into_iter().collect(); + let all = counter.most_common_ordered(); + for k in 0..=counter.len() { + let topk = counter.k_most_common_ordered(k); + assert_eq!(&topk, &all[..k]); + } + } + } + } + + #[test] + fn test_total() { + let counter = Counter::init("".chars()); + let total: usize = counter.total(); + assert_eq!(total, 0); + + let counter = Counter::init("eaddbbccc".chars()); + let total: usize = counter.total(); + assert_eq!(total, 9); + } + + #[test] + fn test_add() { + let d = Counter::<_>::init("abbccc".chars()); + let e = Counter::<_>::init("bccddd".chars()); + + let out = d + e; + let expected = Counter::init("abbbcccccddd".chars()); + assert!(out == expected); + } + + #[test] + fn test_sub() { + let d = Counter::<_>::init("abbccc".chars()); + let e = Counter::<_>::init("bccddd".chars()); + + let out = d - e; + let expected = Counter::init("abc".chars()); + assert!(out == expected); + } + + #[test] + fn test_intersection() { + let d = Counter::<_>::init("abbccc".chars()); + let e = Counter::<_>::init("bccddd".chars()); + + let out = d & e; + let expected = Counter::init("bcc".chars()); + assert!(out == expected); + } + + #[test] + fn test_union() { + let d = Counter::<_>::init("abbccc".chars()); + let e = Counter::<_>::init("bccddd".chars()); + + let out = d | e; + let expected = Counter::init("abbcccddd".chars()); + assert!(out == expected); + } + + #[test] + fn test_delete_key_from_backing_map() { + let mut counter = Counter::<_>::init("aa-bb-cc".chars()); + counter.remove(&'-'); + assert!(counter == Counter::init("aabbcc".chars())); + } + + #[test] + fn test_superset_non_usize_count() { + let mut a: Counter<_, i8> = "abbcccc".chars().collect(); + let mut b: Counter<_, i8> = "abb".chars().collect(); + assert!(a.is_superset(&b)); + // Negative values are possible, a is no longer a superset + a[&'e'] = -1; + assert!(!a.is_superset(&b)); + // Adjust b to make a a superset again + b[&'e'] = -2; + assert!(a.is_superset(&b)); + } + + #[test] + fn test_subset_non_usize_count() { + let mut a: Counter<_, i8> = "abb".chars().collect(); + let mut b: Counter<_, i8> = "abbcccc".chars().collect(); + assert!(a.is_subset(&b)); + // Negative values are possible; a is no longer a subset + b[&'e'] = -1; + assert!(!a.is_subset(&b)); + // Adjust a to make it a subset again + a[&'e'] = -2; + assert!(a.is_subset(&b)); + } +} From 485c8353fe0560083f5ccdae20c77633950673ba Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:34:28 +0900 Subject: [PATCH 02/22] uncomment tests --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c6d627b..004c6a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,7 +284,8 @@ use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Index, IndexMut, Sub, SubAssign, }; -//mod unit_tests; +#[cfg(test)] +mod unit_tests; type CounterMap = HashMap; From 45988a2cb39572e1cc88a8b862cffd7fad6e8e7b Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:34:47 +0900 Subject: [PATCH 03/22] fix declaration --- src/unit_tests.rs | 438 +++++++++++++++++++++++----------------------- 1 file changed, 216 insertions(+), 222 deletions(-) diff --git a/src/unit_tests.rs b/src/unit_tests.rs index 8f9b3f3..c97a774 100644 --- a/src/unit_tests.rs +++ b/src/unit_tests.rs @@ -1,241 +1,235 @@ -#[cfg(test)] -mod tests { - use counter::Counter; - use maplit::hashmap; - use rand::Rng; - use std::collections::HashMap; - #[test] - fn test_creation() { - let _: Counter = Counter::new(); - - let initializer = &[1]; - let counter = Counter::init(initializer); - - let mut expected = HashMap::new(); - static ONE: usize = 1; - expected.insert(&ONE, 1); - assert!(counter.map == expected); - } - #[test] - fn test_update() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - counter.update("aeeeee".chars()); - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_add_update_iterable() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - counter += "aeeeee".chars(); - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } - - #[test] - fn test_add_update_counter() { - let mut counter = Counter::init("abbccc".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - - let other = Counter::init("aeeeee".chars()); - counter += other; - let expected = hashmap! { - 'a' => 2, - 'b' => 2, - 'c' => 3, - 'e' => 5, - }; - assert!(counter.map == expected); - } +use crate::Counter; +use maplit::hashmap; +use std::collections::HashMap; +#[test] +fn test_creation() { + let _: Counter = Counter::new(); + + let initializer = &[1]; + let counter = Counter::init(initializer); + + let mut expected = HashMap::new(); + static ONE: usize = 1; + expected.insert(&ONE, 1); + assert!(counter.map == expected); +} +#[test] +fn test_update() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + counter.update("aeeeee".chars()); + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); +} - #[test] - fn test_subtract() { - let mut counter = Counter::init("abbccc".chars()); - counter.subtract("bbccddd".chars()); - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } +#[test] +fn test_add_update_iterable() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + counter += "aeeeee".chars(); + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); +} - #[test] - fn test_sub_update_iterable() { - let mut counter = Counter::init("abbccc".chars()); - counter -= "bbccddd".chars(); - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } +#[test] +fn test_add_update_counter() { + let mut counter = Counter::init("abbccc".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); + + let other = Counter::init("aeeeee".chars()); + counter += other; + let expected = hashmap! { + 'a' => 2, + 'b' => 2, + 'c' => 3, + 'e' => 5, + }; + assert!(counter.map == expected); +} - #[test] - fn test_sub_update_counter() { - let mut counter = Counter::init("abbccc".chars()); - let other = Counter::init("bbccddd".chars()); - counter -= other; - let expected = hashmap! { - 'a' => 1, - 'c' => 1, - }; - assert!(counter.map == expected); - } +#[test] +fn test_subtract() { + let mut counter = Counter::init("abbccc".chars()); + counter.subtract("bbccddd".chars()); + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); +} - #[test] - fn test_from_iter_simple() { - let counter = "abbccc".chars().collect::>(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - } +#[test] +fn test_sub_update_iterable() { + let mut counter = Counter::init("abbccc".chars()); + counter -= "bbccddd".chars(); + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); +} - #[test] - fn test_from_iter_tuple() { - let items = [('a', 1), ('b', 2), ('c', 3)]; - let counter = items.iter().cloned().collect::>(); - let expected: HashMap = items.iter().cloned().collect(); - assert_eq!(counter.map, expected); - } +#[test] +fn test_sub_update_counter() { + let mut counter = Counter::init("abbccc".chars()); + let other = Counter::init("bbccddd".chars()); + counter -= other; + let expected = hashmap! { + 'a' => 1, + 'c' => 1, + }; + assert!(counter.map == expected); +} - #[test] - fn test_from_iter_tuple_with_duplicates() { - let items = [('a', 1), ('b', 2), ('c', 3)]; - let counter = items - .iter() - .cycle() - .take(items.len() * 2) - .cloned() - .collect::>(); - let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); - assert_eq!(counter.map, expected); - } +#[test] +fn test_from_iter_simple() { + let counter = "abbccc".chars().collect::>(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); +} - #[test] - fn test_extend_simple() { - let mut counter = "abbccc".chars().collect::>(); - counter.extend("bccddd".chars()); - let expected = hashmap! { - 'a' => 1, - 'b' => 3, - 'c' => 5, - 'd' => 3, - }; - assert!(counter.map == expected); - } +#[test] +fn test_from_iter_tuple() { + let items = [('a', 1), ('b', 2), ('c', 3)]; + let counter = items.iter().cloned().collect::>(); + let expected: HashMap = items.iter().cloned().collect(); + assert_eq!(counter.map, expected); +} - #[test] - fn test_extend_tuple() { - let mut counter = "bccddd".chars().collect::>(); - let items = [('a', 1), ('b', 2), ('c', 3)]; - counter.extend(items.iter().cloned()); - let expected = hashmap! { - 'a' => 1, - 'b' => 3, - 'c' => 5, - 'd' => 3, - }; - assert_eq!(counter.map, expected); - } +#[test] +fn test_from_iter_tuple_with_duplicates() { + let items = [('a', 1), ('b', 2), ('c', 3)]; + let counter = items + .iter() + .cycle() + .take(items.len() * 2) + .cloned() + .collect::>(); + let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); + assert_eq!(counter.map, expected); +} - #[test] - fn test_extend_tuple_with_duplicates() { - let mut counter = "ccc".chars().collect::>(); - let items = [('a', 1), ('b', 2), ('c', 3)]; - counter.extend(items.iter().cycle().take(items.len() * 2 - 1).cloned()); - let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); - assert_eq!(counter.map, expected); - } +#[test] +fn test_extend_simple() { + let mut counter = "abbccc".chars().collect::>(); + counter.extend("bccddd".chars()); + let expected = hashmap! { + 'a' => 1, + 'b' => 3, + 'c' => 5, + 'd' => 3, + }; + assert!(counter.map == expected); +} - #[test] - fn test_count_minimal_type() { - #[derive(Debug, Hash, PartialEq, Eq)] - struct Inty { - i: usize, - } +#[test] +fn test_extend_tuple() { + let mut counter = "bccddd".chars().collect::>(); + let items = [('a', 1), ('b', 2), ('c', 3)]; + counter.extend(items.iter().cloned()); + let expected = hashmap! { + 'a' => 1, + 'b' => 3, + 'c' => 5, + 'd' => 3, + }; + assert_eq!(counter.map, expected); +} - impl Inty { - pub fn new(i: usize) -> Inty { - Inty { i } - } - } +#[test] +fn test_extend_tuple_with_duplicates() { + let mut counter = "ccc".chars().collect::>(); + let items = [('a', 1), ('b', 2), ('c', 3)]; + counter.extend(items.iter().cycle().take(items.len() * 2 - 1).cloned()); + let expected: HashMap = items.iter().map(|(c, n)| (*c, n * 2)).collect(); + assert_eq!(counter.map, expected); +} - // - let intys = vec![ - Inty::new(8), - Inty::new(0), - Inty::new(0), - Inty::new(8), - Inty::new(6), - Inty::new(7), - Inty::new(5), - Inty::new(3), - Inty::new(0), - Inty::new(9), - ]; - - let inty_counts = Counter::init(intys); - // println!("{:?}", inty_counts.map); // test runner blanks this - // {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1, - // Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1} - assert!(inty_counts.map.get(&Inty { i: 8 }) == Some(&2)); - assert!(inty_counts.map.get(&Inty { i: 0 }) == Some(&3)); - assert!(inty_counts.map.get(&Inty { i: 6 }) == Some(&1)); +#[test] +fn test_count_minimal_type() { + #[derive(Debug, Hash, PartialEq, Eq)] + struct Inty { + i: usize, } - #[test] - fn test_collect() { - let counter: Counter<_> = "abbccc".chars().collect(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); + impl Inty { + pub fn new(i: usize) -> Inty { + Inty { i } + } } - #[test] - fn test_non_usize_count() { - let counter: Counter<_, i8> = "abbccc".chars().collect(); - let expected = hashmap! { - 'a' => 1, - 'b' => 2, - 'c' => 3, - }; - assert!(counter.map == expected); - } + // + let intys = vec![ + Inty::new(8), + Inty::new(0), + Inty::new(0), + Inty::new(8), + Inty::new(6), + Inty::new(7), + Inty::new(5), + Inty::new(3), + Inty::new(0), + Inty::new(9), + ]; + + let inty_counts = Counter::init(intys); + // println!("{:?}", inty_counts.map); // test runner blanks this + // {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1, + // Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1} + assert!(inty_counts.map.get(&Inty { i: 8 }) == Some(&2)); + assert!(inty_counts.map.get(&Inty { i: 0 }) == Some(&3)); + assert!(inty_counts.map.get(&Inty { i: 6 }) == Some(&1)); +} +#[test] +fn test_collect() { + let counter: Counter<_> = "abbccc".chars().collect(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); +} +#[test] +fn test_non_usize_count() { + let counter: Counter<_, i8> = "abbccc".chars().collect(); + let expected = hashmap! { + 'a' => 1, + 'b' => 2, + 'c' => 3, + }; + assert!(counter.map == expected); } From c9d2bf062916fe6e0c205b56292524eafb0c72d0 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:48:08 +0900 Subject: [PATCH 04/22] split out index this is the minimum necessary changes that passes all tests and checks --- src/impls.rs | 1 + src/impls/index.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 90 ++------------------------------------------- 3 files changed, 95 insertions(+), 87 deletions(-) create mode 100644 src/impls.rs create mode 100644 src/impls/index.rs diff --git a/src/impls.rs b/src/impls.rs new file mode 100644 index 0000000..58bd56b --- /dev/null +++ b/src/impls.rs @@ -0,0 +1 @@ +mod index; diff --git a/src/impls/index.rs b/src/impls/index.rs new file mode 100644 index 0000000..c375a63 --- /dev/null +++ b/src/impls/index.rs @@ -0,0 +1,91 @@ +use crate::Counter; + +use num_traits::Zero; + +use std::borrow::Borrow; +use std::hash::Hash; +use std::ops::{Index, IndexMut}; + +impl Index<&'_ Q> for Counter +where + T: Hash + Eq + Borrow, + Q: Hash + Eq, + N: Zero, +{ + type Output = N; + + /// Index in immutable contexts. + /// + /// Returns a reference to a [`zero`] value for missing keys. + /// + /// [`zero`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ``` + /// # use counter::Counter; + /// let counter = Counter::<_>::init("aabbcc".chars()); + /// assert_eq!(counter[&'a'], 2); + /// assert_eq!(counter[&'b'], 2); + /// assert_eq!(counter[&'c'], 2); + /// assert_eq!(counter[&'d'], 0); + /// ``` + /// + /// Note that the [`zero`] is a struct field but not one of the values of the inner + /// [`HashMap`]. This method does not modify any existing value. + /// + /// [`zero`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// [`HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html + /// + /// ``` + /// # use counter::Counter; + /// let counter = Counter::<_>::init("".chars()); + /// assert_eq!(counter[&'a'], 0); + /// assert_eq!(counter.get(&'a'), None); // as `Deref>` + /// ``` + fn index(&self, key: &'_ Q) -> &N { + self.map.get(key).unwrap_or(&self.zero) + } +} + +impl IndexMut<&'_ Q> for Counter +where + T: Hash + Eq + Borrow, + Q: Hash + Eq + ToOwned, + N: Zero, +{ + /// Index in mutable contexts. + /// + /// If the given key is not present, creates a new entry and initializes it with a [`zero`] + /// value. + /// + /// [`zero`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ``` + /// # use counter::Counter; + /// let mut counter = Counter::<_>::init("aabbcc".chars()); + /// counter[&'c'] += 1; + /// counter[&'d'] += 1; + /// assert_eq!(counter[&'c'], 3); + /// assert_eq!(counter[&'d'], 1); + /// ``` + /// + /// Unlike `Index::index`, the returned mutable reference to the [`zero`] is actually one of the + /// values of the inner [`HashMap`]. + /// + /// [`zero`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// [`HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html + /// + /// ``` + /// # use counter::Counter; + /// let mut counter = Counter::<_>::init("".chars()); + /// assert_eq!(counter.get(&'a'), None); // as `Deref>` + /// let _ = &mut counter[&'a']; + /// assert_eq!(counter.get(&'a'), Some(&0)); + /// ``` + fn index_mut(&mut self, key: &'_ Q) -> &mut N { + self.map.entry(key.to_owned()).or_insert_with(N::zero) + } +} diff --git a/src/lib.rs b/src/lib.rs index 004c6a8..0ebafb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,17 +276,17 @@ use num_traits::{One, Zero}; -use std::borrow::Borrow; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Index, IndexMut, - Sub, SubAssign, + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign, }; #[cfg(test)] mod unit_tests; +mod impls; + type CounterMap = HashMap; #[derive(Clone, PartialEq, Eq, Debug)] @@ -1006,90 +1006,6 @@ where } } -impl Index<&'_ Q> for Counter -where - T: Hash + Eq + Borrow, - Q: Hash + Eq, - N: Zero, -{ - type Output = N; - - /// Index in immutable contexts. - /// - /// Returns a reference to a [`zero`] value for missing keys. - /// - /// [`zero`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ``` - /// # use counter::Counter; - /// let counter = Counter::<_>::init("aabbcc".chars()); - /// assert_eq!(counter[&'a'], 2); - /// assert_eq!(counter[&'b'], 2); - /// assert_eq!(counter[&'c'], 2); - /// assert_eq!(counter[&'d'], 0); - /// ``` - /// - /// Note that the [`zero`] is a struct field but not one of the values of the inner - /// [`HashMap`]. This method does not modify any existing value. - /// - /// [`zero`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// [`HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html - /// - /// ``` - /// # use counter::Counter; - /// let counter = Counter::<_>::init("".chars()); - /// assert_eq!(counter[&'a'], 0); - /// assert_eq!(counter.get(&'a'), None); // as `Deref>` - /// ``` - fn index(&self, key: &'_ Q) -> &N { - self.map.get(key).unwrap_or(&self.zero) - } -} - -impl IndexMut<&'_ Q> for Counter -where - T: Hash + Eq + Borrow, - Q: Hash + Eq + ToOwned, - N: Zero, -{ - /// Index in mutable contexts. - /// - /// If the given key is not present, creates a new entry and initializes it with a [`zero`] - /// value. - /// - /// [`zero`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ``` - /// # use counter::Counter; - /// let mut counter = Counter::<_>::init("aabbcc".chars()); - /// counter[&'c'] += 1; - /// counter[&'d'] += 1; - /// assert_eq!(counter[&'c'], 3); - /// assert_eq!(counter[&'d'], 1); - /// ``` - /// - /// Unlike `Index::index`, the returned mutable reference to the [`zero`] is actually one of the - /// values of the inner [`HashMap`]. - /// - /// [`zero`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// [`HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html - /// - /// ``` - /// # use counter::Counter; - /// let mut counter = Counter::<_>::init("".chars()); - /// assert_eq!(counter.get(&'a'), None); // as `Deref>` - /// let _ = &mut counter[&'a']; - /// assert_eq!(counter.get(&'a'), Some(&0)); - /// ``` - fn index_mut(&mut self, key: &'_ Q) -> &mut N { - self.map.entry(key.to_owned()).or_insert_with(N::zero) - } -} - impl AddAssign for Counter where I: IntoIterator, From 927b62734c1df079974d148acb3cbd6fa63b1834 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:55:38 +0900 Subject: [PATCH 05/22] impls/add.rs --- src/impls.rs | 1 + src/impls/add.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 56 ------------------------------------------- 3 files changed, 63 insertions(+), 56 deletions(-) create mode 100644 src/impls/add.rs diff --git a/src/impls.rs b/src/impls.rs index 58bd56b..2e30b3c 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1 +1,2 @@ +mod add; mod index; diff --git a/src/impls/add.rs b/src/impls/add.rs new file mode 100644 index 0000000..33d66af --- /dev/null +++ b/src/impls/add.rs @@ -0,0 +1,62 @@ +use crate::Counter; + +use num_traits::Zero; + +use std::hash::Hash; +use std::ops::{Add, AddAssign}; + +impl AddAssign for Counter +where + T: Hash + Eq, + N: Zero + AddAssign, +{ + /// Add another counter to this counter. + /// + /// `c += d;` -> `c[x] += d[x]` for all `x` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// c += d; + /// + /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn add_assign(&mut self, rhs: Self) { + for (key, value) in rhs.map { + let entry = self.map.entry(key).or_insert_with(N::zero); + *entry += value; + } + } +} + +impl Add for Counter +where + T: Clone + Hash + Eq, + N: AddAssign + Zero, +{ + type Output = Counter; + + /// Add two counters together. + /// + /// `out = c + d;` -> `out[x] == c[x] + d[x]` for all `x` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// let e = c + d; + /// + /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn add(mut self, rhs: Counter) -> Self::Output { + self += rhs; + self + } +} diff --git a/src/lib.rs b/src/lib.rs index 0ebafb4..373cfd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -582,62 +582,6 @@ where } } -impl AddAssign for Counter -where - T: Hash + Eq, - N: Zero + AddAssign, -{ - /// Add another counter to this counter. - /// - /// `c += d;` -> `c[x] += d[x]` for all `x` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// c += d; - /// - /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn add_assign(&mut self, rhs: Self) { - for (key, value) in rhs.map { - let entry = self.map.entry(key).or_insert_with(N::zero); - *entry += value; - } - } -} - -impl Add for Counter -where - T: Clone + Hash + Eq, - N: AddAssign + Zero, -{ - type Output = Counter; - - /// Add two counters together. - /// - /// `out = c + d;` -> `out[x] == c[x] + d[x]` for all `x` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// let e = c + d; - /// - /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn add(mut self, rhs: Counter) -> Self::Output { - self += rhs; - self - } -} - impl SubAssign for Counter where T: Hash + Eq, From 5dcde63e683a4c438778717e1ce6d3fd7a185539 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:58:57 +0900 Subject: [PATCH 06/22] impls/add.rs step 2 --- src/impls/add.rs | 83 ++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 51 +---------------------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/impls/add.rs b/src/impls/add.rs index 33d66af..3a7aa1b 100644 --- a/src/impls/add.rs +++ b/src/impls/add.rs @@ -1,10 +1,38 @@ use crate::Counter; -use num_traits::Zero; +use num_traits::{One, Zero}; use std::hash::Hash; use std::ops::{Add, AddAssign}; +impl Add for Counter +where + T: Clone + Hash + Eq, + N: AddAssign + Zero, +{ + type Output = Counter; + + /// Add two counters together. + /// + /// `out = c + d;` -> `out[x] == c[x] + d[x]` for all `x` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// let e = c + d; + /// + /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn add(mut self, rhs: Counter) -> Self::Output { + self += rhs; + self + } +} + impl AddAssign for Counter where T: Hash + Eq, @@ -33,30 +61,51 @@ where } } -impl Add for Counter +impl Add for Counter where - T: Clone + Hash + Eq, - N: AddAssign + Zero, + I: IntoIterator, + T: Hash + Eq, + N: AddAssign + Zero + One, { - type Output = Counter; - - /// Add two counters together. - /// - /// `out = c + d;` -> `out[x] == c[x] + d[x]` for all `x` + type Output = Self; + /// Consume `self` producing a `Counter` like `self` updated with the counts of + /// the elements of `I`. /// /// ```rust /// # use counter::Counter; /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); + /// let counter = Counter::init("abbccc".chars()); /// - /// let e = c + d; - /// - /// let expect = [('a', 4), ('b', 3)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); + /// let new_counter = counter + "aeeeee".chars(); + /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] + /// .iter().cloned().collect(); + /// assert_eq!(new_counter.into_map(), expected); /// ``` - fn add(mut self, rhs: Counter) -> Self::Output { - self += rhs; + fn add(mut self, rhs: I) -> Self::Output { + self.update(rhs); self } } + +impl AddAssign for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Directly add the counts of the elements of `I` to `self`. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut counter = Counter::init("abbccc".chars()); + /// + /// counter += "aeeeee".chars(); + /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] + /// .iter().cloned().collect(); + /// assert_eq!(counter.into_map(), expected); + /// ``` + fn add_assign(&mut self, rhs: I) { + self.update(rhs); + } +} diff --git a/src/lib.rs b/src/lib.rs index 373cfd1..3ca771d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -280,7 +280,7 @@ use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign, + AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign, }; #[cfg(test)] mod unit_tests; @@ -950,55 +950,6 @@ where } } -impl AddAssign for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Directly add the counts of the elements of `I` to `self`. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut counter = Counter::init("abbccc".chars()); - /// - /// counter += "aeeeee".chars(); - /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] - /// .iter().cloned().collect(); - /// assert_eq!(counter.into_map(), expected); - /// ``` - fn add_assign(&mut self, rhs: I) { - self.update(rhs); - } -} - -impl Add for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - type Output = Self; - /// Consume `self` producing a `Counter` like `self` updated with the counts of - /// the elements of `I`. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = Counter::init("abbccc".chars()); - /// - /// let new_counter = counter + "aeeeee".chars(); - /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] - /// .iter().cloned().collect(); - /// assert_eq!(new_counter.into_map(), expected); - /// ``` - fn add(mut self, rhs: I) -> Self::Output { - self.update(rhs); - self - } -} - impl SubAssign for Counter where I: IntoIterator, From cb45110762eb8a91780c9943aa64b5feee80c41a Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 19:59:30 +0900 Subject: [PATCH 07/22] revert lint related change --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3ca771d..ad66492 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -336,7 +336,6 @@ where N: Zero, { /// Create a new, empty `Counter` - #[must_use] pub fn new() -> Counter { Counter { map: HashMap::new(), From a275d4bd7f29e47f9b18b0b927879a08fd64f2d1 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:03:11 +0900 Subject: [PATCH 08/22] impls/intersection.rs --- src/impls.rs | 1 + src/impls/intersection.rs | 71 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 69 +------------------------------------ 3 files changed, 73 insertions(+), 68 deletions(-) create mode 100644 src/impls/intersection.rs diff --git a/src/impls.rs b/src/impls.rs index 2e30b3c..81454ee 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,2 +1,3 @@ mod add; mod index; +mod intersection; diff --git a/src/impls/intersection.rs b/src/impls/intersection.rs new file mode 100644 index 0000000..6cb9053 --- /dev/null +++ b/src/impls/intersection.rs @@ -0,0 +1,71 @@ +use crate::Counter; + +use num_traits::Zero; + +use std::hash::Hash; +use std::ops::{BitAnd, BitAndAssign}; + +impl BitAnd for Counter +where + T: Hash + Eq, + N: Ord + Zero, +{ + type Output = Counter; + + /// Returns the intersection of `self` and `rhs` as a new `Counter`. + /// + /// `out = c & d;` -> `out[x] == min(c[x], d[x])` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// let e = c & d; + /// + /// let expect = [('a', 1), ('b', 1)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn bitand(self, mut rhs: Counter) -> Self::Output { + use std::cmp::min; + + let mut counter = Counter::new(); + for (key, lhs_count) in self.map { + if let Some(rhs_count) = rhs.remove(&key) { + let count = min(lhs_count, rhs_count); + counter.map.insert(key, count); + } + } + counter + } +} + +impl BitAndAssign for Counter +where + T: Hash + Eq, + N: Ord + Zero, +{ + /// Updates `self` with the intersection of `self` and `rhs` + /// + /// `c &= d;` -> `c[x] == min(c[x], d[x])` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// c &= d; + /// + /// let expect = [('a', 1), ('b', 1)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn bitand_assign(&mut self, mut rhs: Counter) { + for (key, rhs_count) in rhs.drain() { + if rhs_count < self[&key] { + self.map.insert(key, rhs_count); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index ad66492..f396fff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,9 +279,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{ - AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign, -}; +use std::ops::{AddAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign}; #[cfg(test)] mod unit_tests; @@ -713,71 +711,6 @@ where } } -impl BitAnd for Counter -where - T: Hash + Eq, - N: Ord + Zero, -{ - type Output = Counter; - - /// Returns the intersection of `self` and `rhs` as a new `Counter`. - /// - /// `out = c & d;` -> `out[x] == min(c[x], d[x])` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// let e = c & d; - /// - /// let expect = [('a', 1), ('b', 1)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn bitand(self, mut rhs: Counter) -> Self::Output { - use std::cmp::min; - - let mut counter = Counter::new(); - for (key, lhs_count) in self.map { - if let Some(rhs_count) = rhs.remove(&key) { - let count = min(lhs_count, rhs_count); - counter.map.insert(key, count); - } - } - counter - } -} - -impl BitAndAssign for Counter -where - T: Hash + Eq, - N: Ord + Zero, -{ - /// Updates `self` with the intersection of `self` and `rhs` - /// - /// `c &= d;` -> `c[x] == min(c[x], d[x])` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// c &= d; - /// - /// let expect = [('a', 1), ('b', 1)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn bitand_assign(&mut self, mut rhs: Counter) { - for (key, rhs_count) in rhs.drain() { - if rhs_count < self[&key] { - self.map.insert(key, rhs_count); - } - } - } -} - impl BitOr for Counter where T: Hash + Eq, From b7e584e8a92e0c09ae4e333670051c54b0679012 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:08:04 +0900 Subject: [PATCH 09/22] impls/union.rs --- src/impls.rs | 1 + src/impls/union.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 80 +------------------------------------------ 3 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 src/impls/union.rs diff --git a/src/impls.rs b/src/impls.rs index 81454ee..7b8a7a8 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,3 +1,4 @@ mod add; mod index; mod intersection; +mod union; diff --git a/src/impls/union.rs b/src/impls/union.rs new file mode 100644 index 0000000..2bb1b7b --- /dev/null +++ b/src/impls/union.rs @@ -0,0 +1,84 @@ +use crate::Counter; + +use num_traits::Zero; + +use std::hash::Hash; +use std::ops::{BitOr, BitOrAssign}; + +impl BitOr for Counter +where + T: Hash + Eq, + N: Ord + Zero, +{ + type Output = Counter; + + /// Returns the union of `self` and `rhs` as a new `Counter`. + /// + /// `out = c | d;` -> `out[x] == max(c[x], d[x])` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// let e = c | d; + /// + /// let expect = [('a', 3), ('b', 2)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn bitor(mut self, rhs: Counter) -> Self::Output { + for (key, rhs_value) in rhs.map { + let entry = self.map.entry(key).or_insert_with(N::zero); + // We want to update the value of the now occupied entry in `self` with the maximum of + // its current value and `rhs_value`. If that max is `rhs_value`, we can just update + // the value of the entry. If the max is the current value, we do nothing. Note that + // `Ord::max()` returns the second argument (here `rhs_value`) if its two arguments are + // equal, justifying the use of the weak inequality below instead of a strict + // inequality. + // + // Doing it this way with an inequality instead of actually using `std::cmp::max()` + // lets us avoid trying (and failing) to move the non-copy value out of the entry in + // order to pass it as an argument to `std::cmp::max()`, while still holding a mutable + // reference to the value slot in the entry. + // + // And while using the inequality seemingly only requires the bound `N: PartialOrd`, we + // nevertheless prefer to require `Ord` as though we were using `std::cmp::max()` + // because the semantics of `BitOr` for `Counter` really do not make sense if there are + // possibly non-comparable values of type `N`. + if rhs_value >= *entry { + *entry = rhs_value; + } + } + self + } +} + +impl BitOrAssign for Counter +where + T: Hash + Eq, + N: Ord + Zero, +{ + /// Updates `self` with the union of `self` and `rhs` + /// + /// `c |= d;` -> `c[x] == max(c[x], d[x])` + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// c |= d; + /// + /// let expect = [('a', 3), ('b', 2)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn bitor_assign(&mut self, mut rhs: Counter) { + for (key, rhs_count) in rhs.drain() { + if rhs_count > self[&key] { + self.map.insert(key, rhs_count); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f396fff..48ece13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{AddAssign, BitOr, BitOrAssign, Deref, DerefMut, Sub, SubAssign}; +use std::ops::{AddAssign, Deref, DerefMut, Sub, SubAssign}; #[cfg(test)] mod unit_tests; @@ -711,84 +711,6 @@ where } } -impl BitOr for Counter -where - T: Hash + Eq, - N: Ord + Zero, -{ - type Output = Counter; - - /// Returns the union of `self` and `rhs` as a new `Counter`. - /// - /// `out = c | d;` -> `out[x] == max(c[x], d[x])` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// let e = c | d; - /// - /// let expect = [('a', 3), ('b', 2)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn bitor(mut self, rhs: Counter) -> Self::Output { - for (key, rhs_value) in rhs.map { - let entry = self.map.entry(key).or_insert_with(N::zero); - // We want to update the value of the now occupied entry in `self` with the maximum of - // its current value and `rhs_value`. If that max is `rhs_value`, we can just update - // the value of the entry. If the max is the current value, we do nothing. Note that - // `Ord::max()` returns the second argument (here `rhs_value`) if its two arguments are - // equal, justifying the use of the weak inequality below instead of a strict - // inequality. - // - // Doing it this way with an inequality instead of actually using `std::cmp::max()` - // lets us avoid trying (and failing) to move the non-copy value out of the entry in - // order to pass it as an argument to `std::cmp::max()`, while still holding a mutable - // reference to the value slot in the entry. - // - // And while using the inequality seemingly only requires the bound `N: PartialOrd`, we - // nevertheless prefer to require `Ord` as though we were using `std::cmp::max()` - // because the semantics of `BitOr` for `Counter` really do not make sense if there are - // possibly non-comparable values of type `N`. - if rhs_value >= *entry { - *entry = rhs_value; - } - } - self - } -} - -impl BitOrAssign for Counter -where - T: Hash + Eq, - N: Ord + Zero, -{ - /// Updates `self` with the union of `self` and `rhs` - /// - /// `c |= d;` -> `c[x] == max(c[x], d[x])` - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// c |= d; - /// - /// let expect = [('a', 3), ('b', 2)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn bitor_assign(&mut self, mut rhs: Counter) { - for (key, rhs_count) in rhs.drain() { - if rhs_count > self[&key] { - self.map.insert(key, rhs_count); - } - } - } -} - impl Deref for Counter where T: Hash + Eq, From 0c467d09b344aee2c045772191830abd30250f05 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:13:12 +0900 Subject: [PATCH 10/22] impls/sub.rs step 1 step 2 will split out iterator based parts --- src/impls.rs | 1 + src/impls/sub.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 129 +-------------------------------------------- 3 files changed, 135 insertions(+), 128 deletions(-) create mode 100644 src/impls/sub.rs diff --git a/src/impls.rs b/src/impls.rs index 7b8a7a8..c496618 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,4 +1,5 @@ mod add; mod index; mod intersection; +mod sub; mod union; diff --git a/src/impls/sub.rs b/src/impls/sub.rs new file mode 100644 index 0000000..9413dcf --- /dev/null +++ b/src/impls/sub.rs @@ -0,0 +1,133 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::ops::{Sub, SubAssign}; + +impl Sub for Counter +where + T: Hash + Eq, + N: PartialOrd + PartialEq + SubAssign + Zero, +{ + type Output = Counter; + + /// Subtract (keeping only positive values). + /// + /// `out = c - d;` -> `out[x] == c[x] - d[x]` for all `x`, + /// keeping only items with a value greater than [`N::zero()`]. + /// + /// [`N::zero()`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// let e = c - d; + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn sub(mut self, rhs: Counter) -> Self::Output { + self -= rhs; + self + } +} + +impl SubAssign for Counter +where + T: Hash + Eq, + N: PartialOrd + PartialEq + SubAssign + Zero, +{ + /// Subtract (keeping only positive values). + /// + /// `c -= d;` -> `c[x] -= d[x]` for all `x`, + /// keeping only items with a value greater than [`N::zero()`]. + /// + /// [`N::zero()`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// let d = "abb".chars().collect::>(); + /// + /// c -= d; + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn sub_assign(&mut self, rhs: Self) { + for (key, value) in rhs.map { + let mut remove = false; + if let Some(entry) = self.map.get_mut(&key) { + if *entry >= value { + *entry -= value; + } else { + remove = true; + } + if *entry == N::zero() { + remove = true; + } + } + if remove { + self.map.remove(&key); + } + } + } +} + +impl Sub for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: PartialOrd + SubAssign + Zero + One, +{ + type Output = Self; + /// Consume `self` producing a `Counter` like `self` with the counts of the + /// elements of `I` subtracted, keeping only positive values. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let e = c - "abb".chars(); + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn sub(mut self, rhs: I) -> Self::Output { + self.subtract(rhs); + self + } +} + +impl SubAssign for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: PartialOrd + SubAssign + Zero + One, +{ + /// Directly subtract the counts of the elements of `I` from `self`, + /// keeping only items with a value greater than [`N::zero()`]. + /// + /// [`N::zero()`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// c -= "abb".chars(); + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn sub_assign(&mut self, rhs: I) { + self.subtract(rhs); + } +} diff --git a/src/lib.rs b/src/lib.rs index 48ece13..2f35cec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{AddAssign, Deref, DerefMut, Sub, SubAssign}; +use std::ops::{AddAssign, Deref, DerefMut, SubAssign}; #[cfg(test)] mod unit_tests; @@ -579,82 +579,6 @@ where } } -impl SubAssign for Counter -where - T: Hash + Eq, - N: PartialOrd + PartialEq + SubAssign + Zero, -{ - /// Subtract (keeping only positive values). - /// - /// `c -= d;` -> `c[x] -= d[x]` for all `x`, - /// keeping only items with a value greater than [`N::zero()`]. - /// - /// [`N::zero()`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// c -= d; - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn sub_assign(&mut self, rhs: Self) { - for (key, value) in rhs.map { - let mut remove = false; - if let Some(entry) = self.map.get_mut(&key) { - if *entry >= value { - *entry -= value; - } else { - remove = true; - } - if *entry == N::zero() { - remove = true; - } - } - if remove { - self.map.remove(&key); - } - } - } -} - -impl Sub for Counter -where - T: Hash + Eq, - N: PartialOrd + PartialEq + SubAssign + Zero, -{ - type Output = Counter; - - /// Subtract (keeping only positive values). - /// - /// `out = c - d;` -> `out[x] == c[x] - d[x]` for all `x`, - /// keeping only items with a value greater than [`N::zero()`]. - /// - /// [`N::zero()`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let d = "abb".chars().collect::>(); - /// - /// let e = c - d; - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn sub(mut self, rhs: Counter) -> Self::Output { - self -= rhs; - self - } -} - impl Counter where T: Hash + Eq, @@ -804,57 +728,6 @@ where } } -impl SubAssign for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: PartialOrd + SubAssign + Zero + One, -{ - /// Directly subtract the counts of the elements of `I` from `self`, - /// keeping only items with a value greater than [`N::zero()`]. - /// - /// [`N::zero()`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// c -= "abb".chars(); - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn sub_assign(&mut self, rhs: I) { - self.subtract(rhs); - } -} - -impl Sub for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: PartialOrd + SubAssign + Zero + One, -{ - type Output = Self; - /// Consume `self` producing a `Counter` like `self` with the counts of the - /// elements of `I` subtracted, keeping only positive values. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let e = c - "abb".chars(); - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn sub(mut self, rhs: I) -> Self::Output { - self.subtract(rhs); - self - } -} - impl iter::FromIterator for Counter where T: Hash + Eq, From 76304ed0c29d7bcf7d6a8dab4562df39a2f3d87d Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:16:11 +0900 Subject: [PATCH 11/22] split sub.rs into _iterable and _self --- src/impls.rs | 3 +- src/impls/sub_iterable.rs | 57 +++++++++++++++++++++++++++++++ src/impls/{sub.rs => sub_self.rs} | 53 +--------------------------- 3 files changed, 60 insertions(+), 53 deletions(-) create mode 100644 src/impls/sub_iterable.rs rename src/impls/{sub.rs => sub_self.rs} (59%) diff --git a/src/impls.rs b/src/impls.rs index c496618..4b6e6bd 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,5 +1,6 @@ mod add; mod index; mod intersection; -mod sub; +mod sub_iterable; +mod sub_self; mod union; diff --git a/src/impls/sub_iterable.rs b/src/impls/sub_iterable.rs new file mode 100644 index 0000000..15e5f81 --- /dev/null +++ b/src/impls/sub_iterable.rs @@ -0,0 +1,57 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::ops::{Sub, SubAssign}; + +impl Sub for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: PartialOrd + SubAssign + Zero + One, +{ + type Output = Self; + /// Consume `self` producing a `Counter` like `self` with the counts of the + /// elements of `I` subtracted, keeping only positive values. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let c = "aaab".chars().collect::>(); + /// let e = c - "abb".chars(); + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(e.into_map(), expect); + /// ``` + fn sub(mut self, rhs: I) -> Self::Output { + self.subtract(rhs); + self + } +} + +impl SubAssign for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: PartialOrd + SubAssign + Zero + One, +{ + /// Directly subtract the counts of the elements of `I` from `self`, + /// keeping only items with a value greater than [`N::zero()`]. + /// + /// [`N::zero()`]: + /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut c = "aaab".chars().collect::>(); + /// c -= "abb".chars(); + /// + /// let expect = [('a', 2)].iter().cloned().collect::>(); + /// assert_eq!(c.into_map(), expect); + /// ``` + fn sub_assign(&mut self, rhs: I) { + self.subtract(rhs); + } +} diff --git a/src/impls/sub.rs b/src/impls/sub_self.rs similarity index 59% rename from src/impls/sub.rs rename to src/impls/sub_self.rs index 9413dcf..a213527 100644 --- a/src/impls/sub.rs +++ b/src/impls/sub_self.rs @@ -1,6 +1,6 @@ use crate::Counter; -use num_traits::{One, Zero}; +use num_traits::Zero; use std::hash::Hash; use std::ops::{Sub, SubAssign}; @@ -80,54 +80,3 @@ where } } } - -impl Sub for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: PartialOrd + SubAssign + Zero + One, -{ - type Output = Self; - /// Consume `self` producing a `Counter` like `self` with the counts of the - /// elements of `I` subtracted, keeping only positive values. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let c = "aaab".chars().collect::>(); - /// let e = c - "abb".chars(); - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(e.into_map(), expect); - /// ``` - fn sub(mut self, rhs: I) -> Self::Output { - self.subtract(rhs); - self - } -} - -impl SubAssign for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: PartialOrd + SubAssign + Zero + One, -{ - /// Directly subtract the counts of the elements of `I` from `self`, - /// keeping only items with a value greater than [`N::zero()`]. - /// - /// [`N::zero()`]: - /// https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut c = "aaab".chars().collect::>(); - /// c -= "abb".chars(); - /// - /// let expect = [('a', 2)].iter().cloned().collect::>(); - /// assert_eq!(c.into_map(), expect); - /// ``` - fn sub_assign(&mut self, rhs: I) { - self.subtract(rhs); - } -} From 2414e09fe93a9e61648a413f0e7d223c05d72e7d Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:19:11 +0900 Subject: [PATCH 12/22] split add.rs into _iterable and _self --- src/impls.rs | 3 +- src/impls/add_iterable.rs | 55 +++++++++++++++++++++++++++++++ src/impls/{add.rs => add_self.rs} | 51 +--------------------------- 3 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 src/impls/add_iterable.rs rename src/impls/{add.rs => add_self.rs} (51%) diff --git a/src/impls.rs b/src/impls.rs index 4b6e6bd..29ee5e5 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,4 +1,5 @@ -mod add; +mod add_iterable; +mod add_self; mod index; mod intersection; mod sub_iterable; diff --git a/src/impls/add_iterable.rs b/src/impls/add_iterable.rs new file mode 100644 index 0000000..5ea08d2 --- /dev/null +++ b/src/impls/add_iterable.rs @@ -0,0 +1,55 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::ops::{Add, AddAssign}; + +impl Add for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + type Output = Self; + /// Consume `self` producing a `Counter` like `self` updated with the counts of + /// the elements of `I`. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let counter = Counter::init("abbccc".chars()); + /// + /// let new_counter = counter + "aeeeee".chars(); + /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] + /// .iter().cloned().collect(); + /// assert_eq!(new_counter.into_map(), expected); + /// ``` + fn add(mut self, rhs: I) -> Self::Output { + self.update(rhs); + self + } +} + +impl AddAssign for Counter +where + I: IntoIterator, + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Directly add the counts of the elements of `I` to `self`. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut counter = Counter::init("abbccc".chars()); + /// + /// counter += "aeeeee".chars(); + /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] + /// .iter().cloned().collect(); + /// assert_eq!(counter.into_map(), expected); + /// ``` + fn add_assign(&mut self, rhs: I) { + self.update(rhs); + } +} diff --git a/src/impls/add.rs b/src/impls/add_self.rs similarity index 51% rename from src/impls/add.rs rename to src/impls/add_self.rs index 3a7aa1b..ed2b38a 100644 --- a/src/impls/add.rs +++ b/src/impls/add_self.rs @@ -1,6 +1,6 @@ use crate::Counter; -use num_traits::{One, Zero}; +use num_traits::Zero; use std::hash::Hash; use std::ops::{Add, AddAssign}; @@ -60,52 +60,3 @@ where } } } - -impl Add for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - type Output = Self; - /// Consume `self` producing a `Counter` like `self` updated with the counts of - /// the elements of `I`. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = Counter::init("abbccc".chars()); - /// - /// let new_counter = counter + "aeeeee".chars(); - /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] - /// .iter().cloned().collect(); - /// assert_eq!(new_counter.into_map(), expected); - /// ``` - fn add(mut self, rhs: I) -> Self::Output { - self.update(rhs); - self - } -} - -impl AddAssign for Counter -where - I: IntoIterator, - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Directly add the counts of the elements of `I` to `self`. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut counter = Counter::init("abbccc".chars()); - /// - /// counter += "aeeeee".chars(); - /// let expected: HashMap = [('a', 2), ('b', 2), ('c', 3), ('e', 5)] - /// .iter().cloned().collect(); - /// assert_eq!(counter.into_map(), expected); - /// ``` - fn add_assign(&mut self, rhs: I) { - self.update(rhs); - } -} From ab9a7ce4d591af10ee2a27c4e6b37601b4b091f7 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:51:42 +0900 Subject: [PATCH 13/22] impls/extend.rs --- src/impls.rs | 1 + src/impls/extend.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 71 ----------------------------------------- 3 files changed, 78 insertions(+), 71 deletions(-) create mode 100644 src/impls/extend.rs diff --git a/src/impls.rs b/src/impls.rs index 29ee5e5..ab085f7 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,5 +1,6 @@ mod add_iterable; mod add_self; +mod extend; mod index; mod intersection; mod sub_iterable; diff --git a/src/impls/extend.rs b/src/impls/extend.rs new file mode 100644 index 0000000..a6c08b5 --- /dev/null +++ b/src/impls/extend.rs @@ -0,0 +1,77 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::ops::AddAssign; + +impl Extend for Counter +where + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Extend a `Counter` with an iterator of items. + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut counter = "abbccc".chars().collect::>(); + /// counter.extend("bccddd".chars()); + /// let expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter().cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + fn extend>(&mut self, iter: I) { + self.update(iter); + } +} + +impl Extend<(T, N)> for Counter +where + T: Hash + Eq, + N: AddAssign + Zero, +{ + /// Extend a counter with `(item, count)` tuples. + /// + /// The counts of duplicate items are summed. + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut counter = "abbccc".chars().collect::>(); + /// counter.extend([('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter().cloned()); + /// let expect = [('a', 6), ('b', 4), ('c', 6)].iter() + /// .cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + fn extend>(&mut self, iter: I) { + for (item, item_count) in iter { + let entry = self.map.entry(item).or_insert_with(N::zero); + *entry += item_count; + } + } +} + +impl<'a, T: 'a, N: 'a> Extend<(&'a T, &'a N)> for Counter +where + T: Hash + Eq + Clone, + N: AddAssign + Zero + Clone, +{ + /// Extend a counter with `(item, count)` tuples. + /// + /// You can extend a `Counter` with another `Counter`: + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let mut counter = "abbccc".chars().collect::>(); + /// let another = "bccddd".chars().collect::>(); + /// counter.extend(&another); + /// let expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter() + /// .cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + fn extend>(&mut self, iter: I) { + for (item, item_count) in iter { + let entry = self.map.entry(item.clone()).or_insert_with(N::zero); + *entry += item_count.clone(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2f35cec..f577b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -778,74 +778,3 @@ where cnt } } - -impl Extend for Counter -where - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Extend a `Counter` with an iterator of items. - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut counter = "abbccc".chars().collect::>(); - /// counter.extend("bccddd".chars()); - /// let expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter().cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - fn extend>(&mut self, iter: I) { - self.update(iter); - } -} - -impl Extend<(T, N)> for Counter -where - T: Hash + Eq, - N: AddAssign + Zero, -{ - /// Extend a counter with `(item, count)` tuples. - /// - /// The counts of duplicate items are summed. - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut counter = "abbccc".chars().collect::>(); - /// counter.extend([('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter().cloned()); - /// let expect = [('a', 6), ('b', 4), ('c', 6)].iter() - /// .cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - fn extend>(&mut self, iter: I) { - for (item, item_count) in iter { - let entry = self.map.entry(item).or_insert_with(N::zero); - *entry += item_count; - } - } -} - -impl<'a, T: 'a, N: 'a> Extend<(&'a T, &'a N)> for Counter -where - T: Hash + Eq + Clone, - N: AddAssign + Zero + Clone, -{ - /// Extend a counter with `(item, count)` tuples. - /// - /// You can extend a `Counter` with another `Counter`: - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let mut counter = "abbccc".chars().collect::>(); - /// let another = "bccddd".chars().collect::>(); - /// counter.extend(&another); - /// let expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter() - /// .cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - fn extend>(&mut self, iter: I) { - for (item, item_count) in iter { - let entry = self.map.entry(item.clone()).or_insert_with(N::zero); - *entry += item_count.clone(); - } - } -} From c47b6ff3e7fefe2fc47a6a1b3d3d133e3b4c594e Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 20:56:14 +0900 Subject: [PATCH 14/22] impls/create.rs This one is difficult since there are several `impl Counter` blocks that also create new counters. --- src/impls.rs | 1 + src/impls/create.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 64 ---------------------------------------- 3 files changed, 72 insertions(+), 64 deletions(-) create mode 100644 src/impls/create.rs diff --git a/src/impls.rs b/src/impls.rs index ab085f7..4be2ec5 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,5 +1,6 @@ mod add_iterable; mod add_self; +mod create; mod extend; mod index; mod intersection; diff --git a/src/impls/create.rs b/src/impls/create.rs new file mode 100644 index 0000000..b93562d --- /dev/null +++ b/src/impls/create.rs @@ -0,0 +1,71 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::iter; +use std::ops::AddAssign; + +impl Default for Counter +where + T: Hash + Eq, + N: Default, +{ + fn default() -> Self { + Self { + map: Default::default(), + zero: Default::default(), + } + } +} + +impl iter::FromIterator for Counter +where + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Produce a `Counter` from an iterator of items. This is called automatically + /// by [`Iterator::collect()`]. + /// + /// [`Iterator::collect()`]: + /// https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let counter = "abbccc".chars().collect::>(); + /// let expect = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + /// + fn from_iter>(iter: I) -> Self { + Counter::::init(iter) + } +} + +impl iter::FromIterator<(T, N)> for Counter +where + T: Hash + Eq, + N: AddAssign + Zero, +{ + /// Creates a counter from `(item, count)` tuples. + /// + /// The counts of duplicate items are summed. + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let counter = [('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter() + /// .cloned().collect::>(); + /// let expect = [('a', 5), ('b', 2), ('c', 3)].iter() + /// .cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + fn from_iter>(iter: I) -> Self { + let mut cnt = Counter::new(); + for (item, item_count) in iter { + let entry = cnt.map.entry(item).or_insert_with(N::zero); + *entry += item_count; + } + cnt + } +} diff --git a/src/lib.rs b/src/lib.rs index f577b72..e68bbde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -566,19 +566,6 @@ where } } -impl Default for Counter -where - T: Hash + Eq, - N: Default, -{ - fn default() -> Self { - Self { - map: Default::default(), - zero: Default::default(), - } - } -} - impl Counter where T: Hash + Eq, @@ -727,54 +714,3 @@ where self.map.iter_mut() } } - -impl iter::FromIterator for Counter -where - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Produce a `Counter` from an iterator of items. This is called automatically - /// by [`Iterator::collect()`]. - /// - /// [`Iterator::collect()`]: - /// https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = "abbccc".chars().collect::>(); - /// let expect = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - /// - fn from_iter>(iter: I) -> Self { - Counter::::init(iter) - } -} - -impl iter::FromIterator<(T, N)> for Counter -where - T: Hash + Eq, - N: AddAssign + Zero, -{ - /// Creates a counter from `(item, count)` tuples. - /// - /// The counts of duplicate items are summed. - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = [('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter() - /// .cloned().collect::>(); - /// let expect = [('a', 5), ('b', 2), ('c', 3)].iter() - /// .cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - fn from_iter>(iter: I) -> Self { - let mut cnt = Counter::new(); - for (item, item_count) in iter { - let entry = cnt.map.entry(item).or_insert_with(N::zero); - *entry += item_count; - } - cnt - } -} From c2c7e1305f4aff6c1d88e3a2c05061c84a5bb4ce Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:02:42 +0900 Subject: [PATCH 15/22] impls/into_iter.rs --- src/impls.rs | 1 + src/impls/into_iter.rs | 77 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 74 ---------------------------------------- 3 files changed, 78 insertions(+), 74 deletions(-) create mode 100644 src/impls/into_iter.rs diff --git a/src/impls.rs b/src/impls.rs index 4be2ec5..74c7cee 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -4,6 +4,7 @@ mod create; mod extend; mod index; mod intersection; +mod into_iter; mod sub_iterable; mod sub_self; mod union; diff --git a/src/impls/into_iter.rs b/src/impls/into_iter.rs new file mode 100644 index 0000000..634ba04 --- /dev/null +++ b/src/impls/into_iter.rs @@ -0,0 +1,77 @@ +use crate::Counter; + +use std::hash::Hash; + +impl<'a, T, N> IntoIterator for &'a Counter +where + T: Hash + Eq, +{ + type Item = (&'a T, &'a N); + type IntoIter = std::collections::hash_map::Iter<'a, T, N>; + + fn into_iter(self) -> Self::IntoIter { + self.map.iter() + } +} + +impl IntoIterator for Counter +where + T: Hash + Eq, +{ + type Item = (T, N); + type IntoIter = std::collections::hash_map::IntoIter; + + /// Consumes the `Counter` to produce an iterator that owns the values it returns. + /// + /// # Examples + /// ```rust + /// # use counter::Counter; + /// + /// let counter: Counter<_> = "aaab".chars().collect(); + /// + /// let vec: Vec<_> = counter.into_iter().collect(); + /// + /// for (item, count) in &vec { + /// if item == &'a' { + /// assert_eq!(count, &3); + /// } + /// if item == &'b' { + /// assert_eq!(count, &1); + /// } + /// } + /// ``` + + fn into_iter(self) -> Self::IntoIter { + self.map.into_iter() + } +} + +impl<'a, T, N> IntoIterator for &'a mut Counter +where + T: Hash + Eq, +{ + type Item = (&'a T, &'a mut N); + type IntoIter = std::collections::hash_map::IterMut<'a, T, N>; + + /// Creates an iterator that provides mutable references to the counts, but keeps the keys immutable. + /// + /// # Examples + /// ```rust + /// # use counter::Counter; + /// + /// let mut counter: Counter<_> = "aaab".chars().collect(); + /// + /// for (item, count) in &mut counter { + /// if *item == 'a' { + /// // 'a' is so great it counts as 2 + /// *count *= 2; + /// } + /// } + /// + /// assert_eq!(counter[&'a'], 6); + /// assert_eq!(counter[&'b'], 1); + /// ``` + fn into_iter(self) -> Self::IntoIter { + self.map.iter_mut() + } +} diff --git a/src/lib.rs b/src/lib.rs index e68bbde..2688ea9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -640,77 +640,3 @@ where &mut self.map } } - -impl<'a, T, N> IntoIterator for &'a Counter -where - T: Hash + Eq, -{ - type Item = (&'a T, &'a N); - type IntoIter = std::collections::hash_map::Iter<'a, T, N>; - - fn into_iter(self) -> Self::IntoIter { - self.map.iter() - } -} - -impl IntoIterator for Counter -where - T: Hash + Eq, -{ - type Item = (T, N); - type IntoIter = std::collections::hash_map::IntoIter; - - /// Consumes the `Counter` to produce an iterator that owns the values it returns. - /// - /// # Examples - /// ```rust - /// # use counter::Counter; - /// - /// let counter: Counter<_> = "aaab".chars().collect(); - /// - /// let vec: Vec<_> = counter.into_iter().collect(); - /// - /// for (item, count) in &vec { - /// if item == &'a' { - /// assert_eq!(count, &3); - /// } - /// if item == &'b' { - /// assert_eq!(count, &1); - /// } - /// } - /// ``` - - fn into_iter(self) -> Self::IntoIter { - self.map.into_iter() - } -} - -impl<'a, T, N> IntoIterator for &'a mut Counter -where - T: Hash + Eq, -{ - type Item = (&'a T, &'a mut N); - type IntoIter = std::collections::hash_map::IterMut<'a, T, N>; - - /// Creates an iterator that provides mutable references to the counts, but keeps the keys immutable. - /// - /// # Examples - /// ```rust - /// # use counter::Counter; - /// - /// let mut counter: Counter<_> = "aaab".chars().collect(); - /// - /// for (item, count) in &mut counter { - /// if *item == 'a' { - /// // 'a' is so great it counts as 2 - /// *count *= 2; - /// } - /// } - /// - /// assert_eq!(counter[&'a'], 6); - /// assert_eq!(counter[&'b'], 1); - /// ``` - fn into_iter(self) -> Self::IntoIter { - self.map.iter_mut() - } -} From ac1b595419bb6bf6a2eab1de60de4ee71c70d58f Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:16:04 +0900 Subject: [PATCH 16/22] impls/deref.rs --- src/impls.rs | 1 + src/impls/deref.rs | 26 ++++++++++++++++++++++++++ src/lib.rs | 21 +-------------------- 3 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 src/impls/deref.rs diff --git a/src/impls.rs b/src/impls.rs index 74c7cee..a26c57c 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,6 +1,7 @@ mod add_iterable; mod add_self; mod create; +mod deref; mod extend; mod index; mod intersection; diff --git a/src/impls/deref.rs b/src/impls/deref.rs new file mode 100644 index 0000000..464c980 --- /dev/null +++ b/src/impls/deref.rs @@ -0,0 +1,26 @@ +use crate::Counter; + +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +type CounterMap = HashMap; + +impl Deref for Counter +where + T: Hash + Eq, +{ + type Target = CounterMap; + fn deref(&self) -> &CounterMap { + &self.map + } +} + +impl DerefMut for Counter +where + T: Hash + Eq, +{ + fn deref_mut(&mut self) -> &mut CounterMap { + &mut self.map + } +} diff --git a/src/lib.rs b/src/lib.rs index 2688ea9..b68af05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{AddAssign, Deref, DerefMut, SubAssign}; +use std::ops::{AddAssign, SubAssign}; #[cfg(test)] mod unit_tests; @@ -621,22 +621,3 @@ where .all(|key| self[key] <= other[key]) } } - -impl Deref for Counter -where - T: Hash + Eq, -{ - type Target = CounterMap; - fn deref(&self) -> &CounterMap { - &self.map - } -} - -impl DerefMut for Counter -where - T: Hash + Eq, -{ - fn deref_mut(&mut self) -> &mut CounterMap { - &mut self.map - } -} From 3c4a5f320cb7caaefec86ea2e85db1e195d0e17b Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:19:33 +0900 Subject: [PATCH 17/22] reorg line --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b68af05..b9033a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -274,6 +274,8 @@ //! assert!(counter.into_map() == expected); //! ``` +mod impls; + use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; @@ -283,8 +285,6 @@ use std::ops::{AddAssign, SubAssign}; #[cfg(test)] mod unit_tests; -mod impls; - type CounterMap = HashMap; #[derive(Clone, PartialEq, Eq, Debug)] From b66bdc73bf20caeb6c16aefd42b7bfa20bed7308 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:39:49 +0900 Subject: [PATCH 18/22] move creation related impl --- src/impls.rs | 1 + src/impls/create.rs | 81 +++++++++++++++++--------------------- src/impls/from_iterator.rs | 58 +++++++++++++++++++++++++++ src/lib.rs | 41 +------------------ 4 files changed, 97 insertions(+), 84 deletions(-) create mode 100644 src/impls/from_iterator.rs diff --git a/src/impls.rs b/src/impls.rs index a26c57c..449dc5e 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -3,6 +3,7 @@ mod add_self; mod create; mod deref; mod extend; +mod from_iterator; mod index; mod intersection; mod into_iter; diff --git a/src/impls/create.rs b/src/impls/create.rs index b93562d..89ab716 100644 --- a/src/impls/create.rs +++ b/src/impls/create.rs @@ -2,70 +2,61 @@ use crate::Counter; use num_traits::{One, Zero}; -use std::hash::Hash; -use std::iter; use std::ops::AddAssign; +use std::collections::HashMap; +use std::hash::Hash; -impl Default for Counter +impl Counter where T: Hash + Eq, - N: Default, + N: Zero, { - fn default() -> Self { - Self { - map: Default::default(), - zero: Default::default(), + /// Create a new, empty `Counter` + pub fn new() -> Counter { + Counter { + map: HashMap::new(), + zero: N::zero(), } } } -impl iter::FromIterator for Counter +impl Counter where T: Hash + Eq, N: AddAssign + Zero + One, { - /// Produce a `Counter` from an iterator of items. This is called automatically - /// by [`Iterator::collect()`]. - /// - /// [`Iterator::collect()`]: - /// https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect - /// - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = "abbccc".chars().collect::>(); - /// let expect = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - /// - fn from_iter>(iter: I) -> Self { - Counter::::init(iter) + /// Create a new `Counter` initialized with the given iterable. + pub fn init(iterable: I) -> Counter + where + I: IntoIterator, + { + let mut counter = Counter::new(); + counter.update(iterable); + counter + } + + /// Add the counts of the elements from the given iterable to this counter. + pub fn update(&mut self, iterable: I) + where + I: IntoIterator, + { + for item in iterable { + let entry = self.map.entry(item).or_insert_with(N::zero); + *entry += N::one(); + } } } -impl iter::FromIterator<(T, N)> for Counter + +impl Default for Counter where T: Hash + Eq, - N: AddAssign + Zero, + N: Default, { - /// Creates a counter from `(item, count)` tuples. - /// - /// The counts of duplicate items are summed. - /// ```rust - /// # use counter::Counter; - /// # use std::collections::HashMap; - /// let counter = [('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter() - /// .cloned().collect::>(); - /// let expect = [('a', 5), ('b', 2), ('c', 3)].iter() - /// .cloned().collect::>(); - /// assert_eq!(counter.into_map(), expect); - /// ``` - fn from_iter>(iter: I) -> Self { - let mut cnt = Counter::new(); - for (item, item_count) in iter { - let entry = cnt.map.entry(item).or_insert_with(N::zero); - *entry += item_count; + fn default() -> Self { + Self { + map: Default::default(), + zero: Default::default(), } - cnt } } diff --git a/src/impls/from_iterator.rs b/src/impls/from_iterator.rs new file mode 100644 index 0000000..ef441d7 --- /dev/null +++ b/src/impls/from_iterator.rs @@ -0,0 +1,58 @@ +use crate::Counter; + +use num_traits::{One, Zero}; + +use std::hash::Hash; +use std::iter; +use std::ops::AddAssign; + +impl iter::FromIterator for Counter +where + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Produce a `Counter` from an iterator of items. This is called automatically + /// by [`Iterator::collect()`]. + /// + /// [`Iterator::collect()`]: + /// https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect + /// + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let counter = "abbccc".chars().collect::>(); + /// let expect = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + /// + fn from_iter>(iter: I) -> Self { + Counter::::init(iter) + } +} + +impl iter::FromIterator<(T, N)> for Counter +where + T: Hash + Eq, + N: AddAssign + Zero, +{ + /// Creates a counter from `(item, count)` tuples. + /// + /// The counts of duplicate items are summed. + /// ```rust + /// # use counter::Counter; + /// # use std::collections::HashMap; + /// let counter = [('a', 1), ('b', 2), ('c', 3), ('a', 4)].iter() + /// .cloned().collect::>(); + /// let expect = [('a', 5), ('b', 2), ('c', 3)].iter() + /// .cloned().collect::>(); + /// assert_eq!(counter.into_map(), expect); + /// ``` + fn from_iter>(iter: I) -> Self { + let mut cnt = Counter::new(); + for (item, item_count) in iter { + let entry = cnt.map.entry(item).or_insert_with(N::zero); + *entry += item_count; + } + cnt + } +} diff --git a/src/lib.rs b/src/lib.rs index b9033a9..4248373 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,7 +281,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{AddAssign, SubAssign}; +use std::ops::SubAssign; #[cfg(test)] mod unit_tests; @@ -328,46 +328,9 @@ where } } -impl Counter -where - T: Hash + Eq, - N: Zero, -{ - /// Create a new, empty `Counter` - pub fn new() -> Counter { - Counter { - map: HashMap::new(), - zero: N::zero(), - } - } -} -impl Counter -where - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Create a new `Counter` initialized with the given iterable. - pub fn init(iterable: I) -> Counter - where - I: IntoIterator, - { - let mut counter = Counter::new(); - counter.update(iterable); - counter - } - /// Add the counts of the elements from the given iterable to this counter. - pub fn update(&mut self, iterable: I) - where - I: IntoIterator, - { - for item in iterable { - let entry = self.map.entry(item).or_insert_with(N::zero); - *entry += N::one(); - } - } -} + impl Counter where From b386b78b1e7e2ee7bb40cb5330e85bb286130adf Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:40:32 +0900 Subject: [PATCH 19/22] change name for consistency --- src/impls.rs | 2 +- src/impls/{into_iter.rs => into_iterator.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/impls/{into_iter.rs => into_iterator.rs} (100%) diff --git a/src/impls.rs b/src/impls.rs index 449dc5e..afde736 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -6,7 +6,7 @@ mod extend; mod from_iterator; mod index; mod intersection; -mod into_iter; +mod into_iterator; mod sub_iterable; mod sub_self; mod union; diff --git a/src/impls/into_iter.rs b/src/impls/into_iterator.rs similarity index 100% rename from src/impls/into_iter.rs rename to src/impls/into_iterator.rs From 594e22d5eb0bb2c57bd5662db0e789ed91140824 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:42:09 +0900 Subject: [PATCH 20/22] refactor into self --- src/impls/create.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/impls/create.rs b/src/impls/create.rs index 89ab716..9fadc22 100644 --- a/src/impls/create.rs +++ b/src/impls/create.rs @@ -12,7 +12,7 @@ where N: Zero, { /// Create a new, empty `Counter` - pub fn new() -> Counter { + pub fn new() -> Self { Counter { map: HashMap::new(), zero: N::zero(), @@ -26,7 +26,7 @@ where N: AddAssign + Zero + One, { /// Create a new `Counter` initialized with the given iterable. - pub fn init(iterable: I) -> Counter + pub fn init(iterable: I) -> Self where I: IntoIterator, { From c5d1119221b3b3eb4bef4f282e5f0fe3fcc90709 Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:43:26 +0900 Subject: [PATCH 21/22] refactor non creation related code --- src/impls/create.rs | 11 ----------- src/lib.rs | 19 +++++++++++++++++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/impls/create.rs b/src/impls/create.rs index 9fadc22..7f463aa 100644 --- a/src/impls/create.rs +++ b/src/impls/create.rs @@ -34,17 +34,6 @@ where counter.update(iterable); counter } - - /// Add the counts of the elements from the given iterable to this counter. - pub fn update(&mut self, iterable: I) - where - I: IntoIterator, - { - for item in iterable { - let entry = self.map.entry(item).or_insert_with(N::zero); - *entry += N::one(); - } - } } diff --git a/src/lib.rs b/src/lib.rs index 4248373..712368c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,7 +281,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::SubAssign; +use std::ops::{AddAssign,SubAssign}; #[cfg(test)] mod unit_tests; @@ -329,7 +329,22 @@ where } - +impl Counter +where + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Add the counts of the elements from the given iterable to this counter. + pub fn update(&mut self, iterable: I) + where + I: IntoIterator, + { + for item in iterable { + let entry = self.map.entry(item).or_insert_with(N::zero); + *entry += N::one(); + } + } +} impl Counter From 5161a540bcde716457426d339a78fd895f47e7fe Mon Sep 17 00:00:00 2001 From: chris-ha458 Date: Tue, 29 Aug 2023 21:49:42 +0900 Subject: [PATCH 22/22] move `init` to `from_iterator` --- src/impls/create.rs | 20 +------------------- src/impls/from_iterator.rs | 16 ++++++++++++++++ src/lib.rs | 4 +--- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/impls/create.rs b/src/impls/create.rs index 7f463aa..552b329 100644 --- a/src/impls/create.rs +++ b/src/impls/create.rs @@ -1,8 +1,7 @@ use crate::Counter; -use num_traits::{One, Zero}; +use num_traits::Zero; -use std::ops::AddAssign; use std::collections::HashMap; use std::hash::Hash; @@ -20,23 +19,6 @@ where } } -impl Counter -where - T: Hash + Eq, - N: AddAssign + Zero + One, -{ - /// Create a new `Counter` initialized with the given iterable. - pub fn init(iterable: I) -> Self - where - I: IntoIterator, - { - let mut counter = Counter::new(); - counter.update(iterable); - counter - } -} - - impl Default for Counter where T: Hash + Eq, diff --git a/src/impls/from_iterator.rs b/src/impls/from_iterator.rs index ef441d7..aa76917 100644 --- a/src/impls/from_iterator.rs +++ b/src/impls/from_iterator.rs @@ -6,6 +6,22 @@ use std::hash::Hash; use std::iter; use std::ops::AddAssign; +impl Counter +where + T: Hash + Eq, + N: AddAssign + Zero + One, +{ + /// Create a new `Counter` initialized with the given iterable. + pub fn init(iterable: I) -> Self + where + I: IntoIterator, + { + let mut counter = Counter::new(); + counter.update(iterable); + counter + } +} + impl iter::FromIterator for Counter where T: Hash + Eq, diff --git a/src/lib.rs b/src/lib.rs index 712368c..677361a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,7 +281,7 @@ use num_traits::{One, Zero}; use std::collections::{BinaryHeap, HashMap}; use std::hash::Hash; use std::iter; -use std::ops::{AddAssign,SubAssign}; +use std::ops::{AddAssign, SubAssign}; #[cfg(test)] mod unit_tests; @@ -328,7 +328,6 @@ where } } - impl Counter where T: Hash + Eq, @@ -346,7 +345,6 @@ where } } - impl Counter where T: Hash + Eq,