aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml6
-rw-r--r--src/lib.rs343
3 files changed, 351 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
1/target
2Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..e9a1d2d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,6 @@
1[package]
2name = "zmodn-rs"
3version = "0.1.0"
4edition = "2024"
5
6[dependencies]
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
4use std::fmt;
5use std::ops;
6
7// We assume canonical representative, can compare value for PartialEq
8#[derive(Copy, Clone, Debug, PartialEq)]
9pub struct Zmod<const N: i64> {
10 value: i64
11}
12
13fn canonical_rep<const N: i64>(x: i64) -> i64 {
14 return (x % N + N) % N;
15}
16
17fn 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
25impl<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
43impl<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
49impl<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
57impl<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
65impl<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
73impl<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
79impl<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
85impl<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
93impl<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
101impl<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
109impl<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
115impl<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
121impl<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
129impl<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
137impl<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
145impl<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
153impl<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
159impl<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
165impl<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
173impl<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
181impl<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)]
190mod 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}

Generated with cgit - Back to sebastiano.tronto.net