use criterion::{criterion_group, criterion_main, Criterion, Fun};
use math::BaseNum;
use rand::distributions::{Distribution, Standard};
use rand::prelude::*;

static SEED: [u8; 32] = [3; 32];

#[inline]
fn make_random_vec<T>() -> Vec<T>
where
    T: BaseNum,
    Standard: Distribution<T>,
{
    let mut rng: StdRng = SeedableRng::from_seed(SEED);
    vec![
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
    ]
}

#[inline]
fn compute(x: f64) -> f64 {
    x * 3.6321 + 42314.0
}

#[inline]
fn for_loop_map(v: &mut Vec<f64>) {
    let f = |x| x * 3.6321 + 42314.0;
    for a in v.iter_mut() {
        *a = f(*a);
    }
}

fn map(c: &mut Criterion) {
    let iter_map = Fun::new("Iterator Map", move |b, _| {
        let v = make_random_vec::<f64>();
        b.iter(|| -> Vec<f64> { v.iter().map(|&x| compute(x)).collect() })
    });

    let for_each_loop = Fun::new("For-each Loop", move |b, _| {
        let mut v = make_random_vec::<f64>();
        b.iter(|| {
            let f = |x: &mut f64| *x = compute(*x);
            v.iter_mut().for_each(f);
        })
    });

    let for_loop_map = Fun::new("For-loop Map", move |b, _| {
        let mut v = make_random_vec::<f64>();
        b.iter(|| for_loop_map(&mut v))
    });

    let fns = vec![iter_map, for_each_loop, for_loop_map];
    c.bench_functions("map", fns, ());
}

criterion_group!(benches, map);
criterion_main!(benches);
