use super::Signature;
use crate::algebra::Multivector;
use crate::basis::Blade;
use crate::scalar::Float;
use typenum::{U16, U32};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Conformal2;
impl Signature for Conformal2 {
type NumBlades = U16;
const P: usize = 3; const Q: usize = 1; const R: usize = 0;
#[inline]
fn metric(i: usize) -> i8 {
match i {
0..=2 => 1, 3 => -1, _ => panic!("basis index {i} out of range for Conformal2 (DIM=4)"),
}
}
}
impl Conformal2 {
pub const E_PLUS: usize = 2;
pub const E_MINUS: usize = 3;
pub fn e_infinity<T: Float>() -> Multivector<T, Self> {
let mut mv = Multivector::zero();
mv.set(Blade::basis_vector(Self::E_PLUS), T::one());
mv.set(Blade::basis_vector(Self::E_MINUS), T::one());
mv
}
pub fn e_origin<T: Float>() -> Multivector<T, Self> {
let mut mv = Multivector::zero();
let half = T::one() / T::TWO;
mv.set(Blade::basis_vector(Self::E_MINUS), half);
mv.set(Blade::basis_vector(Self::E_PLUS), -half);
mv
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Conformal3;
impl Signature for Conformal3 {
type NumBlades = U32;
const P: usize = 4; const Q: usize = 1; const R: usize = 0;
#[inline]
fn metric(i: usize) -> i8 {
match i {
0..=3 => 1, 4 => -1, _ => panic!("basis index {i} out of range for Conformal3 (DIM=5)"),
}
}
}
pub type Cl4_1_0 = Conformal3;
impl Conformal3 {
pub const E_PLUS: usize = 3;
pub const E_MINUS: usize = 4;
pub fn e_infinity<T: Float>() -> Multivector<T, Self> {
let mut mv = Multivector::zero();
mv.set(Blade::basis_vector(Self::E_PLUS), T::one());
mv.set(Blade::basis_vector(Self::E_MINUS), T::one());
mv
}
pub fn e_origin<T: Float>() -> Multivector<T, Self> {
let mut mv = Multivector::zero();
let half = T::one() / T::TWO;
mv.set(Blade::basis_vector(Self::E_MINUS), half);
mv.set(Blade::basis_vector(Self::E_PLUS), -half);
mv
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::RELATIVE_EQ_EPS;
use approx::relative_eq;
use proptest::prelude::*;
#[test]
fn conformal2_dimensions() {
assert_eq!(Conformal2::DIM, 4);
assert_eq!(Conformal2::num_blades(), 16);
}
#[test]
fn conformal3_dimensions() {
assert_eq!(Conformal3::DIM, 5);
assert_eq!(Conformal3::num_blades(), 32);
}
#[test]
fn conformal2_pqr_consistency() {
assert_eq!(
Conformal2::P + Conformal2::Q + Conformal2::R,
Conformal2::DIM
);
}
#[test]
fn conformal3_pqr_consistency() {
assert_eq!(
Conformal3::P + Conformal3::Q + Conformal3::R,
Conformal3::DIM
);
}
proptest! {
#[test]
fn conformal2_euclidean_metric_positive(i in 0usize..2) {
prop_assert_eq!(Conformal2::metric(i), 1);
}
#[test]
fn conformal3_euclidean_metric_positive(i in 0usize..3) {
prop_assert_eq!(Conformal3::metric(i), 1);
}
#[test]
fn conformal2_e_infinity_squares_to_zero(s in -100.0f64..100.0) {
let e_inf = Conformal2::e_infinity::<f64>() * s;
let sq = e_inf * e_inf;
prop_assert!(sq.is_zero(RELATIVE_EQ_EPS));
}
#[test]
fn conformal2_e_origin_squares_to_zero(s in -100.0f64..100.0) {
let e_o = Conformal2::e_origin::<f64>() * s;
let sq = e_o * e_o;
prop_assert!(sq.is_zero(RELATIVE_EQ_EPS));
}
#[test]
fn conformal2_e_inf_dot_e_o_equals_minus_one(_dummy in 0..1i32) {
let e_inf = Conformal2::e_infinity::<f64>();
let e_o = Conformal2::e_origin::<f64>();
let dot = e_inf.inner(&e_o).scalar_part();
prop_assert!(relative_eq!(dot, -1.0, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_e_infinity_squares_to_zero(s in -100.0f64..100.0) {
let e_inf = Conformal3::e_infinity::<f64>() * s;
let sq = e_inf * e_inf;
prop_assert!(sq.is_zero(RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_e_origin_squares_to_zero(s in -100.0f64..100.0) {
let e_o = Conformal3::e_origin::<f64>() * s;
let sq = e_o * e_o;
prop_assert!(sq.is_zero(RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_e_inf_dot_e_o_equals_minus_one(_dummy in 0..1i32) {
let e_inf = Conformal3::e_infinity::<f64>();
let e_o = Conformal3::e_origin::<f64>();
let dot = e_inf.inner(&e_o).scalar_part();
prop_assert!(relative_eq!(dot, -1.0, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_geometric_product_associative(
a in any::<Multivector<f64, Conformal3>>(),
b in any::<Multivector<f64, Conformal3>>(),
c in any::<Multivector<f64, Conformal3>>(),
) {
let lhs = (a * b) * c;
let rhs = a * (b * c);
prop_assert!(relative_eq!(lhs, rhs, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_geometric_product_distributive(
a in any::<Multivector<f64, Conformal3>>(),
b in any::<Multivector<f64, Conformal3>>(),
c in any::<Multivector<f64, Conformal3>>(),
) {
let lhs = a * (b + c);
let rhs = (a * b) + (a * c);
prop_assert!(relative_eq!(lhs, rhs, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_reverse_involutory(a in any::<Multivector<f64, Conformal3>>()) {
prop_assert!(relative_eq!(
a.reverse().reverse(),
a,
max_relative = RELATIVE_EQ_EPS
));
}
#[test]
fn conformal3_reverse_antimorphism(
a in any::<Multivector<f64, Conformal3>>(),
b in any::<Multivector<f64, Conformal3>>(),
) {
let lhs = (a * b).reverse();
let rhs = b.reverse() * a.reverse();
prop_assert!(relative_eq!(lhs, rhs, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_outer_associative(
a in any::<Multivector<f64, Conformal3>>(),
b in any::<Multivector<f64, Conformal3>>(),
c in any::<Multivector<f64, Conformal3>>(),
) {
let lhs = a.exterior(&b).exterior(&c);
let rhs = a.exterior(&b.exterior(&c));
prop_assert!(relative_eq!(lhs, rhs, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
#[test]
fn conformal3_euclidean_subspace_inner_product(
ax in -10.0f64..10.0, ay in -10.0f64..10.0, az in -10.0f64..10.0,
bx in -10.0f64..10.0, by in -10.0f64..10.0, bz in -10.0f64..10.0,
) {
let cga_a: Multivector<f64, Conformal3> =
Multivector::vector(&[ax, ay, az, 0.0, 0.0]);
let cga_b: Multivector<f64, Conformal3> =
Multivector::vector(&[bx, by, bz, 0.0, 0.0]);
let cga_dot = cga_a.inner(&cga_b).scalar_part();
let expected_dot = ax * bx + ay * by + az * bz;
prop_assert!(relative_eq!(cga_dot, expected_dot, epsilon = RELATIVE_EQ_EPS, max_relative = RELATIVE_EQ_EPS));
}
}
#[test]
fn conformal2_metric_values() {
assert_eq!(Conformal2::metric(0), 1); assert_eq!(Conformal2::metric(1), 1); assert_eq!(Conformal2::metric(2), 1); assert_eq!(Conformal2::metric(3), -1); }
#[test]
fn conformal3_metric_values() {
assert_eq!(Conformal3::metric(0), 1); assert_eq!(Conformal3::metric(1), 1); assert_eq!(Conformal3::metric(2), 1); assert_eq!(Conformal3::metric(3), 1); assert_eq!(Conformal3::metric(4), -1); }
#[test]
fn conformal3_basis_vector_indices() {
assert_eq!(Conformal3::E_PLUS, 3);
assert_eq!(Conformal3::E_MINUS, 4);
}
#[test]
fn conformal2_basis_vector_indices() {
assert_eq!(Conformal2::E_PLUS, 2);
assert_eq!(Conformal2::E_MINUS, 3);
}
}