use std::fmt;
use ocas_domain::{Domain, EuclideanDomain};
use crate::sparse::{Grevlex, MonomialOrder, SparseMultivariatePolynomial};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RationalPolynomial<D: Domain, O: MonomialOrder = Grevlex> {
pub numerator: SparseMultivariatePolynomial<D, O>,
pub denominator: SparseMultivariatePolynomial<D, O>,
}
impl<D: Domain, O: MonomialOrder> RationalPolynomial<D, O> {
pub fn new(
numerator: SparseMultivariatePolynomial<D, O>,
denominator: SparseMultivariatePolynomial<D, O>,
) -> Self {
debug_assert!(
!denominator.is_zero(),
"RationalPolynomial: denominator must be non-zero"
);
Self {
numerator,
denominator,
}
}
pub fn from_polynomial(poly: SparseMultivariatePolynomial<D, O>) -> Self {
let one = poly.one();
Self {
numerator: poly,
denominator: one,
}
}
pub fn zero(domain: &D, n_vars: usize) -> Self {
let z = SparseMultivariatePolynomial::new(domain.clone(), n_vars);
let one = z.one();
Self {
numerator: z.clone(),
denominator: one,
}
}
pub fn one(domain: &D, n_vars: usize) -> Self {
let o = SparseMultivariatePolynomial::new(domain.clone(), n_vars).one();
Self {
numerator: o.clone(),
denominator: o,
}
}
pub fn is_zero(&self) -> bool {
self.numerator.is_zero()
}
pub fn is_one(&self) -> bool {
self.numerator == self.denominator
}
pub fn n_vars(&self) -> usize {
self.numerator.n_vars()
}
pub fn domain(&self) -> &D {
self.numerator.domain()
}
pub fn neg(&self) -> Self {
Self {
numerator: self.numerator.neg(),
denominator: self.denominator.clone(),
}
}
pub fn inv(&self) -> Option<Self> {
if self.numerator.is_zero() {
return None;
}
Some(Self {
numerator: self.denominator.clone(),
denominator: self.numerator.clone(),
})
}
pub fn pow(&self, k: u32) -> Self {
if k == 0 {
return Self::one(self.domain(), self.n_vars());
}
let mut num = self.numerator.one();
let mut den = self.denominator.one();
let mut base_num = self.numerator.clone();
let mut base_den = self.denominator.clone();
let mut exp = k;
while exp > 0 {
if exp & 1 == 1 {
num = num.mul(&base_num);
den = den.mul(&base_den);
}
base_num = base_num.mul(&base_num);
base_den = base_den.mul(&base_den);
exp >>= 1;
}
Self {
numerator: num,
denominator: den,
}
}
}
impl<D: EuclideanDomain, O: MonomialOrder> RationalPolynomial<D, O> {
pub fn from_num_den(
numerator: SparseMultivariatePolynomial<D, O>,
denominator: SparseMultivariatePolynomial<D, O>,
) -> Self {
if denominator.is_zero() {
panic!("RationalPolynomial::from_num_den: denominator is zero");
}
if numerator.is_zero() {
return Self {
numerator,
denominator,
};
}
let mut rat = Self {
numerator,
denominator,
};
rat.canonicalize();
rat
}
fn canonicalize(&mut self) {
if self.numerator.is_zero() {
return;
}
let num_content = self.numerator.content();
let den_content = self.denominator.content();
let coeff_gcd = self.numerator.domain().gcd(&num_content, &den_content);
if !self.numerator.domain().is_one(&coeff_gcd) {
self.numerator = self.numerator.div_scalar(&coeff_gcd);
self.denominator = self.denominator.div_scalar(&coeff_gcd);
}
if let Some(den_lc) = self.denominator.leading_coeff() {
if let Some(neg_lc) = self.numerator.domain().inv(den_lc) {
self.numerator = self.numerator.mul_scalar(&neg_lc);
self.denominator = self.denominator.mul_scalar(&neg_lc);
}
}
}
pub fn add(&self, other: &Self) -> Self {
if self.is_zero() {
return other.clone();
}
if other.is_zero() {
return self.clone();
}
if self.denominator == other.denominator {
let num = self.numerator.add(&other.numerator);
return Self::from_num_den(num, self.denominator.clone());
}
let ad = self.numerator.mul(&other.denominator);
let bc = other.numerator.mul(&self.denominator);
let num = ad.add(&bc);
let den = self.denominator.mul(&other.denominator);
Self::from_num_den(num, den)
}
pub fn sub(&self, other: &Self) -> Self {
self.add(&other.neg())
}
pub fn mul(&self, other: &Self) -> Self {
if self.is_zero() || other.is_zero() {
return Self::zero(self.domain(), self.n_vars());
}
let num = self.numerator.mul(&other.numerator);
let den = self.denominator.mul(&other.denominator);
Self::from_num_den(num, den)
}
pub fn div(&self, other: &Self) -> Option<Self> {
let inv = other.inv()?;
Some(self.mul(&inv))
}
}
impl<D: Domain, O: MonomialOrder> fmt::Display for RationalPolynomial<D, O>
where
D::Element: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.denominator.is_zero() || self.denominator.n_terms() <= 1 {
let const_val = self.denominator.coeff(&vec![0; self.denominator.n_vars()]);
if self.domain().is_one(&const_val) {
return write!(f, "{:?}", self.numerator);
}
}
write!(f, "({:?}) / ({:?})", self.numerator, self.denominator)
}
}
impl<D: EuclideanDomain, O: MonomialOrder> SparseMultivariatePolynomial<D, O> {
fn div_scalar(&self, scalar: &D::Element) -> Self {
if self.domain().is_one(scalar) {
return self.clone();
}
let inv = self
.domain()
.inv(scalar)
.expect("div_scalar: cannot invert zero");
self.mul_scalar(&inv)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sparse::Lex;
use ocas_domain::{Integer, IntegerDomain};
type ZPoly = SparseMultivariatePolynomial<IntegerDomain, Lex>;
type ZRat = RationalPolynomial<IntegerDomain, Lex>;
fn poly1(terms: Vec<(Vec<usize>, i64)>) -> ZPoly {
ZPoly::from_terms(
IntegerDomain,
1,
terms
.into_iter()
.map(|(e, c)| (e, Integer::from(c)))
.collect(),
)
}
#[allow(dead_code)]
fn poly2(terms: Vec<(Vec<usize>, i64)>, n_vars: usize) -> ZPoly {
ZPoly::from_terms(
IntegerDomain,
n_vars,
terms
.into_iter()
.map(|(e, c)| (e, Integer::from(c)))
.collect(),
)
}
#[test]
fn rational_zero_and_one() {
let z = ZRat::zero(&IntegerDomain, 1);
assert!(z.is_zero());
assert!(!z.is_one());
let o = ZRat::one(&IntegerDomain, 1);
assert!(!o.is_zero());
assert!(o.is_one());
}
#[test]
fn rational_from_polynomial() {
let p = poly1(vec![(vec![0], 1), (vec![1], 1)]);
let r = ZRat::from_polynomial(p.clone());
assert_eq!(r.numerator, p);
assert!(r.denominator.n_terms() <= 1);
}
#[test]
fn rational_neg() {
let num = poly1(vec![(vec![1], 1)]);
let den = poly1(vec![(vec![0], 1), (vec![1], 1)]);
let r = ZRat::new(num, den);
let nr = r.neg();
assert_eq!(nr.numerator.coeff(&[1]), Integer::from(-1));
}
#[test]
fn rational_add_same_den() {
let x = poly1(vec![(vec![1], 1)]);
let one = poly1(vec![(vec![0], 1)]);
let r1 = ZRat::new(one.clone(), x.clone());
let r2 = ZRat::new(one, x.clone());
let sum = r1.add(&r2);
assert_eq!(sum.numerator.coeff(&[0]), Integer::from(2));
}
#[test]
fn rational_add_different_den() {
let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
let one = poly1(vec![(vec![0], 1)]);
let r1 = ZRat::new(one.clone(), x_minus_1);
let r2 = ZRat::new(one, x_plus_1);
let sum = r1.add(&r2);
assert!(!sum.is_zero());
}
#[test]
fn rational_mul() {
let x_plus_1 = poly1(vec![(vec![0], 1), (vec![1], 1)]);
let x_minus_1 = poly1(vec![(vec![0], -1), (vec![1], 1)]);
let r1 = ZRat::new(x_plus_1.clone(), x_minus_1.clone());
let r2 = ZRat::new(x_minus_1, x_plus_1);
let prod = r1.mul(&r2);
assert!(prod.is_one() || (prod.numerator == prod.denominator));
}
#[test]
fn rational_inv() {
let x = poly1(vec![(vec![1], 1)]);
let one = poly1(vec![(vec![0], 1)]);
let r = ZRat::new(x, one);
let r_inv = r.inv().unwrap();
assert_eq!(r_inv.numerator, r_inv.denominator.one());
}
#[test]
fn rational_pow() {
let x = poly1(vec![(vec![1], 1)]);
let one = poly1(vec![(vec![0], 1)]);
let r = ZRat::new(x, one);
let r3 = r.pow(3);
assert_eq!(r3.numerator.coeff(&[3]), Integer::from(1));
assert_eq!(r3.numerator.n_terms(), 1);
}
}