geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
//! The `mv!` macro for concise multivector construction.
//!
//! ```no_run
//! use geoit::mv;
//!
//! let v = mv![0b001 => 3, 0b010 => 4, 0b100 => 5];    // 3e₀ + 4e₁ + 5e₂
//! let p = mv![0b01 => 1/2, 0b10 => 1/2];                // (1/2)e₀ + (1/2)e₁
//! let z = mv![];                                          // zero Mv
//! let s = mv![0 => 7];                                    // scalar 7
//! ```
//!
//! Each entry is `mask => coefficient`. The coefficient can be:
//! - An integer literal: `3`, `-1`
//! - A rational literal: `1/2`, `-3/7`

/// Construct an `Mv` from literal (mask, coefficient) pairs.
///
/// # Syntax
/// - `mv![]` — zero multivector
/// - `mv![mask => integer, ...]` — integer coefficients
/// - `mv![mask => num/den, ...]` — rational coefficients
///
/// # Examples
/// ```ignore
/// let v = mv![0b001 => 3, 0b010 => 4];
/// let p = mv![0b01 => 1/2, 0b10 => 1/2];
/// ```
#[macro_export]
macro_rules! mv {
    // Empty: zero Mv
    [] => {
        $crate::algebra::mv::Mv::new()
    };
    // One or more entries with rational (num/den) or integer coefficients
    [ $( $mask:expr => $num:literal $( / $den:literal )? ),+ $(,)? ] => {
        $crate::algebra::mv::Mv::from_rat_terms(&[
            $( ($mask as $crate::algebra::blade_new::BladeMask,
                $crate::scalar::Rat::new($num as i128, mv!(@den $( $den )? )) ), )+
        ])
    };
    // Internal: default denominator = 1
    (@den) => { 1i128 };
    (@den $den:literal) => { $den as i128 };
}

#[cfg(test)]
mod tests {
    use crate::scalar::{Rat, Scalar};

    #[test]
    fn mv_macro_zero() {
        let z = mv![];
        assert!(z.is_zero());
    }

    #[test]
    fn mv_macro_integer_coeffs() {
        let v = mv![0b001 => 3, 0b010 => 4, 0b100 => 5];
        assert_eq!(v.coefficient(0b001), Scalar::from(3));
        assert_eq!(v.coefficient(0b010), Scalar::from(4));
        assert_eq!(v.coefficient(0b100), Scalar::from(5));
        assert_eq!(v.len(), 3);
    }

    #[test]
    fn mv_macro_rational_coeffs() {
        let p = mv![0b01 => 1/2, 0b10 => 1/2];
        assert_eq!(p.coefficient(0b01), Scalar::Rat(Rat::new(1, 2)));
        assert_eq!(p.coefficient(0b10), Scalar::Rat(Rat::new(1, 2)));
    }

    #[test]
    fn mv_macro_negative() {
        let v = mv![0b001 => -1, 0b010 => 1];
        assert_eq!(v.coefficient(0b001), Scalar::from(-1));
        assert_eq!(v.coefficient(0b010), Scalar::from(1));
    }

    #[test]
    fn mv_macro_scalar() {
        let s = mv![0 => 7];
        assert_eq!(s.coefficient(0), Scalar::from(7));
        assert_eq!(s.len(), 1);
    }

    #[test]
    fn mv_macro_zero_coeff_dropped() {
        let v = mv![0b001 => 0, 0b010 => 3];
        assert_eq!(v.len(), 1);
        assert_eq!(v.coefficient(0b010), Scalar::from(3));
    }

    #[test]
    fn mv_macro_negative_rational() {
        let v = mv![0b01 => -3/7];
        assert_eq!(v.coefficient(0b01), Scalar::Rat(Rat::new(-3, 7)));
    }

    #[test]
    fn mv_macro_trailing_comma() {
        let v = mv![0b001 => 1, 0b010 => 2,];
        assert_eq!(v.len(), 2);
    }
}