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.

switch underlying implementations to be const (#9)

* switch underlying implementations to be const

* increased coverage

authored by

Tyler Holmes and committed by
GitHub
ed857525 78f84acc

+178 -27
+3
Cargo.toml
··· 9 9 documentation = "https://docs.rs/gcd/" 10 10 categories = ["algorithms"] 11 11 12 + [dependencies] 13 + paste = "1" 14 + 12 15 [dev-dependencies] 13 16 criterion = "0.3" 14 17 rand = "0.7"
+175 -27
src/lib.rs
··· 1 1 #![no_std] 2 2 3 - use core::mem; 4 - 5 3 pub trait Gcd { 6 4 /// Determine [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) 7 5 /// using [`gcd_binary`]. ··· 32 30 33 31 macro_rules! gcd_impl { 34 32 ($($T:ty),*) => {$( 35 - impl Gcd for $T { 36 - fn gcd(self, other: $T) -> $T { 37 - self.gcd_binary(other) 38 - } 39 33 40 - fn gcd_binary(self, mut v: $T) -> $T { 41 - let mut u = self; 42 - 34 + paste::paste! { 35 + #[doc = "Const binary GCD implementation for `" $T "`."] 36 + pub const fn [<binary_ $T>](mut u: $T, mut v: $T) -> $T 37 + { 43 38 if u == 0 { return v; } 44 39 if v == 0 { return u; } 45 40 ··· 52 47 v >>= v.trailing_zeros(); 53 48 54 49 if u > v { 55 - mem::swap(&mut u, &mut v); 50 + // mem::swap(&mut u, &mut v); 51 + let temp = u; 52 + u = v; 53 + v = temp; 56 54 } 57 55 58 56 v -= u; // here v >= u ··· 63 61 u << shift 64 62 } 65 63 66 - fn gcd_euclid(self, other: $T) -> $T { 67 - // variable names based off euclidean divison equation: a = b · q + r 68 - let (mut a, mut b) = if self > other { 69 - (self, other) 64 + #[doc = "Const euclid GCD implementation for `" $T "`."] 65 + pub const fn [<euclid_ $T>]( 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 70 } else { 71 - (other, self) 71 + (b, a) 72 72 }; 73 73 74 74 while b != 0 { 75 - mem::swap(&mut a, &mut b); 75 + // mem::swap(&mut a, &mut b); 76 + let temp = a; 77 + a = b; 78 + b = temp; 79 + 76 80 b %= a; 77 81 } 78 82 79 83 a 80 84 } 81 85 } 86 + 87 + 88 + impl Gcd for $T { 89 + fn gcd(self, other: $T) -> $T { 90 + self.gcd_binary(other) 91 + } 92 + 93 + fn gcd_binary(self, v: $T) -> $T { 94 + paste::paste! { 95 + [<binary_ $T>](self, v) 96 + } 97 + } 98 + 99 + fn gcd_euclid(self, other: $T) -> $T { 100 + paste::paste! { 101 + [<euclid_ $T>](self, other) 102 + } 103 + } 104 + } 82 105 )*}; 83 106 } 84 107 ··· 86 109 87 110 #[cfg(test)] 88 111 mod test { 89 - use super::Gcd; 112 + use super::*; 113 + 114 + const U8_GCD_A: [u8; 5] = [140, 1, 140, 33, 225]; 115 + const U8_GCD_B: [u8; 5] = [136, 123, 203, 252, 153]; 116 + const U8_GCD_R: [u8; 5] = [4, 1, 7, 3, 9]; 117 + 118 + const U16_GCD_A: [u16; 5] = [53144, 44062, 65054, 60568, 11932]; 119 + const U16_GCD_B: [u16; 5] = [41105, 5088, 35332, 19184, 54004]; 120 + const U16_GCD_R: [u16; 5] = [1, 2, 22, 8, 4]; 121 + 122 + const U32_GCD_A: [u32; 5] = [3392079986, 273672341, 1353048788, 1491301950, 3569727686]; 123 + const U32_GCD_B: [u32; 5] = [2080089626, 3912533700, 1969135932, 1356732645, 58056677]; 124 + const U32_GCD_R: [u32; 5] = [2, 1, 4, 15, 7]; 125 + 126 + const U64_GCD_A: [u64; 5] = [ 127 + 190266297176832000, 128 + 2040134905096275968, 129 + 16611311494648745984, 130 + 14863931409971066880, 131 + 11777713923171739648, 132 + ]; 133 + const U64_GCD_B: [u64; 5] = [ 134 + 10430732356495263744, 135 + 5701159354248194048, 136 + 7514969329383038976, 137 + 7911906750992527360, 138 + 1994469765110767616, 139 + ]; 140 + const U64_GCD_R: [u64; 5] = [6144, 2048, 4096, 10240, 14336]; 141 + 142 + const U128_GCD_A: [u128; 5] = [ 143 + 183222947567111613556380400704880115712, 144 + 115621006611964852903362423926779019264, 145 + 50724538437787115589243518273596686336, 146 + 18298803717624646317403958239767298048, 147 + 196929845599653749349770751890136498176, 148 + ]; 149 + const U128_GCD_B: [u128; 5] = [ 150 + 283620717889381409474181015983148236800, 151 + 152390035351551984363917166384150216704, 152 + 74996138554240857099554660445327458304, 153 + 245604784002268488089190010796573196288, 154 + 194671916188106984823441978656659865600, 155 + ]; 156 + const U128_GCD_R: [u128; 5] = [ 157 + 37778931862957161709568, 158 + 75557863725914323419136, 159 + 113336795588871485128704, 160 + 151115727451828646838272, 161 + 302231454903657293676544, 162 + ]; 163 + 164 + const USIZE_GCD_A: [usize; 5] = [335286345, 3125888386, 3550412466, 924335944, 2870209473]; 165 + const USIZE_GCD_B: [usize; 5] = [1843742025, 2080426243, 16052620, 1587387560, 24708111]; 166 + const USIZE_GCD_R: [usize; 5] = [15, 1, 2, 8, 3]; 167 + 168 + #[test] 169 + fn test_gcd_basic() { 170 + // some base cases 171 + assert_eq!(0, 0u8.gcd(0)); 172 + assert_eq!(10, 10u8.gcd(0)); 173 + assert_eq!(10, 0u8.gcd(10)); 174 + } 90 175 91 176 #[test] 92 177 fn test_gcd() { 93 - assert_eq!(0, 0u8.gcd_euclid(0)); 94 - assert_eq!(10, 10u8.gcd_euclid(0)); 95 - assert_eq!(10, 0u8.gcd_euclid(10)); 96 - assert_eq!(10, 10u8.gcd_euclid(20)); 97 - assert_eq!(44, 2024u32.gcd_euclid(748)); 178 + // u8 179 + for (ind, val) in U8_GCD_A.iter().enumerate() { 180 + let gcd = val.gcd(U8_GCD_B[ind]); 181 + let egcd = val.gcd_euclid(U8_GCD_B[ind]); 182 + let bgcd = val.gcd_binary(U8_GCD_B[ind]); 183 + assert_eq!(U8_GCD_R[ind], gcd); 184 + assert_eq!(U8_GCD_R[ind], bgcd); 185 + assert_eq!(U8_GCD_R[ind], egcd); 186 + } 187 + 188 + // u16 189 + for (ind, val) in U16_GCD_A.iter().enumerate() { 190 + let gcd = val.gcd(U16_GCD_B[ind]); 191 + let egcd = val.gcd_euclid(U16_GCD_B[ind]); 192 + let bgcd = val.gcd_binary(U16_GCD_B[ind]); 193 + assert_eq!(U16_GCD_R[ind], gcd); 194 + assert_eq!(U16_GCD_R[ind], bgcd); 195 + assert_eq!(U16_GCD_R[ind], egcd); 196 + } 197 + 198 + // u32 199 + for (ind, val) in U32_GCD_A.iter().enumerate() { 200 + let gcd = val.gcd(U32_GCD_B[ind]); 201 + let egcd = val.gcd_euclid(U32_GCD_B[ind]); 202 + let bgcd = val.gcd_binary(U32_GCD_B[ind]); 203 + assert_eq!(U32_GCD_R[ind], gcd); 204 + assert_eq!(U32_GCD_R[ind], bgcd); 205 + assert_eq!(U32_GCD_R[ind], egcd); 206 + } 207 + 208 + // u64 209 + for (ind, val) in U64_GCD_A.iter().enumerate() { 210 + let gcd = val.gcd(U64_GCD_B[ind]); 211 + let egcd = val.gcd_euclid(U64_GCD_B[ind]); 212 + let bgcd = val.gcd_binary(U64_GCD_B[ind]); 213 + assert_eq!(U64_GCD_R[ind], gcd); 214 + assert_eq!(U64_GCD_R[ind], bgcd); 215 + assert_eq!(U64_GCD_R[ind], egcd); 216 + } 98 217 99 - assert_eq!(0, 0u8.gcd_binary(0)); 100 - assert_eq!(10, 10u8.gcd_binary(0)); 101 - assert_eq!(10, 0u8.gcd_binary(10)); 102 - assert_eq!(10, 10u8.gcd_binary(20)); 103 - assert_eq!(44, 2024u32.gcd_binary(748)); 218 + // u128 219 + for (ind, val) in U128_GCD_A.iter().enumerate() { 220 + let gcd = val.gcd(U128_GCD_B[ind]); 221 + let egcd = val.gcd_euclid(U128_GCD_B[ind]); 222 + let bgcd = val.gcd_binary(U128_GCD_B[ind]); 223 + assert_eq!(U128_GCD_R[ind], gcd); 224 + assert_eq!(U128_GCD_R[ind], bgcd); 225 + assert_eq!(U128_GCD_R[ind], egcd); 226 + } 227 + 228 + // usize 229 + for (ind, val) in USIZE_GCD_A.iter().enumerate() { 230 + let gcd = val.gcd(USIZE_GCD_B[ind]); 231 + let egcd = val.gcd_euclid(USIZE_GCD_B[ind]); 232 + let bgcd = val.gcd_binary(USIZE_GCD_B[ind]); 233 + assert_eq!(USIZE_GCD_R[ind], gcd); 234 + assert_eq!(USIZE_GCD_R[ind], bgcd); 235 + assert_eq!(USIZE_GCD_R[ind], egcd); 236 + } 237 + } 238 + 239 + const U32_GCD_R_0: u32 = binary_u32(U32_GCD_A[0], U32_GCD_B[0]); 240 + const U32_GCD_R_1: u32 = euclid_u32(U32_GCD_A[1], U32_GCD_B[1]); 241 + const U32_GCD_R_2: u32 = binary_u32(U32_GCD_A[2], U32_GCD_B[2]); 242 + const U32_GCD_R_3: u32 = euclid_u32(U32_GCD_A[3], U32_GCD_B[3]); 243 + const U32_GCD_R_4: u32 = binary_u32(U32_GCD_A[4], U32_GCD_B[4]); 244 + 245 + #[test] 246 + fn test_const_gcd() { 247 + assert_eq!(U32_GCD_R[0], U32_GCD_R_0); 248 + assert_eq!(U32_GCD_R[1], U32_GCD_R_1); 249 + assert_eq!(U32_GCD_R[2], U32_GCD_R_2); 250 + assert_eq!(U32_GCD_R[3], U32_GCD_R_3); 251 + assert_eq!(U32_GCD_R[4], U32_GCD_R_4); 104 252 } 105 253 }