use crate::rational_polynomial::RationalPolynomial;
use alloc::vec;
use alloc::vec::Vec;
use core::mem::take;
use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, Gcd};
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::polynomial::{Integral, IntegralAssign, Polynomial};
use malachite_nz::integer::Integer;
use malachite_nz::integer_polynomial::IntegerPolynomial;
use malachite_nz::natural::Natural;
fn gcd_with_small(x: &Natural, k: u64) -> u64 {
u64::exact_from(&(x % Natural::from(k))).gcd(k)
}
fn integral_in_place(coefficients: &mut [Integer], denominator: &mut Natural) {
let len = coefficients.len();
let mut divisors = vec![1u64; len];
let mut t = Natural::ONE;
for (k, c) in coefficients.iter_mut().enumerate().skip(2).rev() {
if *c == 0u32 {
continue;
}
let k = u64::exact_from(k);
let g = gcd_with_small(c.unsigned_abs_ref(), k);
if g == k {
c.div_exact_assign(Integer::from(k));
} else {
if g != 1 {
c.div_exact_assign(Integer::from(g));
}
let divisor = k / g;
divisors[usize::exact_from(k)] = divisor;
let d = gcd_with_small(&t, divisor);
if d != divisor {
t *= Natural::from(divisor / d);
}
}
}
if t != 1u32 {
for (c, &divisor) in coefficients.iter_mut().zip(&divisors).skip(2) {
if *c != 0u32 {
*c *= Integer::from((&t).div_exact(Natural::from(divisor)));
}
}
coefficients[1] *= Integer::from(&t);
*denominator *= t;
}
}
fn shifted(mut coefficients: Vec<Integer>) -> Vec<Integer> {
coefficients.insert(0, Integer::ZERO);
coefficients
}
impl Integral for RationalPolynomial {
type Output = Self;
#[inline]
fn integral(mut self) -> Self {
self.integral_assign();
self
}
}
impl Integral for &RationalPolynomial {
type Output = RationalPolynomial;
fn integral(self) -> RationalPolynomial {
if *self == RationalPolynomial::ZERO {
return RationalPolynomial::ZERO;
}
let mut coefficients = shifted(self.numerator.coefficients_asc().to_vec());
let mut denominator = self.denominator.clone();
integral_in_place(&mut coefficients, &mut denominator);
RationalPolynomial {
numerator: IntegerPolynomial::from_coefficients_asc(coefficients),
denominator,
}
}
}
impl IntegralAssign for RationalPolynomial {
fn integral_assign(&mut self) {
if *self == Self::ZERO {
return;
}
let mut coefficients = shifted(take(&mut self.numerator).into_coefficients_asc());
integral_in_place(&mut coefficients, &mut self.denominator);
self.numerator = IntegerPolynomial::from_coefficients_asc(coefficients);
}
}