use alloc::vec::Vec;
use super::{ONE_OVER_12, ONE_OVER_240, ONE_OVER_60, SQRT15_OVER_3, TEN_OVER_3};
use crate::{
float::{from_f64, SemiflowFloat},
graph::Laplacian,
graph_signal::GraphSignal,
scratch::ScratchPool,
state::State,
};
#[allow(clippy::too_many_arguments)]
pub(crate) fn apply_exp_omega6_kernel<F: SemiflowFloat>(
lap1: &Laplacian<F>,
lap2: &Laplacian<F>,
lap3: &Laplacian<F>,
tau: F,
src: &GraphSignal<F>,
dst: &mut GraphSignal<F>,
scratch: &mut ScratchPool<F>,
) {
let n = src.len();
let gl6 = [lap1, lap2, lap3];
let (mut ov, mut op) = (scratch.take_vec(n), scratch.take_vec(n));
let mut sv: [Vec<F>; 20] = core::array::from_fn(|_| scratch.take_vec(n));
let s: &mut [&mut [F]; 20] = &mut sv.each_mut().map(|v| -> &mut [F] { v });
macro_rules! omega6 {
($pv:expr, $dv:expr) => {
apply_omega6(&gl6, tau, $pv, $dv, s)
};
}
omega6!(src.values(), &mut ov); accumulate_taylor6(src, dst, &mut ov, &mut op, |pv, dv| omega6!(pv, dv));
scratch.return_vec(ov);
scratch.return_vec(op);
for buf in sv {
scratch.return_vec(buf);
}
}
fn accumulate_taylor6<F, Step>(
src: &GraphSignal<F>,
dst: &mut GraphSignal<F>,
omega_v: &mut [F],
omega_pow: &mut [F],
mut step: Step,
) where
F: SemiflowFloat,
Step: FnMut(&[F], &mut [F]),
{
let one = F::one();
let two = one + one;
let six = two + two + two;
let f24 = six * (two + two);
let f120 = f24 * (two + two + two - one); let f720 = f120 * (two + two + two);
dst.copy_from(src);
dst.axpy_into_slice(one, omega_v);
omega_pow.copy_from_slice(omega_v);
step(omega_pow, omega_v);
dst.axpy_into_slice(one / two, omega_v);
omega_pow.copy_from_slice(omega_v);
step(omega_pow, omega_v);
dst.axpy_into_slice(one / six, omega_v);
omega_pow.copy_from_slice(omega_v);
step(omega_pow, omega_v);
dst.axpy_into_slice(one / f24, omega_v);
omega_pow.copy_from_slice(omega_v);
step(omega_pow, omega_v);
dst.axpy_into_slice(one / f120, omega_v);
omega_pow.copy_from_slice(omega_v);
step(omega_pow, omega_v);
dst.axpy_into_slice(one / f720, omega_v);
}
fn apply_omega6<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
v: &[F],
out: &mut [F],
s: &mut [&mut [F]; 20],
) {
let sq = from_f64::<F>(SQRT15_OVER_3);
let t3 = from_f64::<F>(TEN_OVER_3);
let i12 = from_f64::<F>(ONE_OVER_12);
let i60 = from_f64::<F>(ONE_OVER_60);
let i240 = from_f64::<F>(ONE_OVER_240);
let n20 = from_f64::<F>(-20.0);
let n = v.len();
let [s00, s01, s02, s03, s04, s05, s06, s07, s08, s09, s10, s11, s12, s13, s14, s15, s16, _s17, _s18, s19] =
s;
phase12_b_and_c1(laps, tau, v, out, s00, s01, s02, s03, s04, sq, t3);
phase3_c2(
laps, tau, out, s00, s01, s02, s03, s05, s06, s07, s08, s09, s10, s11, sq, t3, i60,
);
for i in 0..n {
s06[i] = n20 * s03[i] - out[i] + s00[i]; s07[i] = s04[i] + s05[i]; }
phase5a_lop_rv(
laps, tau, out, s00, s01, s02, s07, s08, s09, s10, s11, s12, s15, sq, t3, n20,
);
phase5b_rop_lv(
laps, tau, s00, s01, s02, s06, s08, s09, s10, s11, s12, s13, s14, s16, s19, sq, t3, i60,
);
for i in 0..n {
out[i] = s03[i] + i12 * out[i] + i240 * (s15[i] - s19[i]);
}
}
#[allow(clippy::too_many_arguments)]
fn phase12_b_and_c1<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
v: &[F],
out: &mut [F],
s00: &mut [F],
s01: &mut [F],
s02: &mut [F],
s03: &mut [F],
s04: &mut [F],
sq: F,
t3: F,
) {
let n = v.len();
laps[0].apply_into_slice(v, s00);
laps[1].apply_into_slice(v, s01);
laps[2].apply_into_slice(v, s02);
for i in 0..n {
let (a1, a2, a3) = (-s00[i], -s01[i], -s02[i]);
s03[i] = tau * a2;
s04[i] = tau * sq * (a3 - a1);
out[i] = tau * t3 * (a3 - a2 - a2 + a1);
}
laps[1].apply_into_slice(s04, s00);
laps[0].apply_into_slice(s03, s01);
laps[2].apply_into_slice(s03, s02);
for i in 0..n {
let b1_b2v = -tau * s00[i];
let b2_b1v = tau * sq * (s01[i] - s02[i]);
s00[i] = b1_b2v - b2_b1v;
}
}
#[allow(clippy::too_many_arguments)]
fn phase3_c2<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
out: &[F],
s00: &[F],
s01: &mut [F],
s02: &mut [F],
s03: &[F],
s05: &mut [F],
s06: &mut [F],
s07: &mut [F],
s08: &mut [F],
s09: &mut [F],
s10: &mut [F],
s11: &mut [F],
sq: F,
t3: F,
i60: F,
) {
let n = out.len();
for i in 0..n {
s01[i] = out[i] + out[i] + s00[i]; }
laps[1].apply_into_slice(s01, s02);
for v in &mut s02[..n] {
*v = -tau * *v;
} laps[0].apply_into_slice(s03, s05);
laps[1].apply_into_slice(s03, s06);
laps[2].apply_into_slice(s03, s07);
for i in 0..n {
s08[i] = tau * sq * (s05[i] - s07[i]); }
laps[1].apply_into_slice(s08, s09);
laps[0].apply_into_slice(s06, s10);
laps[2].apply_into_slice(s06, s11);
for i in 0..n {
let b1_b2_b1v = -tau * s09[i];
let b2_b1_b1v = tau * sq * (-tau) * (s10[i] - s11[i]);
let c1_b1v = b1_b2_b1v - b2_b1_b1v;
let two_b3_b1v = (tau + tau) * t3 * (-s07[i] + s06[i] + s06[i] - s05[i]);
s05[i] = -i60 * (s02[i] - (two_b3_b1v + c1_b1v));
}
}
#[allow(clippy::too_many_arguments)]
fn phase5a_lop_rv<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
out: &[F],
s00: &mut [F],
s01: &mut [F],
s02: &mut [F],
s07: &[F],
s08: &mut [F],
s09: &mut [F],
s10: &mut [F],
s11: &mut [F],
s12: &mut [F],
s15: &mut [F],
sq: F,
t3: F,
n20: F,
) {
let n = s07.len();
laps[0].apply_into_slice(s07, s08);
laps[1].apply_into_slice(s07, s09);
laps[2].apply_into_slice(s07, s10);
for i in 0..n {
s00[i] = -tau * s09[i]; s01[i] = tau * sq * (s08[i] - s10[i]); }
laps[1].apply_into_slice(s01, s02);
laps[0].apply_into_slice(s00, s11);
laps[2].apply_into_slice(s00, s12);
for i in 0..n {
let b3rv = tau * t3 * (-s10[i] + s09[i] + s09[i] - s08[i]);
let b1_b2rv = -tau * s02[i];
let b2_b1rv = tau * sq * (s11[i] - s12[i]);
s15[i] = n20 * s00[i] - b3rv + (b1_b2rv - b2_b1rv);
}
let _ = out; }
#[allow(clippy::too_many_arguments)]
fn phase5b_rop_lv<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
s00: &mut [F],
s01: &mut [F],
s02: &mut [F],
s06: &[F],
s08: &mut [F],
s09: &mut [F],
s10: &mut [F],
s11: &mut [F],
s12: &mut [F],
s13: &mut [F],
s14: &mut [F],
s16: &mut [F],
s19: &mut [F],
sq: F,
t3: F,
i60: F,
) {
let n = s06.len();
laps[0].apply_into_slice(s06, s08);
laps[1].apply_into_slice(s06, s09);
laps[2].apply_into_slice(s06, s10);
for i in 0..n {
s00[i] = -tau * s09[i]; s01[i] = tau * sq * (s08[i] - s10[i]); }
laps[1].apply_into_slice(s01, s02);
laps[0].apply_into_slice(s00, s11);
laps[2].apply_into_slice(s00, s12);
for i in 0..n {
let b3lv = tau * t3 * (-s10[i] + s09[i] + s09[i] - s08[i]);
let c1lv = -tau * s02[i] - tau * sq * (s11[i] - s12[i]);
s02[i] = b3lv + b3lv + c1lv; }
phase5b_c2lv(
laps, tau, s00, s01, s02, s08, s09, s11, s12, s13, s14, s16, s19, sq, t3, i60,
);
}
#[allow(clippy::too_many_arguments)]
fn phase5b_c2lv<F: SemiflowFloat>(
laps: &[&Laplacian<F>; 3],
tau: F,
s00: &[F],
s01: &[F],
s02: &[F],
s08: &mut [F],
s09: &mut [F],
s11: &[F],
s12: &[F],
s13: &mut [F],
s14: &mut [F],
s16: &mut [F],
s19: &mut [F],
sq: F,
t3: F,
i60: F,
) {
let n = s00.len();
laps[1].apply_into_slice(s02, s08);
for v in &mut s08[..n] {
*v = -tau * *v; }
laps[1].apply_into_slice(s00, s09);
for i in 0..n {
s13[i] = tau * sq * (s11[i] - s12[i]);
}
laps[1].apply_into_slice(s13, s14);
laps[0].apply_into_slice(s09, s13);
laps[2].apply_into_slice(s09, s16);
for i in 0..n {
let b1_b2_b1lv = -tau * s14[i];
let b2_b1_b1lv = tau * sq * (-tau) * (s13[i] - s16[i]);
let c1_b1lv = b1_b2_b1lv - b2_b1_b1lv;
let two_b3_b1lv = (tau + tau) * t3 * (-s12[i] + s09[i] + s09[i] - s11[i]);
let c2lv = -i60 * (s08[i] - (two_b3_b1lv + c1_b1lv));
s19[i] = s01[i] + c2lv;
}
}