use std::sync::Arc;
use num_bigint::BigInt;
use num_traits::Zero;
use serde::Deserialize;
use serde::Serialize;
use crate::symbolic::calculus::definite_integrate;
use crate::symbolic::calculus::differentiate;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify_dag::simplify;
use crate::symbolic::vector::Vector;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DifferentialForm {
pub terms: std::collections::BTreeMap<u32, Expr>,
}
#[must_use]
pub fn exterior_derivative(
form: &DifferentialForm,
vars: &[&str],
) -> DifferentialForm {
let mut result_terms = std::collections::BTreeMap::new();
for (blade, coeff) in &form.terms {
for (i, _item) in vars.iter().enumerate() {
let new_blade = (1 << i) | blade;
if new_blade.count_ones() != blade.count_ones() + 1 {
continue;
}
let d_coeff = differentiate(coeff, vars[i]);
let mut sign = 1i64;
for j in 0..i {
if (blade >> j) & 1 == 1 {
sign *= -1;
}
}
let signed_coeff = if sign == 1 {
d_coeff
} else {
simplify(&Expr::new_neg(d_coeff))
};
let entry = result_terms
.entry(new_blade)
.or_insert(Expr::BigInt(BigInt::zero()));
*entry = simplify(&Expr::new_add(entry.clone(), signed_coeff));
}
}
DifferentialForm { terms: result_terms }
}
#[must_use]
pub fn wedge_product(
form1: &DifferentialForm,
form2: &DifferentialForm,
) -> DifferentialForm {
let mut result_terms = std::collections::BTreeMap::new();
for (blade1, coeff1) in &form1.terms {
for (blade2, coeff2) in &form2.terms {
if (blade1 & blade2) != 0 {
continue;
}
let new_blade = blade1 | blade2;
let mut sign = 1i64;
let mut temp_blade2 = *blade2;
while temp_blade2 > 0 {
let i = temp_blade2.trailing_zeros();
let swaps = (blade1 >> (i + 1)).count_ones();
if swaps % 2 != 0 {
sign *= -1;
}
temp_blade2 &= !(1 << i);
}
let new_coeff = simplify(&Expr::new_mul(coeff1.clone(), coeff2.clone()));
let signed_coeff = if sign == 1 {
new_coeff
} else {
simplify(&Expr::new_neg(new_coeff))
};
let entry = result_terms
.entry(new_blade)
.or_insert(Expr::BigInt(BigInt::zero()));
*entry = simplify(&Expr::new_add(entry.clone(), signed_coeff));
}
}
DifferentialForm { terms: result_terms }
}
#[must_use]
pub fn boundary(domain: &Expr) -> Expr {
Expr::Boundary(Arc::new(domain.clone()))
}
#[must_use]
pub fn generalized_stokes_theorem(
omega: &DifferentialForm,
manifold: &Expr,
vars: &[&str],
) -> Expr {
let d_omega = exterior_derivative(omega, vars);
let integral_d_omega = Expr::Integral {
integrand: Arc::new(Expr::Variable(format!("{d_omega:?}"))),
var: Arc::new(Expr::Variable(manifold.to_string())),
lower_bound: Arc::new(Expr::Variable("M".to_string())),
upper_bound: Arc::new(Expr::BigInt(BigInt::zero())),
};
let integral_omega = Expr::Integral {
integrand: Arc::new(Expr::Variable(format!("{omega:?}"))),
var: Arc::new(Expr::Variable(manifold.to_string())),
lower_bound: Arc::new(boundary(manifold)),
upper_bound: Arc::new(Expr::BigInt(BigInt::zero())),
};
Expr::Eq(Arc::new(integral_d_omega), Arc::new(integral_omega))
}
#[must_use]
pub fn gauss_theorem(
vector_field: &Vector,
volume: &Expr,
) -> Expr {
let div_f = super::vector::divergence(vector_field, ("x", "y", "z"));
let integral_div = Expr::VolumeIntegral {
scalar_field: Arc::new(div_f),
volume: Arc::new(volume.clone()),
};
let surface_integral = Expr::SurfaceIntegral {
vector_field: Arc::new(Expr::Variable("F".to_string())),
surface: Arc::new(boundary(volume)),
};
Expr::Eq(Arc::new(integral_div), Arc::new(surface_integral))
}
#[must_use]
pub fn stokes_theorem(
vector_field: &Vector,
surface: &Expr,
) -> Expr {
let curl_f = super::vector::curl(vector_field, ("x", "y", "z"));
let integral_curl = Expr::SurfaceIntegral {
vector_field: Arc::new(curl_f.to_expr()),
surface: Arc::new(surface.clone()),
};
let line_integral = Expr::Integral {
integrand: Arc::new(Expr::Variable("F · dr".to_string())),
var: Arc::new(Expr::Variable("t".to_string())),
lower_bound: Arc::new(boundary(surface)),
upper_bound: Arc::new(Expr::BigInt(BigInt::zero())),
};
Expr::Eq(Arc::new(integral_curl), Arc::new(line_integral))
}
#[must_use]
pub fn greens_theorem(
p: &Expr,
q: &Expr,
domain: &Expr,
) -> Expr {
let dq_dx = differentiate(q, "x");
let dp_dy = differentiate(p, "y");
let integrand_da = simplify(&Expr::new_sub(dq_dx, dp_dy));
let integral_da = definite_integrate(
&integrand_da,
"A",
&Expr::Domain(format!("{domain}")),
&Expr::BigInt(BigInt::zero()),
);
let integrand_line = Expr::new_add(
Expr::new_mul(p.clone(), Expr::Variable("dx".to_string())),
Expr::new_mul(q.clone(), Expr::Variable("dy".to_string())),
);
let line_integral = Expr::Integral {
integrand: Arc::new(integrand_line),
var: Arc::new(Expr::Variable("t".to_string())),
lower_bound: Arc::new(boundary(domain)),
upper_bound: Arc::new(Expr::BigInt(BigInt::zero())),
};
Expr::Eq(Arc::new(integral_da), Arc::new(line_integral))
}