use num_traits::Float;
#[allow(dead_code)]
pub(crate) fn lasq5<A, const PP: usize>(
i0: usize,
n0: usize,
z: &mut [A],
mut tau: A,
sigma: A,
eps: A,
) -> (A, A, A, A, A, A)
where
A: Float,
{
assert!(PP <= 1, "`PP` must be either 0 or 1.");
assert!(i0 + 1 < n0);
let d_thresh = eps * (sigma + tau);
if tau < d_thresh / (A::one() + A::one()) {
tau = A::zero();
}
let mut j4 = 4 * i0 + PP - 3;
let mut e_min = z[j4 + 3];
let mut d = z[j4 - 1] - tau;
let mut d_min = d;
if tau == A::zero() {
for j4 in (4 * i0..=4 * (n0 - 3)).step_by(4) {
z[j4 - PP - 3] = d + z[j4 + PP - 2];
let tmp = z[j4 + PP] / z[j4 - PP - 3];
d = d * tmp - tau;
if d < d_thresh {
d = A::zero();
}
if d < d_min {
d_min = d;
}
z[j4 - PP - 1] = z[j4 + PP - 2] * tmp;
let e = z[j4 - PP - 1];
if e < e_min {
e_min = e;
}
}
} else {
for j4 in (4 * i0..=4 * (n0 - 3)).step_by(4) {
z[j4 - PP - 3] = d + z[j4 + PP - 2];
let tmp = z[j4 + PP] / z[j4 - PP - 3];
d = d * tmp - tau;
if d < d_min {
d_min = d;
}
z[j4 - PP - 1] = z[j4 + PP - 2] * tmp;
let e = z[j4 - PP - 1];
if e < e_min {
e_min = e;
}
}
}
let d_nm2 = d;
let d_min_2 = d_min;
j4 = 4 * (n0 - 2) - PP;
let mut j4_p2 = j4 + 2 * PP - 1;
z[j4 - 3] = d_nm2 + z[j4_p2 - 1];
z[j4 - 1] = z[j4_p2 + 1] * (z[j4_p2 - 1] / z[j4 - 3]);
let d_nm1 = z[j4_p2 + 1] * (d_nm2 / z[j4 - 3]) - tau;
if d_nm1 < d_min {
d_min = d_nm1;
}
let d_min_1 = d_min;
j4 += 4;
j4_p2 = j4 + 2 * PP - 1;
z[j4 - 3] = d_nm1 + z[j4_p2 - 1];
z[j4 - 1] = z[j4_p2 + 1] * (z[j4_p2 - 1] / z[j4 - 3]);
let d_n = z[j4_p2 + 1] * (d_nm1 / z[j4 - 3]) - tau;
if d_n < d_min {
d_min = d_n;
}
z[j4 + 1] = d_n;
z[4 * n0 - PP] = e_min;
(d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2)
}
#[cfg(test)]
mod tests {
#[test]
fn lasq5_ping_with_tau() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0, 2.0, 0.0, 0.0, 0.0, ];
let i0 = 1;
let n0 = 4;
let tau = 0.5;
let sigma = 0.0;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 0>(i0, n0, &mut z, tau, sigma, eps);
assert!(d_min.is_finite());
assert!(d_min_1.is_finite());
assert!(d_min_2.is_finite());
assert!(d_n.is_finite());
assert!(d_nm1.is_finite());
assert!(d_nm2.is_finite());
assert!(d_min <= d_min_1);
assert!(d_min <= d_min_2);
assert!(d_min <= d_n);
assert!(d_min <= d_nm1);
assert!(d_min <= d_nm2);
assert_ne!(z[0], 3.0); }
#[test]
fn lasq5_pong_with_tau() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0, 2.0, 0.0, 0.0, ];
let i0 = 1;
let n0 = 4;
let tau = 0.3;
let sigma = 0.1;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 1>(i0, n0, &mut z, tau, sigma, eps);
assert!(d_min.is_finite());
assert!(d_min_1.is_finite());
assert!(d_min_2.is_finite());
assert!(d_n.is_finite());
assert!(d_nm1.is_finite());
assert!(d_nm2.is_finite());
assert_ne!(z[1], 3.0);
}
#[test]
fn lasq5_ping_zero_tau() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0, 2.0,
0.0, 0.0, 0.0,
];
let i0 = 1;
let n0 = 4;
let tau = 1e-20; let sigma = 0.0;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 0>(i0, n0, &mut z, tau, sigma, eps);
assert!(d_min.is_finite());
assert!(d_min_1.is_finite());
assert!(d_min_2.is_finite());
assert!(d_n.is_finite());
assert!(d_nm1.is_finite());
assert!(d_nm2.is_finite());
assert!(d_min >= -1e-10, "d_min = {}, expected >= -1e-10", d_min);
}
#[test]
fn lasq5_pong_zero_tau() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0,
2.0, 0.0, 0.0,
];
let i0 = 1;
let n0 = 4;
let tau = 1e-20;
let sigma = 0.0;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 1>(i0, n0, &mut z, tau, sigma, eps);
assert!(d_min.is_finite());
assert!(d_min_1.is_finite());
assert!(d_min_2.is_finite());
assert!(d_n.is_finite());
assert!(d_nm1.is_finite());
assert!(d_nm2.is_finite());
}
#[test]
fn lasq5_ping_properties() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 2.0, -1.0, 3.0, 1.0, -1.0, 3.0, 2.0, 2.0, 1.0, 1.0, -1.0, -1.0,
2.0, -3.0, -1.0, 2.0,
];
let i0 = 1;
let n0 = 4;
let tau = 0.1;
let sigma = 0.0;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 0>(i0, n0, &mut z, tau, sigma, eps);
assert!(!d_min.is_nan(), "d_min should not be NaN");
assert!(!d_n.is_nan(), "d_n should not be NaN");
assert!(!d_nm1.is_nan(), "d_nm1 should not be NaN");
assert!(!d_nm2.is_nan(), "d_nm2 should not be NaN");
assert!(!d_min_1.is_nan(), "d_min_1 should not be NaN");
assert!(!d_min_2.is_nan(), "d_min_2 should not be NaN");
}
#[test]
fn lasq5_pong_properties() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 2.0, -1.0, 3.0, 1.0, -1.0, 3.0, 2.0, 2.0, 1.0, 1.0, -1.0, -1.0,
2.0, -3.0, -1.0, 2.0,
];
let i0 = 1;
let n0 = 4;
let tau = 0.1;
let sigma = 0.0;
let eps = 1e-15;
let (d_min, d_min_1, d_min_2, d_n, d_nm1, d_nm2) =
super::lasq5::<f64, 1>(i0, n0, &mut z, tau, sigma, eps);
assert!(!d_min.is_nan(), "d_min should not be NaN");
assert!(!d_n.is_nan(), "d_n should not be NaN");
assert!(!d_nm1.is_nan(), "d_nm1 should not be NaN");
assert!(!d_nm2.is_nan(), "d_nm2 should not be NaN");
assert!(!d_min_1.is_nan(), "d_min_1 should not be NaN");
assert!(!d_min_2.is_nan(), "d_min_2 should not be NaN");
}
#[test]
fn lasq5_emin_storage() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0, 2.0,
0.0, 0.0, 0.0,
];
let i0 = 1;
let n0 = 4;
let tau = 0.5;
let sigma = 0.0;
let eps = 1e-15;
super::lasq5::<f64, 0>(i0, n0, &mut z, tau, sigma, eps);
let emin = z[16];
assert!(emin.is_finite());
assert!(emin >= 0.0);
}
#[test]
fn lasq5_dn_storage() {
let mut z = [
0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 4.0, 0.0, 1.5, 0.0, 2.5, 0.0, 1.0, 0.0, 2.0,
0.0, 0.0, 0.0,
];
let i0 = 1;
let n0 = 4;
let tau = 0.5;
let sigma = 0.0;
let eps = 1e-15;
let (_, _, _, d_n, _, _) = super::lasq5::<f64, 0>(i0, n0, &mut z, tau, sigma, eps);
assert!(d_n.is_finite());
assert!(d_n >= -1.0); }
}