mathhook_core/core/expression/
smart_display.rs

1//! Smart display utilities for natural mathematical notation
2//!
3//! This module provides high-performance, memory-efficient utilities for
4//! displaying mathematical expressions in natural notation (x - y instead of x + -1 * y).
5
6use super::Expression;
7use std::fmt;
8
9/// Smart display utilities
10///
11/// Provides O(1) pattern detection and minimal memory allocation
12/// for natural mathematical notation display.
13pub struct SmartDisplayFormatter;
14
15impl SmartDisplayFormatter {
16    /// Smart formatting for addition that detects subtraction patterns
17    ///
18    /// Converts internal canonical form (x + -1 * y) to natural notation (x - y)
19    /// with optimal performance and minimal allocations.
20    #[inline]
21    pub fn format_addition_smartly(
22        f: &mut fmt::Formatter<'_>,
23        terms: &[Expression],
24    ) -> fmt::Result {
25        if terms.is_empty() {
26            return write!(f, "0");
27        }
28
29        for (i, term) in terms.iter().enumerate() {
30            match term {
31                // Detect -1 * expr pattern (subtraction) - O(1) check
32                Expression::Mul(factors) if Self::is_negative_one_multiplication(factors) => {
33                    let positive_part = Self::extract_positive_part(factors);
34                    if i == 0 {
35                        write!(f, "-{}", positive_part)?;
36                    } else {
37                        write!(f, " - {}", positive_part)?;
38                    }
39                }
40                // Regular positive term
41                _ => {
42                    if i == 0 {
43                        write!(f, "{}", term)?;
44                    } else {
45                        write!(f, " + {}", term)?;
46                    }
47                }
48            }
49        }
50        Ok(())
51    }
52
53    /// Smart formatting for multiplication that detects division patterns
54    ///
55    /// Converts internal canonical form (x * y^-1) to natural notation (x / y)
56    /// with optimal performance and minimal allocations.
57    #[inline]
58    pub fn format_multiplication_smartly(
59        f: &mut fmt::Formatter<'_>,
60        factors: &[Expression],
61    ) -> fmt::Result {
62        if factors.is_empty() {
63            return write!(f, "1");
64        }
65
66        // O(1) division pattern detection: x * y^(-1) → x / y
67        if let Some((dividend, divisor)) = Self::extract_division_parts(factors) {
68            return write!(f, "{} / {}", dividend, divisor);
69        }
70
71        // Regular multiplication - pre-allocate for performance
72        let factor_strs: Vec<String> = factors.iter().map(|factor| format!("{}", factor)).collect();
73        write!(f, "{}", factor_strs.join(" * "))
74    }
75
76    /// O(1) check if factors represent -1 * expr pattern (negated expression)
77    ///
78    /// This detects the canonical form created by the Neg trait implementation:
79    /// Expression::mul(vec![Expression::integer(-1), expr])
80    #[inline(always)]
81    fn is_negative_one_multiplication(factors: &[Expression]) -> bool {
82        // Exact pattern match for Neg trait output: [-1, expr]
83        factors.len() == 2 && Self::is_negative_one(&factors[0])
84    }
85
86    /// O(1) check if expression is -1
87    #[inline(always)]
88    fn is_negative_one(expr: &Expression) -> bool {
89        matches!(expr, Expression::Number(num) if num.is_negative_one())
90    }
91
92    /// Extract positive part from -1 * expr with minimal allocations
93    ///
94    /// Optimized for the exact Neg trait pattern: [-1, expr]
95    /// Uses efficient string formatting with minimal heap allocations.
96    #[inline]
97    fn extract_positive_part(factors: &[Expression]) -> String {
98        // Optimized for Neg trait pattern: exactly 2 factors
99        if factors.len() == 2 {
100            format!("{}", factors[1])
101        } else {
102            // Fallback for complex cases (rare)
103            let positive_factors: Vec<String> =
104                factors[1..].iter().map(|f| format!("{}", f)).collect();
105            positive_factors.join(" * ")
106        }
107    }
108
109    /// Check if an expression is in negated form
110    ///
111    /// This is a high-level utility that checks if an expression
112    /// represents a negated value using the canonical Neg trait pattern.
113    #[inline]
114    pub fn is_negated_expression(expr: &Expression) -> bool {
115        match expr {
116            Expression::Mul(factors) => Self::is_negative_one_multiplication(factors),
117            _ => false,
118        }
119    }
120
121    /// Extract the positive form of a negated expression
122    ///
123    /// If the expression is negated (-1 * expr), returns the positive part.
124    /// Otherwise returns None.
125    #[inline]
126    pub fn extract_negated_expression(expr: &Expression) -> Option<&Expression> {
127        match expr {
128            Expression::Mul(factors) if Self::is_negative_one_multiplication(factors) => {
129                Some(&factors[1])
130            }
131            _ => None,
132        }
133    }
134
135    /// O(1) check if factors represent division pattern: x * y^(-1)
136    ///
137    /// Detects the canonical form created by division operations
138    /// where division is represented as multiplication by negative power.
139    #[inline(always)]
140    pub fn is_division_pattern(factors: &[Expression]) -> bool {
141        factors.len() == 2 && Self::is_negative_power(&factors[1])
142    }
143
144    /// O(1) check if expression is y^(-1) (reciprocal)
145    #[inline(always)]
146    fn is_negative_power(expr: &Expression) -> bool {
147        match expr {
148            Expression::Pow(_, exp) => Self::is_negative_one(exp),
149            _ => false,
150        }
151    }
152
153    /// Extract dividend and divisor from division pattern
154    ///
155    /// For x * y^(-1), returns (x, y) for formatting as x / y
156    #[inline]
157    pub fn extract_division_parts(factors: &[Expression]) -> Option<(&Expression, &Expression)> {
158        if factors.len() == 2 {
159            if let Expression::Pow(base, exp) = &factors[1] {
160                if Self::is_negative_one(exp) {
161                    return Some((&factors[0], base));
162                }
163            }
164        }
165        None
166    }
167
168    /// Check if an expression is in division form
169    ///
170    /// High-level utility to detect if multiplication represents division
171    #[inline]
172    pub fn is_division_expression(expr: &Expression) -> bool {
173        match expr {
174            Expression::Mul(factors) => Self::is_division_pattern(factors),
175            _ => false,
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    use crate::{expr, Expression};
184
185    #[test]
186    fn test_negative_one_detection() {
187        let neg_one = Expression::integer(-1);
188        assert!(SmartDisplayFormatter::is_negative_one(&neg_one));
189
190        let pos_one = Expression::integer(1);
191        assert!(!SmartDisplayFormatter::is_negative_one(&pos_one));
192    }
193
194    #[test]
195    fn test_subtraction_pattern_detection() {
196        let factors = vec![Expression::integer(-1), expr!(y)];
197        assert!(SmartDisplayFormatter::is_negative_one_multiplication(
198            &factors
199        ));
200
201        let factors = vec![Expression::integer(2), expr!(y)];
202        assert!(!SmartDisplayFormatter::is_negative_one_multiplication(
203            &factors
204        ));
205    }
206
207    #[test]
208    fn test_positive_part_extraction() {
209        let factors = vec![Expression::integer(-1), expr!(y)];
210        let result = SmartDisplayFormatter::extract_positive_part(&factors);
211        assert_eq!(result, "y");
212
213        let factors = vec![Expression::integer(-1), expr!(x), expr!(y)];
214        let result = SmartDisplayFormatter::extract_positive_part(&factors);
215        assert_eq!(result, "x * y");
216    }
217
218    #[test]
219    fn test_division_pattern_detection() {
220        // Test x * y^(-1) pattern
221        let factors = vec![expr!(x), Expression::pow(expr!(y), Expression::integer(-1))];
222        assert!(SmartDisplayFormatter::is_division_pattern(&factors));
223
224        // Test regular multiplication
225        let factors = vec![expr!(x), expr!(y)];
226        assert!(!SmartDisplayFormatter::is_division_pattern(&factors));
227    }
228
229    #[test]
230    fn test_division_parts_extraction() {
231        let factors = vec![expr!(x), Expression::pow(expr!(y), Expression::integer(-1))];
232
233        let result = SmartDisplayFormatter::extract_division_parts(&factors);
234        assert!(result.is_some());
235
236        if let Some((dividend, divisor)) = result {
237            assert_eq!(dividend, &expr!(x));
238            assert_eq!(divisor, &expr!(y));
239        }
240    }
241
242    #[test]
243    fn test_high_level_utilities() {
244        // Test negated expression detection
245        let negated = Expression::mul(vec![Expression::integer(-1), expr!(x)]);
246        assert!(SmartDisplayFormatter::is_negated_expression(&negated));
247
248        // Test division expression detection
249        let division = Expression::mul(vec![
250            expr!(x),
251            Expression::pow(expr!(y), Expression::integer(-1)),
252        ]);
253        assert!(SmartDisplayFormatter::is_division_expression(&division));
254    }
255}