use super::fields::{Block, FieldStore, read, write};
use super::rows::CoeffRows;
#[inline(always)]
pub(crate) fn flush(x: f32) -> f32 {
const C: f32 = f32::from_bits(25 << 23); (x + C) - C
}
#[inline(always)]
#[allow(clippy::needless_range_loop)] fn h_row(h: &mut [f32], da: &[f32], db: &[f32], a: &[f32], b: &[f32], c: &[f32], d: &[f32]) {
let n = h.len();
let (da, db, a, b, c, d) = (&da[..n], &db[..n], &a[..n], &b[..n], &c[..n], &d[..n]);
for k in 0..n {
h[k] = flush(da[k] * h[k] - db[k] * ((a[k] - b[k]) - (c[k] - d[k])));
}
}
#[inline(always)]
fn weight(a: f32, p: f64, b: f32) -> f64 {
let w = (1.0 + a as f64) * p / b as f64;
if b != 0.0 { w } else { 0.0 }
}
#[inline(always)]
#[allow(clippy::needless_range_loop)]
fn h_row_energy<const COMMIT: bool>(
h: &mut [f32],
da: &[f32],
db: &[f32],
a: &[f32],
b: &[f32],
c: &[f32],
d: &[f32],
) -> f64 {
let n = h.len();
let (da, db, a, b, c, d) = (&da[..n], &db[..n], &a[..n], &b[..n], &c[..n], &d[..n]);
let mut acc = [0.0f64; 4];
let mut one = |k: usize, acc: &mut f64| {
let old = h[k];
let new = flush(da[k] * old - db[k] * ((a[k] - b[k]) - (c[k] - d[k])));
*acc += weight(da[k], old as f64 * new as f64, db[k]);
if COMMIT {
h[k] = new;
}
};
let full = n & !3;
for k0 in (0..full).step_by(4) {
for (l, acc) in acc.iter_mut().enumerate() {
one(k0 + l, acc);
}
}
for k in full..n {
one(k, &mut acc[k & 3]);
}
(acc[0] + acc[1]) + (acc[2] + acc[3])
}
#[inline(always)]
fn hrow<const ENERGY: bool, const COMMIT: bool>(
h: &mut [f32],
da: &[f32],
db: &[f32],
a: &[f32],
b: &[f32],
c: &[f32],
d: &[f32],
) -> f64 {
if ENERGY {
h_row_energy::<COMMIT>(h, da, db, a, b, c, d)
} else {
h_row(h, da, db, a, b, c, d);
0.0
}
}
#[allow(clippy::needless_range_loop)]
fn e_energy_row(acc: &mut [f64; 4], r: usize, u: &[f32], ca: &[f32], cb: &[f32]) {
let n = u.len();
let (ca, cb) = (&ca[..n], &cb[..n]);
let s = r & 3;
let mut loc: [f64; 4] = core::array::from_fn(|l| acc[(s + l) & 3]);
let one = |k: usize, acc: &mut f64| {
let x = u[k] as f64;
*acc += weight(ca[k], x * x, cb[k]);
};
let full = n & !3;
for k0 in (0..full).step_by(4) {
for (l, acc) in loc.iter_mut().enumerate() {
one(k0 + l, acc);
}
}
for k in full..n {
one(k, &mut loc[k & 3]);
}
for (l, v) in loc.into_iter().enumerate() {
acc[(s + l) & 3] = v;
}
}
#[inline(always)]
#[allow(clippy::needless_range_loop)]
fn e_row(u: &mut [f32], ca: &[f32], cb: &[f32], a: &[f32], b: &[f32], c: &[f32], d: &[f32]) {
let n = u.len();
let (ca, cb, a, b, c, d) = (&ca[..n], &cb[..n], &a[..n], &b[..n], &c[..n], &d[..n]);
for k in 0..n {
u[k] = flush(ca[k] * u[k] + cb[k] * ((a[k] - b[k]) - (c[k] - d[k])));
}
}
fn plane(v: &[f32], local: usize, p: usize) -> &[f32] {
&v[local * p..(local + 1) * p]
}
pub(crate) fn update_h_slab(store: &FieldStore, co: &CoeffRows<'_>, s: usize) {
h_slab::<false, true>(store, co, s, &mut |_, _| {});
}
pub(crate) fn energy_h_slab<const COMMIT: bool>(
store: &FieldStore,
co: &CoeffRows<'_>,
s: usize,
sink: &mut dyn FnMut(usize, f64),
) {
h_slab::<true, COMMIT>(store, co, s, sink);
}
fn h_slab<const ENERGY: bool, const COMMIT: bool>(
store: &FieldStore,
co: &CoeffRows<'_>,
s: usize,
sink: &mut dyn FnMut(usize, f64),
) {
let lay = &store.layout;
let [nx, ny, nz] = lay.n;
let p = lay.plane;
let (p0, p1) = lay.planes(s);
let mut iw = write(&store.i[s]);
let uo = read(&store.u[s]);
let un = store.u.get(s + 1).map(read);
let [ix, iy, iz] = &mut *iw;
for gi in p0..p1 {
let l = gi - p0;
let (ux, uy, uz) = (
plane(&uo[0], l, p),
plane(&uo[1], l, p),
plane(&uo[2], l, p),
);
let ix = &mut ix[l * p..(l + 1) * p];
let iy = &mut iy[l * p..(l + 1) * p];
let iz = &mut iz[l * p..(l + 1) * p];
let mut wh = 0.0f64;
let m = nz - 1;
for j in 0..ny - 1 {
let r = j * nz;
wh += hrow::<ENERGY, COMMIT>(
&mut ix[r..r + m],
co.row(6, gi, j),
co.row(9, gi, j),
&uz[r + nz..],
&uz[r..],
&uy[r + 1..],
&uy[r..],
);
}
if gi + 1 < nx {
let next: &Block = if gi + 1 < p1 {
&uo
} else {
un.as_deref().expect("next slab")
};
let ln = if gi + 1 < p1 { l + 1 } else { 0 };
let (uyn, uzn) = (plane(&next[1], ln, p), plane(&next[2], ln, p));
for j in 0..ny {
let r = j * nz;
wh += hrow::<ENERGY, COMMIT>(
&mut iy[r..r + m],
co.row(7, gi, j),
co.row(10, gi, j),
&ux[r + 1..],
&ux[r..],
&uzn[r..],
&uz[r..],
);
}
for j in 0..ny - 1 {
let r = j * nz;
wh += hrow::<ENERGY, COMMIT>(
&mut iz[r..r + nz],
co.row(8, gi, j),
co.row(11, gi, j),
&uyn[r..],
&uy[r..],
&ux[r + nz..],
&ux[r..],
);
}
}
if ENERGY {
let mut we = 0.0f64;
for (a, uc) in [ux, uy, uz].into_iter().enumerate() {
let mut acc = [0.0f64; 4];
for j in 0..ny {
let r = j * nz;
let (ca, cb) = (co.row(a, gi, j), co.row(3 + a, gi, j));
e_energy_row(&mut acc, r, &uc[r..r + nz], ca, cb);
}
we += (acc[0] + acc[1]) + (acc[2] + acc[3]);
}
sink(gi, we + wh);
}
}
}
pub(crate) fn update_e_slab(store: &FieldStore, co: &CoeffRows<'_>, zero: &[f32], s: usize) {
let lay = &store.layout;
let [nx, ny, nz] = lay.n;
let p = lay.plane;
let (p0, p1) = lay.planes(s);
let mut uw = write(&store.u[s]);
let io = read(&store.i[s]);
let ip = if s > 0 {
Some(read(&store.i[s - 1]))
} else {
None
};
let [ux, uy, uz] = &mut *uw;
let z1 = &zero[..1];
for gi in p0..p1 {
let l = gi - p0;
let (ix, iy, iz) = (
plane(&io[0], l, p),
plane(&io[1], l, p),
plane(&io[2], l, p),
);
let (iyp, izp) = if gi == 0 {
(&zero[..p], &zero[..p])
} else if gi > p0 {
(plane(&io[1], l - 1, p), plane(&io[2], l - 1, p))
} else {
let prev = ip.as_deref().expect("previous slab");
let lp = prev[1].len() / p - 1;
(plane(&prev[1], lp, p), plane(&prev[2], lp, p))
};
let ux = &mut ux[l * p..(l + 1) * p];
let uy = &mut uy[l * p..(l + 1) * p];
let uz = &mut uz[l * p..(l + 1) * p];
if gi + 1 < nx {
for j in 0..ny {
let r = j * nz;
let (ca, cb) = (co.row(0, gi, j), co.row(3, gi, j));
let izm = if j == 0 { &zero[..nz] } else { &iz[r - nz..r] };
e_row(&mut ux[r..r + 1], ca, cb, &iz[r..], izm, &iy[r..], z1);
e_row(
&mut ux[r + 1..r + nz],
&ca[1..],
&cb[1..],
&iz[r + 1..],
&izm[1..],
&iy[r + 1..],
&iy[r..],
);
}
}
for j in 0..ny - 1 {
let r = j * nz;
let (ca, cb) = (co.row(1, gi, j), co.row(4, gi, j));
e_row(&mut uy[r..r + 1], ca, cb, &ix[r..], z1, &iz[r..], &izp[r..]);
e_row(
&mut uy[r + 1..r + nz],
&ca[1..],
&cb[1..],
&ix[r + 1..],
&ix[r..],
&iz[r + 1..],
&izp[r + 1..],
);
}
let m = nz - 1;
for j in 0..ny {
let r = j * nz;
let ixm = if j == 0 { &zero[..nz] } else { &ix[r - nz..r] };
e_row(
&mut uz[r..r + m],
co.row(2, gi, j),
co.row(5, gi, j),
&iy[r..],
&iyp[r..],
&ix[r..],
ixm,
);
}
}
}