use crate::rational_polynomial::RationalPolynomial;
use alloc::vec::Vec;
use core::mem::{swap, take};
use core::ops::{Add, AddAssign};
use core::ptr;
use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, Gcd, Parity};
use malachite_base::num::basic::traits::{NegativeOne, One};
use malachite_base::polynomial::Polynomial;
use malachite_nz::integer::Integer;
use malachite_nz::integer_polynomial::IntegerPolynomial;
use malachite_nz::integer_polynomial::arithmetic::scalar_add_mul::integers_add_mul_scalar_assign;
use malachite_nz::integer_vector::arithmetic::content_chained::integers_content_chained;
use malachite_nz::integer_vector::arithmetic::scalar_mul::integers_mul_scalar_assign;
use malachite_nz::natural::Natural;
pub(crate) fn add_or_sub_owned_ref(
x: IntegerPolynomial,
a: Natural,
y: &IntegerPolynomial,
b: &Natural,
negate_y: bool,
) -> RationalPolynomial {
add_or_sub_coefficients(
x.into_coefficients_asc(),
a,
y.coefficients_asc(),
b,
negate_y,
false,
)
}
pub(crate) fn add_or_sub_coefficients(
mut xs: Vec<Integer>,
a: Natural,
ys: &[Integer],
b: &Natural,
negate_y: bool,
truncated: bool,
) -> RationalPolynomial {
if a == *b {
let sign = if negate_y {
Integer::NEGATIVE_ONE
} else {
Integer::ONE
};
integers_add_mul_scalar_assign(&mut xs, ys, &sign);
let mut numerator = IntegerPolynomial::from_coefficients_asc(xs);
let mut denominator = a;
if denominator != 1u32 {
let g = integers_content_chained(numerator.coefficients_asc(), &denominator);
if g != 1u32 {
denominator.div_exact_assign(&g);
numerator.div_exact_assign(Integer::from(g));
}
}
return RationalPolynomial {
numerator,
denominator,
};
}
let g = if a != 1u32 && *b != 1u32 {
(&a).gcd(b)
} else {
Natural::ONE
};
if g == 1u32 {
integers_mul_scalar_assign(&mut xs, &Integer::from(b));
let mut denominator = &a * b;
let a = Integer::from(a);
let y_multiplier = if negate_y { -a } else { a };
integers_add_mul_scalar_assign(&mut xs, ys, &y_multiplier);
let mut numerator = IntegerPolynomial::from_coefficients_asc(xs);
if truncated {
let e = integers_content_chained(numerator.coefficients_asc(), &denominator);
if e != 1u32 {
denominator.div_exact_assign(&e);
numerator.div_exact_assign(Integer::from(e));
}
}
return RationalPolynomial {
numerator,
denominator,
};
}
let a_over_g = Integer::from((&a).div_exact(&g));
let y_multiplier = if negate_y { -a_over_g } else { a_over_g };
let b_over_g = b.div_exact(&g);
integers_mul_scalar_assign(&mut xs, &Integer::from(&b_over_g));
integers_add_mul_scalar_assign(&mut xs, ys, &y_multiplier);
let mut denominator = a * b_over_g;
let e = integers_content_chained(&xs, if truncated { &denominator } else { &g });
let mut numerator = IntegerPolynomial::from_coefficients_asc(xs);
if e != 1u32 {
denominator.div_exact_assign(&e);
numerator.div_exact_assign(Integer::from(e));
}
RationalPolynomial {
numerator,
denominator,
}
}
fn add_owned_owned(mut p: RationalPolynomial, mut q: RationalPolynomial) -> RationalPolynomial {
if q.numerator.len() > p.numerator.len() {
swap(&mut p, &mut q);
}
add_or_sub_owned_ref(
p.numerator,
p.denominator,
&q.numerator,
&q.denominator,
false,
)
}
impl Add<Self> for RationalPolynomial {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
add_owned_owned(self, other)
}
}
impl Add<&Self> for RationalPolynomial {
type Output = Self;
#[inline]
fn add(self, other: &Self) -> Self {
add_or_sub_owned_ref(
self.numerator,
self.denominator,
&other.numerator,
&other.denominator,
false,
)
}
}
impl Add<RationalPolynomial> for &RationalPolynomial {
type Output = RationalPolynomial;
#[inline]
fn add(self, other: RationalPolynomial) -> RationalPolynomial {
add_or_sub_owned_ref(
other.numerator,
other.denominator,
&self.numerator,
&self.denominator,
false,
)
}
}
impl Add<&RationalPolynomial> for &RationalPolynomial {
type Output = RationalPolynomial;
fn add(self, other: &RationalPolynomial) -> RationalPolynomial {
if ptr::eq(self, other) {
return if self.denominator.even() {
RationalPolynomial {
numerator: self.numerator.clone(),
denominator: &self.denominator >> 1u32,
}
} else {
RationalPolynomial {
numerator: IntegerPolynomial::from_coefficients_asc(
self.numerator
.coefficients_asc()
.iter()
.map(|c| c << 1u32)
.collect::<Vec<_>>(),
),
denominator: self.denominator.clone(),
}
};
}
add_or_sub_owned_ref(
self.numerator.clone(),
self.denominator.clone(),
&other.numerator,
&other.denominator,
false,
)
}
}
impl AddAssign<Self> for RationalPolynomial {
fn add_assign(&mut self, other: Self) {
*self = add_owned_owned(take(self), other);
}
}
impl AddAssign<&Self> for RationalPolynomial {
fn add_assign(&mut self, other: &Self) {
let p = take(self);
*self = add_or_sub_owned_ref(
p.numerator,
p.denominator,
&other.numerator,
&other.denominator,
false,
);
}
}