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.
- pqlattice/__init__.py +6 -0
- pqlattice/_backends/__init__.py +0 -0
- pqlattice/_backends/_fast.py +75 -0
- pqlattice/_backends/_native.py +33 -0
- pqlattice/_backends/_protocol.py +15 -0
- pqlattice/_utils.py +201 -0
- pqlattice/integer/__init__.py +8 -0
- pqlattice/integer/_integer.py +55 -0
- pqlattice/integer/_modintring.py +246 -0
- pqlattice/integer/_modring.py +165 -0
- pqlattice/integer/_primality.py +78 -0
- pqlattice/integer/_primes.py +57 -0
- pqlattice/lattice/__init__.py +44 -0
- pqlattice/lattice/_bkz.py +87 -0
- pqlattice/lattice/_cvp.py +62 -0
- pqlattice/lattice/_embeddings.py +149 -0
- pqlattice/lattice/_gso.py +43 -0
- pqlattice/lattice/_hkz.py +20 -0
- pqlattice/lattice/_lattice.py +137 -0
- pqlattice/lattice/_lll.py +93 -0
- pqlattice/lattice/_svp.py +89 -0
- pqlattice/lattice/embeddings.py +3 -0
- pqlattice/linalg/__init__.py +37 -0
- pqlattice/linalg/_linalg.py +306 -0
- pqlattice/linalg/_modint.py +209 -0
- pqlattice/linalg/_utils.py +167 -0
- pqlattice/polynomial/__init__.py +5 -0
- pqlattice/polynomial/_modpolyqring.py +185 -0
- pqlattice/polynomial/_modpolyring.py +267 -0
- pqlattice/polynomial/_poly.py +250 -0
- pqlattice/polynomial/poly.py +3 -0
- pqlattice/py.typed +0 -0
- pqlattice/random/__init__.py +7 -0
- pqlattice/random/_distribution.py +303 -0
- pqlattice/random/_lattice.py +53 -0
- pqlattice/random/_lwe.py +109 -0
- pqlattice/random/_lwr.py +41 -0
- pqlattice/random/_prime.py +53 -0
- pqlattice/random/distribution.py +3 -0
- pqlattice/settings.py +66 -0
- pqlattice/typing/__init__.py +4 -0
- pqlattice/typing/_types.py +18 -0
- pqlattice/typing/_types_validator.py +57 -0
- pqlattice-0.1.2.dist-info/METADATA +33 -0
- pqlattice-0.1.2.dist-info/RECORD +47 -0
- pqlattice-0.1.2.dist-info/WHEEL +4 -0
- pqlattice-0.1.2.dist-info/licenses/LICENSE +7 -0
pqlattice/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from fractions import Fraction
|
|
2
|
+
|
|
3
|
+
from . import integer, lattice, linalg, polynomial, random, settings, typing
|
|
4
|
+
from ._utils import as_integer, as_rational, show
|
|
5
|
+
|
|
6
|
+
__all__ = ["settings", "integer", "lattice", "linalg", "polynomial", "random", "typing", "as_integer", "as_rational", "Fraction", "show"]
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import Any, override
|
|
2
|
+
|
|
3
|
+
from .._utils import as_integer
|
|
4
|
+
from ..typing import Array, Matrix, SquareMatrix, Vector
|
|
5
|
+
from ._protocol import BackendInterface
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import fpylll
|
|
9
|
+
|
|
10
|
+
HAS_FPYLLL = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
HAS_FPYLLL = False
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import flint
|
|
16
|
+
|
|
17
|
+
HAS_FLINT = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
HAS_FLINT = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FastBackend(BackendInterface):
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
if not (HAS_FPYLLL and HAS_FLINT):
|
|
25
|
+
raise RuntimeError("Fast backend is unavailable - dependencies missing")
|
|
26
|
+
|
|
27
|
+
def _to_fpylll(self, a: Array) -> Any:
|
|
28
|
+
return fpylll.IntegerMatrix.from_matrix(a.tolist())
|
|
29
|
+
|
|
30
|
+
def _from_fpylll(self, mat: Any) -> Array:
|
|
31
|
+
data = [list(row) for row in mat]
|
|
32
|
+
return as_integer(data)
|
|
33
|
+
|
|
34
|
+
def _to_flint(self, a: Array) -> Any:
|
|
35
|
+
return flint.fmpz_mat(a.tolist())
|
|
36
|
+
|
|
37
|
+
def _from_flint(self, mat: Any) -> Array:
|
|
38
|
+
return as_integer(mat.table())
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
def lll(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix:
|
|
42
|
+
mat = self._to_fpylll(lattice_basis)
|
|
43
|
+
fpylll.LLL.reduction(mat, delta=delta)
|
|
44
|
+
return self._from_fpylll(mat)
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
def bkz(self, lattice_basis: SquareMatrix, block_size: int, delta: float) -> SquareMatrix:
|
|
48
|
+
mat = self._to_fpylll(lattice_basis)
|
|
49
|
+
# fpylll.BKZ.reduction(mat, delta=delta)
|
|
50
|
+
mat_bkz = fpylll.BKZ.reduction(mat, fpylll.BKZ.Param(block_size=block_size))
|
|
51
|
+
return self._from_fpylll(mat_bkz)
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
def hkz(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix:
|
|
55
|
+
return self.bkz(lattice_basis, len(lattice_basis), delta)
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
def shortest_vector(self, lattice_basis: SquareMatrix) -> Vector:
|
|
59
|
+
mat = self._to_fpylll(lattice_basis)
|
|
60
|
+
fpylll.LLL.reduction(mat)
|
|
61
|
+
sv = fpylll.SVP.shortest_vector(mat, pruning=False, preprocess=False)
|
|
62
|
+
return as_integer(sv)
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
def closest_vector(self, lattice_basis: SquareMatrix, target_vector: Vector) -> Vector:
|
|
66
|
+
A = self._from_fpylll(lattice_basis)
|
|
67
|
+
t = target_vector.tolist()
|
|
68
|
+
v0 = fpylll.CVP.closest_vector(A, t)
|
|
69
|
+
return as_integer(v0)
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
def hnf(self, matrix: Matrix) -> tuple[Matrix, SquareMatrix]:
|
|
73
|
+
mat = self._to_flint(matrix)
|
|
74
|
+
H, U = mat.hnf(transform=True)
|
|
75
|
+
return self._from_flint(H), self._from_flint(U)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from ..lattice._bkz import bkz
|
|
4
|
+
from ..lattice._hkz import hkz
|
|
5
|
+
from ..lattice._lll import lll
|
|
6
|
+
from ..lattice._svp import shortest_vector
|
|
7
|
+
from ..linalg._linalg import hnf
|
|
8
|
+
from ..typing import Matrix, SquareMatrix, Vector
|
|
9
|
+
from ._protocol import BackendInterface
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NativeBackend(BackendInterface):
|
|
13
|
+
@override
|
|
14
|
+
def lll(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix:
|
|
15
|
+
return lll(lattice_basis, delta)
|
|
16
|
+
|
|
17
|
+
@override
|
|
18
|
+
def bkz(self, lattice_basis: SquareMatrix, block_size: int, delta: float) -> SquareMatrix:
|
|
19
|
+
_ = delta
|
|
20
|
+
return bkz(lattice_basis, block_size)
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
def hkz(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix:
|
|
24
|
+
_ = delta
|
|
25
|
+
return hkz(lattice_basis)
|
|
26
|
+
|
|
27
|
+
@override
|
|
28
|
+
def shortest_vector(self, lattice_basis: SquareMatrix) -> Vector:
|
|
29
|
+
return shortest_vector(lattice_basis)
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
def hnf(self, matrix: Matrix) -> tuple[Matrix, SquareMatrix]:
|
|
33
|
+
return hnf(matrix)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from ..typing import Matrix, SquareMatrix, Vector
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BackendInterface(Protocol):
|
|
7
|
+
def lll(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix: ...
|
|
8
|
+
|
|
9
|
+
def bkz(self, lattice_basis: SquareMatrix, block_size: int, delta: float) -> SquareMatrix: ...
|
|
10
|
+
|
|
11
|
+
def hkz(self, lattice_basis: SquareMatrix, delta: float) -> SquareMatrix: ...
|
|
12
|
+
|
|
13
|
+
def shortest_vector(self, lattice_basis: SquareMatrix) -> Vector: ...
|
|
14
|
+
|
|
15
|
+
def hnf(self, matrix: Matrix) -> tuple[Matrix, SquareMatrix]: ...
|
pqlattice/_utils.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from fractions import Fraction
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.typing import ArrayLike
|
|
6
|
+
|
|
7
|
+
from .typing import Array, Matrix, Vector, is_integer, is_Matrix, is_rational, is_Vector, validate_aliases
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def as_integer(obj: ArrayLike) -> Array:
|
|
11
|
+
"""
|
|
12
|
+
Helper function that converts given obj to numpy's array of python's ints allowing arbitrary large elements
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
obj : ArrayLike
|
|
17
|
+
object to be converted to numpy's array
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
Array
|
|
22
|
+
numpy's array with dtype=object and elements converted to int
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
--------
|
|
26
|
+
>>> import pqlattice as pq
|
|
27
|
+
>>> pq.as_integer([3**100, 2**100, 5**50])
|
|
28
|
+
array([515377520732011331036461129765621272702107522001,
|
|
29
|
+
1267650600228229401496703205376,
|
|
30
|
+
88817841970012523233890533447265625], dtype=object)
|
|
31
|
+
"""
|
|
32
|
+
arr = np.array(obj, dtype=object)
|
|
33
|
+
if arr.size == 0:
|
|
34
|
+
return arr
|
|
35
|
+
else:
|
|
36
|
+
return (np.vectorize(int)(arr.flat).reshape(arr.shape)).astype(object)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def as_rational(obj: ArrayLike) -> Array:
|
|
40
|
+
"""
|
|
41
|
+
Helper function that converts given obj to numpy's array of python's fractions.Fraction allowing arbitrary big rational elements
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
obj : ArrayLike
|
|
46
|
+
object to be converted to numpy's array
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
Array
|
|
51
|
+
numpy's array with dtype=object and elements converted to fractions.Fraction
|
|
52
|
+
|
|
53
|
+
Examples
|
|
54
|
+
--------
|
|
55
|
+
>>> import pqlattice as pq
|
|
56
|
+
>>> pq.as_rational([3**100, 2**100, 5**50])
|
|
57
|
+
array([Fraction(515377520732011331036461129765621272702107522001, 1),
|
|
58
|
+
Fraction(1267650600228229401496703205376, 1),
|
|
59
|
+
Fraction(88817841970012523233890533447265625, 1)], dtype=object)
|
|
60
|
+
"""
|
|
61
|
+
arr = np.array(obj, dtype=object)
|
|
62
|
+
if arr.size == 0:
|
|
63
|
+
return arr
|
|
64
|
+
else:
|
|
65
|
+
return (np.vectorize(Fraction)(arr.flat).reshape(arr.shape)).astype(object)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def zeros_vec(n: int) -> Vector:
|
|
69
|
+
return as_integer(np.zeros((n,)))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def zeros_mat(rows: int, cols: int | None = None) -> Matrix:
|
|
73
|
+
if cols is None:
|
|
74
|
+
cols = rows
|
|
75
|
+
return as_integer(np.zeros((rows, cols)))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@validate_aliases
|
|
79
|
+
def show(a: Array, max_rows: int = 10, max_cols: int = 10, val_width: int = 15):
|
|
80
|
+
"""
|
|
81
|
+
Helper function that prints the numpy's array in a human redable format
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
a : Array
|
|
86
|
+
The array to print
|
|
87
|
+
max_rows : int, optional
|
|
88
|
+
Max number of rows to display before truncating, by default 10
|
|
89
|
+
max_cols : int, optional
|
|
90
|
+
Max number of columns to display before truncating, by default 10
|
|
91
|
+
val_width : int, optional
|
|
92
|
+
Max characters per cell, by default 15. If a string representation of element is longer, it is truncated e.g 1234...5678
|
|
93
|
+
|
|
94
|
+
Examples
|
|
95
|
+
--------
|
|
96
|
+
>>> import pqlattice as pq
|
|
97
|
+
>>> M = pq.random.distribution.Uniform(0, 2**50, seed=0).sample_matrix(7, 5)
|
|
98
|
+
>>> pq.show(M)
|
|
99
|
+
Matrix of integers with shape: 7 x 5
|
|
100
|
+
====================================
|
|
101
|
+
[0] [1] [2] [3] [4]
|
|
102
|
+
[0] 867496826243021 91162487198805 109421..4040930 806253307773444 491889324856851
|
|
103
|
+
[1] 313616384600182 314680579360371 213540430176889 330931104930059 222394738660569
|
|
104
|
+
[2] 166055160201467 743539086037546 796665326308852 712012953150114 460445890320316
|
|
105
|
+
[3] 996855368208390 140240390954947 210028256050344 750154124310314 141827853726696
|
|
106
|
+
[4] 499232256057935 320872572303314 205400145011268 110177..2031755 678794279728913
|
|
107
|
+
[5] 655478801553847 281048514639229 749289460799082 457570956347073 647748016542327
|
|
108
|
+
[6] 206336435080453 713924001980837 545175556185458 414036094290124 74247901643189
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def format_val(val: Any) -> str:
|
|
112
|
+
s = str(val)
|
|
113
|
+
if len(s) > val_width:
|
|
114
|
+
mid = (val_width - 2) // 2
|
|
115
|
+
remainder = (val_width - 2) % 2
|
|
116
|
+
return f"{s[:mid]}..{s[-(mid + remainder) :]}"
|
|
117
|
+
return s
|
|
118
|
+
|
|
119
|
+
element_type: str = f"{a.dtype}"
|
|
120
|
+
if is_integer(a):
|
|
121
|
+
element_type = "integers"
|
|
122
|
+
if is_rational(a):
|
|
123
|
+
element_type = "rationals"
|
|
124
|
+
|
|
125
|
+
info_header = f"numpy array of {element_type} with shape: {a.shape}"
|
|
126
|
+
|
|
127
|
+
is_v = False
|
|
128
|
+
if is_Matrix(a):
|
|
129
|
+
rows, cols = a.shape
|
|
130
|
+
info_header = f"Matrix of {element_type} with shape: {rows} x {cols}"
|
|
131
|
+
elif is_Vector(a):
|
|
132
|
+
dim = a.shape[0]
|
|
133
|
+
info_header = f"Vector of {element_type} with shape: {dim}"
|
|
134
|
+
is_v = True
|
|
135
|
+
rows, cols = 1, dim
|
|
136
|
+
else:
|
|
137
|
+
print(info_header)
|
|
138
|
+
print("=" * len(info_header))
|
|
139
|
+
print(f"{a}")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
print(info_header)
|
|
143
|
+
print("=" * len(info_header))
|
|
144
|
+
|
|
145
|
+
ellipsis_col_idx: int | None = None
|
|
146
|
+
ellipsis_row_idx: int | None = None
|
|
147
|
+
if rows <= max_rows:
|
|
148
|
+
row_indices = list(range(rows))
|
|
149
|
+
show_row_ellipsis = False
|
|
150
|
+
else:
|
|
151
|
+
# Top half and bottom half
|
|
152
|
+
r_cut = max_rows // 2
|
|
153
|
+
row_indices = list(range(r_cut)) + list(range(rows - r_cut, rows))
|
|
154
|
+
show_row_ellipsis = True
|
|
155
|
+
ellipsis_row_idx = r_cut
|
|
156
|
+
|
|
157
|
+
if cols <= max_cols:
|
|
158
|
+
col_indices = list(range(cols))
|
|
159
|
+
show_col_ellipsis = False
|
|
160
|
+
else:
|
|
161
|
+
c_cut = max_cols // 2
|
|
162
|
+
col_indices = list(range(c_cut)) + list(range(cols - c_cut, cols))
|
|
163
|
+
show_col_ellipsis = True
|
|
164
|
+
ellipsis_col_idx = c_cut
|
|
165
|
+
|
|
166
|
+
header = [""]
|
|
167
|
+
for i, c_idx in enumerate(col_indices):
|
|
168
|
+
if show_col_ellipsis and i == ellipsis_col_idx:
|
|
169
|
+
header.append("...")
|
|
170
|
+
header.append(f"[{c_idx}]")
|
|
171
|
+
|
|
172
|
+
table_data = [header]
|
|
173
|
+
|
|
174
|
+
for i, r_idx in enumerate(row_indices):
|
|
175
|
+
if show_row_ellipsis and i == ellipsis_row_idx:
|
|
176
|
+
ellipsis_row = ["..."] + ["..."] * (len(header) - 1)
|
|
177
|
+
table_data.append(ellipsis_row)
|
|
178
|
+
|
|
179
|
+
row_str = [f"[{r_idx}]"]
|
|
180
|
+
|
|
181
|
+
for j, c_idx in enumerate(col_indices):
|
|
182
|
+
if show_col_ellipsis and j == ellipsis_col_idx:
|
|
183
|
+
row_str.append("...")
|
|
184
|
+
|
|
185
|
+
val = a[c_idx] if is_v else a[r_idx, c_idx]
|
|
186
|
+
row_str.append(format_val(val))
|
|
187
|
+
|
|
188
|
+
table_data.append(row_str)
|
|
189
|
+
|
|
190
|
+
num_display_cols = len(table_data[0])
|
|
191
|
+
col_widths = [0] * num_display_cols
|
|
192
|
+
|
|
193
|
+
for row in table_data:
|
|
194
|
+
for idx, cell in enumerate(row):
|
|
195
|
+
col_widths[idx] = max(col_widths[idx], len(cell))
|
|
196
|
+
|
|
197
|
+
for row in table_data:
|
|
198
|
+
formatted_row: list[str] = []
|
|
199
|
+
for idx, cell in enumerate(row):
|
|
200
|
+
formatted_row.append(cell.rjust(col_widths[idx]))
|
|
201
|
+
print(" ".join(formatted_row))
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from math import gcd
|
|
2
|
+
|
|
3
|
+
from ._integer import eea
|
|
4
|
+
from ._modintring import ModIntRing
|
|
5
|
+
from ._modring import cmodl, cmodr, mod, modinv, modpow
|
|
6
|
+
from ._primality import is_prime
|
|
7
|
+
|
|
8
|
+
__all__ = ["gcd", "eea", "mod", "cmodl", "cmodr", "modinv", "modpow", "ModIntRing", "is_prime"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import cast, overload
|
|
2
|
+
|
|
3
|
+
from .._utils import as_integer
|
|
4
|
+
from ..typing import Array
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@overload
|
|
8
|
+
def eea(a: int, b: int) -> tuple[int, int, int]: ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@overload
|
|
12
|
+
def eea(a: Array, b: int) -> tuple[Array, Array, Array]: ...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def eea(a: int | Array, b: int) -> tuple[int, int, int] | tuple[Array, Array, Array]:
|
|
16
|
+
"""_summary_
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
a : int | Array
|
|
21
|
+
_description_
|
|
22
|
+
b : int
|
|
23
|
+
_description_
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
tuple[int, int, int] | tuple[Array, Array, Array]
|
|
28
|
+
_description_
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(a, int):
|
|
31
|
+
return _eea(a, b)
|
|
32
|
+
else:
|
|
33
|
+
return tuple(as_integer([_eea(cast(int, el), b) for el in a]).T)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _eea(a: int, b: int) -> tuple[int, int, int]:
|
|
37
|
+
if a == 0 and b == 0:
|
|
38
|
+
raise ValueError("a and b can't be both zero")
|
|
39
|
+
|
|
40
|
+
old_s, s = 1, 0
|
|
41
|
+
old_r, r = a, b
|
|
42
|
+
while r != 0:
|
|
43
|
+
q = old_r // r
|
|
44
|
+
old_r, r = r, old_r - q * r
|
|
45
|
+
old_s, s = s, old_s - q * s
|
|
46
|
+
|
|
47
|
+
t = 0 if b == 0 else (old_r - old_s * a) // b
|
|
48
|
+
s = old_s
|
|
49
|
+
gcd = old_r
|
|
50
|
+
if gcd < 0:
|
|
51
|
+
gcd = -gcd
|
|
52
|
+
s = -s
|
|
53
|
+
t = -t
|
|
54
|
+
|
|
55
|
+
return gcd, s, t
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from typing import overload
|
|
2
|
+
|
|
3
|
+
from ..typing import Array
|
|
4
|
+
from ._modring import cmodl, cmodr, mod, modinv, modpow
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ModIntRing:
|
|
8
|
+
def __init__(self, modulus: int):
|
|
9
|
+
"""_summary_
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
modulus : int
|
|
14
|
+
_description_
|
|
15
|
+
|
|
16
|
+
Raises
|
|
17
|
+
------
|
|
18
|
+
ValueError
|
|
19
|
+
_description_
|
|
20
|
+
"""
|
|
21
|
+
if abs(modulus) < 2:
|
|
22
|
+
raise ValueError(f"absolute value of modulus has to greater than one, given modulus: {modulus}")
|
|
23
|
+
|
|
24
|
+
self._modulus = abs(modulus)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def q(self) -> int:
|
|
28
|
+
"""_summary_
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
_type_
|
|
33
|
+
_description_
|
|
34
|
+
"""
|
|
35
|
+
return self._modulus
|
|
36
|
+
|
|
37
|
+
def is_zero(self, a: int) -> bool:
|
|
38
|
+
"""_summary_
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
a : int
|
|
43
|
+
_description_
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
bool
|
|
48
|
+
_description_
|
|
49
|
+
"""
|
|
50
|
+
return self.mod(a) == 0
|
|
51
|
+
|
|
52
|
+
@overload
|
|
53
|
+
def mod(self, a: int) -> int: ...
|
|
54
|
+
|
|
55
|
+
@overload
|
|
56
|
+
def mod(self, a: Array) -> Array: ...
|
|
57
|
+
|
|
58
|
+
def mod(self, a: int | Array) -> int | Array:
|
|
59
|
+
"""_summary_
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
a : int | Array
|
|
64
|
+
_description_
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
int | Array
|
|
69
|
+
_description_
|
|
70
|
+
"""
|
|
71
|
+
return mod(a, self.q)
|
|
72
|
+
|
|
73
|
+
@overload
|
|
74
|
+
def pow(self, a: int, r: int) -> int: ...
|
|
75
|
+
|
|
76
|
+
@overload
|
|
77
|
+
def pow(self, a: Array, r: int) -> Array: ...
|
|
78
|
+
|
|
79
|
+
def pow(self, a: int | Array, r: int) -> int | Array:
|
|
80
|
+
"""_summary_
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
a : int | Array
|
|
85
|
+
_description_
|
|
86
|
+
r : int
|
|
87
|
+
_description_
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
int | Array
|
|
92
|
+
_description_
|
|
93
|
+
"""
|
|
94
|
+
return self.mod(modpow(a, r, self.q))
|
|
95
|
+
|
|
96
|
+
@overload
|
|
97
|
+
def inv(self, a: int) -> int: ...
|
|
98
|
+
|
|
99
|
+
@overload
|
|
100
|
+
def inv(self, a: Array) -> Array: ...
|
|
101
|
+
|
|
102
|
+
def inv(self, a: int | Array) -> int | Array:
|
|
103
|
+
"""_summary_
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
a : int | Array
|
|
108
|
+
_description_
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
int | Array
|
|
113
|
+
_description_
|
|
114
|
+
"""
|
|
115
|
+
return self.mod(modinv(a, self.q))
|
|
116
|
+
|
|
117
|
+
@overload
|
|
118
|
+
def neg(self, a: int) -> int: ...
|
|
119
|
+
|
|
120
|
+
@overload
|
|
121
|
+
def neg(self, a: Array) -> Array: ...
|
|
122
|
+
|
|
123
|
+
def neg(self, a: int | Array) -> int | Array:
|
|
124
|
+
"""_summary_
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
a : int | Array
|
|
129
|
+
_description_
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
int | Array
|
|
134
|
+
_description_
|
|
135
|
+
"""
|
|
136
|
+
return self.mod(-a)
|
|
137
|
+
|
|
138
|
+
def add(self, a: int, b: int) -> int:
|
|
139
|
+
"""_summary_
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
a : int
|
|
144
|
+
_description_
|
|
145
|
+
b : int
|
|
146
|
+
_description_
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
int
|
|
151
|
+
_description_
|
|
152
|
+
"""
|
|
153
|
+
return self.mod(a + b)
|
|
154
|
+
|
|
155
|
+
def mul(self, a: int, b: int) -> int:
|
|
156
|
+
"""_summary_
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
a : int
|
|
161
|
+
_description_
|
|
162
|
+
b : int
|
|
163
|
+
_description_
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
int
|
|
168
|
+
_description_
|
|
169
|
+
"""
|
|
170
|
+
return self.mod(a * b)
|
|
171
|
+
|
|
172
|
+
def div(self, a: int, b: int) -> int:
|
|
173
|
+
"""_summary_
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
a : int
|
|
178
|
+
_description_
|
|
179
|
+
b : int
|
|
180
|
+
_description_
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
int
|
|
185
|
+
_description_
|
|
186
|
+
"""
|
|
187
|
+
return self.mul(a, self.inv(b))
|
|
188
|
+
|
|
189
|
+
def sub(self, a: int, b: int) -> int:
|
|
190
|
+
"""_summary_
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
a : int
|
|
195
|
+
_description_
|
|
196
|
+
b : int
|
|
197
|
+
_description_
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
int
|
|
202
|
+
_description_
|
|
203
|
+
"""
|
|
204
|
+
return self.mod(a - b)
|
|
205
|
+
|
|
206
|
+
@overload
|
|
207
|
+
def cmodl(self, a: int) -> int: ...
|
|
208
|
+
|
|
209
|
+
@overload
|
|
210
|
+
def cmodl(self, a: Array) -> Array: ...
|
|
211
|
+
|
|
212
|
+
def cmodl(self, a: int | Array) -> int | Array:
|
|
213
|
+
"""_summary_
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
a : int | Array
|
|
218
|
+
_description_
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
int | Array
|
|
223
|
+
_description_
|
|
224
|
+
"""
|
|
225
|
+
return cmodl(a, self.q)
|
|
226
|
+
|
|
227
|
+
@overload
|
|
228
|
+
def cmodr(self, a: int) -> int: ...
|
|
229
|
+
|
|
230
|
+
@overload
|
|
231
|
+
def cmodr(self, a: Array) -> Array: ...
|
|
232
|
+
|
|
233
|
+
def cmodr(self, a: int | Array) -> int | Array:
|
|
234
|
+
"""_summary_
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
a : int | Array
|
|
239
|
+
_description_
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
int | Array
|
|
244
|
+
_description_
|
|
245
|
+
"""
|
|
246
|
+
return cmodr(a, self.q)
|