1use std::fmt;
13
14use ocas_domain::{Domain, EuclideanDomain};
15
16use crate::sparse::{Grevlex, MonomialOrder, SparseMultivariatePolynomial};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct RationalPolynomial<D: Domain, O: MonomialOrder = Grevlex> {
27 pub numerator: SparseMultivariatePolynomial<D, O>,
29 pub denominator: SparseMultivariatePolynomial<D, O>,
31}
32
33impl<D: Domain, O: MonomialOrder> RationalPolynomial<D, O> {
34 pub fn new(
43 numerator: SparseMultivariatePolynomial<D, O>,
44 denominator: SparseMultivariatePolynomial<D, O>,
45 ) -> Self {
46 debug_assert!(
47 !denominator.is_zero(),
48 "RationalPolynomial: denominator must be non-zero"
49 );
50 Self {
51 numerator,
52 denominator,
53 }
54 }
55
56 pub fn from_polynomial(poly: SparseMultivariatePolynomial<D, O>) -> Self {
58 let one = poly.one();
59 Self {
60 numerator: poly,
61 denominator: one,
62 }
63 }
64
65 pub fn zero(domain: &D, n_vars: usize) -> Self {
67 let z = SparseMultivariatePolynomial::new(domain.clone(), n_vars);
68 let one = z.one();
69 Self {
70 numerator: z.clone(),
71 denominator: one,
72 }
73 }
74
75 pub fn one(domain: &D, n_vars: usize) -> Self {
77 let o = SparseMultivariatePolynomial::new(domain.clone(), n_vars).one();
78 Self {
79 numerator: o.clone(),
80 denominator: o,
81 }
82 }
83
84 pub fn is_zero(&self) -> bool {
86 self.numerator.is_zero()
87 }
88
89 pub fn is_one(&self) -> bool {
91 self.numerator == self.denominator
92 }
93
94 pub fn n_vars(&self) -> usize {
96 self.numerator.n_vars()
97 }
98
99 pub fn domain(&self) -> &D {
101 self.numerator.domain()
102 }
103
104 pub fn neg(&self) -> Self {
106 Self {
107 numerator: self.numerator.neg(),
108 denominator: self.denominator.clone(),
109 }
110 }
111
112 pub fn inv(&self) -> Option<Self> {
116 if self.numerator.is_zero() {
117 return None;
118 }
119 Some(Self {
120 numerator: self.denominator.clone(),
121 denominator: self.numerator.clone(),
122 })
123 }
124
125 pub fn pow(&self, k: u32) -> Self {
127 if k == 0 {
128 return Self::one(self.domain(), self.n_vars());
129 }
130 let mut num = self.numerator.one();
132 let mut den = self.denominator.one();
133 let mut base_num = self.numerator.clone();
134 let mut base_den = self.denominator.clone();
135 let mut exp = k;
136 while exp > 0 {
137 if exp & 1 == 1 {
138 num = num.mul(&base_num);
139 den = den.mul(&base_den);
140 }
141 base_num = base_num.mul(&base_num);
142 base_den = base_den.mul(&base_den);
143 exp >>= 1;
144 }
145 Self {
146 numerator: num,
147 denominator: den,
148 }
149 }
150}
151
152impl<D: EuclideanDomain, O: MonomialOrder> RationalPolynomial<D, O> {
153 pub fn from_num_den(
163 numerator: SparseMultivariatePolynomial<D, O>,
164 denominator: SparseMultivariatePolynomial<D, O>,
165 ) -> Self {
166 if denominator.is_zero() {
167 panic!("RationalPolynomial::from_num_den: denominator is zero");
168 }
169 if numerator.is_zero() {
170 return Self {
171 numerator,
172 denominator,
173 };
174 }
175 let mut rat = Self {
176 numerator,
177 denominator,
178 };
179 rat.canonicalize();
180 rat
181 }
182
183 fn canonicalize(&mut self) {
188 if self.numerator.is_zero() {
189 return;
190 }
191 let num_content = self.numerator.content();
198 let den_content = self.denominator.content();
199 let coeff_gcd = self.numerator.domain().gcd(&num_content, &den_content);
200
201 if !self.numerator.domain().is_one(&coeff_gcd) {
202 self.numerator = self.numerator.div_scalar(&coeff_gcd);
203 self.denominator = self.denominator.div_scalar(&coeff_gcd);
204 }
205
206 if self.numerator.n_vars() == 1 {
210 let num_d = sparse_to_dense_uni(&self.numerator);
211 let den_d = sparse_to_dense_uni(&self.denominator);
212 let g = num_d.gcd(&den_d);
213 if g.degree().unwrap_or(0) > 0
214 && let (Some((nq, r1)), Some((dq, r2))) = (num_d.div_rem(&g), den_d.div_rem(&g))
215 {
216 debug_assert!(r1.is_zero() && r2.is_zero());
217 self.numerator = dense_to_sparse_uni(&nq);
218 self.denominator = dense_to_sparse_uni(&dq);
219 }
220 }
221
222 if let Some(den_lc) = self.denominator.leading_coeff() {
226 if let Some(neg_lc) = self.numerator.domain().inv(den_lc) {
229 self.numerator = self.numerator.mul_scalar(&neg_lc);
231 self.denominator = self.denominator.mul_scalar(&neg_lc);
232 }
233 }
234 }
235
236 pub fn add(&self, other: &Self) -> Self {
244 if self.is_zero() {
245 return other.clone();
246 }
247 if other.is_zero() {
248 return self.clone();
249 }
250
251 if self.denominator == other.denominator {
253 let num = self.numerator.add(&other.numerator);
254 return Self::from_num_den(num, self.denominator.clone());
255 }
256
257 let ad = self.numerator.mul(&other.denominator);
260 let bc = other.numerator.mul(&self.denominator);
261 let num = ad.add(&bc);
262 let den = self.denominator.mul(&other.denominator);
263 Self::from_num_den(num, den)
264 }
265
266 pub fn sub(&self, other: &Self) -> Self {
268 self.add(&other.neg())
269 }
270
271 pub fn mul(&self, other: &Self) -> Self {
276 if self.is_zero() || other.is_zero() {
277 return Self::zero(self.domain(), self.n_vars());
278 }
279
280 let num = self.numerator.mul(&other.numerator);
284 let den = self.denominator.mul(&other.denominator);
285 Self::from_num_den(num, den)
286 }
287
288 pub fn div(&self, other: &Self) -> Option<Self> {
290 let inv = other.inv()?;
291 Some(self.mul(&inv))
292 }
293}
294
295impl<D: Domain, O: MonomialOrder> fmt::Display for RationalPolynomial<D, O>
300where
301 D::Element: fmt::Display,
302{
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 if self.denominator.is_zero() || self.denominator.n_terms() <= 1 {
305 let const_val = self.denominator.coeff(&vec![0; self.denominator.n_vars()]);
307 if self.domain().is_one(&const_val) {
308 return write!(f, "{:?}", self.numerator);
309 }
310 }
311 write!(f, "({:?}) / ({:?})", self.numerator, self.denominator)
312 }
313}
314
315impl<D: EuclideanDomain, O: MonomialOrder> SparseMultivariatePolynomial<D, O> {
320 fn div_scalar(&self, scalar: &D::Element) -> Self {
322 if self.domain().is_one(scalar) {
323 return self.clone();
324 }
325 let inv = self
326 .domain()
327 .inv(scalar)
328 .expect("div_scalar: cannot invert zero");
329 self.mul_scalar(&inv)
330 }
331}
332
333fn sparse_to_dense_uni<D: EuclideanDomain, O: MonomialOrder>(
339 p: &SparseMultivariatePolynomial<D, O>,
340) -> crate::DenseUnivariatePolynomial<D> {
341 debug_assert_eq!(p.n_vars(), 1);
342 let deg = p.degree_in(0);
343 let mut coeffs = vec![p.domain().zero(); deg + 1];
344 for (exp, coeff) in p.terms_ref() {
345 coeffs[exp[0]] = coeff.clone();
346 }
347 crate::DenseUnivariatePolynomial::from_coeffs(p.domain().clone(), coeffs)
348}
349
350fn dense_to_sparse_uni<D: EuclideanDomain, O: MonomialOrder>(
352 p: &crate::DenseUnivariatePolynomial<D>,
353) -> SparseMultivariatePolynomial<D, O> {
354 let terms = p
355 .coeffs()
356 .iter()
357 .enumerate()
358 .filter(|&(_, c)| !p.domain().is_zero(c))
359 .map(|(i, c)| (vec![i], c.clone()))
360 .collect();
361 SparseMultivariatePolynomial::from_terms(p.domain().clone(), 1, terms)
362}
363
364#[cfg(test)]
369mod tests {
370 use super::*;
371 use crate::sparse::Lex;
372 use ocas_domain::{Integer, IntegerDomain};
373
374 type ZPoly = SparseMultivariatePolynomial<IntegerDomain, Lex>;
375 type ZRat = RationalPolynomial<IntegerDomain, Lex>;
376
377 fn poly1(terms: Vec<(Vec<usize>, i64)>) -> ZPoly {
378 ZPoly::from_terms(
379 IntegerDomain,
380 1,
381 terms
382 .into_iter()
383 .map(|(e, c)| (e, Integer::from(c)))
384 .collect(),
385 )
386 }
387
388 #[allow(dead_code)]
389 fn poly2(terms: Vec<(Vec<usize>, i64)>, n_vars: usize) -> ZPoly {
390 ZPoly::from_terms(
391 IntegerDomain,
392 n_vars,
393 terms
394 .into_iter()
395 .map(|(e, c)| (e, Integer::from(c)))
396 .collect(),
397 )
398 }
399
400 #[test]
401 fn rational_zero_and_one() {
402 let z = ZRat::zero(&IntegerDomain, 1);
403 assert!(z.is_zero());
404 assert!(!z.is_one());
405
406 let o = ZRat::one(&IntegerDomain, 1);
407 assert!(!o.is_zero());
408 assert!(o.is_one());
409 }
410
411 #[test]
412 fn rational_from_polynomial() {
413 let p = poly1(vec![(vec![0], 1), (vec![1], 1)]);
415 let r = ZRat::from_polynomial(p.clone());
416 assert_eq!(r.numerator, p);
417 assert!(r.denominator.n_terms() <= 1);
418 }
419
420 #[test]
421 fn rational_neg() {
422 let num = poly1(vec![(vec![1], 1)]);
424 let den = poly1(vec![(vec![0], 1), (vec![1], 1)]);
425 let r = ZRat::new(num, den);
426 let nr = r.neg();
427 assert_eq!(nr.numerator.coeff(&[1]), Integer::from(-1));
429 }
430
431 #[test]
432 fn rational_add_same_den() {
433 let x = poly1(vec![(vec![1], 1)]);
435 let one = poly1(vec![(vec![0], 1)]);
436
437 let r1 = ZRat::new(one.clone(), x.clone());
438 let r2 = ZRat::new(one, x.clone());
439 let sum = r1.add(&r2);
440
441 assert_eq!(sum.numerator.coeff(&[0]), Integer::from(2));
443 }
444
445 #[test]
446 fn rational_add_different_den() {
447 let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
450 let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
451 let one = poly1(vec![(vec![0], 1)]);
452
453 let r1 = ZRat::new(one.clone(), x_minus_1);
454 let r2 = ZRat::new(one, x_plus_1);
455 let sum = r1.add(&r2);
456
457 assert!(!sum.is_zero());
459 }
461
462 #[test]
463 fn rational_mul() {
464 let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
466 let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
467
468 let r1 = ZRat::new(x_plus_1.clone(), x_minus_1.clone());
469 let r2 = ZRat::new(x_minus_1, x_plus_1);
470 let prod = r1.mul(&r2);
471
472 assert!(prod.is_one() || (prod.numerator == prod.denominator));
474 }
475
476 #[test]
477 fn rational_inv() {
478 let x = poly1(vec![(vec![1], 1)]);
479 let one = poly1(vec![(vec![0], 1)]);
480 let r = ZRat::new(x, one);
481 let r_inv = r.inv().unwrap();
482
483 assert_eq!(r_inv.numerator, r_inv.denominator.one());
485 }
486
487 #[test]
488 fn rational_pow() {
489 let x = poly1(vec![(vec![1], 1)]);
491 let one = poly1(vec![(vec![0], 1)]);
492 let r = ZRat::new(x, one);
493 let r3 = r.pow(3);
494
495 assert_eq!(r3.numerator.coeff(&[3]), Integer::from(1));
497 assert_eq!(r3.numerator.n_terms(), 1);
498 }
499}