malevich 0.7.0

Terminal plotting: a small grammar of marks, honest axes, millions of points
Documentation
use super::kde;

#[test]
fn the_density_integrates_to_about_one() {
    let values: Vec<f64> = (0..2000)
        .map(|i| {
            let i = i as f64;
            ((i * 0.731).sin() + (i * 1.13).sin() + (i * 2.71).sin()) * 2.0
        })
        .collect();
    let (positions, densities) = kde(&values, 512).unwrap();
    let step = positions[1] - positions[0];
    let integral: f64 = densities.iter().sum::<f64>() * step;
    assert!((integral - 1.0).abs() < 0.02, "integral {integral}");
}

#[test]
fn the_mode_sits_near_the_data_center() {
    let values: Vec<f64> = (0..3000)
        .map(|i| {
            let i = i as f64;
            5.0 + ((i * 0.97).sin() + (i * 1.31).sin() + (i * 2.63).sin()) / 3.0
        })
        .collect();
    let (positions, densities) = kde(&values, 256).unwrap();
    let peak = densities
        .iter()
        .enumerate()
        .max_by(|a, b| a.1.total_cmp(b.1))
        .map(|(i, _)| positions[i])
        .unwrap();
    assert!((peak - 5.0).abs() < 0.5, "peak at {peak}");
}

#[test]
fn degenerate_and_empty_samples_behave() {
    assert!(kde(&[f64::NAN], 128).is_none());
    let (_, densities) = kde(&[7.0; 50], 128).unwrap();
    assert!(densities.iter().all(|d| d.is_finite()));
}