1use std::collections::{BTreeMap, BTreeSet};
15use std::fmt;
16
17use crate::numeric::Rational;
18use crate::poly::Poly;
19
20type Exponents = Vec<u32>;
22
23fn trimmed(mut e: Exponents) -> Exponents {
24 while e.last() == Some(&0) {
25 e.pop();
26 }
27 e
28}
29
30#[derive(Clone, PartialEq, Eq, Hash, Default)]
33pub struct Polynomial {
34 terms: BTreeMap<Exponents, Rational>,
35}
36
37impl Polynomial {
38 pub fn zero() -> Polynomial {
39 Polynomial::default()
40 }
41
42 pub fn one() -> Polynomial {
43 Polynomial::constant(Rational::one())
44 }
45
46 pub fn constant(c: Rational) -> Polynomial {
47 Polynomial::monomial(c, Vec::new())
48 }
49
50 pub fn var(i: usize) -> Polynomial {
52 let mut e = vec![0; i + 1];
53 e[i] = 1;
54 Polynomial::monomial(Rational::one(), e)
55 }
56
57 pub fn monomial(c: Rational, exponents: Vec<u32>) -> Polynomial {
59 let mut terms = BTreeMap::new();
60 if !c.is_zero() {
61 terms.insert(trimmed(exponents), c);
62 }
63 Polynomial { terms }
64 }
65
66 pub fn from_univariate(p: &Poly, v: usize) -> Polynomial {
68 let mut out = Polynomial::zero();
69 for (k, c) in p.coeffs().iter().enumerate() {
70 let mut e = vec![0; v + 1];
71 e[v] = k as u32;
72 out.add_term(e, c.clone());
73 }
74 out
75 }
76
77 pub fn is_zero(&self) -> bool {
78 self.terms.is_empty()
79 }
80
81 pub fn as_constant(&self) -> Option<Rational> {
83 match self.terms.len() {
84 0 => Some(Rational::zero()),
85 1 => self.terms.get(&Vec::new()).cloned(),
86 _ => None,
87 }
88 }
89
90 pub fn terms(&self) -> impl Iterator<Item = (&[u32], &Rational)> {
92 self.terms.iter().map(|(e, c)| (e.as_slice(), c))
93 }
94
95 fn add_term(&mut self, e: Exponents, c: Rational) {
96 if c.is_zero() {
97 return;
98 }
99 let e = trimmed(e);
100 let sum = match self.terms.get(&e) {
101 Some(old) => old.add(&c),
102 None => c,
103 };
104 if sum.is_zero() {
105 self.terms.remove(&e);
106 } else {
107 self.terms.insert(e, sum);
108 }
109 }
110
111 pub fn add(&self, o: &Polynomial) -> Polynomial {
112 let mut out = self.clone();
113 for (e, c) in &o.terms {
114 out.add_term(e.clone(), c.clone());
115 }
116 out
117 }
118
119 pub fn neg(&self) -> Polynomial {
120 Polynomial {
121 terms: self.terms.iter().map(|(e, c)| (e.clone(), c.negated())).collect(),
122 }
123 }
124
125 pub fn sub(&self, o: &Polynomial) -> Polynomial {
126 self.add(&o.neg())
127 }
128
129 pub fn scale(&self, k: &Rational) -> Polynomial {
130 if k.is_zero() {
131 return Polynomial::zero();
132 }
133 Polynomial {
134 terms: self.terms.iter().map(|(e, c)| (e.clone(), c.mul(k))).collect(),
135 }
136 }
137
138 pub fn mul(&self, o: &Polynomial) -> Polynomial {
139 let mut out = Polynomial::zero();
140 for (e1, c1) in &self.terms {
141 for (e2, c2) in &o.terms {
142 let n = e1.len().max(e2.len());
143 let e: Exponents = (0..n)
144 .map(|i| e1.get(i).copied().unwrap_or(0) + e2.get(i).copied().unwrap_or(0))
145 .collect();
146 out.add_term(e, c1.mul(c2));
147 }
148 }
149 out
150 }
151
152 pub fn pow(&self, k: u32) -> Polynomial {
153 let mut acc = Polynomial::one();
154 let mut base = self.clone();
155 let mut e = k;
156 while e > 0 {
157 if e & 1 == 1 {
158 acc = acc.mul(&base);
159 }
160 e >>= 1;
161 if e > 0 {
162 base = base.mul(&base);
163 }
164 }
165 acc
166 }
167
168 pub fn degree_in(&self, v: usize) -> u32 {
170 self.terms.keys().map(|e| e.get(v).copied().unwrap_or(0)).max().unwrap_or(0)
171 }
172
173 pub fn variables(&self) -> BTreeSet<usize> {
175 let mut out = BTreeSet::new();
176 for e in self.terms.keys() {
177 for (i, k) in e.iter().enumerate() {
178 if *k > 0 {
179 out.insert(i);
180 }
181 }
182 }
183 out
184 }
185
186 pub fn coefficient_in(&self, v: usize, k: u32) -> Polynomial {
188 let mut out = Polynomial::zero();
189 for (e, c) in &self.terms {
190 if e.get(v).copied().unwrap_or(0) == k {
191 let mut e = e.clone();
192 if v < e.len() {
193 e[v] = 0;
194 }
195 out.add_term(e, c.clone());
196 }
197 }
198 out
199 }
200
201 pub fn leading_in(&self, v: usize) -> Polynomial {
203 self.coefficient_in(v, self.degree_in(v))
204 }
205
206 pub fn substitute(&self, v: usize, x: &Rational) -> Polynomial {
208 let mut out = Polynomial::zero();
209 for (e, c) in &self.terms {
210 let k = e.get(v).copied().unwrap_or(0);
211 let mut e = e.clone();
212 if v < e.len() {
213 e[v] = 0;
214 }
215 let factor = x.pow(k as i32).expect("a nonnegative power");
216 out.add_term(e, c.mul(&factor));
217 }
218 out
219 }
220
221 pub fn compose(&self, v: usize, p: &Polynomial) -> Polynomial {
223 let mut out = Polynomial::zero();
224 for k in 0..=self.degree_in(v) {
225 let c = self.coefficient_in(v, k);
226 if !c.is_zero() {
227 out = out.add(&c.mul(&p.pow(k)));
228 }
229 }
230 out
231 }
232
233 pub fn derivative(&self, v: usize) -> Polynomial {
235 let mut out = Polynomial::zero();
236 for (e, c) in &self.terms {
237 let k = e.get(v).copied().unwrap_or(0);
238 if k == 0 {
239 continue;
240 }
241 let mut e = e.clone();
242 e[v] = k - 1;
243 out.add_term(e, c.mul(&Rational::from_i64(i64::from(k))));
244 }
245 out
246 }
247
248 pub fn to_univariate(&self, v: usize) -> Option<Poly> {
250 if self.variables().iter().any(|w| *w != v) {
251 return None;
252 }
253 let mut c = vec![Rational::zero(); self.degree_in(v) as usize + 1];
254 for (e, k) in &self.terms {
255 c[e.get(v).copied().unwrap_or(0) as usize] = k.clone();
256 }
257 Some(Poly::from_coeffs(c))
258 }
259
260 pub fn resultant(&self, o: &Polynomial, v: usize) -> Polynomial {
264 if self.is_zero() || o.is_zero() {
265 return Polynomial::zero();
266 }
267 let (da, db) = (self.degree_in(v), o.degree_in(v));
268 if da == 0 {
269 return self.pow(db);
270 }
271 if db == 0 {
272 return o.pow(da);
273 }
274 let others: BTreeSet<usize> = self.variables().union(&o.variables()).copied().filter(|w| *w != v).collect();
275 let Some(&w) = others.iter().next_back() else {
276 let a = self.to_univariate(v).expect("only x_v occurs");
277 let b = o.to_univariate(v).expect("only x_v occurs");
278 return Polynomial::constant(a.resultant(&b));
279 };
280 let bound = da * o.degree_in(w) + db * self.degree_in(w);
281 let (la, lb) = (self.leading_in(v), o.leading_in(v));
282 let mut nodes: Vec<Rational> = Vec::new();
283 let mut values: Vec<Polynomial> = Vec::new();
284 for x in points() {
285 if nodes.len() > bound as usize {
286 break;
287 }
288 if la.substitute(w, &x).is_zero() || lb.substitute(w, &x).is_zero() {
289 continue;
290 }
291 values.push(self.substitute(w, &x).resultant(&o.substitute(w, &x), v));
292 nodes.push(x);
293 }
294 interpolate(w, &nodes, values)
295 }
296}
297
298fn points() -> impl Iterator<Item = Rational> {
300 (0i64..).map(|i| Rational::from_i64(if i % 2 == 1 { (i + 1) / 2 } else { -(i / 2) }))
301}
302
303fn interpolate(w: usize, nodes: &[Rational], mut values: Vec<Polynomial>) -> Polynomial {
306 let n = nodes.len();
307 for j in 1..n {
308 for i in (j..n).rev() {
309 let gap = nodes[i].sub(&nodes[i - j]);
310 let inv = gap.recip().expect("the nodes are distinct");
311 values[i] = values[i].sub(&values[i - 1]).scale(&inv);
312 }
313 }
314 let mut out = values[n - 1].clone();
315 for i in (0..n - 1).rev() {
316 let factor = Polynomial::var(w).sub(&Polynomial::constant(nodes[i].clone()));
317 out = out.mul(&factor).add(&values[i]);
318 }
319 out
320}
321
322impl fmt::Debug for Polynomial {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 write!(f, "{self}")
325 }
326}
327
328impl fmt::Display for Polynomial {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 if self.is_zero() {
332 return f.write_str("0");
333 }
334 for (n, (e, c)) in self.terms.iter().rev().enumerate() {
335 let monomial: Vec<String> = e
336 .iter()
337 .enumerate()
338 .filter(|(_, k)| **k > 0)
339 .map(|(i, k)| if *k == 1 { format!("x{i}") } else { format!("x{i}^{k}") })
340 .collect();
341 let negative = c.is_negative();
342 let magnitude = if negative { c.negated() } else { c.clone() };
343 let body = match (monomial.is_empty(), magnitude == Rational::one()) {
344 (true, _) => magnitude.to_string(),
345 (false, true) => monomial.join("·"),
346 (false, false) => format!("{magnitude}·{}", monomial.join("·")),
347 };
348 match (n, negative) {
349 (0, true) => write!(f, "-{body}")?,
350 (0, false) => write!(f, "{body}")?,
351 (_, true) => write!(f, " - {body}")?,
352 (_, false) => write!(f, " + {body}")?,
353 }
354 }
355 Ok(())
356 }
357}
358
359#[cfg(test)]
360#[path = "../../logicaffeine_tests/unit/logicaffeine_base/elimination/tests.rs"]
361mod tests;