use super::types::*;
#[allow(unused_imports)]
use crate::prelude::*;
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{One, Signed, Zero};
type Polynomial = super::Polynomial;
pub(super) fn gcd_bigint(mut a: BigInt, mut b: BigInt) -> BigInt {
while !b.is_zero() {
let t = &a % &b;
a = b;
b = t;
}
a.abs()
}
pub(super) fn count_sign_variations(seq: &[Polynomial], var: Var, point: &BigRational) -> usize {
let mut signs = Vec::new();
for poly in seq {
let val = poly.eval_at(var, point);
let c = val.constant_term();
if !c.is_zero() {
signs.push(if c.is_positive() { 1 } else { -1 });
}
}
let mut variations = 0;
for i in 1..signs.len() {
if signs[i] != signs[i - 1] {
variations += 1;
}
}
variations
}
pub(super) fn cauchy_root_bound(poly: &Polynomial, var: Var) -> BigRational {
if poly.is_zero() {
return BigRational::one();
}
let deg = poly.degree(var);
if deg == 0 {
return BigRational::one();
}
let lc = poly.univ_coeff(var, deg);
if lc.is_zero() {
return BigRational::one();
}
let lc_abs = lc.abs();
let mut max_ratio = BigRational::zero();
for k in 0..deg {
let coeff = poly.univ_coeff(var, k);
if !coeff.is_zero() {
let ratio = coeff.abs() / &lc_abs;
if ratio > max_ratio {
max_ratio = ratio;
}
}
}
BigRational::one() + max_ratio
}
pub(super) fn fujiwara_root_bound(poly: &Polynomial, var: Var) -> BigRational {
if poly.is_zero() {
return BigRational::one();
}
let deg = poly.degree(var);
if deg == 0 {
return BigRational::one();
}
let lc = poly.univ_coeff(var, deg);
if lc.is_zero() {
return BigRational::one();
}
let lc_abs = lc.abs();
let mut max_val = BigRational::zero();
for k in 0..deg {
let coeff = poly.univ_coeff(var, k);
if !coeff.is_zero() {
let ratio = coeff.abs() / &lc_abs;
let exp = deg - k;
let approx_root = rational_nth_root_approx(&ratio, exp);
if approx_root > max_val {
max_val = approx_root;
}
}
}
BigRational::from_integer(BigInt::from(2)) * max_val
}
pub(super) fn lagrange_positive_root_bound(poly: &Polynomial, var: Var) -> BigRational {
if poly.is_zero() {
return BigRational::one();
}
let deg = poly.degree(var);
if deg == 0 {
return BigRational::one();
}
let lc = poly.univ_coeff(var, deg);
if lc.is_zero() {
return BigRational::one();
}
let lc_abs = lc.abs();
let mut max_val = BigRational::zero();
let lc_positive = lc.is_positive();
for k in 0..deg {
let coeff = poly.univ_coeff(var, k);
if !coeff.is_zero() && coeff.is_positive() != lc_positive {
let ratio = coeff.abs() / &lc_abs;
let exp = deg - k;
let approx_root = rational_nth_root_approx(&ratio, exp);
if approx_root > max_val {
max_val = approx_root;
}
}
}
if max_val.is_zero() {
BigRational::one()
} else {
max_val
}
}
pub(super) fn rational_nth_root_approx(target: &BigRational, n: u32) -> BigRational {
use crate::rational::pow_uint;
if n == 0 {
return BigRational::one();
}
if n == 1 {
return target.clone();
}
if target.is_zero() {
return BigRational::zero();
}
let mut low = BigRational::zero();
let mut high = target.clone() + BigRational::one();
for _ in 0..100 {
let mid = (&low + &high) / BigRational::from_integer(BigInt::from(2));
let mid_pow_n = pow_uint(&mid, n);
if &mid_pow_n == target {
return mid;
}
if mid_pow_n < *target {
low = mid;
} else {
high = mid.clone();
}
let diff = &high - &low;
if diff < BigRational::new(BigInt::one(), BigInt::from(1000000)) {
return high;
}
}
high
}
pub(super) fn count_coefficient_sign_variations(poly: &Polynomial, var: Var) -> usize {
if poly.is_zero() {
return 0;
}
let deg = poly.degree(var);
if deg == 0 {
return 0;
}
let mut coeffs = Vec::new();
for k in 0..=deg {
let coeff = poly.univ_coeff(var, k);
if !coeff.is_zero() {
coeffs.push(coeff);
}
}
let mut variations = 0;
for i in 1..coeffs.len() {
if coeffs[i].is_positive() != coeffs[i - 1].is_positive() {
variations += 1;
}
}
variations
}
pub(super) fn descartes_positive_roots(poly: &Polynomial, var: Var) -> (usize, usize) {
let variations = count_coefficient_sign_variations(poly, var);
let lower = variations % 2;
(lower, variations)
}
pub(super) fn descartes_negative_roots(poly: &Polynomial, var: Var) -> (usize, usize) {
let deg = poly.degree(var);
let mut neg_poly_terms = Vec::new();
for k in 0..=deg {
let coeff = poly.univ_coeff(var, k);
if !coeff.is_zero() {
let adjusted_coeff = if k % 2 == 1 { -coeff } else { coeff };
if k == 0 {
neg_poly_terms.push(Term::constant(adjusted_coeff));
} else {
neg_poly_terms.push(Term::new(adjusted_coeff, Monomial::from_var_power(var, k)));
}
}
}
let neg_poly = Polynomial::from_terms(neg_poly_terms, poly.order);
descartes_positive_roots(&neg_poly, var)
}
pub fn rational_sqrt(n: &BigRational) -> Option<BigRational> {
if n.is_negative() {
return None;
}
if n.is_zero() {
return Some(BigRational::zero());
}
let numer = n.numer();
let denom = n.denom();
let sqrt_numer = integer_sqrt(numer)?;
let sqrt_denom = integer_sqrt(denom)?;
Some(BigRational::new(sqrt_numer, sqrt_denom))
}
pub(super) fn integer_sqrt(n: &BigInt) -> Option<BigInt> {
if n.is_negative() {
return None;
}
if n.is_zero() {
return Some(BigInt::zero());
}
if n.is_one() {
return Some(BigInt::one());
}
let mut x: BigInt = n.clone();
let mut y: BigInt = (&x + BigInt::one()) >> 1;
while y < x {
x = y.clone();
y = (&x + (n / &x)) >> 1;
}
if &x * &x == *n { Some(x) } else { None }
}