use criterion::{black_box, criterion_group, criterion_main, Criterion};

use slicefire::*;

#[derive(Debug, Copy, Clone)]
#[allow(unused)]
struct Foo {
    x: i32,
    y: i64,
}

impl From<i32> for Foo {
    fn from(x: i32) -> Self {
        Self { x, y: x as i64 * 2 }
    }
}

fn indices(size: usize) -> (Vec<usize>, Vec<Foo>) {
    let indices = (0..(size)).map(|i| i % (size - 2)).collect::<Vec<usize>>();
    let vs = (0..size).map(|v| (v as i32).into()).collect::<Vec<Foo>>();
    (indices, vs)
}

fn write_single_value_with_size(size: usize, c: &mut Criterion) {
    let (indices, mut vs) = indices(size);
    let val = black_box(Foo::from(10));
    c.bench_function(&format!("writes {}", size), |b| {
        b.iter(|| writes(&mut vs, black_box(val), &indices))
    });
    c.bench_function(&format!("writes_discard {}", size), |b| {
        b.iter(|| writes_discard(&mut vs, black_box(val), &indices))
    });
    c.bench_function(&format!("writes_par {}", size), |b| {
        b.iter(|| writes_par(&mut vs, black_box(val), &indices))
    });
    c.bench_function(&format!("writes_par_discard {}", size), |b| {
        b.iter(|| writes_par_discard(&mut vs, black_box(val), &indices))
    });
    c.bench_function(&format!("writes_par_shard_discard {}", size), |b| {
        b.iter(|| writes_par_shard_discard(&mut vs, black_box(val), &indices))
    });
}

fn write_values_with_size(size: usize, c: &mut Criterion) {
    let (indices, mut vs) = indices(size);
    let vals = (0..20).map(|v| v.into()).collect::<Vec<Foo>>();
    let vals = &vals;
    c.bench_function(&format!("writes vals {}", size), |b| {
        b.iter(|| {
            for val in vals {
                writes(&mut vs, black_box(*val), &indices);
            }
        })
    });
    c.bench_function(&format!("write_discards vals {}", size), |b| {
        b.iter(|| {
            for val in vals {
                writes_discard(&mut vs, black_box(*val), &indices);
            }
        })
    });
    c.bench_function(&format!("writes_par vals {}", size), |b| {
        b.iter(|| {
            for val in vals {
                writes_par(&mut vs, black_box(*val), &indices);
            }
        })
    });

    c.bench_function(&format!("writes_par_discard vals {}", size), |b| {
        b.iter(|| {
            for val in vals {
                writes_par_discard(&mut vs, black_box(*val), &indices);
            }
        })
    });

    c.bench_function(&format!("writes_par_shard_discard vals {}", size), |b| {
        b.iter(|| {
            for val in vals {
                writes_par_shard_discard(&mut vs, black_box(*val), &indices);
            }
        })
    });
}

fn write_values(c: &mut Criterion) {
    write_values_with_size(1_000, c);
    write_values_with_size(10_000, c);
    write_values_with_size(100_000, c);
}

fn write_single_value(c: &mut Criterion) {
    write_single_value_with_size(1_000, c);
    write_single_value_with_size(10_000, c);
    write_single_value_with_size(100_000, c);
}

criterion_group!(benches, write_single_value, write_values);
criterion_main!(benches);