Coverage for src\pqlattice\polynomial\_modpolyqring.py: 74%
46 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 modinv
2from ..typing import Vector, validate_aliases
3from ._modpolyring import ModIntPolyRing
4from ._poly import zero_poly
7class ModIntPolyQuotientRing:
8 @validate_aliases
9 def __init__(self, poly_modulus: Vector, int_modulus: int) -> None:
10 """_summary_
12 Parameters
13 ----------
14 poly_modulus : Vector
15 _description_
16 int_modulus : int
17 _description_
18 """
19 self.poly_modulus = poly_modulus
20 self.int_modulus = int_modulus
21 self.Zm = ModIntPolyRing(int_modulus)
23 @property
24 def quotient(self) -> Vector:
25 """_summary_
27 Returns
28 -------
29 Vector
30 _description_
31 """
32 return self.poly_modulus
34 @validate_aliases
35 def reduce(self, polynomial: Vector) -> Vector:
36 """_summary_
38 Parameters
39 ----------
40 polynomial : Vector
41 _description_
43 Returns
44 -------
45 Vector
46 _description_
47 """
48 return self.Zm.rem(polynomial, self.poly_modulus)
50 @validate_aliases
51 def add(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
52 """_summary_
54 Parameters
55 ----------
56 polynomial_a : Vector
57 _description_
58 polynomial_b : Vector
59 _description_
61 Returns
62 -------
63 Vector
64 _description_
65 """
66 return self.reduce(self.Zm.add(polynomial_a, polynomial_b))
68 @validate_aliases
69 def sub(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
70 """_summary_
72 Parameters
73 ----------
74 polynomial_a : Vector
75 _description_
76 polynomial_b : Vector
77 _description_
79 Returns
80 -------
81 Vector
82 _description_
83 """
84 return self.reduce(self.Zm.sub(polynomial_a, polynomial_b))
86 @validate_aliases
87 def mul(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
88 """_summary_
90 Parameters
91 ----------
92 polynomial_a : Vector
93 _description_
94 polynomial_b : Vector
95 _description_
97 Returns
98 -------
99 Vector
100 _description_
101 """
102 return self.reduce(self.Zm.mul(polynomial_a, polynomial_b))
104 @validate_aliases
105 def inv(self, polynomial: Vector) -> Vector:
106 """_summary_
108 Parameters
109 ----------
110 polynomial : Vector
111 _description_
113 Returns
114 -------
115 Vector
116 _description_
118 Raises
119 ------
120 ValueError
121 _description_
122 """
123 if not self.Zm.coprime(polynomial, self.poly_modulus):
124 raise ValueError("Inverse does not exists")
126 gcd, u, _ = self.Zm.eea(polynomial, self.poly_modulus)
128 c = modinv(gcd, self.int_modulus)
130 return self.reduce(u * c)
133def construct_ring(p: str, N: int, q: int) -> ModIntPolyQuotientRing:
134 """_summary_
136 Parameters
137 ----------
138 p : str
139 _description_
140 N : int
141 _description_
142 q : int
143 _description_
145 Returns
146 -------
147 ModIntPolyQuotientRing
148 _description_
150 Raises
151 ------
152 ValueError
153 _description_
154 """
155 g = zero_poly(N)
156 match p:
157 case "-" | "X^N - 1":
158 g[[0, N]] = -1, 1
159 pass
160 case "+" | "X^N + 1":
161 g[[0, N]] = 1, 1
162 pass
163 case "prime" | "X^N - x - 1":
164 g[[0, 1, N]] = -1, -1, 1
165 case _:
166 raise ValueError(f"Unknown symbol: {p}")
168 return ModIntPolyQuotientRing(g, q)