mathhook_core/core/polynomial/properties.rs
1//! Polynomial Properties
2//!
3//! Core polynomial properties: degree, leading coefficient, content, primitive part.
4//! These methods are cached using thread-local LRU caching for performance.
5
6use super::classification::PolynomialClassification;
7use crate::core::{Expression, Symbol};
8
9mod content;
10mod degree;
11#[cfg(test)]
12mod tests;
13
14// Re-export for internal use within polynomial module
15pub(crate) use content::{compute_content_impl, divide_by_integer};
16pub(crate) use degree::{
17 compute_leading_coefficient_impl, compute_total_degree_impl, degree_cached,
18};
19
20/// Trait for polynomial properties
21///
22/// Provides methods for computing polynomial properties such as degree,
23/// leading coefficient, content, and primitive part. Results are cached
24/// using thread-local LRU caching for performance.
25///
26/// # Examples
27///
28/// ```rust
29/// use mathhook_core::core::polynomial::{PolynomialClassification, PolynomialProperties};
30/// use mathhook_core::{expr, symbol};
31///
32/// let x = symbol!(x);
33/// let poly = expr!(x ^ 3);
34///
35/// assert_eq!(poly.degree(&x), Some(3));
36/// ```
37pub trait PolynomialProperties: PolynomialClassification {
38 /// Compute degree with respect to a variable
39 ///
40 /// Returns the highest power of the variable in the polynomial.
41 ///
42 /// # Arguments
43 ///
44 /// * `var` - The variable to compute degree for
45 ///
46 /// # Returns
47 ///
48 /// `Some(degree)` if the expression is a polynomial in the variable,
49 /// `None` if not a polynomial.
50 ///
51 /// # Examples
52 ///
53 /// ```rust
54 /// use mathhook_core::core::polynomial::PolynomialProperties;
55 /// use mathhook_core::{expr, symbol};
56 ///
57 /// let x = symbol!(x);
58 ///
59 /// assert_eq!(expr!(x ^ 3).degree(&x), Some(3));
60 /// assert_eq!(expr!(5).degree(&x), Some(0));
61 /// ```
62 fn degree(&self, var: &Symbol) -> Option<i64>;
63
64 /// Compute total degree (sum of degrees in all variables)
65 ///
66 /// For multivariate polynomials, returns the maximum sum of exponents
67 /// across all terms.
68 ///
69 /// # Examples
70 ///
71 /// ```rust
72 /// use mathhook_core::core::polynomial::PolynomialProperties;
73 /// use mathhook_core::{expr, symbol};
74 ///
75 /// let x = symbol!(x);
76 /// let y = symbol!(y);
77 /// let poly = expr!(x * y); // x*y has total degree 2
78 ///
79 /// assert_eq!(poly.total_degree(), Some(2));
80 /// ```
81 fn total_degree(&self) -> Option<i64>;
82
83 /// Get leading coefficient with respect to a variable
84 ///
85 /// Returns the coefficient of the highest degree term.
86 ///
87 /// # Arguments
88 ///
89 /// * `var` - The variable to find leading coefficient for
90 ///
91 /// # Examples
92 ///
93 /// ```rust
94 /// use mathhook_core::core::polynomial::PolynomialProperties;
95 /// use mathhook_core::{expr, symbol};
96 ///
97 /// let x = symbol!(x);
98 /// let poly = expr!((5 * (x ^ 2)) + (3 * x) + 1);
99 ///
100 /// // Leading coefficient of 5x^2 + 3x + 1 is 5
101 /// let lc = poly.leading_coefficient(&x);
102 /// ```
103 fn leading_coefficient(&self, var: &Symbol) -> Expression;
104
105 /// Extract content (GCD of all coefficients)
106 ///
107 /// The content is the GCD of all numeric coefficients in the polynomial.
108 ///
109 /// # Examples
110 ///
111 /// ```rust
112 /// use mathhook_core::core::polynomial::PolynomialProperties;
113 /// use mathhook_core::{expr, symbol};
114 ///
115 /// let x = symbol!(x);
116 /// let poly = expr!((6 * (x ^ 2)) + (9 * x) + 3);
117 ///
118 /// // Content of 6x^2 + 9x + 3 is 3
119 /// let content = poly.content();
120 /// ```
121 fn content(&self) -> Expression;
122
123 /// Extract primitive part (polynomial divided by content)
124 ///
125 /// The primitive part is the polynomial with content factored out.
126 ///
127 /// # Examples
128 ///
129 /// ```rust
130 /// use mathhook_core::core::polynomial::PolynomialProperties;
131 /// use mathhook_core::{expr, symbol};
132 ///
133 /// let x = symbol!(x);
134 /// let poly = expr!((6 * (x ^ 2)) + (9 * x) + 3);
135 ///
136 /// // Primitive part of 6x^2 + 9x + 3 is 2x^2 + 3x + 1
137 /// let pp = poly.primitive_part();
138 /// ```
139 fn primitive_part(&self) -> Expression;
140}
141
142impl PolynomialProperties for Expression {
143 fn degree(&self, var: &Symbol) -> Option<i64> {
144 degree_cached(self, var)
145 }
146
147 fn total_degree(&self) -> Option<i64> {
148 let vars = self.polynomial_variables();
149 if vars.is_empty() {
150 return Some(0);
151 }
152
153 // For total degree, we need to find the maximum sum of degrees in any term
154 compute_total_degree_impl(self, &vars)
155 }
156
157 fn leading_coefficient(&self, var: &Symbol) -> Expression {
158 compute_leading_coefficient_impl(self, var)
159 }
160
161 fn content(&self) -> Expression {
162 compute_content_impl(self)
163 }
164
165 fn primitive_part(&self) -> Expression {
166 let content = self.content();
167 if content.is_zero() || content == Expression::integer(1) {
168 self.clone()
169 } else {
170 // Divide polynomial by content
171 divide_by_integer(self, &content)
172 }
173 }
174}