pqlattice 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. pqlattice/__init__.py +6 -0
  2. pqlattice/_backends/__init__.py +0 -0
  3. pqlattice/_backends/_fast.py +75 -0
  4. pqlattice/_backends/_native.py +33 -0
  5. pqlattice/_backends/_protocol.py +15 -0
  6. pqlattice/_utils.py +201 -0
  7. pqlattice/integer/__init__.py +8 -0
  8. pqlattice/integer/_integer.py +55 -0
  9. pqlattice/integer/_modintring.py +246 -0
  10. pqlattice/integer/_modring.py +165 -0
  11. pqlattice/integer/_primality.py +78 -0
  12. pqlattice/integer/_primes.py +57 -0
  13. pqlattice/lattice/__init__.py +44 -0
  14. pqlattice/lattice/_bkz.py +87 -0
  15. pqlattice/lattice/_cvp.py +62 -0
  16. pqlattice/lattice/_embeddings.py +149 -0
  17. pqlattice/lattice/_gso.py +43 -0
  18. pqlattice/lattice/_hkz.py +20 -0
  19. pqlattice/lattice/_lattice.py +137 -0
  20. pqlattice/lattice/_lll.py +93 -0
  21. pqlattice/lattice/_svp.py +89 -0
  22. pqlattice/lattice/embeddings.py +3 -0
  23. pqlattice/linalg/__init__.py +37 -0
  24. pqlattice/linalg/_linalg.py +306 -0
  25. pqlattice/linalg/_modint.py +209 -0
  26. pqlattice/linalg/_utils.py +167 -0
  27. pqlattice/polynomial/__init__.py +5 -0
  28. pqlattice/polynomial/_modpolyqring.py +185 -0
  29. pqlattice/polynomial/_modpolyring.py +267 -0
  30. pqlattice/polynomial/_poly.py +250 -0
  31. pqlattice/polynomial/poly.py +3 -0
  32. pqlattice/py.typed +0 -0
  33. pqlattice/random/__init__.py +7 -0
  34. pqlattice/random/_distribution.py +303 -0
  35. pqlattice/random/_lattice.py +53 -0
  36. pqlattice/random/_lwe.py +109 -0
  37. pqlattice/random/_lwr.py +41 -0
  38. pqlattice/random/_prime.py +53 -0
  39. pqlattice/random/distribution.py +3 -0
  40. pqlattice/settings.py +66 -0
  41. pqlattice/typing/__init__.py +4 -0
  42. pqlattice/typing/_types.py +18 -0
  43. pqlattice/typing/_types_validator.py +57 -0
  44. pqlattice-0.1.2.dist-info/METADATA +33 -0
  45. pqlattice-0.1.2.dist-info/RECORD +47 -0
  46. pqlattice-0.1.2.dist-info/WHEEL +4 -0
  47. pqlattice-0.1.2.dist-info/licenses/LICENSE +7 -0
