ruint/algorithms/gcd/
mod.rs1#![allow(clippy::module_name_repetitions)]
2
3mod matrix;
7
8pub use self::matrix::Matrix as LehmerMatrix;
9use crate::Uint;
10use core::mem::swap;
11
12#[doc = crate::algorithms::unstable_warning!()]
14#[inline]
16#[must_use]
17pub fn gcd<const BITS: usize, const LIMBS: usize>(
18 mut a: Uint<BITS, LIMBS>,
19 mut b: Uint<BITS, LIMBS>,
20) -> Uint<BITS, LIMBS> {
21 if b > a {
22 swap(&mut a, &mut b);
23 }
24 while b != Uint::ZERO {
25 debug_assert!(a >= b);
26 let m = LehmerMatrix::from(a, b);
27 if m == LehmerMatrix::IDENTITY {
28 a %= b;
32 swap(&mut a, &mut b);
33 } else {
34 m.apply(&mut a, &mut b);
35 }
36 }
37 a
38}
39
40#[doc = crate::algorithms::unstable_warning!()]
42#[inline]
61#[must_use]
62pub fn gcd_extended<const BITS: usize, const LIMBS: usize>(
63 mut a: Uint<BITS, LIMBS>,
64 mut b: Uint<BITS, LIMBS>,
65) -> (
66 Uint<BITS, LIMBS>,
67 Uint<BITS, LIMBS>,
68 Uint<BITS, LIMBS>,
69 bool,
70) {
71 if BITS == 0 {
72 return (Uint::ZERO, Uint::ZERO, Uint::ZERO, false);
73 }
74 let swapped = a < b;
75 if swapped {
76 swap(&mut a, &mut b);
77 }
78
79 let mut s0 = Uint::ONE;
81 let mut s1 = Uint::ZERO;
82 let mut t0 = Uint::ZERO;
83 let mut t1 = Uint::ONE;
84 let mut even = true;
85 while b != Uint::ZERO {
86 debug_assert!(a >= b);
87 let m = LehmerMatrix::from(a, b);
88 if m == LehmerMatrix::IDENTITY {
89 let q = a / b;
93 a -= q * b;
94 swap(&mut a, &mut b);
95 s0 -= q * s1;
96 swap(&mut s0, &mut s1);
97 t0 -= q * t1;
98 swap(&mut t0, &mut t1);
99 even = !even;
100 } else {
101 m.apply(&mut a, &mut b);
102 m.apply(&mut s0, &mut s1);
103 m.apply(&mut t0, &mut t1);
104 even ^= !m.4;
105 }
106 }
107 if even {
109 t0 = Uint::ZERO - t0;
111 } else {
112 s0 = Uint::ZERO - s0;
114 }
115 if swapped {
116 swap(&mut s0, &mut t0);
117 even = !even;
118 }
119 (a, s0, t0, even)
120}
121
122#[doc = crate::algorithms::unstable_warning!()]
124#[inline]
140#[must_use]
141pub fn inv_mod<const BITS: usize, const LIMBS: usize>(
142 num: Uint<BITS, LIMBS>,
143 modulus: Uint<BITS, LIMBS>,
144) -> Option<Uint<BITS, LIMBS>> {
145 if BITS == 0 || modulus.is_zero() {
146 return None;
147 }
148 let mut a = modulus;
149 let mut b = num;
150 if b >= a {
151 b %= a;
152 }
153 if b.is_zero() {
154 return None;
155 }
156
157 let mut t0 = Uint::ZERO;
158 let mut t1 = Uint::ONE;
159 let mut even = true;
160 while b != Uint::ZERO {
161 debug_assert!(a >= b);
162 let m = LehmerMatrix::from(a, b);
163 if m == LehmerMatrix::IDENTITY {
164 let q = a / b;
168 a -= q * b;
169 swap(&mut a, &mut b);
170 t0 -= q * t1;
171 swap(&mut t0, &mut t1);
172 even = !even;
173 } else {
174 m.apply(&mut a, &mut b);
175 m.apply(&mut t0, &mut t1);
176 even ^= !m.4;
177 }
178 }
179 if a == Uint::ONE {
180 Some(if even { modulus + t0 } else { t0 })
182 } else {
183 None
184 }
185}
186
187#[cfg(test)]
188#[allow(clippy::cast_lossless)]
189mod tests {
190 use super::*;
191 use crate::{const_for, nlimbs};
192 use proptest::{proptest, test_runner::Config};
193
194 #[test]
195 fn test_gcd_one() {
196 use core::str::FromStr;
197 const BITS: usize = 129;
198 const LIMBS: usize = nlimbs(BITS);
199 type U = Uint<BITS, LIMBS>;
200 let a = U::from_str("0x006d7c4641f88b729a97889164dd8d07db").unwrap();
201 let b = U::from_str("0x01de6ef6f3caa963a548d7a411b05b9988").unwrap();
202 assert_eq!(gcd(a, b), gcd_ref(a, b));
203 }
204
205 fn gcd_ref<const BITS: usize, const LIMBS: usize>(
207 mut a: Uint<BITS, LIMBS>,
208 mut b: Uint<BITS, LIMBS>,
209 ) -> Uint<BITS, LIMBS> {
210 while b != Uint::ZERO {
211 a %= b;
212 swap(&mut a, &mut b);
213 }
214 a
215 }
216
217 #[test]
218 #[allow(clippy::absurd_extreme_comparisons)] fn test_gcd() {
220 const_for!(BITS in SIZES {
221 const LIMBS: usize = nlimbs(BITS);
222 type U = Uint<BITS, LIMBS>;
223 let config = Config { cases: 10, ..Default::default()};
224 proptest!(config, |(a: U, b: U)| {
225 assert_eq!(gcd(a, b), gcd_ref(a, b));
226 });
227 });
228 }
229
230 #[test]
231 fn test_gcd_extended() {
232 const_for!(BITS in SIZES {
233 const LIMBS: usize = nlimbs(BITS);
234 type U = Uint<BITS, LIMBS>;
235 let config = Config { cases: 5, ..Default::default() };
236 proptest!(config, |(a: U, b: U)| {
237 let (g, x, y, sign) = gcd_extended(a, b);
238 assert_eq!(g, gcd_ref(a, b));
239 if sign {
240 assert_eq!(a * x - b * y, g);
241 } else {
242 assert_eq!(b * y - a * x, g);
243 }
244 });
245 });
246 }
247}