use faer::{Mat, MatRef};
use rand::prelude::*;
use rand_distr::Normal;
use crate::prelude::*;
#[derive(Clone, Debug)]
pub struct RandomSvdResults<T> {
pub u: faer::Mat<T>,
pub v: faer::Mat<T>,
pub s: Vec<T>,
}
pub fn get_top_eigenvalues<T>(matrix: &Mat<T>, top_n: usize) -> Vec<(T, Vec<T>)>
where
T: BixverseFloat,
{
assert_symmetric_mat!(matrix);
let eigendecomp = matrix.eigen().unwrap();
let s = eigendecomp.S();
let u = eigendecomp.U();
let mut eigenpairs = s
.column_vector()
.iter()
.zip(u.col_iter())
.map(|(l, v)| {
let l_real = l.re;
let v_real = v.iter().map(|v_i| v_i.re).collect::<Vec<T>>();
(l_real, v_real)
})
.collect::<Vec<(T, Vec<T>)>>();
eigenpairs.sort_by(|a, b| b.0.total_cmp(&a.0));
let res: Vec<(T, Vec<T>)> = eigenpairs.into_iter().take(top_n).collect();
res
}
pub fn randomised_svd<T>(
x: MatRef<T>,
rank: usize,
seed: usize,
oversampling: Option<usize>,
n_power_iter: Option<usize>,
) -> RandomSvdResults<T>
where
T: BixverseFloat,
{
let ncol = x.ncols();
let nrow = x.nrows();
let os = oversampling.unwrap_or(10);
let sample_size = (rank + os).min(ncol.min(nrow));
let n_iter = n_power_iter.unwrap_or(2);
let mut rng = StdRng::seed_from_u64(seed as u64);
let normal = Normal::new(0.0, 1.0).unwrap();
let omega = Mat::from_fn(ncol, sample_size, |_, _| {
T::from_f64(normal.sample(&mut rng)).unwrap()
});
let y = x * omega;
let mut q = y.qr().compute_thin_Q();
for _ in 0..n_iter {
let z = x.transpose() * q;
q = (x * z).qr().compute_thin_Q();
}
let b = q.transpose() * x;
let svd = b.thin_svd().unwrap();
RandomSvdResults {
u: q * svd.U(),
v: svd.V().cloned(),
s: svd.S().column_vector().iter().copied().collect(),
}
}