Small Rust library for calculating greatest common divisor crates.io/crates/gcd
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add nonzero versions (#11)

* Formatting changes

* Inline trait methods

* Add nonzero versions, deduplicate test code

authored by

Clar Fon and committed by
GitHub
25286fb7 eaf5dc62

+139 -107
+139 -107
src/lib.rs
··· 1 1 #![no_std] 2 + use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; 2 3 3 4 pub trait Gcd { 4 5 /// Determine [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) ··· 30 31 31 32 macro_rules! gcd_impl { 32 33 ($(($T:ty) $binary:ident $euclid:ident),*) => {$( 33 - 34 - 35 - #[doc = concat!("Const binary GCD implementation for `", stringify!($T), "`.")] 36 - pub const fn $binary(mut u: $T, mut v: $T) -> $T 37 - { 38 - if u == 0 { return v; } 39 - if v == 0 { return u; } 40 - 41 - let shift = (u | v).trailing_zeros(); 42 - u >>= shift; 43 - v >>= shift; 44 - u >>= u.trailing_zeros(); 45 - 46 - loop { 47 - v >>= v.trailing_zeros(); 34 + #[doc = concat!("Const binary GCD implementation for `", stringify!($T), "`.")] 35 + pub const fn $binary(mut u: $T, mut v: $T) -> $T 36 + { 37 + if u == 0 { return v; } 38 + if v == 0 { return u; } 48 39 49 - #[allow(clippy::manual_swap)] 50 - if u > v { 51 - // mem::swap(&mut u, &mut v); 52 - let temp = u; 53 - u = v; 54 - v = temp; 55 - } 40 + let shift = (u | v).trailing_zeros(); 41 + u >>= shift; 42 + v >>= shift; 43 + u >>= u.trailing_zeros(); 56 44 57 - v -= u; // here v >= u 45 + loop { 46 + v >>= v.trailing_zeros(); 58 47 59 - if v == 0 { break; } 48 + #[allow(clippy::manual_swap)] 49 + if u > v { 50 + // mem::swap(&mut u, &mut v); 51 + let temp = u; 52 + u = v; 53 + v = temp; 60 54 } 61 55 62 - u << shift 56 + v -= u; // here v >= u 57 + 58 + if v == 0 { break; } 63 59 } 64 60 65 - #[doc = concat!("Const euclid GCD implementation for `", stringify!($T), "`.")] 66 - pub const fn $euclid( a: $T, b: $T) -> $T 67 - { 68 - // variable names based off euclidean division equation: a = b · q + r 69 - let (mut a, mut b) = if a > b { 70 - (a, b) 71 - } else { 72 - (b, a) 73 - }; 61 + u << shift 62 + } 74 63 75 - #[allow(clippy::manual_swap)] 76 - while b != 0 { 77 - // mem::swap(&mut a, &mut b); 78 - let temp = a; 79 - a = b; 80 - b = temp; 64 + #[doc = concat!("Const euclid GCD implementation for `", stringify!($T), "`.")] 65 + pub const fn $euclid(a: $T, b: $T) -> $T 66 + { 67 + // variable names based off euclidean division equation: a = b · q + r 68 + let (mut a, mut b) = if a > b { 69 + (a, b) 70 + } else { 71 + (b, a) 72 + }; 81 73 82 - b %= a; 83 - } 74 + #[allow(clippy::manual_swap)] 75 + while b != 0 { 76 + // mem::swap(&mut a, &mut b); 77 + let temp = a; 78 + a = b; 79 + b = temp; 84 80 85 - a 81 + b %= a; 86 82 } 87 83 88 - 84 + a 85 + } 89 86 90 87 impl Gcd for $T { 88 + #[inline] 91 89 fn gcd(self, other: $T) -> $T { 92 90 self.gcd_binary(other) 93 91 } 94 92 93 + #[inline] 95 94 fn gcd_binary(self, v: $T) -> $T { 96 - 97 - $binary(self, v) 98 - 95 + $binary(self, v) 99 96 } 100 97 98 + #[inline] 101 99 fn gcd_euclid(self, other: $T) -> $T { 102 - 103 - $euclid(self, other) 104 - 100 + $euclid(self, other) 105 101 } 106 102 } 107 103 )*}; ··· 116 112 (usize) binary_usize euclid_usize 117 113 } 118 114 115 + macro_rules! gcd_impl_nonzero { 116 + ($(($T:ty) $binary_nonzero:ident/$binary:ident $euclid_nonzero:ident/$euclid:ident),*) => {$( 117 + #[doc = concat!("Const binary GCD implementation for `", stringify!($T), "`.")] 118 + pub const fn $binary_nonzero(u: $T, v: $T) -> $T 119 + { 120 + match <$T>::new($binary(u.get(), v.get())) { 121 + Some(x) => x, 122 + None => unreachable!(), 123 + } 124 + } 125 + 126 + #[doc = concat!("Const euclid GCD implementation for `", stringify!($T), "`.")] 127 + pub const fn $euclid_nonzero(a: $T, b: $T) -> $T 128 + { 129 + match <$T>::new($euclid(a.get(), b.get())) { 130 + Some(x) => x, 131 + None => unreachable!(), 132 + } 133 + } 134 + 135 + impl Gcd for $T { 136 + #[inline] 137 + fn gcd(self, other: $T) -> $T { 138 + self.gcd_binary(other) 139 + } 140 + 141 + #[inline] 142 + fn gcd_binary(self, v: $T) -> $T { 143 + $binary_nonzero(self, v) 144 + } 145 + 146 + #[inline] 147 + fn gcd_euclid(self, other: $T) -> $T { 148 + $euclid_nonzero(self, other) 149 + } 150 + } 151 + )*} 152 + } 153 + 154 + gcd_impl_nonzero! { 155 + (NonZeroU8) binary_nonzero_u8/binary_u8 euclid_nonzero_u8/euclid_u8, 156 + (NonZeroU16) binary_nonzero_u16/binary_u16 euclid_nonzero_u16/euclid_u16, 157 + (NonZeroU32) binary_nonzero_u32/binary_u32 euclid_nonzero_u32/euclid_u32, 158 + (NonZeroU64) binary_nonzero_u64/binary_u64 euclid_nonzero_u64/euclid_u64, 159 + (NonZeroU128) binary_nonzero_u128/binary_u128 euclid_nonzero_u128/euclid_u128, 160 + (NonZeroUsize) binary_nonzero_usize/binary_usize euclid_nonzero_usize/euclid_usize 161 + } 162 + 119 163 #[cfg(test)] 120 164 mod test { 121 165 use super::*; 166 + use core::fmt::Debug; 122 167 123 168 const U8_GCD_A: [u8; 5] = [140, 1, 140, 33, 225]; 124 169 const U8_GCD_B: [u8; 5] = [136, 123, 203, 252, 153]; ··· 182 227 assert_eq!(10, 0u8.gcd(10)); 183 228 } 184 229 185 - #[test] 186 - fn test_gcd() { 187 - // u8 188 - for (ind, val) in U8_GCD_A.iter().enumerate() { 189 - let gcd = val.gcd(U8_GCD_B[ind]); 190 - let egcd = val.gcd_euclid(U8_GCD_B[ind]); 191 - let bgcd = val.gcd_binary(U8_GCD_B[ind]); 192 - assert_eq!(U8_GCD_R[ind], gcd); 193 - assert_eq!(U8_GCD_R[ind], bgcd); 194 - assert_eq!(U8_GCD_R[ind], egcd); 195 - } 230 + fn verify_gcd<T>(a: T, b: T, r: T) 231 + where 232 + T: Gcd + Copy + PartialEq + Debug, 233 + { 234 + let gcd = a.gcd(b); 235 + let egcd = a.gcd_euclid(b); 236 + let bgcd = a.gcd_binary(b); 237 + assert_eq!(r, gcd, "{:?}.gcd({:?})", a, b); 238 + assert_eq!(r, egcd, "{:?}.gcd_euclid({:?})", a, b); 239 + assert_eq!(r, bgcd, "{:?}.gcd_binary({:?})", a, b); 240 + } 196 241 197 - // u16 198 - for (ind, val) in U16_GCD_A.iter().enumerate() { 199 - let gcd = val.gcd(U16_GCD_B[ind]); 200 - let egcd = val.gcd_euclid(U16_GCD_B[ind]); 201 - let bgcd = val.gcd_binary(U16_GCD_B[ind]); 202 - assert_eq!(U16_GCD_R[ind], gcd); 203 - assert_eq!(U16_GCD_R[ind], bgcd); 204 - assert_eq!(U16_GCD_R[ind], egcd); 242 + fn test_gcd_ty<T, NZ, const N: usize>( 243 + new: impl Fn(T) -> Option<NZ>, 244 + zero: T, 245 + a: &[T; N], 246 + b: &[T; N], 247 + r: &[T; N], 248 + ) where 249 + T: Gcd + Copy + PartialEq + Debug, 250 + NZ: Gcd + Copy + PartialEq + Debug, 251 + { 252 + for ind in 0..N { 253 + let a = new(a[ind]).unwrap(); 254 + let b = new(b[ind]).unwrap(); 255 + let r = new(r[ind]).unwrap(); 256 + verify_gcd(a, b, r); 205 257 } 206 258 207 - // u32 208 - for (ind, val) in U32_GCD_A.iter().enumerate() { 209 - let gcd = val.gcd(U32_GCD_B[ind]); 210 - let egcd = val.gcd_euclid(U32_GCD_B[ind]); 211 - let bgcd = val.gcd_binary(U32_GCD_B[ind]); 212 - assert_eq!(U32_GCD_R[ind], gcd); 213 - assert_eq!(U32_GCD_R[ind], bgcd); 214 - assert_eq!(U32_GCD_R[ind], egcd); 215 - } 259 + let a = a[0]; 260 + verify_gcd(zero, a, a); 261 + verify_gcd(a, zero, a); 262 + } 216 263 217 - // u64 218 - for (ind, val) in U64_GCD_A.iter().enumerate() { 219 - let gcd = val.gcd(U64_GCD_B[ind]); 220 - let egcd = val.gcd_euclid(U64_GCD_B[ind]); 221 - let bgcd = val.gcd_binary(U64_GCD_B[ind]); 222 - assert_eq!(U64_GCD_R[ind], gcd); 223 - assert_eq!(U64_GCD_R[ind], bgcd); 224 - assert_eq!(U64_GCD_R[ind], egcd); 225 - } 226 - 227 - // u128 228 - for (ind, val) in U128_GCD_A.iter().enumerate() { 229 - let gcd = val.gcd(U128_GCD_B[ind]); 230 - let egcd = val.gcd_euclid(U128_GCD_B[ind]); 231 - let bgcd = val.gcd_binary(U128_GCD_B[ind]); 232 - assert_eq!(U128_GCD_R[ind], gcd); 233 - assert_eq!(U128_GCD_R[ind], bgcd); 234 - assert_eq!(U128_GCD_R[ind], egcd); 235 - } 236 - 237 - // usize 238 - for (ind, val) in USIZE_GCD_A.iter().enumerate() { 239 - let gcd = val.gcd(USIZE_GCD_B[ind]); 240 - let egcd = val.gcd_euclid(USIZE_GCD_B[ind]); 241 - let bgcd = val.gcd_binary(USIZE_GCD_B[ind]); 242 - assert_eq!(USIZE_GCD_R[ind], gcd); 243 - assert_eq!(USIZE_GCD_R[ind], bgcd); 244 - assert_eq!(USIZE_GCD_R[ind], egcd); 245 - } 264 + #[test] 265 + fn test_gcd() { 266 + test_gcd_ty(NonZeroU8::new, 0, &U8_GCD_A, &U8_GCD_B, &U8_GCD_R); 267 + test_gcd_ty(NonZeroU16::new, 0, &U16_GCD_A, &U16_GCD_B, &U16_GCD_R); 268 + test_gcd_ty(NonZeroU32::new, 0, &U32_GCD_A, &U32_GCD_B, &U32_GCD_R); 269 + test_gcd_ty(NonZeroU64::new, 0, &U64_GCD_A, &U64_GCD_B, &U64_GCD_R); 270 + test_gcd_ty(NonZeroU128::new, 0, &U128_GCD_A, &U128_GCD_B, &U128_GCD_R); 271 + test_gcd_ty( 272 + NonZeroUsize::new, 273 + 0, 274 + &USIZE_GCD_A, 275 + &USIZE_GCD_B, 276 + &USIZE_GCD_R, 277 + ); 246 278 } 247 279 248 280 const U32_GCD_R_0: u32 = binary_u32(U32_GCD_A[0], U32_GCD_B[0]);