use crate::rational_polynomial::RationalPolynomial;
use alloc::borrow::Cow;
use core::mem::take;
use core::ops::{Mul, MulAssign};
use core::ptr;
use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, Square};
use malachite_base::num::basic::traits::{One, Zero};
use malachite_nz::integer::Integer;
use malachite_nz::integer_polynomial::IntegerPolynomial;
use malachite_nz::integer_vector::arithmetic::content_chained::integers_content_chained;
use malachite_nz::natural::Natural;
pub(crate) fn cross_gcds(
x: &IntegerPolynomial,
a: &Natural,
y: &IntegerPolynomial,
b: &Natural,
) -> (Natural, Natural) {
let gcd_1 = if *b == 1u32 {
Natural::ONE
} else {
integers_content_chained(x.coefficients_asc(), b)
};
let gcd_2 = if *a == 1u32 {
Natural::ONE
} else {
integers_content_chained(y.coefficients_asc(), a)
};
(gcd_1, gcd_2)
}
fn div_exact_cows(x: &mut Cow<'_, IntegerPolynomial>, b: &mut Cow<'_, Natural>, g: &Natural) {
if *g == 1u32 {
return;
}
let g_integer = Integer::from(g);
match x {
Cow::Borrowed(x_ref) => *x = Cow::Owned((*x_ref).div_exact(g_integer)),
Cow::Owned(x_owned) => x_owned.div_exact_assign(g_integer),
}
match b {
Cow::Borrowed(b_ref) => *b = Cow::Owned((*b_ref).div_exact(g)),
Cow::Owned(b_owned) => b_owned.div_exact_assign(g),
}
}
fn mul_cows<'a, T>(x: Cow<'a, T>, y: Cow<'a, T>) -> T
where
T: Clone + Mul<T, Output = T> + Mul<&'a T, Output = T>,
&'a T: Mul<T, Output = T> + Mul<&'a T, Output = T>,
{
match (x, y) {
(Cow::Owned(x), Cow::Owned(y)) => x * y,
(Cow::Owned(x), Cow::Borrowed(y)) => x * y,
(Cow::Borrowed(x), Cow::Owned(y)) => x * y,
(Cow::Borrowed(x), Cow::Borrowed(y)) => x * y,
}
}
fn mul_cow(
mut x: Cow<'_, IntegerPolynomial>,
mut a: Cow<'_, Natural>,
mut y: Cow<'_, IntegerPolynomial>,
mut b: Cow<'_, Natural>,
) -> RationalPolynomial {
if *x == IntegerPolynomial::ZERO || *y == IntegerPolynomial::ZERO {
return RationalPolynomial::ZERO;
}
let (gcd_1, gcd_2) = cross_gcds(&x, &a, &y, &b);
div_exact_cows(&mut x, &mut b, &gcd_1);
div_exact_cows(&mut y, &mut a, &gcd_2);
RationalPolynomial {
numerator: mul_cows(x, y),
denominator: mul_cows(a, b),
}
}
crate_test_fn! {mul_divide_after(
p: &RationalPolynomial,
q: &RationalPolynomial,
) -> RationalPolynomial {
if *p == RationalPolynomial::ZERO || *q == RationalPolynomial::ZERO {
return RationalPolynomial::ZERO;
}
let (gcd_1, gcd_2) = cross_gcds(&p.numerator, &p.denominator, &q.numerator, &q.denominator);
let g = gcd_1 * gcd_2;
let mut numerator = &p.numerator * &q.numerator;
let mut denominator = &p.denominator * &q.denominator;
if g != 1u32 {
numerator.div_exact_assign(Integer::from(&g));
denominator.div_exact_assign(g);
}
RationalPolynomial {
numerator,
denominator,
}
}}
impl Mul<Self> for RationalPolynomial {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
mul_cow(
Cow::Owned(self.numerator),
Cow::Owned(self.denominator),
Cow::Owned(other.numerator),
Cow::Owned(other.denominator),
)
}
}
impl Mul<&Self> for RationalPolynomial {
type Output = Self;
#[inline]
fn mul(self, other: &Self) -> Self {
mul_cow(
Cow::Owned(self.numerator),
Cow::Owned(self.denominator),
Cow::Borrowed(&other.numerator),
Cow::Borrowed(&other.denominator),
)
}
}
impl Mul<RationalPolynomial> for &RationalPolynomial {
type Output = RationalPolynomial;
#[inline]
fn mul(self, other: RationalPolynomial) -> RationalPolynomial {
other * self
}
}
impl Mul<&RationalPolynomial> for &RationalPolynomial {
type Output = RationalPolynomial;
fn mul(self, other: &RationalPolynomial) -> RationalPolynomial {
if ptr::eq(self, other) {
return self.square();
}
mul_cow(
Cow::Borrowed(&self.numerator),
Cow::Borrowed(&self.denominator),
Cow::Borrowed(&other.numerator),
Cow::Borrowed(&other.denominator),
)
}
}
impl MulAssign<Self> for RationalPolynomial {
fn mul_assign(&mut self, other: Self) {
*self = take(self) * other;
}
}
impl MulAssign<&Self> for RationalPolynomial {
fn mul_assign(&mut self, other: &Self) {
*self = take(self) * other;
}
}