cereal_lib 2.1.1

Does the cereal box problem in either single or multi-threaded mode
Documentation
// TODO: Generic number types?

use rand::Rng;
use statistical;
use std::{
    sync::{self, mpsc::Sender},
    thread,
};

/*Calculates the number of tries to get all the toys from the cereal boxes. Uses the provided sender
and/or returns the number of tries*/
fn calculate(
    number_of_toys: usize,
    number_of_loops: u32,
    sender: Option<Sender<Vec<i32>>>,
) -> Vec<i32> {
    let mut probability: Vec<i32> = Vec::new();
    // Simulation loop
    for _i in 0..number_of_loops {
        let mut prizes = vec![0; number_of_toys];
        let mut opens = 0;
        // loop until owns every prize
        while prizes.contains(&0) {
            let high = prizes.len();
            prizes[rand::thread_rng().gen_range(0, high)] += 1;
            opens += 1
        }
        probability.push(opens)
    }
    probability.shrink_to_fit();
    match sender {
        Some(tx) => tx.send(probability.clone()).unwrap(),
        None => (),
    }
    probability
}

/// Contains the raw data from simulations and cashing for output of statistical analysis methods
#[derive(Debug, Clone)]
pub struct CerealResults {
    data: Vec<i32>,
    mean: Option<f64>,
    median: Option<i32>,
    max: Option<i32>,
    min: Option<i32>,
    cache: bool,
}

impl CerealResults {
    fn new(data: Vec<i32>) -> CerealResults {
        CerealResults {
            data,
            mean: None,
            median: None,
            max: None,
            min: None,
            cache: false,
        }
    }
    pub fn mean(&self) -> f64 {
        /*let mean = || statistical::mean(
            self.data
                .clone()
                .into_iter()
                .map(|datum| datum as f64)
                .collect::<Vec<f64>>()
                .as_ref(),
        );
        if self.cache {
            if let Some(mean) = self.mean {
                mean
            } else {
                self.mean = Some(mean());
                self.mean.unwrap()
            }
        } else {
            mean()
        }*/
        statistical::mean(
            self.data
                .clone()
                .into_iter()
                .map(|datum| datum as f64)
                .collect::<Vec<f64>>()
                .as_ref(),
        )
    }
        pub fn median(&self) -> i32 {
            /*let median = || statistical::median(self.data.as_slice());
            if self.cache {
                if let Some(median) = self.median {
                    median
                } else {
                    self.median = Some(median());
                    self.median.unwrap()
                }
            } else {
                median()
            }*/
            statistical::median(self.data.as_slice())
        }
        pub fn max(&self) -> i32 {
            /*if let Some(max) = self.max {
                max
            } else {
                let max = *self.data.iter().max().unwrap();
                self.median = Some(max);
                max
            }*/
            *self.data.iter().max().unwrap()
        }
        pub fn min(&self) -> i32 {
            /*if let Some(min) = self.min {
                min
            } else {
                let min = *self.data.iter().min().unwrap();
                self.median = Some(min);
                min
            }*/
            *self.data.iter().min().unwrap()
        }
    }
    /**
        Runs a multi-threaded simulation of the cereal box problem with ability to change number of toys types simulated

        Panics any parameter is less than 0
    */
    pub fn simulation_change_toys(
        number_of_toys: usize,
        number_of_loops: u32,
        number_of_threads: u32,
    ) -> CerealResults {
        match number_of_loops {
            0 => CerealResults::new(vec![0]),
            _ => {
                let child_load = (number_of_loops as f32 / number_of_threads as f32).floor() as u32;
                // Create channel
                let (tx, rx) = sync::mpsc::channel();
                for _i in 0..number_of_threads {
                    // Create a sender and a number of loop for the other thread
                    let tx_clone = sync::mpsc::Sender::clone(&tx);
                    // Spawn thread for calculations
                    thread::spawn(move || {
                        calculate(number_of_toys, child_load.clone(), Some(tx_clone))
                    });
                }
                let load = (number_of_loops as f32 / number_of_threads as f32).ceil() as u32;
                calculate(number_of_toys, load, Some(tx));
                let mut probability = Vec::new();
                rx.iter()
                    .for_each(|mut datum| probability.append(&mut datum));
                CerealResults::new(probability)
            }
        }
    }

    /**
        Runs a multi-threaded simulation of the cereal box problem with ability to change number of toys types simulated

        Panics any parameter is less than 0
    */

    pub fn simulation(number_of_loops: u32, number_of_threads: u32) -> CerealResults {
        simulation_change_toys(6, number_of_loops, number_of_threads)
    }

    /**
        Runs a single-threaded simulation of the cereal box problem with number of toys types at 6

        Panics any parameter is less than 0
    */

    pub fn single_thread_change_toys(number_of_toys: usize, number_of_loops: u32) -> CerealResults {
        // Avoid divide by zero errors with this check
        match number_of_loops {
            0 => CerealResults::new(vec![0]),
            _ => CerealResults::new(calculate(number_of_toys, number_of_loops, None)),
        }
    }

    /**
        Runs a single-threaded simulation of the cereal box problem with number of toys types at 6

        Panics any parameter is less than 0
    */

    pub fn simulation_single_thread(number_of_loops: u32) -> CerealResults {
        single_thread_change_toys(6, number_of_loops)
    }