1use crate::symbolic::core::{Expr, Monomial, SparsePolynomial};
41use crate::symbolic::polynomial::{add_poly, mul_poly};
42use crate::symbolic::simplify::is_zero;
43use crate::symbolic::simplify_dag::simplify;
44use std::cmp::Ordering;
45use std::collections::BTreeMap;
46#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
48#[repr(C)]
49pub enum MonomialOrder {
50 Lexicographical,
51 GradedLexicographical,
52 GradedReverseLexicographical,
53}
54pub(crate) fn compare_monomials(m1: &Monomial, m2: &Monomial, order: MonomialOrder) -> Ordering {
56 match order {
57 MonomialOrder::Lexicographical => m1.0.iter().cmp(m2.0.iter()),
58 _ => m1.0.iter().cmp(m2.0.iter()),
59 }
60}
61pub fn poly_division_multivariate(
76 f: &SparsePolynomial,
77 divisors: &[SparsePolynomial],
78 order: MonomialOrder,
79) -> Result<(Vec<SparsePolynomial>, SparsePolynomial), String> {
80 let mut quotients = vec![
81 SparsePolynomial {
82 terms: BTreeMap::new()
83 };
84 divisors.len()
85 ];
86 let mut remainder = SparsePolynomial {
87 terms: BTreeMap::new(),
88 };
89 let mut p = f.clone();
90 while !p.terms.is_empty() {
91 let mut division_occurred = false;
92 let lead_term_p = match p.terms.keys().max_by(|a, b| compare_monomials(a, b, order)) {
93 Some(lt) => lt.clone(),
94 None => continue,
95 };
96 for (i, divisor) in divisors.iter().enumerate() {
97 if divisor.terms.is_empty() {
98 continue;
99 }
100 let lead_term_g = match divisor
101 .terms
102 .keys()
103 .max_by(|a, b| compare_monomials(a, b, order))
104 {
105 Some(lt) => lt.clone(),
106 None => unreachable!(),
107 };
108 if is_divisible(&lead_term_p, &lead_term_g) {
109 let coeff_p = match p.terms.get(&lead_term_p) {
110 Some(c) => c,
111 None => {
112 return Err("Logic error: lead term not in polynomial terms".to_string());
113 }
114 };
115 let coeff_g = match divisor.terms.get(&lead_term_g) {
116 Some(c) => c,
117 None => {
118 return Err("Logic error: lead term not found in divisor terms".to_string());
119 }
120 };
121 let coeff_ratio = simplify(&Expr::new_div(coeff_p.clone(), coeff_g.clone()));
122 let mono_ratio = subtract_monomials(&lead_term_p, &lead_term_g);
123 let mut t_terms = BTreeMap::new();
124 t_terms.insert(mono_ratio, coeff_ratio);
125 let t = SparsePolynomial { terms: t_terms };
126 quotients[i] = add_poly("ients[i], &t);
127 let t_g = mul_poly(&t, divisor);
128 p = subtract_poly(&p, &t_g);
129 division_occurred = true;
130 break;
131 }
132 }
133 if !division_occurred {
134 let coeff = match p.terms.remove(&lead_term_p) {
135 Some(c) => c,
136 None => {
137 return Err("Logic error: lead term not found for removal".to_string());
138 }
139 };
140 remainder.terms.insert(lead_term_p, coeff);
141 }
142 }
143 Ok((quotients, remainder))
144}
145pub(crate) fn is_divisible(m1: &Monomial, m2: &Monomial) -> bool {
146 m2.0.iter()
147 .all(|(var, exp2)| m1.0.get(var).is_some_and(|exp1| exp1 >= exp2))
148}
149pub(crate) fn subtract_monomials(m1: &Monomial, m2: &Monomial) -> Monomial {
150 let mut result = m1.0.clone();
151 for (var, exp2) in &m2.0 {
152 let exp1 = result.entry(var.clone()).or_insert(0);
153 *exp1 -= exp2;
154 }
155 Monomial(result.into_iter().filter(|(_, exp)| *exp > 0).collect())
156}
157pub fn subtract_poly(p1: &SparsePolynomial, p2: &SparsePolynomial) -> SparsePolynomial {
158 let mut result_terms = p1.terms.clone();
159 for (mono, coeff) in &p2.terms {
160 let entry = result_terms
161 .entry(mono.clone())
162 .or_insert_with(|| Expr::Constant(0.0));
163 *entry = simplify(&Expr::new_sub(entry.clone(), coeff.clone()));
164 }
165 result_terms.retain(|_, v| !is_zero(v));
166 SparsePolynomial {
167 terms: result_terms,
168 }
169}
170pub(crate) fn leading_term(p: &SparsePolynomial, order: MonomialOrder) -> Option<(Monomial, Expr)> {
172 p.terms
173 .iter()
174 .max_by(|(m1, _), (m2, _)| compare_monomials(m1, m2, order))
175 .map(|(m, c)| (m.clone(), c.clone()))
176}
177pub(crate) fn lcm_monomial(m1: &Monomial, m2: &Monomial) -> Monomial {
179 let mut lcm_map = m1.0.clone();
180 for (var, &exp2) in &m2.0 {
181 let exp1 = lcm_map.entry(var.clone()).or_insert(0);
182 *exp1 = std::cmp::max(*exp1, exp2);
183 }
184 Monomial(lcm_map)
185}
186pub(crate) fn s_polynomial(
189 p1: &SparsePolynomial,
190 p2: &SparsePolynomial,
191 order: MonomialOrder,
192) -> Option<SparsePolynomial> {
193 let (lm1, lc1) = leading_term(p1, order)?;
194 let (lm2, lc2) = leading_term(p2, order)?;
195 let lcm = lcm_monomial(&lm1, &lm2);
196 let t1_mono = subtract_monomials(&lcm, &lm1);
197 let t1_coeff = simplify(&Expr::new_div(Expr::Constant(1.0), lc1));
198 let mut t1_terms = BTreeMap::new();
199 t1_terms.insert(t1_mono, t1_coeff);
200 let t1 = SparsePolynomial { terms: t1_terms };
201 let t2_mono = subtract_monomials(&lcm, &lm2);
202 let t2_coeff = simplify(&Expr::new_div(Expr::Constant(1.0), lc2));
203 let mut t2_terms = BTreeMap::new();
204 t2_terms.insert(t2_mono, t2_coeff);
205 let t2 = SparsePolynomial { terms: t2_terms };
206 let term1 = mul_poly(&t1, p1);
207 let term2 = mul_poly(&t2, p2);
208 Some(subtract_poly(&term1, &term2))
209}
210pub fn buchberger(
224 basis: &[SparsePolynomial],
225 order: MonomialOrder,
226) -> Result<Vec<SparsePolynomial>, String> {
227 if basis.is_empty() {
228 return Ok(vec![]);
229 }
230 let mut g = basis.to_vec();
231 let mut pairs: Vec<(usize, usize)> = (0..g.len())
232 .flat_map(|i| (i + 1..g.len()).map(move |j| (i, j)))
233 .collect();
234 while let Some((i, j)) = pairs.pop() {
235 if let Some(s_poly) = s_polynomial(&g[i], &g[j], order) {
236 let (_, remainder) = poly_division_multivariate(&s_poly, &g, order)?;
237 if !remainder.terms.is_empty() {
238 let new_poly_idx = g.len();
239 for k in 0..new_poly_idx {
240 pairs.push((k, new_poly_idx));
241 }
242 g.push(remainder);
243 }
244 }
245 }
246 Ok(g)
247}