diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-23 11:21:11 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-23 11:21:11 +0200 |
| commit | c07b47ee1a7aa3469753fa2ec49f3b499defac55 (patch) | |
| tree | f75eaa5878a4b5ba5824dc6eceebb4ba3d6615f7 | |
| parent | 51bb82df499e35cdc13d33dd24eca85d7abd0aaf (diff) | |
| download | zmodn-rs-c07b47ee1a7aa3469753fa2ec49f3b499defac55.tar.gz zmodn-rs-c07b47ee1a7aa3469753fa2ec49f3b499defac55.zip | |
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | src/lib.rs | 83 |
2 files changed, 44 insertions, 42 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ea48d6 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | A small library for integers modulo n, similar to | ||
| 2 | [zmodn](https://git.tronto.net/zmodn/file/README.md.html) | ||
| 3 | but in Rust. | ||
| @@ -1,20 +1,19 @@ | |||
| 1 | // TODO: make Zmod argument type generic | ||
| 2 | // TODO: re-implement ECM? | ||
| 3 | |||
| 4 | use std::fmt; | 1 | use std::fmt; |
| 5 | use std::ops; | 2 | use std::ops; |
| 6 | 3 | ||
| 4 | pub type BaseInt = i64; | ||
| 5 | |||
| 7 | // We assume canonical representative, can compare value for PartialEq | 6 | // We assume canonical representative, can compare value for PartialEq |
| 8 | #[derive(Copy, Clone, Debug, PartialEq)] | 7 | #[derive(Copy, Clone, Debug, PartialEq)] |
| 9 | pub struct Zmod<const N: i64> { | 8 | pub struct Zmod<const N: BaseInt> { |
| 10 | value: i64 | 9 | value: BaseInt |
| 11 | } | 10 | } |
| 12 | 11 | ||
| 13 | fn canonical_rep<const N: i64>(x: i64) -> i64 { | 12 | fn canonical_rep<const N: BaseInt>(x: BaseInt) -> BaseInt { |
| 14 | return (x % N + N) % N; | 13 | return (x % N + N) % N; |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { | 16 | fn extended_gcd(a: BaseInt, b: BaseInt) -> (BaseInt, BaseInt, BaseInt) { |
| 18 | if b == 0 { | 17 | if b == 0 { |
| 19 | return (a, 1, 0); | 18 | return (a, 1, 0); |
| 20 | } | 19 | } |
| @@ -22,15 +21,15 @@ fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { | |||
| 22 | (g, y, x - y*(a/b)) | 21 | (g, y, x - y*(a/b)) |
| 23 | } | 22 | } |
| 24 | 23 | ||
| 25 | impl<const N: i64> Zmod<N> { | 24 | impl<const N: BaseInt> Zmod<N> { |
| 26 | pub fn from(x: i64) -> Zmod<N> { | 25 | pub fn from(x: BaseInt) -> Zmod<N> { |
| 27 | #[cfg(debug_assertions)] | 26 | #[cfg(debug_assertions)] |
| 28 | assert!(N > 1, "modulus must be greater than 1"); | 27 | assert!(N > 1, "modulus must be greater than 1"); |
| 29 | 28 | ||
| 30 | Zmod::<N> { value: canonical_rep::<N>(x) } | 29 | Zmod::<N> { value: canonical_rep::<N>(x) } |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | fn inverse(self) -> Result<Zmod<N>, i64> { | 32 | fn inverse(self) -> Result<Zmod<N>, BaseInt> { |
| 34 | let (g, a, _) = extended_gcd(self.value, N); | 33 | let (g, a, _) = extended_gcd(self.value, N); |
| 35 | if g == 1 { | 34 | if g == 1 { |
| 36 | Ok(Zmod::<N>::from(a)) | 35 | Ok(Zmod::<N>::from(a)) |
| @@ -40,13 +39,13 @@ impl<const N: i64> Zmod<N> { | |||
| 40 | } | 39 | } |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | impl<const N: i64> fmt::Display for Zmod<N> { | 42 | impl<const N: BaseInt> fmt::Display for Zmod<N> { |
| 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 45 | write!(f, "({} mod {})", self.value, N) | 44 | write!(f, "({} mod {})", self.value, N) |
| 46 | } | 45 | } |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | impl<const N: i64> ops::Add for Zmod<N> { | 48 | impl<const N: BaseInt> ops::Add for Zmod<N> { |
| 50 | type Output = Zmod<N>; | 49 | type Output = Zmod<N>; |
| 51 | 50 | ||
| 52 | fn add(self, z: Zmod<N>) -> Zmod<N> { | 51 | fn add(self, z: Zmod<N>) -> Zmod<N> { |
| @@ -54,15 +53,15 @@ impl<const N: i64> ops::Add for Zmod<N> { | |||
| 54 | } | 53 | } |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | impl<const N: i64> ops::Add<i64> for Zmod<N> { | 56 | impl<const N: BaseInt> ops::Add<BaseInt> for Zmod<N> { |
| 58 | type Output = Zmod<N>; | 57 | type Output = Zmod<N>; |
| 59 | 58 | ||
| 60 | fn add(self, z: i64) -> Zmod<N> { | 59 | fn add(self, z: BaseInt) -> Zmod<N> { |
| 61 | Zmod::<N>::from(self.value + z) | 60 | Zmod::<N>::from(self.value + z) |
| 62 | } | 61 | } |
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | impl<const N: i64> ops::Add<Zmod<N>> for i64 { | 64 | impl<const N: BaseInt> ops::Add<Zmod<N>> for BaseInt { |
| 66 | type Output = Zmod::<N>; | 65 | type Output = Zmod::<N>; |
| 67 | 66 | ||
| 68 | fn add(self, z: Zmod::<N>) -> Zmod<N> { | 67 | fn add(self, z: Zmod::<N>) -> Zmod<N> { |
| @@ -70,19 +69,19 @@ impl<const N: i64> ops::Add<Zmod<N>> for i64 { | |||
| 70 | } | 69 | } |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | impl<const N: i64> ops::AddAssign for Zmod<N> { | 72 | impl<const N: BaseInt> ops::AddAssign for Zmod<N> { |
| 74 | fn add_assign(&mut self, z: Zmod<N>) { | 73 | fn add_assign(&mut self, z: Zmod<N>) { |
| 75 | self.value = canonical_rep::<N>(self.value + z.value); | 74 | self.value = canonical_rep::<N>(self.value + z.value); |
| 76 | } | 75 | } |
| 77 | } | 76 | } |
| 78 | 77 | ||
| 79 | impl<const N: i64> ops::AddAssign<i64> for Zmod<N> { | 78 | impl<const N: BaseInt> ops::AddAssign<BaseInt> for Zmod<N> { |
| 80 | fn add_assign(&mut self, z: i64) { | 79 | fn add_assign(&mut self, z: BaseInt) { |
| 81 | self.value = canonical_rep::<N>(self.value + z); | 80 | self.value = canonical_rep::<N>(self.value + z); |
| 82 | } | 81 | } |
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | impl<const N: i64> ops::Sub for Zmod<N> { | 84 | impl<const N: BaseInt> ops::Sub for Zmod<N> { |
| 86 | type Output = Zmod<N>; | 85 | type Output = Zmod<N>; |
| 87 | 86 | ||
| 88 | fn sub(self, z: Zmod<N>) -> Zmod<N> { | 87 | fn sub(self, z: Zmod<N>) -> Zmod<N> { |
| @@ -90,15 +89,15 @@ impl<const N: i64> ops::Sub for Zmod<N> { | |||
| 90 | } | 89 | } |
| 91 | } | 90 | } |
| 92 | 91 | ||
| 93 | impl<const N: i64> ops::Sub<i64> for Zmod<N> { | 92 | impl<const N: BaseInt> ops::Sub<BaseInt> for Zmod<N> { |
| 94 | type Output = Zmod<N>; | 93 | type Output = Zmod<N>; |
| 95 | 94 | ||
| 96 | fn sub(self, z: i64) -> Zmod<N> { | 95 | fn sub(self, z: BaseInt) -> Zmod<N> { |
| 97 | Zmod::<N>::from(self.value - z) | 96 | Zmod::<N>::from(self.value - z) |
| 98 | } | 97 | } |
| 99 | } | 98 | } |
| 100 | 99 | ||
| 101 | impl<const N: i64> ops::Sub<Zmod<N>> for i64 { | 100 | impl<const N: BaseInt> ops::Sub<Zmod<N>> for BaseInt { |
| 102 | type Output = Zmod<N>; | 101 | type Output = Zmod<N>; |
| 103 | 102 | ||
| 104 | fn sub(self, z: Zmod<N>) -> Zmod<N> { | 103 | fn sub(self, z: Zmod<N>) -> Zmod<N> { |
| @@ -106,19 +105,19 @@ impl<const N: i64> ops::Sub<Zmod<N>> for i64 { | |||
| 106 | } | 105 | } |
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | impl<const N: i64> ops::SubAssign for Zmod<N> { | 108 | impl<const N: BaseInt> ops::SubAssign for Zmod<N> { |
| 110 | fn sub_assign(&mut self, z: Zmod<N>) { | 109 | fn sub_assign(&mut self, z: Zmod<N>) { |
| 111 | self.value = canonical_rep::<N>(self.value - z.value); | 110 | self.value = canonical_rep::<N>(self.value - z.value); |
| 112 | } | 111 | } |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 115 | impl<const N: i64> ops::SubAssign<i64> for Zmod<N> { | 114 | impl<const N: BaseInt> ops::SubAssign<BaseInt> for Zmod<N> { |
| 116 | fn sub_assign(&mut self, z: i64) { | 115 | fn sub_assign(&mut self, z: BaseInt) { |
| 117 | self.value = canonical_rep::<N>(self.value - z); | 116 | self.value = canonical_rep::<N>(self.value - z); |
| 118 | } | 117 | } |
| 119 | } | 118 | } |
| 120 | 119 | ||
| 121 | impl<const N: i64> ops::Neg for Zmod<N> { | 120 | impl<const N: BaseInt> ops::Neg for Zmod<N> { |
| 122 | type Output = Zmod<N>; | 121 | type Output = Zmod<N>; |
| 123 | 122 | ||
| 124 | fn neg(self) -> Zmod<N> { | 123 | fn neg(self) -> Zmod<N> { |
| @@ -126,7 +125,7 @@ impl<const N: i64> ops::Neg for Zmod<N> { | |||
| 126 | } | 125 | } |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | impl<const N: i64> ops::Mul for Zmod<N> { | 128 | impl<const N: BaseInt> ops::Mul for Zmod<N> { |
| 130 | type Output = Zmod<N>; | 129 | type Output = Zmod<N>; |
| 131 | 130 | ||
| 132 | fn mul(self, z: Zmod<N>) -> Zmod<N> { | 131 | fn mul(self, z: Zmod<N>) -> Zmod<N> { |
| @@ -134,15 +133,15 @@ impl<const N: i64> ops::Mul for Zmod<N> { | |||
| 134 | } | 133 | } |
| 135 | } | 134 | } |
| 136 | 135 | ||
| 137 | impl<const N: i64> ops::Mul<i64> for Zmod<N> { | 136 | impl<const N: BaseInt> ops::Mul<BaseInt> for Zmod<N> { |
| 138 | type Output = Zmod<N>; | 137 | type Output = Zmod<N>; |
| 139 | 138 | ||
| 140 | fn mul(self, z: i64) -> Zmod<N> { | 139 | fn mul(self, z: BaseInt) -> Zmod<N> { |
| 141 | Zmod::<N>::from(self.value * z) | 140 | Zmod::<N>::from(self.value * z) |
| 142 | } | 141 | } |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | impl<const N: i64> ops::Mul<Zmod<N>> for i64 { | 144 | impl<const N: BaseInt> ops::Mul<Zmod<N>> for BaseInt { |
| 146 | type Output = Zmod<N>; | 145 | type Output = Zmod<N>; |
| 147 | 146 | ||
| 148 | fn mul(self, z: Zmod<N>) -> Zmod<N> { | 147 | fn mul(self, z: Zmod<N>) -> Zmod<N> { |
| @@ -150,38 +149,38 @@ impl<const N: i64> ops::Mul<Zmod<N>> for i64 { | |||
| 150 | } | 149 | } |
| 151 | } | 150 | } |
| 152 | 151 | ||
| 153 | impl<const N: i64> ops::MulAssign for Zmod<N> { | 152 | impl<const N: BaseInt> ops::MulAssign for Zmod<N> { |
| 154 | fn mul_assign(&mut self, z: Zmod<N>) { | 153 | fn mul_assign(&mut self, z: Zmod<N>) { |
| 155 | self.value = canonical_rep::<N>(self.value * z.value); | 154 | self.value = canonical_rep::<N>(self.value * z.value); |
| 156 | } | 155 | } |
| 157 | } | 156 | } |
| 158 | 157 | ||
| 159 | impl<const N: i64> ops::MulAssign<i64> for Zmod<N> { | 158 | impl<const N: BaseInt> ops::MulAssign<BaseInt> for Zmod<N> { |
| 160 | fn mul_assign(&mut self, z: i64) { | 159 | fn mul_assign(&mut self, z: BaseInt) { |
| 161 | self.value = canonical_rep::<N>(self.value * z); | 160 | self.value = canonical_rep::<N>(self.value * z); |
| 162 | } | 161 | } |
| 163 | } | 162 | } |
| 164 | 163 | ||
| 165 | impl<const N: i64> ops::Div for Zmod<N> { | 164 | impl<const N: BaseInt> ops::Div for Zmod<N> { |
| 166 | type Output = Result<Zmod<N>, i64>; | 165 | type Output = Result<Zmod<N>, BaseInt>; |
| 167 | 166 | ||
| 168 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, i64> { | 167 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, BaseInt> { |
| 169 | Ok(self * z.inverse()?) | 168 | Ok(self * z.inverse()?) |
| 170 | } | 169 | } |
| 171 | } | 170 | } |
| 172 | 171 | ||
| 173 | impl<const N: i64> ops::Div<i64> for Zmod<N> { | 172 | impl<const N: BaseInt> ops::Div<BaseInt> for Zmod<N> { |
| 174 | type Output = Result<Zmod<N>, i64>; | 173 | type Output = Result<Zmod<N>, BaseInt>; |
| 175 | 174 | ||
| 176 | fn div(self, z: i64) -> Result<Zmod<N>, i64> { | 175 | fn div(self, z: BaseInt) -> Result<Zmod<N>, BaseInt> { |
| 177 | self / Zmod::<N>::from(z) | 176 | self / Zmod::<N>::from(z) |
| 178 | } | 177 | } |
| 179 | } | 178 | } |
| 180 | 179 | ||
| 181 | impl<const N: i64> ops::Div<Zmod<N>> for i64 { | 180 | impl<const N: BaseInt> ops::Div<Zmod<N>> for BaseInt { |
| 182 | type Output = Result<Zmod<N>, i64>; | 181 | type Output = Result<Zmod<N>, BaseInt>; |
| 183 | 182 | ||
| 184 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, i64> { | 183 | fn div(self, z: Zmod<N>) -> Result<Zmod<N>, BaseInt> { |
| 185 | Zmod::<N>::from(self) / z | 184 | Zmod::<N>::from(self) / z |
| 186 | } | 185 | } |
| 187 | } | 186 | } |
