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 let Some(den_lc) = self.denominator.leading_coeff() {
210 if let Some(neg_lc) = self.numerator.domain().inv(den_lc) {
213 self.numerator = self.numerator.mul_scalar(&neg_lc);
215 self.denominator = self.denominator.mul_scalar(&neg_lc);
216 }
217 }
218 }
219
220 pub fn add(&self, other: &Self) -> Self {
228 if self.is_zero() {
229 return other.clone();
230 }
231 if other.is_zero() {
232 return self.clone();
233 }
234
235 if self.denominator == other.denominator {
237 let num = self.numerator.add(&other.numerator);
238 return Self::from_num_den(num, self.denominator.clone());
239 }
240
241 let ad = self.numerator.mul(&other.denominator);
244 let bc = other.numerator.mul(&self.denominator);
245 let num = ad.add(&bc);
246 let den = self.denominator.mul(&other.denominator);
247 Self::from_num_den(num, den)
248 }
249
250 pub fn sub(&self, other: &Self) -> Self {
252 self.add(&other.neg())
253 }
254
255 pub fn mul(&self, other: &Self) -> Self {
260 if self.is_zero() || other.is_zero() {
261 return Self::zero(self.domain(), self.n_vars());
262 }
263
264 let num = self.numerator.mul(&other.numerator);
268 let den = self.denominator.mul(&other.denominator);
269 Self::from_num_den(num, den)
270 }
271
272 pub fn div(&self, other: &Self) -> Option<Self> {
274 let inv = other.inv()?;
275 Some(self.mul(&inv))
276 }
277}
278
279impl<D: Domain, O: MonomialOrder> fmt::Display for RationalPolynomial<D, O>
284where
285 D::Element: fmt::Display,
286{
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 if self.denominator.is_zero() || self.denominator.n_terms() <= 1 {
289 let const_val = self.denominator.coeff(&vec![0; self.denominator.n_vars()]);
291 if self.domain().is_one(&const_val) {
292 return write!(f, "{:?}", self.numerator);
293 }
294 }
295 write!(f, "({:?}) / ({:?})", self.numerator, self.denominator)
296 }
297}
298
299impl<D: EuclideanDomain, O: MonomialOrder> SparseMultivariatePolynomial<D, O> {
304 fn div_scalar(&self, scalar: &D::Element) -> Self {
306 if self.domain().is_one(scalar) {
307 return self.clone();
308 }
309 let inv = self
310 .domain()
311 .inv(scalar)
312 .expect("div_scalar: cannot invert zero");
313 self.mul_scalar(&inv)
314 }
315}
316
317#[cfg(test)]
322mod tests {
323 use super::*;
324 use crate::sparse::Lex;
325 use ocas_domain::{Integer, IntegerDomain};
326
327 type ZPoly = SparseMultivariatePolynomial<IntegerDomain, Lex>;
328 type ZRat = RationalPolynomial<IntegerDomain, Lex>;
329
330 fn poly1(terms: Vec<(Vec<usize>, i64)>) -> ZPoly {
331 ZPoly::from_terms(
332 IntegerDomain,
333 1,
334 terms
335 .into_iter()
336 .map(|(e, c)| (e, Integer::from(c)))
337 .collect(),
338 )
339 }
340
341 #[allow(dead_code)]
342 fn poly2(terms: Vec<(Vec<usize>, i64)>, n_vars: usize) -> ZPoly {
343 ZPoly::from_terms(
344 IntegerDomain,
345 n_vars,
346 terms
347 .into_iter()
348 .map(|(e, c)| (e, Integer::from(c)))
349 .collect(),
350 )
351 }
352
353 #[test]
354 fn rational_zero_and_one() {
355 let z = ZRat::zero(&IntegerDomain, 1);
356 assert!(z.is_zero());
357 assert!(!z.is_one());
358
359 let o = ZRat::one(&IntegerDomain, 1);
360 assert!(!o.is_zero());
361 assert!(o.is_one());
362 }
363
364 #[test]
365 fn rational_from_polynomial() {
366 let p = poly1(vec![(vec![0], 1), (vec![1], 1)]);
368 let r = ZRat::from_polynomial(p.clone());
369 assert_eq!(r.numerator, p);
370 assert!(r.denominator.n_terms() <= 1);
371 }
372
373 #[test]
374 fn rational_neg() {
375 let num = poly1(vec![(vec![1], 1)]);
377 let den = poly1(vec![(vec![0], 1), (vec![1], 1)]);
378 let r = ZRat::new(num, den);
379 let nr = r.neg();
380 assert_eq!(nr.numerator.coeff(&[1]), Integer::from(-1));
382 }
383
384 #[test]
385 fn rational_add_same_den() {
386 let x = poly1(vec![(vec![1], 1)]);
388 let one = poly1(vec![(vec![0], 1)]);
389
390 let r1 = ZRat::new(one.clone(), x.clone());
391 let r2 = ZRat::new(one, x.clone());
392 let sum = r1.add(&r2);
393
394 assert_eq!(sum.numerator.coeff(&[0]), Integer::from(2));
396 }
397
398 #[test]
399 fn rational_add_different_den() {
400 let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
403 let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
404 let one = poly1(vec![(vec![0], 1)]);
405
406 let r1 = ZRat::new(one.clone(), x_minus_1);
407 let r2 = ZRat::new(one, x_plus_1);
408 let sum = r1.add(&r2);
409
410 assert!(!sum.is_zero());
412 }
414
415 #[test]
416 fn rational_mul() {
417 let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
419 let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
420
421 let r1 = ZRat::new(x_plus_1.clone(), x_minus_1.clone());
422 let r2 = ZRat::new(x_minus_1, x_plus_1);
423 let prod = r1.mul(&r2);
424
425 assert!(prod.is_one() || (prod.numerator == prod.denominator));
427 }
428
429 #[test]
430 fn rational_inv() {
431 let x = poly1(vec![(vec![1], 1)]);
432 let one = poly1(vec![(vec![0], 1)]);
433 let r = ZRat::new(x, one);
434 let r_inv = r.inv().unwrap();
435
436 assert_eq!(r_inv.numerator, r_inv.denominator.one());
438 }
439
440 #[test]
441 fn rational_pow() {
442 let x = poly1(vec![(vec![1], 1)]);
444 let one = poly1(vec![(vec![0], 1)]);
445 let r = ZRat::new(x, one);
446 let r3 = r.pow(3);
447
448 assert_eq!(r3.numerator.coeff(&[3]), Integer::from(1));
450 assert_eq!(r3.numerator.n_terms(), 1);
451 }
452}