Coverage for src\pqlattice\polynomial\_modpolyring.py: 61%
66 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-07 03:12 +0100
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-07 03:12 +0100
1from ..integer._modring import mod, modinv
2from ..typing import Vector, validate_aliases
3from . import _poly as poly
6class ModIntPolyRing:
7 def __init__(self, modulus: int):
8 """_summary_
10 Parameters
11 ----------
12 modulus : int
13 _description_
15 Raises
16 ------
17 ValueError
18 _description_
19 """
20 if modulus <= 1:
21 raise ValueError("Modulus has to be greater than 1")
23 self.modulus = modulus
25 @validate_aliases
26 def reduce(self, polynomial: Vector) -> Vector:
27 """_summary_
29 Parameters
30 ----------
31 polynomial : Vector
32 _description_
34 Returns
35 -------
36 Vector
37 _description_
38 """
39 return poly.trim(mod(polynomial, self.modulus))
41 @validate_aliases
42 def is_zero(self, polynomial: Vector) -> bool:
43 """_summary_
45 Parameters
46 ----------
47 polynomial : Vector
48 _description_
50 Returns
51 -------
52 bool
53 _description_
54 """
55 return poly.is_zero_poly(self.reduce(polynomial))
57 @validate_aliases
58 def deg(self, polynomial: Vector) -> int:
59 """_summary_
61 Parameters
62 ----------
63 polynomial : Vector
64 _description_
66 Returns
67 -------
68 int
69 _description_
70 """
71 return poly.deg(self.reduce(polynomial))
73 @validate_aliases
74 def add(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
75 """_summary_
77 Parameters
78 ----------
79 polynomial_a : Vector
80 _description_
81 polynomial_b : Vector
82 _description_
84 Returns
85 -------
86 Vector
87 _description_
88 """
89 return self.reduce(poly.add(polynomial_a, polynomial_b))
91 @validate_aliases
92 def sub(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
93 """_summary_
95 Parameters
96 ----------
97 polynomial_a : Vector
98 _description_
99 polynomial_b : Vector
100 _description_
102 Returns
103 -------
104 Vector
105 _description_
106 """
107 return self.reduce(poly.sub(polynomial_a, polynomial_b))
109 @validate_aliases
110 def mul(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
111 """_summary_
113 Parameters
114 ----------
115 polynomial_a : Vector
116 _description_
117 polynomial_b : Vector
118 _description_
120 Returns
121 -------
122 Vector
123 _description_
124 """
125 return self.reduce(poly.mul(polynomial_a, polynomial_b))
127 @validate_aliases
128 def euclidean_div(self, polynomial_a: Vector, polynomial_b: Vector) -> tuple[Vector, Vector]:
129 """_summary_
131 Parameters
132 ----------
133 polynomial_a : Vector
134 _description_
135 polynomial_b : Vector
136 _description_
138 Returns
139 -------
140 tuple[Vector, Vector]
141 _description_
143 Raises
144 ------
145 ZeroDivisionError
146 _description_
147 """
148 if self.is_zero(polynomial_b):
149 raise ZeroDivisionError("Can't divide by zero polynomial")
151 q = poly.zero_poly()
152 r = self.reduce(polynomial_a)
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))
161 return q, r
163 @validate_aliases
164 def rem(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
165 """_summary_
167 Parameters
168 ----------
169 polynomial_a : Vector
170 _description_
171 polynomial_b : Vector
172 _description_
174 Returns
175 -------
176 Vector
177 _description_
178 """
179 _, r = self.euclidean_div(polynomial_a, polynomial_b)
180 return r
182 def to_monic(self, polynomial: Vector) -> Vector:
183 """_summary_
185 Parameters
186 ----------
187 polynomial : Vector
188 _description_
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)
198 def gcd(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
199 """_summary_
201 Parameters
202 ----------
203 polynomial_a : Vector
204 _description_
205 polynomial_b : Vector
206 _description_
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
218 while not self.is_zero(r1):
219 r0, r1 = r1, self.rem(r0, r1)
221 return r0
223 def eea(self, polynomial_a: Vector, polynomial_b: Vector):
224 """_summary_
226 Parameters
227 ----------
228 polynomial_a : Vector
229 _description_
230 polynomial_b : Vector
231 _description_
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)
242 while not self.is_zero(f1):
243 q, r = self.euclidean_div(f0, f1)
245 f0, f1 = f1, r
247 a0, a1 = a1, self.sub(a0, self.mul(q, a1))
248 b0, b1 = b1, self.sub(b0, self.mul(q, b1))
250 return f0, a0, b0
252 def coprime(self, polynomial_a: Vector, polynomial_b: Vector) -> bool:
253 """_summary_
255 Parameters
256 ----------
257 polynomial_a : Vector
258 _description_
259 polynomial_b : Vector
260 _description_
262 Returns
263 -------
264 bool
265 _description_
266 """
267 return all(self.to_monic(self.gcd(polynomial_a, polynomial_b)) == poly.monomial(1, 0))