mathhook_core/core/commutativity.rs
1//! Commutativity tracking for expressions
2//!
3//! Supports noncommutative algebra (matrices, operators, quaternions) while
4//! maintaining default commutative behavior for scalars.
5//!
6//! Commutativity is computed on-demand from symbol types, not stored in expressions.
7
8/// Commutativity of an expression or operation
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum Commutativity {
11 /// Operation is commutative (a*b = b*a)
12 /// Examples: scalar multiplication, addition
13 Commutative,
14
15 /// Operation is noncommutative (a*b may not equal b*a)
16 /// Examples: matrix multiplication, operator multiplication, quaternion multiplication
17 Noncommutative,
18}
19
20impl Commutativity {
21 /// Can factors be sorted during canonicalization?
22 ///
23 /// Returns true only if commutativity is guaranteed.
24 pub fn can_sort(self) -> bool {
25 matches!(self, Commutativity::Commutative)
26 }
27
28 /// Combine commutativity of multiple factors
29 ///
30 /// Rule: If ANY factor is noncommutative, the entire product is noncommutative.
31 /// Only if ALL factors are commutative is the product commutative.
32 ///
33 /// # Examples
34 ///
35 /// ```
36 /// use mathhook_core::core::commutativity::Commutativity;
37 ///
38 /// // All commutative → result commutative
39 /// let factors = vec![Commutativity::Commutative, Commutativity::Commutative];
40 /// assert_eq!(Commutativity::combine(factors), Commutativity::Commutative);
41 ///
42 /// // Any noncommutative → result noncommutative
43 /// let factors = vec![Commutativity::Commutative, Commutativity::Noncommutative];
44 /// assert_eq!(Commutativity::combine(factors), Commutativity::Noncommutative);
45 /// ```
46 pub fn combine<I>(factors: I) -> Self
47 where
48 I: IntoIterator<Item = Self>,
49 {
50 for comm in factors {
51 if comm == Commutativity::Noncommutative {
52 return Commutativity::Noncommutative;
53 }
54 }
55 Commutativity::Commutative
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_commutativity_can_sort() {
65 assert!(Commutativity::Commutative.can_sort());
66 assert!(!Commutativity::Noncommutative.can_sort());
67 }
68
69 #[test]
70 fn test_combine_all_commutative() {
71 let factors = vec![Commutativity::Commutative, Commutativity::Commutative];
72 assert_eq!(Commutativity::combine(factors), Commutativity::Commutative);
73 }
74
75 #[test]
76 fn test_combine_any_noncommutative() {
77 let factors = vec![Commutativity::Commutative, Commutativity::Noncommutative];
78 assert_eq!(
79 Commutativity::combine(factors),
80 Commutativity::Noncommutative
81 );
82 }
83
84 #[test]
85 fn test_combine_all_noncommutative() {
86 let factors = vec![Commutativity::Noncommutative, Commutativity::Noncommutative];
87 assert_eq!(
88 Commutativity::combine(factors),
89 Commutativity::Noncommutative
90 );
91 }
92
93 #[test]
94 fn test_combine_empty() {
95 let factors: Vec<Commutativity> = vec![];
96 assert_eq!(Commutativity::combine(factors), Commutativity::Commutative);
97 }
98}