use crate::calculus::differential::gradient_4d;
use crate::calculus::fields::divergence_4d;
pub fn laplacian_4d<F>(f: &F, point: &[f64; 4], h: f64) -> f64
where
F: Fn(&[f64]) -> f64,
{
let mut laplacian = 0.0;
for i in 0..4 {
let mut p_plus = *point;
let mut p_minus = *point;
p_plus[i] += h;
p_minus[i] -= h;
laplacian += (f(&p_plus) - 2.0 * f(point) + f(&p_minus)) / (h * h);
}
laplacian
}
pub fn directional_derivative<F>(f: &F, point: &[f64; 4], direction: &[f64; 4], h: f64) -> f64
where
F: Fn(&[f64]) -> f64,
{
let grad = gradient_4d(f, point, h);
let norm =
(direction[0].powi(2) + direction[1].powi(2) + direction[2].powi(2) + direction[3].powi(2))
.sqrt();
if norm == 0.0 {
return 0.0;
}
let unit_dir = [
direction[0] / norm,
direction[1] / norm,
direction[2] / norm,
direction[3] / norm,
];
grad[0] * unit_dir[0] + grad[1] * unit_dir[1] + grad[2] * unit_dir[2] + grad[3] * unit_dir[3]
}
pub fn grad_div_4d<F>(field: &F, point: &[f64; 4], h: f64) -> [f64; 4]
where
F: Fn(&[f64; 4]) -> [f64; 4],
{
let div_func = |p: &[f64]| divergence_4d(field, &[p[0], p[1], p[2], p[3]], h);
gradient_4d(&div_func, point, h)
}
pub fn div_grad_4d<F>(f: &F, point: &[f64; 4], h: f64) -> f64
where
F: Fn(&[f64]) -> f64,
{
let grad_field = |p: &[f64; 4]| gradient_4d(f, p, h);
divergence_4d(&grad_field, point, h)
}
pub fn biharmonic_4d<F>(f: &F, point: &[f64; 4], h: f64) -> f64
where
F: Fn(&[f64]) -> f64,
{
let laplacian_func = |p: &[f64]| laplacian_4d(f, &[p[0], p[1], p[2], p[3]], h);
laplacian_4d(&laplacian_func, point, h)
}
pub fn dalembertian<F>(f: &F, point: &[f64; 4], h: f64) -> f64
where
F: Fn(&[f64]) -> f64,
{
let mut p_plus = *point;
let mut p_minus = *point;
p_plus[3] += h;
p_minus[3] -= h;
let d2_dt2 = (f(&p_plus) - 2.0 * f(point) + f(&p_minus)) / (h * h);
let mut laplacian_spatial = 0.0;
for i in 0..3 {
p_plus = *point;
p_minus = *point;
p_plus[i] += h;
p_minus[i] -= h;
laplacian_spatial += (f(&p_plus) - 2.0 * f(point) + f(&p_minus)) / (h * h);
}
d2_dt2 - laplacian_spatial
}
pub fn flux_through_hypersurface<F>(
field: &F,
center: &[f64; 4],
normal: &[f64; 4],
area: f64,
) -> f64
where
F: Fn(&[f64; 4]) -> [f64; 4],
{
let field_val = field(center);
let dot_product = field_val[0] * normal[0]
+ field_val[1] * normal[1]
+ field_val[2] * normal[2]
+ field_val[3] * normal[3];
dot_product * area
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_laplacian_4d() {
let f = |p: &[f64]| p[0] * p[0] + p[1] * p[1] + p[2] * p[2] + p[3] * p[3];
let lap = laplacian_4d(&f, &[1.0, 2.0, 3.0, 4.0], 1e-5);
assert!((lap - 8.0).abs() < 1e-3);
}
#[test]
fn test_laplacian_harmonic() {
let f = |p: &[f64]| p[0] * p[0] - p[1] * p[1] - p[2] * p[2] - p[3] * p[3];
let lap = laplacian_4d(&f, &[1.0, 1.0, 1.0, 1.0], 1e-5);
assert!((lap + 4.0).abs() < 0.1);
}
#[test]
fn test_div_grad_equals_laplacian() {
let f = |p: &[f64]| p[0] * p[0] + p[1] * p[1] + p[2] * p[2] + p[3] * p[3];
let point = [1.0, 2.0, 3.0, 4.0];
let lap = laplacian_4d(&f, &point, 1e-5);
let div_grad = div_grad_4d(&f, &point, 1e-5);
assert!((lap - div_grad).abs() < 1e-2);
}
#[test]
fn test_dalembertian() {
let f = |p: &[f64]| (p[0] - p[3]).sin();
let point = [0.0, 0.0, 0.0, 0.0];
let box_op = dalembertian(&f, &point, 1e-4);
assert!(box_op.abs() < 1e-1);
}
}