use rustnum as np;
fn main() {
let n: usize = 500;
let mut rng = np::random::Rng::seed(1);
let z = rng.standard_normal(&[n, 2]);
let mix = np::array![[3.0, 1.0], [0.0, 1.0]];
let x = z.matmul(&mix);
let mean = x.mean_axis(0);
let xc = &x - &mean;
let cov = xc.t().matmul(&xc) / (n as f64 - 1.0);
println!("mean = {mean}");
println!("covariance =\n{cov}");
let (vals, vecs) = np::linalg::eigh(&cov);
let vals = vals.flip(0);
let vecs = vecs.flip(1);
println!("eigenvalues = {vals}");
println!("components (columns) =\n{vecs}");
let ratio = &vals / vals.sum();
println!("explained variance ratio = {ratio}");
let scores = xc.matmul(&vecs);
println!("scores shape = {:?}", scores.shape());
let score_cov = scores.t().matmul(&scores) / (n as f64 - 1.0);
println!("covariance of scores =\n{score_cov}");
assert!((ratio.sum() - 1.0).abs() < 1e-9);
assert!(ratio.get(&[0]) > 0.85);
assert!(score_cov.get(&[0, 1]).abs() < 1e-8);
}