cs-utils 0.21.1

Common utilities.
Documentation
use std::collections::HashSet;
use rand::distributions::uniform::SampleRange;

use crate::random_number;

/// Picks random N items from a vector or slice of items.
/// 
/// ### Panics
/// 
/// * if items_count is larger than total number of items
///   in a collection
/// 
/// ### Examples
/// 
/// ```
/// #[cfg(any(features = "test", test))]
/// {
///     use cs_utils::test::pick_random;
/// 
///     let test_vector = vec![1, 2, 3, 4, 5, 6];
///     let random_items = pick_random(&test_vector, 3);
///     
///     assert_eq!(
///         random_items.len(),
///         3,
///         "Must have 3 items.",
///     );
/// 
///     for item in random_items {
///         assert!(
///             test_vector.contains(item),
///         );
///     }
/// }
/// ```
pub fn pick_random<'a, T>(
    collection: &'a [T],
    items_count: usize,
) -> Vec<&'a T> {
    let iter = collection.iter();
    let (_, len) = iter.size_hint();
    let len = len.expect("Gennot get iterator length.");

    assert!(
        items_count <= len,
        "Count must be smaller or equal to item length.",
    );

    let mut set = HashSet::new();
    while set.len() < items_count {
        set.insert(
            random_number(0..len)
        );
    };

    let result = iter
        .enumerate()
        .filter_map(|(index, item)| {
            if set.contains(&index) {
                return Some(item);
            }

            return None;
        }).collect();

    return result;
}

/// Picks random items from a vector or slice of items.
/// 
/// ### Panics
/// 
/// * if items_count is larger than total number of items
///   in a collection
/// 
/// ### Examples
/// 
/// ```
/// #[cfg(any(features = "test", test))]
/// {
///     use cs_utils::test::pick_random_rg;
/// 
///     let test_vector = vec![1, 2, 3, 4, 5, 6];
///     let random_items = pick_random_rg(&test_vector, 2..=3);
///     
///     assert!(
///         random_items.len() >= 2,
///         "Must have at least 2 items.",
///     );
/// 
///     assert!(
///         random_items.len() <= 3,
///         "Must have at most 3 items.",
///     );
/// 
///     for item in random_items {
///         assert!(
///             test_vector.contains(item),
///         );
///     }
/// }
/// ```
pub fn pick_random_rg<'a, T, R: SampleRange<usize>>(
    collection: &'a [T],
    range: R,
) -> Vec<&'a T> {
    let items_count = random_number(range);

    return pick_random(collection, items_count);

}

#[cfg(test)]
mod tests {
    use crate::traits::Random;
    use crate::random_number;

    impl Random for u8 {
        fn random() -> Self {
            return random_number(0..=u8::MAX);
        }
    }

    mod pick_random {
        use random_vec::random_vec_rg;
        use crate::{random_number, test::{random_vec, pick_random}};

        #[test]
        fn picks_random_items_from_vector() {
            let test_vector: Vec<u8> = random_vec_rg(100..=200);
            let original_test_vector_length = test_vector.len();

            let items_count = random_number(30..=50);
            let random_items = pick_random(&test_vector, items_count);

            assert_eq!(
                random_items.len(),
                items_count,
                "Must return vector with {} references.", items_count,
            );

            assert_eq!(
                test_vector.len(),
                original_test_vector_length,
                "Test vector must not change.",
            );

            for item in random_items {
                assert!(
                    test_vector.contains(item),
                    "Test vector must contain random item.",
                );
            }
        }

        #[test]
        fn picks_random_items_from_slice() {
            let test_vector: Vec<u8> = random_vec_rg(100..=200);
            let original_test_vector_length = test_vector.len();

            let items_count = random_number(3..=5);
            let random_items = pick_random(&test_vector[..], items_count);

            assert_eq!(
                random_items.len(),
                items_count,
                "Must return vector with {} references.", items_count,
            );

            assert_eq!(
                test_vector.len(),
                original_test_vector_length,
                "Test vector must not change.",
            );

            for item in random_items {
                assert!(
                    test_vector.contains(item),
                    "Test vector must contain random item.",
                );
            }
        }
    }

    mod pick_random_rg {
        use random_vec::random_vec_rg;
        use crate::{test::{random_vec, pick_random_rg}};

        #[test]
        fn picks_random_items_from_vector() {
            let test_vector: Vec<u8> = random_vec_rg(100..=200);
            let original_test_vector_length = test_vector.len();

            let random_items = pick_random_rg(&test_vector, 30..=50);

            assert!(
                random_items.len() >= 30,
                "Must return vector with at least 30 references.",
            );

            assert!(
                random_items.len() <= 50,
                "Must return vector with at most 50 references.",
            );

            assert_eq!(
                test_vector.len(),
                original_test_vector_length,
                "Test vector must not change.",
            );

            for item in random_items {
                assert!(
                    test_vector.contains(item),
                    "Test vector must contain random item.",
                );
            }
        }

        #[test]
        fn picks_random_items_from_slice() {
            let test_vector: Vec<u8> = random_vec_rg(100..=200);
            let original_test_vector_length = test_vector.len();

            let random_items = pick_random_rg(&test_vector[..], 30..=50);

            assert!(
                random_items.len() >= 30,
                "Must return vector with at least 30 references.",
            );

            assert!(
                random_items.len() <= 50,
                "Must return vector with at most 50 references.",
            );

            assert_eq!(
                test_vector.len(),
                original_test_vector_length,
                "Test vector must not change.",
            );

            for item in random_items {
                assert!(
                    test_vector.contains(item),
                    "Test vector must contain random item.",
                );
            }
        }
    }
}