@@ -0,0 +1,267 @@
1
+ from ..integer._modring import mod, modinv
2
+ from ..typing import Vector, validate_aliases
3
+ from . import _poly as poly
4
+
5
+
6
+ class ModIntPolyRing:
7
+ def __init__(self, modulus: int):
8
+ """_summary_
9
+
10
+ Parameters
11
+ ----------
12
+ modulus : int
13
+ _description_
14
+
15
+ Raises
16
+ ------
17
+ ValueError
18
+ _description_
19
+ """
20
+ if modulus <= 1:
21
+ raise ValueError("Modulus has to be greater than 1")
22
+
23
+ self.modulus = modulus
24
+
25
+ @validate_aliases
26
+ def reduce(self, polynomial: Vector) -> Vector:
27
+ """_summary_
28
+
29
+ Parameters
30
+ ----------
31
+ polynomial : Vector
32
+ _description_
33
+
34
+ Returns
35
+ -------
36
+ Vector
37
+ _description_
38
+ """
39
+ return poly.trim(mod(polynomial, self.modulus))
40
+
41
+ @validate_aliases
42
+ def is_zero(self, polynomial: Vector) -> bool:
43
+ """_summary_
44
+
45
+ Parameters
46
+ ----------
47
+ polynomial : Vector
48
+ _description_
49
+
50
+ Returns
51
+ -------
52
+ bool
53
+ _description_
54
+ """
55
+ return poly.is_zero_poly(self.reduce(polynomial))
56
+
57
+ @validate_aliases
58
+ def deg(self, polynomial: Vector) -> int:
59
+ """_summary_
60
+
61
+ Parameters
62
+ ----------
63
+ polynomial : Vector
64
+ _description_
65
+
66
+ Returns
67
+ -------
68
+ int
69
+ _description_
70
+ """
71
+ return poly.deg(self.reduce(polynomial))
72
+
73
+ @validate_aliases
74
+ def add(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
75
+ """_summary_
76
+
77
+ Parameters
78
+ ----------
79
+ polynomial_a : Vector
80
+ _description_
81
+ polynomial_b : Vector
82
+ _description_
83
+
84
+ Returns
85
+ -------
86
+ Vector
87
+ _description_
88
+ """
89
+ return self.reduce(poly.add(polynomial_a, polynomial_b))
90
+
91
+ @validate_aliases
92
+ def sub(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
93
+ """_summary_
94
+
95
+ Parameters
96
+ ----------
97
+ polynomial_a : Vector
98
+ _description_
99
+ polynomial_b : Vector
100
+ _description_
101
+
102
+ Returns
103
+ -------
104
+ Vector
105
+ _description_
106
+ """
107
+ return self.reduce(poly.sub(polynomial_a, polynomial_b))
108
+
109
+ @validate_aliases
110
+ def mul(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
111
+ """_summary_
112
+
113
+ Parameters
114
+ ----------
115
+ polynomial_a : Vector
116
+ _description_
117
+ polynomial_b : Vector
118
+ _description_
119
+
120
+ Returns
121
+ -------
122
+ Vector
123
+ _description_
124
+ """
125
+ return self.reduce(poly.mul(polynomial_a, polynomial_b))
126
+
127
+ @validate_aliases
128
+ def euclidean_div(self, polynomial_a: Vector, polynomial_b: Vector) -> tuple[Vector, Vector]:
129
+ """_summary_
130
+
131
+ Parameters
132
+ ----------
133
+ polynomial_a : Vector
134
+ _description_
135
+ polynomial_b : Vector
136
+ _description_
137
+
138
+ Returns
139
+ -------
140
+ tuple[Vector, Vector]
141
+ _description_
142
+
143
+ Raises
144
+ ------
145
+ ZeroDivisionError
146
+ _description_
147
+ """
148
+ if self.is_zero(polynomial_b):
149
+ raise ZeroDivisionError("Can't divide by zero polynomial")
150
+
151
+ q = poly.zero_poly()
152
+ r = self.reduce(polynomial_a)
153
+
154
+ d = self.deg(polynomial_b)
155
+ c: int = polynomial_b[d]
156
+ while (dr := self.deg(r)) >= d:
157
+ s = poly.monomial(r[dr] * modinv(c, self.modulus), dr - d)
158
+ q = self.add(q, s)
159
+ r = self.sub(r, self.mul(s, polynomial_b))
160
+
161
+ return q, r
162
+
163
+ @validate_aliases
164
+ def rem(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
165
+ """_summary_
166
+
167
+ Parameters
168
+ ----------
169
+ polynomial_a : Vector
170
+ _description_
171
+ polynomial_b : Vector
172
+ _description_
173
+
174
+ Returns
175
+ -------
176
+ Vector
177
+ _description_
178
+ """
179
+ _, r = self.euclidean_div(polynomial_a, polynomial_b)
180
+ return r
181
+
182
+ def to_monic(self, polynomial: Vector) -> Vector:
183
+ """_summary_
184
+
185
+ Parameters
186
+ ----------
187
+ polynomial : Vector
188
+ _description_
189
+
190
+ Returns
191
+ -------
192
+ Vector
193
+ _description_
194
+ """
195
+ leading_coeff: int = polynomial[self.deg(polynomial)]
196
+ return self.reduce(modinv(leading_coeff, self.modulus) * polynomial)
197
+
198
+ def gcd(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
199
+ """_summary_
200
+
201
+ Parameters
202
+ ----------
203
+ polynomial_a : Vector
204
+ _description_
205
+ polynomial_b : Vector
206
+ _description_
207
+
208
+ Returns
209
+ -------
210
+ Vector
211
+ _description_
212
+ """
213
+ r0 = self.reduce(polynomial_a)
214
+ r1 = self.reduce(polynomial_b)
215
+ if poly.deg(r1) > poly.deg(r0):
216
+ r0, r1 = r1, r0
217
+
218
+ while not self.is_zero(r1):
219
+ r0, r1 = r1, self.rem(r0, r1)
220
+
221
+ return r0
222
+
223
+ def eea(self, polynomial_a: Vector, polynomial_b: Vector):
224
+ """_summary_
225
+
226
+ Parameters
227
+ ----------
228
+ polynomial_a : Vector
229
+ _description_
230
+ polynomial_b : Vector
231
+ _description_
232
+
233
+ Returns
234
+ -------
235
+ _type_
236
+ _description_
237
+ """
238
+ f0, f1 = self.reduce(polynomial_a), self.reduce(polynomial_b)
239
+ a0, a1 = poly.monomial(1, 0), poly.zero_poly()
240
+ b0, b1 = poly.zero_poly(), poly.monomial(1, 0)
241
+
242
+ while not self.is_zero(f1):
243
+ q, r = self.euclidean_div(f0, f1)
244
+
245
+ f0, f1 = f1, r
246
+
247
+ a0, a1 = a1, self.sub(a0, self.mul(q, a1))
248
+ b0, b1 = b1, self.sub(b0, self.mul(q, b1))
249
+
250
+ return f0, a0, b0
251
+
252
+ def coprime(self, polynomial_a: Vector, polynomial_b: Vector) -> bool:
253
+ """_summary_
254
+
255
+ Parameters
256
+ ----------
257
+ polynomial_a : Vector
258
+ _description_
259
+ polynomial_b : Vector
260
+ _description_
261
+
262
+ Returns
263
+ -------
264
+ bool
265
+ _description_
266
+ """
267
+ return all(self.to_monic(self.gcd(polynomial_a, polynomial_b)) == poly.monomial(1, 0))
@@ -0,0 +1,250 @@
1
+ import numpy as np
2
+ from numpy.typing import ArrayLike
3
+
4
+ from .._utils import as_integer
5
+ from ..typing import Vector, validate_aliases
6
+
7
+
8
+ @validate_aliases
9
+ def make_poly(data: ArrayLike) -> Vector:
10
+ """_summary_
11
+
12
+ Parameters
13
+ ----------
14
+ data : Iterable[int | float]
15
+ _description_
16
+
17
+ Returns
18
+ -------
19
+ Vector
20
+ _description_
21
+
22
+ Raises
23
+ ------
24
+ ValueError
25
+ _description_
26
+ """
27
+ arr = as_integer(data)
28
+
29
+ if arr.ndim != 1:
30
+ raise ValueError(f"Expected 1D iterable, got {arr.ndim}D")
31
+
32
+ return arr
33
+
34
+
35
+ @validate_aliases
36
+ def is_zero_poly(p: Vector) -> bool:
37
+ """_summary_
38
+
39
+ Parameters
40
+ ----------
41
+ p : Vector
42
+ _description_
43
+
44
+ Returns
45
+ -------
46
+ bool
47
+ _description_
48
+
49
+ Raises
50
+ ------
51
+ ValueError
52
+ _description_
53
+ """
54
+ if len(p) == 0:
55
+ raise ValueError("Empty coefficient array is not a proper polynomial")
56
+
57
+ return np.count_nonzero(p) == 0
58
+
59
+
60
+ @validate_aliases
61
+ def deg(p: Vector) -> int:
62
+ """_summary_
63
+
64
+ Parameters
65
+ ----------
66
+ p : Vector
67
+ _description_
68
+
69
+ Returns
70
+ -------
71
+ int
72
+ _description_
73
+
74
+ Raises
75
+ ------
76
+ ValueError
77
+ _description_
78
+ """
79
+ if len(p) == 0:
80
+ raise ValueError("Empty coefficient array is not a proper polynomial")
81
+ nonzeros = np.nonzero(p)[0]
82
+ if len(nonzeros) == 0:
83
+ return -1
84
+ # raise ValueError("Degree of zero polynomial is undefined")
85
+ else:
86
+ return nonzeros[-1]
87
+
88
+
89
+ @validate_aliases
90
+ def pad(p: Vector, max_deg: int) -> Vector:
91
+ """_summary_
92
+
93
+ Parameters
94
+ ----------
95
+ p : Vector
96
+ _description_
97
+ max_deg : int
98
+ _description_
99
+
100
+ Returns
101
+ -------
102
+ Vector
103
+ _description_
104
+
105
+ Raises
106
+ ------
107
+ ValueError
108
+ _description_
109
+ """
110
+ if is_zero_poly(p):
111
+ return zero_poly(max_deg)
112
+
113
+ d = deg(p)
114
+ if max_deg < d:
115
+ raise ValueError("max_deg has to be greater or equal to the degree of a given polynomial p")
116
+
117
+ return as_integer(np.pad(trim(p), (0, max_deg - d)))
118
+
119
+
120
+ @validate_aliases
121
+ def trim(p: Vector) -> Vector:
122
+ """_summary_
123
+
124
+ Parameters
125
+ ----------
126
+ p : Vector
127
+ _description_
128
+
129
+ Returns
130
+ -------
131
+ Vector
132
+ _description_
133
+ """
134
+ if is_zero_poly(p):
135
+ return as_integer([0])
136
+
137
+ return p[: deg(p) + 1].copy()
138
+
139
+
140
+ @validate_aliases
141
+ def add(p: Vector, q: Vector) -> Vector:
142
+ """_summary_
143
+
144
+ Parameters
145
+ ----------
146
+ p : Vector
147
+ _description_
148
+ q : Vector
149
+ _description_
150
+
151
+ Returns
152
+ -------
153
+ Vector
154
+ _description_
155
+ """
156
+ max_deg = max(deg(p), deg(q), 0)
157
+ return trim(pad(p, max_deg) + pad(q, max_deg))
158
+
159
+
160
+ @validate_aliases
161
+ def sub(p: Vector, q: Vector) -> Vector:
162
+ """_summary_
163
+
164
+ Parameters
165
+ ----------
166
+ p : Vector
167
+ _description_
168
+ q : Vector
169
+ _description_
170
+
171
+ Returns
172
+ -------
173
+ Vector
174
+ _description_
175
+ """
176
+ max_deg = max(deg(p), deg(q), 0)
177
+ return trim(pad(p, max_deg) - pad(q, max_deg))
178
+
179
+
180
+ @validate_aliases
181
+ def mul(p: Vector, q: Vector) -> Vector:
182
+ """_summary_
183
+
184
+ Parameters
185
+ ----------
186
+ p : Vector
187
+ _description_
188
+ q : Vector
189
+ _description_
190
+
191
+ Returns
192
+ -------
193
+ Vector
194
+ _description_
195
+ """
196
+ return trim(np.polymul(p[::-1], q[::-1])[::-1])
197
+
198
+
199
+ @validate_aliases
200
+ def monomial(coeff: int, degree: int) -> Vector:
201
+ """_summary_
202
+
203
+ Parameters
204
+ ----------
205
+ coeff : int
206
+ _description_
207
+ degree : int
208
+ _description_
209
+
210
+ Returns
211
+ -------
212
+ Vector
213
+ _description_
214
+
215
+ Raises
216
+ ------
217
+ ValueError
218
+ _description_
219
+ """
220
+ if degree < 0:
221
+ raise ValueError("degree has to be non negative")
222
+
223
+ p = as_integer([0] * (degree + 1))
224
+ p[degree] = coeff
225
+ return p
226
+
227
+
228
+ @validate_aliases
229
+ def zero_poly(max_deg: int = 0) -> Vector:
230
+ """_summary_
231
+
232
+ Parameters
233
+ ----------
234
+ max_deg : int, optional
235
+ _description_, by default 0
236
+
237
+ Returns
238
+ -------
239
+ Vector
240
+ _description_
241
+
242
+ Raises
243
+ ------
244
+ ValueError
245
+ _description_
246
+ """
247
+ if max_deg < 0:
248
+ raise ValueError("degree has to be non negative")
249
+
250
+ return as_integer([0] * (max_deg + 1))
@@ -0,0 +1,3 @@
1
+ from ._poly import add, deg, is_zero_poly, make_poly, monomial, mul, pad, sub, trim, zero_poly
2
+
3
+ __all__ = ["make_poly", "is_zero_poly", "deg", "pad", "trim", "add", "sub", "mul", "monomial", "zero_poly"]
pqlattice/py.typed ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ from . import distribution
2
+ from ._lattice import randlattice
3
+ from ._lwe import LWE
4
+ from ._lwr import LWR
5
+ from ._prime import randprime
6
+
7
+ __all__ = ["randprime", "randlattice", "distribution", "LWE", "LWR"]