use std::ops::Add;
use std::ops::Sub;
use num_bigint::BigInt;
use num_traits::One;
use serde::Deserialize;
use serde::Serialize;
use crate::symbolic::calculus::differentiate;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify::is_zero;
use crate::symbolic::simplify_dag::simplify;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Vector {
pub x: Expr,
pub y: Expr,
pub z: Expr,
}
impl Vector {
#[must_use]
pub const fn new(
x: Expr,
y: Expr,
z: Expr,
) -> Self {
Self { x, y, z }
}
#[must_use]
pub fn magnitude(&self) -> Expr {
simplify(&Expr::new_sqrt(Expr::new_add(
Expr::new_add(
Expr::new_pow(self.x.clone(), Expr::BigInt(BigInt::from(2))),
Expr::new_pow(self.y.clone(), Expr::BigInt(BigInt::from(2))),
),
Expr::new_pow(self.z.clone(), Expr::BigInt(BigInt::from(2))),
)))
}
#[must_use]
pub fn dot(
&self,
other: &Self,
) -> Expr {
simplify(&Expr::new_add(
Expr::new_add(
Expr::new_mul(self.x.clone(), other.x.clone()),
Expr::new_mul(self.y.clone(), other.y.clone()),
),
Expr::new_mul(self.z.clone(), other.z.clone()),
))
}
#[must_use]
pub fn cross(
&self,
other: &Self,
) -> Self {
let x_comp = simplify(&Expr::new_sub(
Expr::new_mul(self.y.clone(), other.z.clone()),
Expr::new_mul(self.z.clone(), other.y.clone()),
));
let y_comp = simplify(&Expr::new_sub(
Expr::new_mul(self.z.clone(), other.x.clone()),
Expr::new_mul(self.x.clone(), other.z.clone()),
));
let z_comp = simplify(&Expr::new_sub(
Expr::new_mul(self.x.clone(), other.y.clone()),
Expr::new_mul(self.y.clone(), other.x.clone()),
));
Self::new(x_comp, y_comp, z_comp)
}
#[must_use]
pub fn normalize(&self) -> Self {
let mag = self.magnitude();
if is_zero(&mag) {
return self.clone();
}
self.scalar_mul(&Expr::new_div(Expr::BigInt(BigInt::one()), mag))
}
#[must_use]
pub fn scalar_mul(
&self,
scalar: &Expr,
) -> Self {
Self::new(
simplify(&Expr::new_mul(scalar.clone(), self.x.clone())),
simplify(&Expr::new_mul(scalar.clone(), self.y.clone())),
simplify(&Expr::new_mul(scalar.clone(), self.z.clone())),
)
}
#[must_use]
pub fn angle(
&self,
other: &Self,
) -> Expr {
let dot_prod = self.dot(other);
let mag_self = self.magnitude();
let mag_other = other.magnitude();
let cos_theta = simplify(&Expr::new_div(dot_prod, Expr::new_mul(mag_self, mag_other)));
simplify(&Expr::new_arccos(cos_theta))
}
#[must_use]
pub fn project_onto(
&self,
other: &Self,
) -> Self {
let dot_prod = self.dot(other);
let mag_other_sq = other.dot(other);
if is_zero(&mag_other_sq) {
return Self::new(
Expr::Constant(0.0),
Expr::Constant(0.0),
Expr::Constant(0.0),
);
}
let scalar = simplify(&Expr::new_div(dot_prod, mag_other_sq));
other.scalar_mul(&scalar)
}
#[must_use]
pub fn to_expr(&self) -> Expr {
Expr::Vector(vec![self.x.clone(), self.y.clone(), self.z.clone()])
}
}
impl Add for Vector {
type Output = Self;
fn add(
self,
other: Self,
) -> Self {
Self::new(
simplify(&Expr::new_add(self.x, other.x)),
simplify(&Expr::new_add(self.y, other.y)),
simplify(&Expr::new_add(self.z, other.z)),
)
}
}
impl Sub for Vector {
type Output = Self;
fn sub(
self,
other: Self,
) -> Self {
Self::new(
simplify(&Expr::new_sub(self.x, other.x)),
simplify(&Expr::new_sub(self.y, other.y)),
simplify(&Expr::new_sub(self.z, other.z)),
)
}
}
#[must_use]
pub fn gradient(
scalar_field: &Expr,
vars: (&str, &str, &str),
) -> Vector {
let df_dx = differentiate(scalar_field, vars.0);
let df_dy = differentiate(scalar_field, vars.1);
let df_dz = differentiate(scalar_field, vars.2);
Vector::new(df_dx, df_dy, df_dz)
}
#[must_use]
pub fn divergence(
vector_field: &Vector,
vars: (&str, &str, &str),
) -> Expr {
let d_fx_dx = differentiate(&vector_field.x, vars.0);
let d_fy_dy = differentiate(&vector_field.y, vars.1);
let d_fz_dz = differentiate(&vector_field.z, vars.2);
simplify(&Expr::new_add(Expr::new_add(d_fx_dx, d_fy_dy), d_fz_dz))
}
#[must_use]
pub fn curl(
vector_field: &Vector,
vars: (&str, &str, &str),
) -> Vector {
let d_fz_dy = differentiate(&vector_field.z, vars.1);
let d_fy_dz = differentiate(&vector_field.y, vars.2);
let d_fx_dz = differentiate(&vector_field.x, vars.2);
let d_fz_dx = differentiate(&vector_field.z, vars.0);
let d_fy_dx = differentiate(&vector_field.y, vars.0);
let d_fx_dy = differentiate(&vector_field.x, vars.1);
let x_comp = simplify(&Expr::new_sub(d_fz_dy, d_fy_dz));
let y_comp = simplify(&Expr::new_sub(d_fx_dz, d_fz_dx));
let z_comp = simplify(&Expr::new_sub(d_fy_dx, d_fx_dy));
Vector::new(x_comp, y_comp, z_comp)
}
#[must_use]
pub fn directional_derivative(
scalar_field: &Expr,
direction: &Vector,
vars: (&str, &str, &str),
) -> Expr {
let grad_f = gradient(scalar_field, vars);
grad_f.dot(direction)
}
#[must_use]
pub fn partial_derivative_vector(
vector_field: &Vector,
var: &str,
) -> Vector {
Vector::new(
differentiate(&vector_field.x, var),
differentiate(&vector_field.y, var),
differentiate(&vector_field.z, var),
)
}