diff options
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6369b9f --- /dev/null +++ b/src/lib.rs | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | // TODO: make Zmod argument type generic | ||
| 2 | // TODO: re-implement ECM? | ||
| 3 | |||
| 4 | use std::fmt; | ||
| 5 | use std::ops; | ||
| 6 | |||
| 7 | // We assume canonical representative, can compare value for PartialEq | ||
| 8 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
| 9 | pub struct Zmod<const N: i64> { | ||
| 10 | value: i64 | ||
| 11 | } | ||
| 12 | |||
| 13 | fn canonical_rep<const N: i64>(x: i64) -> i64 { | ||
| 14 | return (x % N + N) % N; | ||
| 15 | } | ||
| 16 | |||
| 17 | fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { | ||
| 18 | if b == 0 { | ||
| 19 | return (a, 1, 0); | ||
| 20 | } | ||
| 21 | let (g, x, y) = extended_gcd(b, a%b); | ||
| 22 | (g, y, x - y*(a/b)) | ||
| 23 | } | ||
| 24 | |||
| 25 | impl<const N: i64> Zmod<N> { | ||
| 26 | pub fn from(x: i64) -> Zmod<N> { | ||
| 27 | #[cfg(debug_assertions)] | ||
| 28 | assert!(N > 1, "modulus must be greater than 1"); | ||
| 29 | |||
| 30 | Zmod::<N> { value: canonical_rep::<N>(x) } | ||
| 31 | } | ||
| 32 | |||
| 33 | fn inverse(self) -> Result<Zmod<N>, i64> { | ||
| 34 | let (g, a, _) = extended_gcd(self.value, N); | ||
| 35 | if g == 1 { | ||
| 36 | Ok(Zmod::<N>::from(a)) | ||
| 37 | } else { | ||
| 38 | Err(g) | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | impl<const N: i64> fmt::Display for Zmod<N> { | ||
| 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| 45 | write!(f, "({} mod {})", self.value, N) | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | impl<const N: i64> ops::Add for Zmod<N> { | ||
| 50 | type Output = Zmod<N>; | ||
| 51 | |||
| 52 | fn add(self, z: Zmod<N>) -> Zmod<N> { | ||
| 53 | Zmod::<N>::from(self.value + z.value) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<const N: i64> ops::Add<i64> for Zmod<N> { | ||
| 58 | type Output = Zmod<N>; | ||
| 59 | |||
| 60 | fn add(self, z: i64) -> Zmod<N> { | ||
| 61 | Zmod::<N>::from(self.value + z) | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl<const N: i64> ops::Add<Zmod<N>> for i64 { | ||
| 66 | type Output = Zmod::<N>; | ||
| 67 | |||
| 68 | fn add(self, z: Zmod::<N>) -> Zmod<N> { | ||
| 69 | Zmod::<N>::from(self + z.value) | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | impl<const N: i64> ops::AddAssign for Zmod<N> { | ||
| 74 | fn add_assign(&mut self, z: Zmod<N>) { | ||
| 75 | self.value = canonical_rep::<N>(self.value + z.value); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<const N: i64> ops::AddAssign<i64> for Zmod<N> { | ||
| 80 | fn add_assign(&mut self, z: i64) { | ||
| 81 | self.value = canonical_rep::<N>(self.value + z); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | impl<const N: i64> ops::Sub for Zmod<N> { | ||
| 86 | type Output = Zmod<N>; | ||
| 87 | |||
| 88 | fn sub(self, z: Zmod<N>) -> Zmod<N> { | ||
| 89 | Zmod::<N>::from(self.value - z.value) | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | impl<const N: i64> ops::Sub<i64> for Zmod<N> { | ||
| 94 | type Output = Zmod<N>; | ||
| 95 | |||
| 96 | fn sub(self, z: i64) -> Zmod<N> { | ||
| 97 | Zmod::<N>::from(self.value - z) | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | impl<const N: i64> ops::Sub<Zmod<N>> for i64 { | ||
| 102 | type Output = Zmod<N>; | ||
| 103 | |||
| 104 | fn sub(self, z: Zmod<N>) -> Zmod<N> { | ||
| 105 | Zmod::<N>::from(self - z.value) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | impl<const N: i64> ops::SubAssign for Zmod<N> { | ||
| 110 | fn sub_assign(&mut self, z: Zmod<N>) { | ||
| 111 | self.value = canonical_rep::<N>(self.value - z.value); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | impl<const N: i64> ops::SubAssign<i64> for Zmod<N> { | ||
| 116 | fn sub_assign(&mut self, z: i64) { | ||
| 117 | self.value = canonical_rep::<N>(self.value - z); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<const N: i64> ops::Neg for Zmod<N> { | ||
| 122 | type Output = Zmod<N>; | ||
| 123 | |||
| 124 | fn neg(self) -> Zmod<N> { | ||
| 125 | Zmod::<N>::from(-self.value) | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<const N: i64> ops::Mul for Zmod<N> { | ||
| 130 | type Output = Zmod<N>; | ||
| 131 | |||
| 132 | fn mul(self, z: Zmod<N>) -> Zmod<N> { | ||
| 133 | Zmod::<N>::from(self.value * z.value) | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<const N: i64> ops::Mul<i64> for Zmod<N> { | ||
| 138 | type Output = Zmod<N>; | ||
| 139 | |||
| 140 | fn mul(self, z: i64) -> Zmod<N> { | ||
| 141 | Zmod::<N>::from(self.value * z) | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | impl<const N: i64> ops::Mul<Zmod<N>> for i64 { | ||
| 146 | type Output = Zmod<N>; | ||
| 147 | |||
| 148 | fn mul(self, z: Zmod<N>) -> Zmod<N> { | ||
| 149 | Zmod::<N>::from(self * z.value) | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl<const N: i64> ops::MulAssign for Zmod<N> { | ||
| 154 | fn mul_assign(&mut self, z: Zmod<N>) { | ||
| 155 | self.value = canonical_rep::<N>(self.value * z.value); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | impl<const N: i64> ops::MulAssign<i64> for Zmod<N> { | ||
| 160 | fn mul_assign(&mut self, z: i64) { | ||
| 161 | self.value = canonical_rep::<N>(self.value * z); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | impl<const N: i64> ops::Div for Zmod<N> { | ||
| 166 | type Output = Result<Zmod<N>, i64>; | ||
| 167 | |||
| 168 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, i64> { | ||
| 169 | Ok(self * z.inverse()?) | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | impl<const N: i64> ops::Div<i64> for Zmod<N> { | ||
| 174 | type Output = Result<Zmod<N>, i64>; | ||
| 175 | |||
| 176 | fn div(self, z: i64) -> Result<Zmod<N>, i64> { | ||
| 177 | self / Zmod::<N>::from(z) | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | impl<const N: i64> ops::Div<Zmod<N>> for i64 { | ||
| 182 | type Output = Result<Zmod<N>, i64>; | ||
| 183 | |||
| 184 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, i64> { | ||
| 185 | Zmod::<N>::from(self) / z | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(test)] | ||
| 190 | mod tests { | ||
| 191 | use super::*; | ||
| 192 | |||
| 193 | #[test] | ||
| 194 | fn fmt_simple() { | ||
| 195 | let x = Zmod::<5>::from(3); | ||
| 196 | assert_eq!(x.to_string(), "(3 mod 5)"); | ||
| 197 | } | ||
| 198 | |||
| 199 | #[test] | ||
| 200 | fn two_is_zero_mod_two() { | ||
| 201 | assert_eq!(Zmod::<2>::from(2), Zmod::<2>::from(0)); | ||
| 202 | } | ||
| 203 | |||
| 204 | #[test] | ||
| 205 | fn negative_one_is_one_mod_two() { | ||
| 206 | assert_eq!(Zmod::<2>::from(-1), Zmod::<2>::from(1)); | ||
| 207 | } | ||
| 208 | |||
| 209 | #[test] | ||
| 210 | #[should_panic] | ||
| 211 | fn negative_modulus_panic() { | ||
| 212 | let _ = Zmod::<-3>::from(0); | ||
| 213 | } | ||
| 214 | |||
| 215 | #[test] | ||
| 216 | #[should_panic] | ||
| 217 | fn modulus_one_panic() { | ||
| 218 | let _ = Zmod::<1>::from(0); | ||
| 219 | } | ||
| 220 | |||
| 221 | #[test] | ||
| 222 | fn add_zmod_zmod() { | ||
| 223 | let one = Zmod::<2>::from(1); | ||
| 224 | assert_eq!(one + one, Zmod::<2>::from(0)); | ||
| 225 | } | ||
| 226 | |||
| 227 | #[test] | ||
| 228 | fn add_zmod_num() { | ||
| 229 | let one = Zmod::<4>::from(1); | ||
| 230 | assert_eq!(one + 3, Zmod::<4>::from(0)); | ||
| 231 | } | ||
| 232 | |||
| 233 | #[test] | ||
| 234 | fn add_num_zmod() { | ||
| 235 | assert_eq!(3 + Zmod::<9>::from(-4), Zmod::<9>::from(8)); | ||
| 236 | } | ||
| 237 | |||
| 238 | #[test] | ||
| 239 | fn add_assign_zmod_zmod() { | ||
| 240 | let mut x = Zmod::<7>::from(-2); | ||
| 241 | x += Zmod::<7>::from(25); | ||
| 242 | assert_eq!(x, Zmod::<7>::from(2)); | ||
| 243 | } | ||
| 244 | |||
| 245 | #[test] | ||
| 246 | fn add_assign_zmod_num() { | ||
| 247 | let mut x = Zmod::<3>::from(2); | ||
| 248 | x += 2; | ||
| 249 | assert_eq!(x, Zmod::<3>::from(1)); | ||
| 250 | } | ||
| 251 | |||
| 252 | #[test] | ||
| 253 | fn subtract_zmod_zmod() { | ||
| 254 | let x = Zmod::<5>::from(2); | ||
| 255 | let y = Zmod::<5>::from(-4); | ||
| 256 | assert_eq!(x - y, Zmod::<5>::from(1)); | ||
| 257 | } | ||
| 258 | |||
| 259 | #[test] | ||
| 260 | fn subtract_zmod_num() { | ||
| 261 | assert_eq!(Zmod::<3>::from(1) - 2, Zmod::<3>::from(-1)); | ||
| 262 | } | ||
| 263 | |||
| 264 | #[test] | ||
| 265 | fn subtract_num_zmod() { | ||
| 266 | assert_eq!(2 - Zmod::<7>::from(5), Zmod::<7>::from(4)); | ||
| 267 | } | ||
| 268 | |||
| 269 | #[test] | ||
| 270 | fn subtract_assign_zmod_zmod() { | ||
| 271 | let mut x = Zmod::<15>::from(32); | ||
| 272 | x -= Zmod::<15>::from(12); | ||
| 273 | assert_eq!(x, Zmod::<15>::from(20)); | ||
| 274 | } | ||
| 275 | |||
| 276 | #[test] | ||
| 277 | fn subtract_assign_zmod_num() { | ||
| 278 | let mut x = Zmod::<17>::from(11); | ||
| 279 | x -= 20; | ||
| 280 | assert_eq!(x, Zmod::<17>::from(-9)); | ||
| 281 | } | ||
| 282 | |||
| 283 | #[test] | ||
| 284 | fn neg() { | ||
| 285 | assert_eq!(-Zmod::<14>::from(18), Zmod::<14>::from(-18)); | ||
| 286 | } | ||
| 287 | |||
| 288 | #[test] | ||
| 289 | fn multiply_zmod_zmod() { | ||
| 290 | let x = Zmod::<9>::from(6); | ||
| 291 | let y = Zmod::<9>::from(-3); | ||
| 292 | assert_eq!(x * y, Zmod::<9>::from(0)); | ||
| 293 | } | ||
| 294 | |||
| 295 | #[test] | ||
| 296 | fn multiply_zmod_num() { | ||
| 297 | assert_eq!(Zmod::<5>::from(4) * 2, Zmod::<5>::from(3)); | ||
| 298 | } | ||
| 299 | |||
| 300 | #[test] | ||
| 301 | fn multiply_num_zmod() { | ||
| 302 | assert_eq!(6 * Zmod::<7>::from(5), Zmod::<7>::from(30)); | ||
| 303 | } | ||
| 304 | |||
| 305 | #[test] | ||
| 306 | fn multiply_assign_zmod_zmod() { | ||
| 307 | let mut x = Zmod::<15>::from(3); | ||
| 308 | x *= Zmod::<15>::from(7); | ||
| 309 | assert_eq!(x, Zmod::<15>::from(6)); | ||
| 310 | } | ||
| 311 | |||
| 312 | #[test] | ||
| 313 | fn multiply_assign_zmod_num() { | ||
| 314 | let mut x = Zmod::<17>::from(11); | ||
| 315 | x *= 4; | ||
| 316 | assert_eq!(x, Zmod::<17>::from(10)); | ||
| 317 | } | ||
| 318 | |||
| 319 | #[test] | ||
| 320 | fn inverse_one() { | ||
| 321 | let x = Zmod::<44>::from(1); | ||
| 322 | assert_eq!(x.inverse(), Ok(x)); | ||
| 323 | } | ||
| 324 | |||
| 325 | #[test] | ||
| 326 | fn inverse_12_35() { | ||
| 327 | let x = Zmod::<35>::from(12); | ||
| 328 | assert_eq!(x.inverse(), Ok(Zmod::<35>::from(3))); | ||
| 329 | } | ||
| 330 | |||
| 331 | #[test] | ||
| 332 | fn inverse_fail() { | ||
| 333 | let x = Zmod::<9>::from(6); | ||
| 334 | assert_eq!(x.inverse(), Err(3)); | ||
| 335 | } | ||
| 336 | |||
| 337 | #[test] | ||
| 338 | fn divide_success() { | ||
| 339 | let x = Zmod::<35>::from(15); | ||
| 340 | let d = Zmod::<35>::from(6); | ||
| 341 | assert_eq!(x / d, Ok(Zmod::<35>::from(20))); | ||
| 342 | } | ||
| 343 | } | ||
