use crate::math::complex::Float;
pub(super) struct Columns<'a, R> {
pub data: &'a mut [R],
pub rows: usize,
pub ld: usize,
}
pub(super) trait Vectors<R> {
fn rotate(&mut self, i: usize, j: usize, c: R, s: R);
fn negate(&mut self, j: usize);
fn swap(&mut self, i: usize, j: usize);
}
impl<R: Float> Columns<'_, R> {
#[inline]
pub fn rotate(&mut self, i: usize, j: usize, c: R, s: R) {
let (rows, ld) = (self.rows, self.ld);
let (lo, hi) = (i.min(j), i.max(j));
let (left, right) = self.data.split_at_mut(hi * ld);
let (p, q) = (&mut left[lo * ld..lo * ld + rows], &mut right[..rows]);
let (x, y) = if i < j { (p, q) } else { (q, p) };
rotate_real(x, y, c, s);
}
}
impl<R: Float> Vectors<R> for Columns<'_, R> {
fn rotate(&mut self, i: usize, j: usize, c: R, s: R) {
Columns::rotate(self, i, j, c, s);
}
fn negate(&mut self, j: usize) {
for x in &mut self.data[j * self.ld..j * self.ld + self.rows] {
*x = -*x;
}
}
fn swap(&mut self, i: usize, j: usize) {
let (rows, ld) = (self.rows, self.ld);
let (left, right) = self.data.split_at_mut(j * ld);
left[i * ld..i * ld + rows].swap_with_slice(&mut right[..rows]);
}
}
#[inline(never)]
fn rotate_real<R: Float>(x: &mut [R], y: &mut [R], c: R, s: R) {
for (x, y) in x.iter_mut().zip(y.iter_mut()) {
let (a, b) = (*x, *y);
*x = fma(c, a, s * b);
*y = fma(c, b, -(s * a));
}
}
const FMA: bool = cfg!(any(target_arch = "aarch64", target_feature = "fma"));
#[inline(always)]
pub(super) fn fma<R: Float>(a: R, b: R, c: R) -> R {
if FMA { a.mul_add(b, c) } else { a * b + c }
}
#[inline]
pub(super) fn rotg<R: Float>(a: R, b: R) -> (R, R, R) {
let roe = if a.abs() > b.abs() { a } else { b };
let ss = a * a + b * b;
let r = if ss >= R::MIN_POSITIVE / R::EPSILON && ss <= R::MAX {
ss.sqrt()
} else {
a.hypot(b)
};
if r == R::ZERO {
return (R::ZERO, R::ONE, R::ZERO);
}
let r = r.copysign(roe);
(r, a / r, b / r)
}
struct Chase<R> {
r: R,
cs: R,
sn: R,
f: R,
b: R,
g: R,
x: R,
}
#[inline(always)]
fn chase<R: Float>(f: R, g: R, a: R, b: R, x: R) -> Chase<R> {
let ss = fma(f, f, g * g);
if ss >= R::MIN_POSITIVE / R::EPSILON && ss <= R::MAX {
let roe = if f.abs() > g.abs() { f } else { g };
let r = ss.sqrt().copysign(roe);
Chase {
r,
f: fma(f, a, g * b) / r,
g: g * x / r,
cs: f / r,
sn: g / r,
b: fma(f, b, -(g * a)) / r,
x: f * x / r,
}
} else {
let (r, cs, sn) = rotg(f, g);
Chase {
r,
cs,
sn,
f: cs * a + sn * b,
b: cs * b - sn * a,
g: sn * x,
x: cs * x,
}
}
}
pub(super) fn bidiagonal_qr<R: Float, U: Vectors<R>, V: Vectors<R>>(
s: &mut [R],
e: &mut [R],
mut u: Option<&mut U>,
mut v: Option<&mut V>,
) -> bool {
let p = s.len();
let zero = R::ZERO;
let tol = R::from_f64(4.0) * R::EPSILON;
let mut m = p;
let mut iter = 0;
while m > 0 {
if iter >= 75 {
for x in &mut s[..m] {
*x = R::NAN;
}
return false;
}
let mut l = m - 1;
while l > 0 {
let test = s[l - 1].abs() + s[l].abs();
if e[l - 1].abs() <= tol * test {
e[l - 1] = zero;
break;
}
l -= 1;
}
let kase;
if l == m - 1 {
kase = 4;
} else {
let mut ls = m;
while ls > l {
let mut test = zero;
if ls != m {
test += e[ls - 1].abs();
}
if ls != l + 1 {
test += e[ls - 2].abs();
}
if s[ls - 1].abs() <= tol * test {
s[ls - 1] = zero;
break;
}
ls -= 1;
}
if ls == l {
kase = 3;
} else if ls == m {
kase = 1;
} else {
kase = 2;
l = ls;
}
}
l += 1;
match kase {
1 => {
let mut f = e[m - 2];
e[m - 2] = zero;
for k in (l..m).rev() {
let (r, cs, sn) = rotg(s[k - 1], f);
s[k - 1] = r;
if k != l {
f = -sn * e[k - 2];
e[k - 2] = cs * e[k - 2];
}
if let Some(v) = v.as_deref_mut() {
v.rotate(k - 1, m - 1, cs, sn);
}
}
}
2 => {
let mut f = e[l - 2];
e[l - 2] = zero;
for k in l..=m {
let (r, cs, sn) = rotg(s[k - 1], f);
s[k - 1] = r;
f = -sn * e[k - 1];
e[k - 1] = cs * e[k - 1];
if let Some(u) = u.as_deref_mut() {
u.rotate(k - 1, l - 2, cs, sn);
}
}
}
3 => {
let scale = s[m - 1]
.abs()
.max(s[m - 2].abs())
.max(e[m - 2].abs())
.max(s[l - 1].abs())
.max(e[l - 1].abs());
let sm = s[m - 1] / scale;
let smm1 = s[m - 2] / scale;
let emm1 = e[m - 2] / scale;
let sl = s[l - 1] / scale;
let el = e[l - 1] / scale;
let b = ((smm1 + sm) * (smm1 - sm) + emm1 * emm1) * R::HALF;
let c = (sm * emm1) * (sm * emm1);
let mut shift = zero;
if b != zero || c != zero {
shift = (b * b + c).sqrt();
if b < zero {
shift = -shift;
}
shift = c / (b + shift);
}
let mut f = (sl + sm) * (sl - sm) + shift;
let mut g = sl * el;
for k in l..m {
let c = chase(f, g, s[k - 1], e[k - 1], s[k]);
if k != l {
e[k - 2] = c.r;
}
(f, e[k - 1], g, s[k]) = (c.f, c.b, c.g, c.x);
if let Some(v) = v.as_deref_mut() {
v.rotate(k - 1, k, c.cs, c.sn);
}
let c = chase(f, g, e[k - 1], s[k], e[k]);
s[k - 1] = c.r;
(f, s[k], g, e[k]) = (c.f, c.b, c.g, c.x);
if let Some(u) = u.as_deref_mut() {
u.rotate(k - 1, k, c.cs, c.sn);
}
}
e[m - 2] = f;
iter += 1;
}
_ => {
if s[l - 1] < zero {
s[l - 1] = -s[l - 1];
if let Some(v) = v.as_deref_mut() {
v.negate(l - 1);
}
}
let mut l = l;
while l < p && s[l - 1] < s[l] {
s.swap(l - 1, l);
if let Some(v) = v.as_deref_mut() {
v.swap(l - 1, l);
}
if let Some(u) = u.as_deref_mut() {
u.swap(l - 1, l);
}
l += 1;
}
iter = 0;
m -= 1;
}
}
}
true
}