use crate::algebra::mv::Mv;
use crate::algebra::ops;
use crate::algebra::signature::Signature;
use crate::scalar::Scalar;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub enum FieldOp {
#[default]
ScalarProduct,
OuterProduct,
LeftContraction,
GradeProduct(u8),
InnerProduct,
GeometricProduct,
}
#[derive(Clone, Debug)]
pub struct ProbeSpec {
pub construction_index: usize,
pub arity: usize,
}
impl FieldOp {
pub fn evaluate_mv(&self, probe: &Mv, object: &Mv, sig: &Signature) -> Mv {
match self {
FieldOp::ScalarProduct => ops::scalar_product(probe, object, sig),
FieldOp::OuterProduct => ops::outer(probe, object, sig),
FieldOp::LeftContraction => ops::left_contract(probe, object, sig),
FieldOp::GradeProduct(k) => {
let result = ops::geometric(probe, object, sig);
ops::grade_project(&result, *k)
}
FieldOp::InnerProduct => ops::inner(probe, object, sig),
FieldOp::GeometricProduct => ops::geometric(probe, object, sig),
}
}
pub fn evaluate_scalar(&self, probe: &Mv, object: &Mv, sig: &Signature) -> Scalar {
let result = self.evaluate_mv(probe, object, sig);
result.coefficient(0)
}
pub fn evaluate_norm_sq(&self, probe: &Mv, object: &Mv, sig: &Signature) -> Scalar {
let result = self.evaluate_mv(probe, object, sig);
ops::norm_squared(&result, sig)
}
pub fn is_on_surface(&self, probe: &Mv, object: &Mv, sig: &Signature) -> bool {
self.evaluate_mv(probe, object, sig).is_zero()
}
pub fn from_keyword(s: &str) -> Option<Self> {
match s {
"ipns" | "scalar_product" => Some(FieldOp::ScalarProduct),
"opns" | "outer_product" => Some(FieldOp::OuterProduct),
"lcontract" | "left_contraction" => Some(FieldOp::LeftContraction),
"inner" | "inner_product" => Some(FieldOp::InnerProduct),
"geometric" | "geometric_product" => Some(FieldOp::GeometricProduct),
_ => None,
}
}
pub fn keyword(&self) -> String {
match self {
FieldOp::ScalarProduct => "ipns".to_string(),
FieldOp::OuterProduct => "opns".to_string(),
FieldOp::LeftContraction => "lcontract".to_string(),
FieldOp::GradeProduct(k) => format!("grade_product({})", k),
FieldOp::InnerProduct => "inner".to_string(),
FieldOp::GeometricProduct => "geometric".to_string(),
}
}
}
impl std::fmt::Display for FieldOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.keyword())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Rat;
#[test]
fn default_is_scalar_product() {
assert!(matches!(FieldOp::default(), FieldOp::ScalarProduct));
}
#[test]
fn keyword_roundtrip() {
for op in &[
FieldOp::ScalarProduct,
FieldOp::OuterProduct,
FieldOp::LeftContraction,
FieldOp::InnerProduct,
FieldOp::GeometricProduct,
] {
let kw = op.keyword();
let parsed = FieldOp::from_keyword(&kw).unwrap();
assert_eq!(parsed.keyword(), kw);
}
}
#[test]
fn scalar_product_returns_scalar_mv() {
let sig = Signature::new(0, 0, 3).unwrap();
let probe = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let object = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let result = FieldOp::ScalarProduct.evaluate_mv(&probe, &object, &sig);
assert_eq!(result.coefficient(0), Scalar::from(1i64));
assert_eq!(result.len(), 1);
}
#[test]
fn scalar_product_orthogonal() {
let sig = Signature::new(0, 0, 3).unwrap();
let probe = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let object = Mv::from_rat_terms(&[(0b010, Rat::from(1))]);
let val = FieldOp::ScalarProduct.evaluate_scalar(&probe, &object, &sig);
assert_eq!(val, Scalar::from(0i64));
}
#[test]
fn outer_product_returns_full_mv() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(1))]); let b = Mv::from_rat_terms(&[(0b010, Rat::from(1))]); let result = FieldOp::OuterProduct.evaluate_mv(&a, &b, &sig);
assert_eq!(result.coefficient(0b011), Scalar::from(1i64));
assert_eq!(result.len(), 1);
}
#[test]
fn left_contraction_returns_full_mv() {
let sig = Signature::new(0, 1, 2).unwrap();
let probe = Mv::from_rat_terms(&[(0b010, Rat::from(1))]); let object = Mv::from_rat_terms(&[(0b110, Rat::from(1))]); let result = FieldOp::LeftContraction.evaluate_mv(&probe, &object, &sig);
assert!(!result.is_zero());
}
#[test]
fn is_on_surface_test() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
assert!(FieldOp::OuterProduct.is_on_surface(&a, &a, &sig));
}
#[test]
fn inner_product_field_op() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(3))]);
let b = Mv::from_rat_terms(&[(0b001, Rat::from(4))]);
let result = FieldOp::InnerProduct.evaluate_mv(&a, &b, &sig);
assert_eq!(result.coefficient(0), Scalar::from(12i64));
}
#[test]
fn geometric_product_field_op() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let b = Mv::from_rat_terms(&[(0b010, Rat::from(1))]);
let result = FieldOp::GeometricProduct.evaluate_mv(&a, &b, &sig);
assert_eq!(result.coefficient(0b011), Scalar::from(1i64));
}
#[test]
fn norm_sq_evaluation() {
let sig = Signature::new(0, 0, 3).unwrap();
let probe = Mv::from_rat_terms(&[(0b001, Rat::from(3))]);
let object = Mv::from_rat_terms(&[(0b001, Rat::from(4))]);
let ns = FieldOp::ScalarProduct.evaluate_norm_sq(&probe, &object, &sig);
assert_eq!(ns, Scalar::from(144i64));
}
}