Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
duyuefeng0708
GitHub Repository: duyuefeng0708/Cryptography-From-First-Principle
Path: blob/main/shared/cryptolab/modular.py
483 views
unlisted
1
"""
2
Modular arithmetic classes mirroring SageMath's Mod() and Zmod() API.
3
4
Mod(a, n) creates an element of Z/nZ with automatic wrapping.
5
Zmod(n) creates the ring Z/nZ with iteration, unit listing, and operation tables.
6
"""
7
8
from .number_theory import gcd, inverse_mod, power_mod, euler_phi
9
10
11
# ---------------------------------------------------------------------------
12
# Mod class: an element of Z/nZ
13
# ---------------------------------------------------------------------------
14
15
class Mod:
16
"""
17
An element of Z/nZ with automatic modular arithmetic.
18
19
Usage mirrors SageMath:
20
a = Mod(3, 7)
21
a + Mod(5, 7) # -> 1
22
a ** 2 # -> 2
23
~a # -> 5 (modular inverse)
24
"""
25
26
__slots__ = ('_value', '_modulus')
27
28
def __init__(self, value, modulus):
29
modulus = int(modulus)
30
if modulus < 1:
31
raise ValueError(f"Modulus must be positive, got {modulus}")
32
self._modulus = modulus
33
self._value = int(value) % modulus
34
35
@property
36
def value(self):
37
return self._value
38
39
@property
40
def modulus(self):
41
return self._modulus
42
43
# --- Representation ---
44
45
def __repr__(self):
46
return str(self._value)
47
48
def __str__(self):
49
return str(self._value)
50
51
def __int__(self):
52
return self._value
53
54
def __index__(self):
55
return self._value
56
57
def __float__(self):
58
return float(self._value)
59
60
# --- Comparison and hashing ---
61
62
def _check_compatible(self, other):
63
if isinstance(other, Mod):
64
if self._modulus != other._modulus:
65
raise ValueError(
66
f"Cannot combine Mod elements with different moduli "
67
f"({self._modulus} vs {other._modulus})"
68
)
69
return other._value
70
return int(other)
71
72
def __eq__(self, other):
73
if isinstance(other, Mod):
74
return self._value == other._value and self._modulus == other._modulus
75
return self._value == int(other) % self._modulus
76
77
def __ne__(self, other):
78
return not self.__eq__(other)
79
80
def __hash__(self):
81
# Must be consistent with __eq__: since Mod(4,12) == 4,
82
# hash(Mod(4,12)) must equal hash(4).
83
return hash(self._value)
84
85
def __bool__(self):
86
return self._value != 0
87
88
# --- Arithmetic ---
89
90
def __add__(self, other):
91
v = self._check_compatible(other)
92
return Mod(self._value + v, self._modulus)
93
94
def __radd__(self, other):
95
return Mod(int(other) + self._value, self._modulus)
96
97
def __sub__(self, other):
98
v = self._check_compatible(other)
99
return Mod(self._value - v, self._modulus)
100
101
def __rsub__(self, other):
102
return Mod(int(other) - self._value, self._modulus)
103
104
def __mul__(self, other):
105
v = self._check_compatible(other)
106
return Mod(self._value * v, self._modulus)
107
108
def __rmul__(self, other):
109
return Mod(int(other) * self._value, self._modulus)
110
111
def __neg__(self):
112
return Mod(-self._value, self._modulus)
113
114
def __pow__(self, exp):
115
exp = int(exp)
116
if exp < 0:
117
# Negative exponent: compute inverse first
118
inv = inverse_mod(self._value, self._modulus)
119
return Mod(power_mod(inv, -exp, self._modulus), self._modulus)
120
return Mod(power_mod(self._value, exp, self._modulus), self._modulus)
121
122
def __invert__(self):
123
"""~x returns the modular inverse."""
124
return Mod(inverse_mod(self._value, self._modulus), self._modulus)
125
126
def __truediv__(self, other):
127
v = self._check_compatible(other)
128
inv = inverse_mod(v, self._modulus)
129
return Mod(self._value * inv, self._modulus)
130
131
def __mod__(self, other):
132
return Mod(self._value % int(other), self._modulus)
133
134
# --- Ordering (useful for sorting) ---
135
136
def __lt__(self, other):
137
if isinstance(other, Mod):
138
return self._value < other._value
139
return self._value < int(other)
140
141
def __le__(self, other):
142
if isinstance(other, Mod):
143
return self._value <= other._value
144
return self._value <= int(other)
145
146
# --- Group theory methods ---
147
148
def multiplicative_order(self):
149
"""
150
The smallest positive k such that self^k = 1 (mod n).
151
Raises ValueError if self is not a unit.
152
"""
153
if gcd(self._value, self._modulus) != 1:
154
raise ValueError(
155
f"{self._value} is not a unit mod {self._modulus} "
156
f"(gcd = {gcd(self._value, self._modulus)})"
157
)
158
result = 1
159
current = self._value
160
while current != 1:
161
current = (current * self._value) % self._modulus
162
result += 1
163
return result
164
165
def additive_order(self):
166
"""
167
The smallest positive k such that k * self = 0 (mod n).
168
Equals n / gcd(self.value, n).
169
"""
170
g = gcd(self._value, self._modulus)
171
if g == 0:
172
return 1
173
return self._modulus // g
174
175
def pow_verbose(self, exp):
176
"""Compute self^exp with step-by-step printing."""
177
result = power_mod(self._value, int(exp), self._modulus, verbose=True)
178
return Mod(result, self._modulus)
179
180
def parent(self):
181
"""Return the ring this element belongs to (like SageMath's .parent())."""
182
return ZmodRing(self._modulus)
183
184
185
# ---------------------------------------------------------------------------
186
# ZmodRing class: the ring Z/nZ
187
# ---------------------------------------------------------------------------
188
189
class ZmodRing:
190
"""
191
The ring Z/nZ. Mirrors SageMath's Zmod(n) / Integers(n).
192
193
Usage:
194
R = Zmod(7)
195
a = R(3) # -> Mod(3, 7)
196
list(R) # -> [Mod(0,7), Mod(1,7), ..., Mod(6,7)]
197
R.order() # -> 7
198
R.list_of_elements_of_multiplicative_group() # units
199
"""
200
201
def __init__(self, n):
202
self._n = int(n)
203
if self._n < 1:
204
raise ValueError(f"Modulus must be positive, got {self._n}")
205
206
def __repr__(self):
207
return f"Ring of integers modulo {self._n}"
208
209
def __call__(self, value):
210
"""Create a Mod element: R(3) -> Mod(3, n)."""
211
return Mod(value, self._n)
212
213
def __iter__(self):
214
"""Iterate over all elements: Mod(0,n), Mod(1,n), ..., Mod(n-1,n)."""
215
for i in range(self._n):
216
yield Mod(i, self._n)
217
218
def __contains__(self, item):
219
if isinstance(item, Mod):
220
return item.modulus == self._n
221
return True # Any integer can be reduced mod n
222
223
def order(self):
224
"""Number of elements in the ring."""
225
return self._n
226
227
def list(self):
228
"""All elements as a list."""
229
return [Mod(i, self._n) for i in range(self._n)]
230
231
def list_of_elements_of_multiplicative_group(self):
232
"""Units of Z/nZ: elements with gcd(a, n) = 1."""
233
return [Mod(a, self._n) for a in range(1, self._n) if gcd(a, self._n) == 1]
234
235
def addition_table(self, style='elements'):
236
"""
237
Print the addition table for Z/nZ.
238
style='elements' prints values, style='list' returns a 2D list.
239
"""
240
n = self._n
241
table = [[(i + j) % n for j in range(n)] for i in range(n)]
242
if style == 'list':
243
return table
244
self._print_table(table, '+', list(range(n)))
245
return None
246
247
def multiplication_table(self, style='elements'):
248
"""
249
Print the multiplication table for Z/nZ.
250
style='elements' prints values, style='list' returns a 2D list.
251
"""
252
n = self._n
253
table = [[(i * j) % n for j in range(n)] for i in range(n)]
254
if style == 'list':
255
return table
256
self._print_table(table, '*', list(range(n)))
257
return None
258
259
def _print_table(self, table, op, labels):
260
"""Format and print an operation table."""
261
n = len(labels)
262
# Determine column width
263
w = max(len(str(x)) for row in table for x in row)
264
w = max(w, len(str(max(labels))), len(op))
265
266
# Header
267
header = f"{op:>{w}} |" + "".join(f" {labels[j]:>{w}}" for j in range(n))
268
print(header)
269
print("-" * len(header))
270
271
# Rows
272
for i in range(n):
273
row = f"{labels[i]:>{w}} |" + "".join(f" {table[i][j]:>{w}}" for j in range(n))
274
print(row)
275
276
277
# ---------------------------------------------------------------------------
278
# Factory function
279
# ---------------------------------------------------------------------------
280
281
def Zmod(n):
282
"""Create the ring Z/nZ. Mirrors SageMath's Zmod(n)."""
283
return ZmodRing(n)
284
285
286
def Integers(n):
287
"""Alias for Zmod(n). Mirrors SageMath's Integers(n)."""
288
return ZmodRing(n)
289
290