use smallvec::SmallVec;
use crate::integer::big::ops::normalize::{gcd, simplify_fraction_without_info};
use crate::integer::big::{NonZeroUbig, Ubig};
use crate::rational::big::Big;
use crate::sign::Sign;
use crate::traits::Gcd;
impl<const S: usize> Gcd for Big<S> {
fn gcd(&self, other: &Self) -> Option<Self> {
Some(match (self.sign, other.sign) {
(Sign::Zero, Sign::Zero) => <Self as num_traits::Zero>::zero(),
(Sign::Zero, _) => Self {
sign: Sign::Positive,
numerator: other.numerator.clone(),
denominator: other.denominator.clone(),
},
(_, Sign::Zero) => Self {
sign: Sign::Positive,
numerator: self.numerator.clone(),
denominator: self.denominator.clone(),
},
_ => Self {
sign: Sign::Positive,
numerator: numerator_gcd(&self.numerator, &other.numerator),
denominator: denominator_lcm(&self.denominator, &other.denominator),
},
})
}
}
fn numerator_gcd<const S: usize>(left: &Ubig<S>, right: &Ubig<S>) -> Ubig<S> {
if num_traits::One::is_one(left) || num_traits::One::is_one(right) {
return Ubig::new(1);
}
if **left == **right {
return left.clone();
}
unsafe { Ubig::from_inner_unchecked(gcd(left, right)) }
}
fn denominator_lcm<const S: usize>(left: &NonZeroUbig<S>, right: &NonZeroUbig<S>) -> NonZeroUbig<S> {
let mut reduced = SmallVec::<[usize; S]>::from_slice(left);
let mut other = SmallVec::<[usize; S]>::from_slice(right);
unsafe { simplify_fraction_without_info(&mut reduced, &mut other) };
let reduced = unsafe { NonZeroUbig::from_inner_unchecked(reduced) };
reduced * right.clone()
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use crate::{Gcd, RationalBig, RB};
fn big(value: &str) -> RationalBig {
RationalBig::from_str(value).unwrap()
}
#[test]
fn integers_get_the_ordinary_answer() {
assert_eq!(RB!(4).gcd(&RB!(6)), Some(RB!(2)));
assert_eq!(RB!(7).gcd(&RB!(13)), Some(RB!(1)));
assert_eq!(RB!(12).gcd(&RB!(12)), Some(RB!(12)));
assert_eq!(RB!(1).gcd(&RB!(9)), Some(RB!(1)));
assert_eq!(RB!(9).gcd(&RB!(1)), Some(RB!(1)));
}
#[test]
fn the_sign_of_the_operands_says_nothing_about_what_divides_them() {
assert_eq!(RB!(-4).gcd(&RB!(6)), Some(RB!(2)));
assert_eq!(RB!(-4).gcd(&RB!(-6)), Some(RB!(2)));
}
#[test]
fn zero_is_a_multiple_of_everything() {
assert_eq!(RB!(0).gcd(&RB!(0)), Some(RB!(0)));
assert_eq!(RB!(0).gcd(&RB!(-6)), Some(RB!(6)));
assert_eq!(RB!(6, 5).gcd(&RB!(0)), Some(RB!(6, 5)));
}
#[test]
fn fractions_take_the_numerator_gcd_over_the_denominator_lcm() {
assert_eq!(RB!(1, 2).gcd(&RB!(1, 3)), Some(RB!(1, 6)));
assert_eq!(RB!(2, 3).gcd(&RB!(4, 9)), Some(RB!(2, 9)));
assert_eq!(RB!(3, 4).gcd(&RB!(5, 6)), Some(RB!(1, 12)));
assert_eq!(RB!(1, 2).gcd(&RB!(1, 2)), Some(RB!(1, 2)));
}
#[test]
fn dividing_by_it_leaves_coprime_integers() {
for (left, right) in [
(RB!(1, 2), RB!(1, 3)),
(RB!(3, 4), RB!(5, 6)),
(RB!(-7, 15), RB!(14, 25)),
(RB!(9), RB!(6)),
] {
let divisor = left.gcd(&right).unwrap();
let one = &left / &divisor;
let other = &right / &divisor;
assert_eq!(&one * &divisor, left);
assert_eq!(&other * &divisor, right);
assert_eq!(one.gcd(&other), Some(RB!(1)));
}
}
#[test]
fn every_small_pair_against_the_definition() {
let divides = |value: &RationalBig, divisor: &RationalBig| {
num_traits::One::is_one(&(value / divisor).denominator)
};
for p1 in 1..10_i64 {
for q1 in 1..10_i64 {
for p2 in 1..10_i64 {
for q2 in 1..10_i64 {
let left = RationalBig::from((p1, q1));
let right = RationalBig::from((p2, q2));
let divisor = left.gcd(&right).unwrap();
assert!(divides(&left, &divisor), "{left} / {divisor}");
assert!(divides(&right, &divisor), "{right} / {divisor}");
for multiple in [2_i64, 3, 5] {
let larger = &divisor * &RationalBig::from(multiple);
assert!(
!divides(&left, &larger) || !divides(&right, &larger),
"{larger} divides both {left} and {right}",
);
}
}
}
}
}
}
#[test]
fn values_that_do_not_fit_in_a_word() {
let left = big("340282366920938463463374607431768211456"); let right = big("170141183460469231731687303715884105728"); assert_eq!(left.gcd(&right), Some(right.clone()));
let third = &RationalBig::from(1) / &left;
assert_eq!(third.gcd(&third), Some(third));
}
}