use crate::Complex64;
use num_complex::ComplexFloat;
const SAFMIN: f64 = f64::MIN_POSITIVE;
pub(super) const DLAMCH_E: f64 = f64::EPSILON / 2.0;
pub(super) fn zero() -> Complex64 {
Complex64::new(0.0, 0.0)
}
pub(super) fn dlapy2(x: f64, y: f64) -> f64 {
let xabs = x.abs();
let yabs = y.abs();
let w = xabs.max(yabs);
let z = xabs.min(yabs);
if z == 0.0 { w } else { w * (1.0 + (z / w) * (z / w)).sqrt() }
}
pub(super) fn dlapy3(x: f64, y: f64, z: f64) -> f64 {
let xabs = x.abs();
let yabs = y.abs();
let zabs = z.abs();
let w = xabs.max(yabs).max(zabs);
if w == 0.0 || w > f64::MAX || !w.is_finite() {
xabs + yabs + zabs
} else {
w * ((xabs / w) * (xabs / w) + (yabs / w) * (yabs / w) + (zabs / w) * (zabs / w)).sqrt()
}
}
pub(super) fn dlae2(a: f64, b: f64, c: f64) -> (f64, f64) {
let sm = a + c;
let df = a - c;
let adf = df.abs();
let tb = b + b;
let ab = tb.abs();
let (acmx, acmn) = if a.abs() > c.abs() { (a, c) } else { (c, a) };
let rt = if adf > ab {
adf * (1.0 + (ab / adf) * (ab / adf)).sqrt()
} else if adf < ab {
ab * (1.0 + (adf / ab) * (adf / ab)).sqrt()
} else {
ab * 2.0f64.sqrt()
};
if sm < 0.0 {
let rt1 = 0.5 * (sm - rt);
let rt2 = (acmx / rt1) * acmn - (b / rt1) * b;
(rt1, rt2)
} else if sm > 0.0 {
let rt1 = 0.5 * (sm + rt);
let rt2 = (acmx / rt1) * acmn - (b / rt1) * b;
(rt1, rt2)
} else {
(0.5 * rt, -0.5 * rt)
}
}
pub(super) fn dlaev2(a: f64, b: f64, c: f64) -> (f64, f64, f64, f64) {
let sm = a + c;
let df = a - c;
let adf = df.abs();
let tb = b + b;
let ab = tb.abs();
let (acmx, acmn) = if a.abs() > c.abs() { (a, c) } else { (c, a) };
let rt = if adf > ab {
adf * (1.0 + (ab / adf) * (ab / adf)).sqrt()
} else if adf < ab {
ab * (1.0 + (adf / ab) * (adf / ab)).sqrt()
} else {
ab * 2.0f64.sqrt()
};
let (rt1, rt2, sgn1);
if sm < 0.0 {
rt1 = 0.5 * (sm - rt);
sgn1 = -1;
rt2 = (acmx / rt1) * acmn - (b / rt1) * b;
} else if sm > 0.0 {
rt1 = 0.5 * (sm + rt);
sgn1 = 1;
rt2 = (acmx / rt1) * acmn - (b / rt1) * b;
} else {
rt1 = 0.5 * rt;
rt2 = -0.5 * rt;
sgn1 = 1;
}
let (cs, sgn2) = if df >= 0.0 { (df + rt, 1) } else { (df - rt, -1) };
let acs = cs.abs();
let (mut cs1, mut sn1) = if acs > ab {
let ct = -tb / cs;
let sn1 = 1.0 / (1.0 + ct * ct).sqrt();
(ct * sn1, sn1)
} else if ab == 0.0 {
(1.0, 0.0)
} else {
let tn = -cs / tb;
let cs1 = 1.0 / (1.0 + tn * tn).sqrt();
(cs1, tn * cs1)
};
if sgn1 == sgn2 {
let tn = cs1;
cs1 = -sn1;
sn1 = tn;
}
(rt1, rt2, cs1, sn1)
}
fn dladiv2(a: f64, b: f64, c: f64, d: f64, r: f64, t: f64) -> f64 {
if r != 0.0 {
let br = b * r;
if br != 0.0 { (a + br) * t } else { a * t + (b * t) * r }
} else {
(a + d * (b / c)) * t
}
}
fn dladiv1(a: f64, b: f64, c: f64, d: f64) -> (f64, f64) {
let r = d / c;
let t = 1.0 / (c + d * r);
let p = dladiv2(a, b, c, d, r, t);
let q = dladiv2(b, -a, c, d, r, t);
(p, q)
}
pub(super) fn dladiv(a: f64, b: f64, c: f64, d: f64) -> (f64, f64) {
const BS: f64 = 2.0;
let ov = f64::MAX;
let un = SAFMIN;
let eps = DLAMCH_E;
let be = BS / (eps * eps);
let mut aa = a;
let mut bb = b;
let mut cc = c;
let mut dd = d;
let ab = a.abs().max(b.abs());
let cd = c.abs().max(d.abs());
let mut s = 1.0;
if ab >= 0.5 * ov {
aa *= 0.5;
bb *= 0.5;
s *= 2.0;
}
if cd >= 0.5 * ov {
cc *= 0.5;
dd *= 0.5;
s *= 0.5;
}
if ab <= un * BS / eps {
aa *= be;
bb *= be;
s /= be;
}
if cd <= un * BS / eps {
cc *= be;
dd *= be;
s *= be;
}
let (mut p, mut q) = if d.abs() <= c.abs() {
dladiv1(aa, bb, cc, dd)
} else {
let (pp, qq) = dladiv1(bb, aa, dd, cc);
(pp, -qq)
};
p *= s;
q *= s;
(p, q)
}
pub(super) fn zladiv(x: Complex64, y: Complex64) -> Complex64 {
if y.re.abs() >= y.im.abs() {
let r = y.im / y.re;
let den = y.re + r * y.im;
Complex64::new((x.re + r * x.im) / den, (x.im - r * x.re) / den)
} else {
let r = y.re / y.im;
let den = y.im + r * y.re;
Complex64::new((x.re * r + x.im) / den, (x.im * r - x.re) / den)
}
}
fn lascl_g<T: Copy + std::ops::Mul<f64, Output = T>>(cfrom: f64, cto: f64, a: &mut [T], lda: usize, m: usize, n: usize) {
if m == 0 || n == 0 {
return;
}
let smlnum = SAFMIN;
let bignum = 1.0 / smlnum;
let mut cfromc = cfrom;
let mut ctoc = cto;
loop {
let cfrom1 = cfromc * smlnum;
let mul;
let done;
if cfrom1 == cfromc {
mul = ctoc / cfromc;
done = true;
} else {
let cto1 = ctoc / bignum;
if cto1 == ctoc {
mul = ctoc;
done = true;
cfromc = 1.0;
} else if cfrom1.abs() > ctoc.abs() && ctoc != 0.0 {
mul = smlnum;
done = false;
cfromc = cfrom1;
} else if cto1.abs() > cfromc.abs() {
mul = bignum;
done = false;
ctoc = cto1;
} else {
mul = ctoc / cfromc;
done = true;
if mul == 1.0 {
return;
}
}
}
for j in 0..n {
for i in 0..m {
a[i + j * lda] = a[i + j * lda] * mul;
}
}
if done {
return;
}
}
}
pub(crate) fn dlascl_g(cfrom: f64, cto: f64, a: &mut [f64], lda: usize, m: usize, n: usize) {
lascl_g(cfrom, cto, a, lda, m, n)
}
pub(super) fn zlascl_g(cfrom: f64, cto: f64, a: &mut [Complex64], lda: usize, m: usize, n: usize) {
lascl_g(cfrom, cto, a, lda, m, n)
}
pub(super) fn zlarf_left(a: &mut [Complex64], lda: usize, r0: usize, m: usize, c0: usize, ncols: usize, v: &[Complex64], tau: Complex64) {
if tau == zero() || m == 0 || ncols == 0 {
return;
}
let mut w = vec![zero(); ncols];
for (j, wj) in w.iter_mut().enumerate() {
let mut s = zero();
for i in 0..m {
s += a[(r0 + i) + (c0 + j) * lda] * v[i].conj();
}
*wj = s;
}
for (j, &wj) in w.iter().enumerate() {
for i in 0..m {
a[(r0 + i) + (c0 + j) * lda] -= tau * v[i] * wj;
}
}
}
pub(super) fn zlarf_right(a: &mut [Complex64], lda: usize, r0: usize, m: usize, c0: usize, ncols: usize, v: &[Complex64], tau: Complex64) {
if tau == zero() || m == 0 || ncols == 0 {
return;
}
let mut w = vec![zero(); m];
for (i, wi) in w.iter_mut().enumerate() {
let mut s = zero();
for j in 0..ncols {
s += a[(r0 + i) + (c0 + j) * lda] * v[j];
}
*wi = s;
}
for (i, &wi) in w.iter().enumerate() {
for j in 0..ncols {
a[(r0 + i) + (c0 + j) * lda] -= tau * wi * v[j].conj();
}
}
}
pub(super) struct Laln2 {
pub(super) x11: f64,
pub(super) x21: f64,
pub(super) x12: f64,
pub(super) x22: f64,
pub(super) scale: f64,
pub(super) xnorm: f64,
}
#[allow(clippy::too_many_arguments)]
pub(super) fn dlaln2(
ltrans: bool,
na: usize,
nw: usize,
smin: f64,
ca: f64,
a11: f64,
a21: f64,
a12: f64,
a22: f64,
d1: f64,
d2: f64,
b11: f64,
b21: f64,
b12: f64,
b22: f64,
wr: f64,
wi: f64,
) -> Laln2 {
const IPIVOT: [[usize; 4]; 4] = [[1, 2, 3, 4], [2, 1, 4, 3], [3, 4, 1, 2], [4, 3, 2, 1]];
const RSWAP: [bool; 4] = [false, true, false, true];
const ZSWAP: [bool; 4] = [false, false, true, true];
let smlnum = 2.0 * SAFMIN;
let bignum = 1.0 / smlnum;
let smini = smin.max(smlnum);
let mut scale = 1.0;
let (x11, x21, x12, x22, xnorm);
if na == 1 {
if nw == 1 {
let mut csr = ca * a11 - wr * d1;
let mut cnorm = csr.abs();
if cnorm < smini {
csr = smini;
cnorm = smini;
}
let bnorm = b11.abs();
if cnorm < 1.0 && bnorm > 1.0 && bnorm > bignum * cnorm {
scale = 1.0 / bnorm;
}
x11 = (b11 * scale) / csr;
x21 = 0.0;
x12 = 0.0;
x22 = 0.0;
xnorm = x11.abs();
} else {
let mut csr = ca * a11 - wr * d1;
let mut csi = -wi * d1;
let mut cnorm = csr.abs() + csi.abs();
if cnorm < smini {
csr = smini;
csi = 0.0;
cnorm = smini;
}
let bnorm = b11.abs() + b12.abs();
if cnorm < 1.0 && bnorm > 1.0 && bnorm > bignum * cnorm {
scale = 1.0 / bnorm;
}
let (p, q) = dladiv(scale * b11, scale * b12, csr, csi);
x11 = p;
x21 = 0.0;
x12 = q;
x22 = 0.0;
xnorm = x11.abs() + x12.abs();
}
} else {
let crv = if ltrans {
[ca * a11 - wr * d1, ca * a12, ca * a21, ca * a22 - wr * d2]
} else {
[ca * a11 - wr * d1, ca * a21, ca * a12, ca * a22 - wr * d2]
};
if nw == 1 {
let mut cmax = 0.0;
let mut icmax = 0usize;
for j in 0..4 {
if crv[j].abs() > cmax {
cmax = crv[j].abs();
icmax = j;
}
}
if cmax < smini {
let bnorm = b11.abs().max(b21.abs());
if smini < 1.0 && bnorm > 1.0 && bnorm > bignum * smini {
scale = 1.0 / bnorm;
}
let temp = scale / smini;
return Laln2 {
x11: temp * b11,
x21: temp * b21,
x12: 0.0,
x22: 0.0,
scale,
xnorm: temp * bnorm,
};
}
let ur11 = crv[icmax];
let cr21 = crv[IPIVOT[icmax][1] - 1];
let ur12 = crv[IPIVOT[icmax][2] - 1];
let cr22 = crv[IPIVOT[icmax][3] - 1];
let ur11r = 1.0 / ur11;
let lr21 = ur11r * cr21;
let mut ur22 = cr22 - ur12 * lr21;
if ur22.abs() < smini {
ur22 = smini;
}
let (br1, mut br2) = if RSWAP[icmax] { (b21, b11) } else { (b11, b21) };
br2 -= lr21 * br1;
let bbnd = (br1 * (ur22 * ur11r)).abs().max(br2.abs());
if bbnd > 1.0 && ur22.abs() < 1.0 && bbnd >= bignum * ur22.abs() {
scale = 1.0 / bbnd;
}
let xr2 = (br2 * scale) / ur22;
let xr1 = (scale * br1) * ur11r - xr2 * (ur11r * ur12);
let (mut xa, mut xb) = if ZSWAP[icmax] { (xr2, xr1) } else { (xr1, xr2) };
let mut xnorm = xr1.abs().max(xr2.abs());
if xnorm > 1.0 && cmax > 1.0 && xnorm > bignum / cmax {
let temp = cmax / bignum;
xa *= temp;
xb *= temp;
xnorm *= temp;
scale *= temp;
}
return Laln2 {
x11: xa,
x21: xb,
x12: 0.0,
x22: 0.0,
scale,
xnorm,
};
}
let civ = [-wi * d1, 0.0, 0.0, -wi * d2];
let mut cmax = 0.0;
let mut icmax = 0usize;
for j in 0..4 {
let m = crv[j].abs() + civ[j].abs();
if m > cmax {
cmax = m;
icmax = j;
}
}
if cmax < smini {
let bnorm = (b11.abs() + b12.abs()).max(b21.abs() + b22.abs());
if smini < 1.0 && bnorm > 1.0 && bnorm > bignum * smini {
scale = 1.0 / bnorm;
}
let temp = scale / smini;
return Laln2 {
x11: temp * b11,
x21: temp * b21,
x12: temp * b12,
x22: temp * b22,
scale,
xnorm: temp * bnorm,
};
}
let ur11 = crv[icmax];
let ui11 = civ[icmax];
let cr21 = crv[IPIVOT[icmax][1] - 1];
let ci21 = civ[IPIVOT[icmax][1] - 1];
let ur12 = crv[IPIVOT[icmax][2] - 1];
let ui12 = civ[IPIVOT[icmax][2] - 1];
let cr22 = crv[IPIVOT[icmax][3] - 1];
let ci22 = civ[IPIVOT[icmax][3] - 1];
let (ur11r, ui11r, lr21, li21, ur12s, ui12s, mut ur22, mut ui22);
if icmax == 0 || icmax == 3 {
if ur11.abs() > ui11.abs() {
let temp = ui11 / ur11;
ur11r = 1.0 / (ur11 * (1.0 + temp * temp));
ui11r = -temp * ur11r;
} else {
let temp = ur11 / ui11;
ui11r = -1.0 / (ui11 * (1.0 + temp * temp));
ur11r = -temp * ui11r;
}
lr21 = cr21 * ur11r;
li21 = cr21 * ui11r;
ur12s = ur12 * ur11r;
ui12s = ur12 * ui11r;
ur22 = cr22 - ur12 * lr21;
ui22 = ci22 - ur12 * li21;
} else {
ur11r = 1.0 / ur11;
ui11r = 0.0;
lr21 = cr21 * ur11r;
li21 = ci21 * ur11r;
ur12s = ur12 * ur11r;
ui12s = ui12 * ur11r;
ur22 = cr22 - ur12 * lr21 + ui12 * li21;
ui22 = -ur12 * li21 - ui12 * lr21;
}
let u22abs = ur22.abs() + ui22.abs();
if u22abs < smini {
ur22 = smini;
ui22 = 0.0;
}
let (mut br1, mut br2, mut bi1, mut bi2) = if RSWAP[icmax] { (b21, b11, b22, b12) } else { (b11, b21, b12, b22) };
br2 = br2 - lr21 * br1 + li21 * bi1;
bi2 = bi2 - li21 * br1 - lr21 * bi1;
let bbnd = ((br1.abs() + bi1.abs()) * (u22abs * (ur11r.abs() + ui11r.abs()))).max(br2.abs() + bi2.abs());
if bbnd > 1.0 && u22abs < 1.0 && bbnd >= bignum * u22abs {
scale = 1.0 / bbnd;
br1 *= scale;
bi1 *= scale;
br2 *= scale;
bi2 *= scale;
}
let (xr2, xi2) = dladiv(br2, bi2, ur22, ui22);
let xr1 = ur11r * br1 - ui11r * bi1 - ur12s * xr2 + ui12s * xi2;
let xi1 = ui11r * br1 + ur11r * bi1 - ui12s * xr2 - ur12s * xi2;
let (mut xa, mut xb, mut xc, mut xd) = if ZSWAP[icmax] { (xr2, xr1, xi2, xi1) } else { (xr1, xr2, xi1, xi2) };
let mut xnorm = (xr1.abs() + xi1.abs()).max(xr2.abs() + xi2.abs());
if xnorm > 1.0 && cmax > 1.0 && xnorm > bignum / cmax {
let temp = cmax / bignum;
xa *= temp;
xb *= temp;
xc *= temp;
xd *= temp;
xnorm *= temp;
scale *= temp;
}
return Laln2 {
x11: xa,
x21: xb,
x12: xc,
x22: xd,
scale,
xnorm,
};
}
Laln2 {
x11,
x21,
x12,
x22,
scale,
xnorm,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn cmplx(re: f64, im: f64) -> Complex64 {
Complex64::new(re, im)
}
#[test]
fn dlapy2_survives_the_squares() {
assert_eq!(dlapy2(3.0, 4.0), 5.0);
assert_eq!(dlapy2(-3.0, -4.0), 5.0);
assert_eq!(dlapy2(5.0, 0.0), 5.0);
assert_eq!(dlapy2(0.0, -5.0), 5.0);
assert_eq!(dlapy2(0.0, 0.0), 0.0);
let big = dlapy2(1e200, 1e200);
assert!(big.is_finite(), "dlapy2(1e200, 1e200) = {big}");
assert!((big / (1e200 * 2.0f64.sqrt()) - 1.0).abs() < 1e-15);
let small = dlapy2(1e-200, 1e-200);
assert!(small > 0.0, "dlapy2(1e-200, 1e-200) underflowed");
assert!((small / (1e-200 * 2.0f64.sqrt()) - 1.0).abs() < 1e-15);
}
#[test]
fn dlapy3_survives_the_squares() {
assert_eq!(dlapy3(1.0, 2.0, 2.0), 3.0);
assert_eq!(dlapy3(0.0, 0.0, 0.0), 0.0);
assert_eq!(dlapy3(-3.0, 4.0, 0.0), 5.0);
let big = dlapy3(1e200, 1e200, 1e200);
assert!(big.is_finite(), "dlapy3(1e200, ...) = {big}");
assert!((big / (1e200 * 3.0f64.sqrt()) - 1.0).abs() < 1e-15);
let small = dlapy3(1e-200, 1e-200, 1e-200);
assert!(small > 0.0);
}
#[test]
fn dlae2_gives_the_eigenvalues_of_a_symmetric_block() {
let cases = [
(2.0, 1.0, 2.0),
(1.0, 0.0, 3.0),
(0.0, 1.0, 0.0), (-2.0, 0.5, -5.0), (4.0, -3.0, 4.0), (1.0, 0.0, 1.0), ];
for (a, b, c) in cases {
let (rt1, rt2) = dlae2(a, b, c);
let scale = a.abs().max(b.abs()).max(c.abs()).max(1.0);
assert!(
rt1.abs() >= rt2.abs() - 1e-14 * scale,
"({a},{b},{c}): |rt1| = {} < |rt2| = {}, but rt1 is the larger in modulus",
rt1.abs(),
rt2.abs()
);
assert!(
((rt1 + rt2) - (a + c)).abs() <= 1e-14 * scale,
"({a},{b},{c}): trace {} != {}",
rt1 + rt2,
a + c
);
assert!(
(rt1 * rt2 - (a * c - b * b)).abs() <= 1e-13 * scale * scale,
"({a},{b},{c}): determinant {} != {}",
rt1 * rt2,
a * c - b * b
);
for rt in [rt1, rt2] {
let p = (a - rt) * (c - rt) - b * b;
assert!(p.abs() <= 1e-13 * scale * scale, "({a},{b},{c}): rt = {rt} is not a root ({p})");
}
}
}
#[test]
fn dlaev2_returns_a_diagonalising_rotation() {
let cases = [
(2.0, 1.0, 2.0),
(1.0, 0.0, 3.0),
(0.0, 1.0, 0.0),
(-2.0, 0.5, -5.0),
(4.0, -3.0, 4.0),
(1.0, 2.0, 1.0), (0.0, 0.0, 0.0),
];
for (a, b, c) in cases {
let (rt1, rt2, cs, sn) = dlaev2(a, b, c);
let scale = a.abs().max(b.abs()).max(c.abs()).max(1.0);
assert!(
(cs * cs + sn * sn - 1.0).abs() <= 1e-14,
"({a},{b},{c}): rotation is not unit ({cs},{sn})"
);
let r11 = cs;
let r21 = sn;
let r12 = -sn;
let r22 = cs;
let d11 = r11 * (a * r11 + b * r21) + r21 * (b * r11 + c * r21);
let d22 = r12 * (a * r12 + b * r22) + r22 * (b * r12 + c * r22);
let d12 = r11 * (a * r12 + b * r22) + r21 * (b * r12 + c * r22);
assert!((d11 - rt1).abs() <= 1e-13 * scale, "({a},{b},{c}): R^T A R (1,1) = {d11} != rt1 = {rt1}");
assert!((d22 - rt2).abs() <= 1e-13 * scale, "({a},{b},{c}): R^T A R (2,2) = {d22} != rt2 = {rt2}");
assert!(d12.abs() <= 1e-13 * scale, "({a},{b},{c}): R^T A R (1,2) = {d12} is not zero");
}
}
#[test]
fn dladiv2_covers_its_three_branches() {
let cases = [
(1.0, 2.0, 3.0, 4.0), (1.0, 0.0, 3.0, 4.0), (2.0, 5.0, 7.0, 0.0), (-1.5, 0.25, 2.0, -8.0),
];
for (a, b, c, d) in cases {
let (r, t) = if d == 0.0 {
(0.0, 1.0 / c)
} else {
let r = d / c;
(r, 1.0 / (c + d * r))
};
let p = dladiv2(a, b, c, d, r, t);
let want = (a * c + b * d) / (c * c + d * d);
let scale = want.abs().max(1.0);
assert!((p - want).abs() <= 1e-13 * scale, "({a},{b},{c},{d}): p = {p} != {want}");
}
}
#[test]
fn zladiv_survives_a_huge_divisor() {
let q = zladiv(cmplx(1.0, 2.0), cmplx(3.0, 4.0));
assert!((q.re - 0.44).abs() < 1e-15, "re = {}", q.re);
assert!((q.im - 0.08).abs() < 1e-15, "im = {}", q.im);
let q = zladiv(cmplx(2.0, 1.0), cmplx(4.0, 3.0));
assert!((q.re - 0.44).abs() < 1e-15, "re = {}", q.re);
assert!((q.im + 0.08).abs() < 1e-15, "im = {}", q.im);
let q = zladiv(cmplx(1e300, 0.0), cmplx(1e300, 1e300));
assert!(q.re.is_finite() && q.im.is_finite(), "q = {q:?}");
assert!((q.re - 0.5).abs() < 1e-14, "re = {}", q.re);
assert!((q.im + 0.5).abs() < 1e-14, "im = {}", q.im);
}
#[test]
fn lascl_g_scales_by_the_ratio() {
for (cfrom, cto) in [(1.0, 2.0), (2.0, 1.0), (1.0, 1.0), (1e-5, 1.0), (1.0, 1e5)] {
let mut a = vec![1.0, -2.0, 3.0, -4.0];
dlascl_g(cfrom, cto, &mut a, 2, 2, 2);
let ratio = cto / cfrom;
for (k, v) in a.iter().enumerate() {
let want = [1.0, -2.0, 3.0, -4.0][k] * ratio;
assert!(
(v - want).abs() <= 1e-13 * want.abs().max(1.0),
"cfrom = {cfrom}, cto = {cto}: a[{k}] = {v} != {want}"
);
}
}
let mut empty: Vec<f64> = vec![];
dlascl_g(1.0, 2.0, &mut empty, 0, 0, 0);
let mut z = vec![cmplx(1.0, -1.0), cmplx(2.0, 0.0)];
zlascl_g(1.0, 4.0, &mut z, 2, 2, 1);
assert_eq!(z[0], cmplx(4.0, -4.0));
assert_eq!(z[1], cmplx(8.0, 0.0));
}
#[test]
fn lascl_g_survives_ratios_that_overflow_or_underflow() {
let cases = [
(1e-300, 1e-300, 1e300, 1e300),
(1e300, 1e300, 1e-300, 1e-300),
(5.0, 1.0, 0.0, 0.0),
(1e-100, 1e300, 1e300, 1e-100),
];
for (a_in, cfrom, cto, expected) in cases {
let mut a = vec![a_in, -a_in];
dlascl_g(cfrom, cto, &mut a, 1, 1, 2); assert!(a[0].is_finite(), "cfrom = {cfrom}, cto = {cto}: a[0] = {} is not finite", a[0]);
for (k, v) in a.iter().enumerate() {
let sign = if k == 0 { 1.0 } else { -1.0 };
let want = sign * expected;
assert!(
(v - want).abs() <= 1e-13 * want.abs().max(f64::MIN_POSITIVE),
"cfrom = {cfrom}, cto = {cto}: a[{k}] = {v} != {want}"
);
}
}
}
#[test]
fn zlarf_matches_the_explicit_reflector() {
let m = 3;
let n = 2;
let mut a = vec![
cmplx(1.0, 1.0),
cmplx(2.0, -1.0),
cmplx(-3.0, 0.5),
cmplx(0.5, 2.0),
cmplx(1.5, -0.5),
cmplx(2.5, 1.0),
];
let a0 = a.clone();
let v = vec![cmplx(1.0, 0.0), cmplx(0.5, -1.5), cmplx(-0.5, 0.25)];
let tau = cmplx(1.75, 0.5);
let mut p = vec![cmplx(0.0, 0.0); m * m];
for i in 0..m {
for j in 0..m {
let delta = if i == j { 1.0 } else { 0.0 };
p[i + j * m] = cmplx(delta, 0.0) - tau * v[i] * v[j].conj();
}
}
let mut want = vec![cmplx(0.0, 0.0); m * n];
for i in 0..m {
for j in 0..n {
let mut acc = cmplx(0.0, 0.0);
for k in 0..m {
acc += p[i + k * m] * a0[k + j * m];
}
want[i + j * m] = acc;
}
}
zlarf_left(&mut a, m, 0, m, 0, n, &v, tau);
for k in 0..m * n {
assert!(
(a[k] - want[k]).norm() <= 1e-14 * want[k].norm().max(1.0),
"zlarf_left at {k}: {:?} != {:?}",
a[k],
want[k]
);
}
let mut a = a0.clone();
let mut want = vec![cmplx(0.0, 0.0); m * n];
for i in 0..m {
for j in 0..n {
let mut acc = cmplx(0.0, 0.0);
for k in 0..n {
acc += a0[i + k * m] * p[k + j * m];
}
want[i + j * m] = acc;
}
}
let vn = v[0..n].to_vec();
zlarf_right(&mut a, m, 0, m, 0, n, &vn, tau);
for k in 0..m * n {
assert!(
(a[k] - want[k]).norm() <= 1e-14 * want[k].norm().max(1.0),
"zlarf_right at {k}: {:?} != {:?}",
a[k],
want[k]
);
}
let mut a = a0.clone();
zlarf_left(&mut a, m, 0, m, 0, n, &v, cmplx(0.0, 0.0));
assert_eq!(a, a0);
}
#[test]
fn zlarf_respects_the_block_bounds() {
let ld = 4;
let mut a = vec![cmplx(9.0, 9.0); ld * 3];
for i in 0..ld {
for j in 0..3 {
a[i + j * ld] = cmplx(i as f64 + 1.0, -(j as f64));
}
}
let a0 = a.clone();
let v = vec![cmplx(1.0, 0.0), cmplx(0.0, 1.0)];
zlarf_left(&mut a, ld, 1, 2, 1, 2, &v, cmplx(2.0, 0.0));
for j in 0..3 {
assert_eq!(a[0 + j * ld], a0[0 + j * ld], "row 0, column {j} changed");
}
for i in 0..ld {
assert_eq!(a[i], a0[i], "column 0, row {i} changed");
}
assert!(a.iter().zip(a0.iter()).any(|(x, y)| x != y));
}
#[test]
fn dladiv_matches_the_reference_on_the_rescale_arms() {
let cases: [([f64; 4], [f64; 2]); 12] = [
([1e308, 1e308, 1e308, 1e308], [9.99999999999999889e-01, 0.0]),
([1e-308, 1e-308, 1e-308, 1e-308], [9.99999999999999889e-01, 0.0]),
([4e-320, 0.0, 1e-320, 0.0], [3.99999999999999956e0, 0.0]),
([1e300, 1e300, 1e300, 1e300], [1.0, 0.0]),
([3.0, 2.0, 1.0, 1.0], [2.5, -0.5]),
([1.0, 0.0, 2.0, 1.0], [4.00000000000000022e-01, -2.00000000000000011e-01]),
([1e-300, 1e-300, 1e300, -1e300], [0.0, 0.0]),
([1e200, -1e200, 1e-200, 1e200], [-1.0, -1.0]),
([1.0, 1.0, 1e308, 1e308], [9.99999999999999909e-309, 0.0]),
([1e-300, 0.0, 1.0, 1.0], [5.00000000000000013e-301, -5.00000000000000013e-301]),
([1.0, 1.0, 1e-300, 1e-300], [9.99999999999999904e299, 0.0]),
([2.0, 1.0, 1.0, 3.0], [4.99999999999999944e-01, -0.5]),
];
for (k, ([a, b, c, d], want)) in cases.iter().enumerate() {
let (p, q) = dladiv(*a, *b, *c, *d);
assert_eq!([p, q], *want, "case {k}");
}
}
}