1use smallvec::SmallVec;
3
4use crate::integer::big::ops::normalize::{gcd, simplify_fraction_without_info};
5use crate::integer::big::{NonZeroUbig, Ubig};
6use crate::rational::big::Big;
7use crate::sign::Sign;
8use crate::traits::Gcd;
9
10impl<const S: usize> Gcd for Big<S> {
13 fn gcd(&self, other: &Self) -> Option<Self> {
14 Some(match (self.sign, other.sign) {
15 (Sign::Zero, Sign::Zero) => <Self as num_traits::Zero>::zero(),
16 (Sign::Zero, _) => Self {
19 sign: Sign::Positive,
20 numerator: other.numerator.clone(),
21 denominator: other.denominator.clone(),
22 },
23 (_, Sign::Zero) => Self {
24 sign: Sign::Positive,
25 numerator: self.numerator.clone(),
26 denominator: self.denominator.clone(),
27 },
28 _ => Self {
29 sign: Sign::Positive,
30 numerator: numerator_gcd(&self.numerator, &other.numerator),
31 denominator: denominator_lcm(&self.denominator, &other.denominator),
32 },
33 })
34 }
35}
36
37fn numerator_gcd<const S: usize>(left: &Ubig<S>, right: &Ubig<S>) -> Ubig<S> {
42 if num_traits::One::is_one(left) || num_traits::One::is_one(right) {
43 return Ubig::new(1);
44 }
45 if **left == **right {
46 return left.clone();
47 }
48
49 unsafe { Ubig::from_inner_unchecked(gcd(left, right)) }
51}
52
53fn denominator_lcm<const S: usize>(left: &NonZeroUbig<S>, right: &NonZeroUbig<S>) -> NonZeroUbig<S> {
58 let mut reduced = SmallVec::<[usize; S]>::from_slice(left);
59 let mut other = SmallVec::<[usize; S]>::from_slice(right);
60 unsafe { simplify_fraction_without_info(&mut reduced, &mut other) };
62
63 let reduced = unsafe { NonZeroUbig::from_inner_unchecked(reduced) };
66 reduced * right.clone()
67}
68
69#[cfg(test)]
70mod test {
71 use std::str::FromStr;
72
73 use crate::{Gcd, RationalBig, RB};
74
75 fn big(value: &str) -> RationalBig {
76 RationalBig::from_str(value).unwrap()
77 }
78
79 #[test]
80 fn integers_get_the_ordinary_answer() {
81 assert_eq!(RB!(4).gcd(&RB!(6)), Some(RB!(2)));
82 assert_eq!(RB!(7).gcd(&RB!(13)), Some(RB!(1)));
83 assert_eq!(RB!(12).gcd(&RB!(12)), Some(RB!(12)));
84 assert_eq!(RB!(1).gcd(&RB!(9)), Some(RB!(1)));
85 assert_eq!(RB!(9).gcd(&RB!(1)), Some(RB!(1)));
86 }
87
88 #[test]
89 fn the_sign_of_the_operands_says_nothing_about_what_divides_them() {
90 assert_eq!(RB!(-4).gcd(&RB!(6)), Some(RB!(2)));
91 assert_eq!(RB!(-4).gcd(&RB!(-6)), Some(RB!(2)));
92 }
93
94 #[test]
95 fn zero_is_a_multiple_of_everything() {
96 assert_eq!(RB!(0).gcd(&RB!(0)), Some(RB!(0)));
97 assert_eq!(RB!(0).gcd(&RB!(-6)), Some(RB!(6)));
98 assert_eq!(RB!(6, 5).gcd(&RB!(0)), Some(RB!(6, 5)));
99 }
100
101 #[test]
103 fn fractions_take_the_numerator_gcd_over_the_denominator_lcm() {
104 assert_eq!(RB!(1, 2).gcd(&RB!(1, 3)), Some(RB!(1, 6)));
105 assert_eq!(RB!(2, 3).gcd(&RB!(4, 9)), Some(RB!(2, 9)));
106 assert_eq!(RB!(3, 4).gcd(&RB!(5, 6)), Some(RB!(1, 12)));
107 assert_eq!(RB!(1, 2).gcd(&RB!(1, 2)), Some(RB!(1, 2)));
108 }
109
110 #[test]
112 fn dividing_by_it_leaves_coprime_integers() {
113 for (left, right) in [
114 (RB!(1, 2), RB!(1, 3)),
115 (RB!(3, 4), RB!(5, 6)),
116 (RB!(-7, 15), RB!(14, 25)),
117 (RB!(9), RB!(6)),
118 ] {
119 let divisor = left.gcd(&right).unwrap();
120 let one = &left / &divisor;
121 let other = &right / &divisor;
122
123 assert_eq!(&one * &divisor, left);
124 assert_eq!(&other * &divisor, right);
125 assert_eq!(one.gcd(&other), Some(RB!(1)));
126 }
127 }
128
129 #[test]
134 fn every_small_pair_against_the_definition() {
135 let divides = |value: &RationalBig, divisor: &RationalBig| {
137 num_traits::One::is_one(&(value / divisor).denominator)
138 };
139
140 for p1 in 1..10_i64 {
141 for q1 in 1..10_i64 {
142 for p2 in 1..10_i64 {
143 for q2 in 1..10_i64 {
144 let left = RationalBig::from((p1, q1));
145 let right = RationalBig::from((p2, q2));
146 let divisor = left.gcd(&right).unwrap();
147
148 assert!(divides(&left, &divisor), "{left} / {divisor}");
149 assert!(divides(&right, &divisor), "{right} / {divisor}");
150
151 for multiple in [2_i64, 3, 5] {
154 let larger = &divisor * &RationalBig::from(multiple);
155 assert!(
156 !divides(&left, &larger) || !divides(&right, &larger),
157 "{larger} divides both {left} and {right}",
158 );
159 }
160 }
161 }
162 }
163 }
164 }
165
166 #[test]
167 fn values_that_do_not_fit_in_a_word() {
168 let left = big("340282366920938463463374607431768211456"); let right = big("170141183460469231731687303715884105728"); assert_eq!(left.gcd(&right), Some(right.clone()));
171
172 let third = &RationalBig::from(1) / &left;
173 assert_eq!(third.gcd(&third), Some(third));
174 }
175}