mathhook_core/core/expression/
smart_display.rs1use super::Expression;
7use std::fmt;
8
9pub struct SmartDisplayFormatter;
14
15impl SmartDisplayFormatter {
16 #[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 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 _ => {
42 if i == 0 {
43 write!(f, "{}", term)?;
44 } else {
45 write!(f, " + {}", term)?;
46 }
47 }
48 }
49 }
50 Ok(())
51 }
52
53 #[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 if let Some((dividend, divisor)) = Self::extract_division_parts(factors) {
68 return write!(f, "{} / {}", dividend, divisor);
69 }
70
71 let factor_strs: Vec<String> = factors.iter().map(|factor| format!("{}", factor)).collect();
73 write!(f, "{}", factor_strs.join(" * "))
74 }
75
76 #[inline(always)]
81 fn is_negative_one_multiplication(factors: &[Expression]) -> bool {
82 factors.len() == 2 && Self::is_negative_one(&factors[0])
84 }
85
86 #[inline(always)]
88 fn is_negative_one(expr: &Expression) -> bool {
89 matches!(expr, Expression::Number(num) if num.is_negative_one())
90 }
91
92 #[inline]
97 fn extract_positive_part(factors: &[Expression]) -> String {
98 if factors.len() == 2 {
100 format!("{}", factors[1])
101 } else {
102 let positive_factors: Vec<String> =
104 factors[1..].iter().map(|f| format!("{}", f)).collect();
105 positive_factors.join(" * ")
106 }
107 }
108
109 #[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 #[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 #[inline(always)]
140 pub fn is_division_pattern(factors: &[Expression]) -> bool {
141 factors.len() == 2 && Self::is_negative_power(&factors[1])
142 }
143
144 #[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 #[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 #[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 let factors = vec![expr!(x), Expression::pow(expr!(y), Expression::integer(-1))];
222 assert!(SmartDisplayFormatter::is_division_pattern(&factors));
223
224 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 let negated = Expression::mul(vec![Expression::integer(-1), expr!(x)]);
246 assert!(SmartDisplayFormatter::is_negated_expression(&negated));
247
248 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}