Coverage for src\pqlattice\polynomial\_modpolyqring.py: 74%

1 from ..integer._modring import modinv

2 from ..typing import Vector, validate_aliases

3 from ._modpolyring import ModIntPolyRing

4 from ._poly import zero_poly

5

6

7 class ModIntPolyQuotientRing:

8 @validate_aliases

9 def __init__(self, poly_modulus: Vector, int_modulus: int) -> None:

10 """_summary_

11

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)

22

23 @property

24 def quotient(self) -> Vector:

25 """_summary_

26

27 Returns

28 -------

29 Vector

30 _description_

31 """

32 return self.poly_modulus

33

34 @validate_aliases

35 def reduce(self, polynomial: Vector) -> Vector:

36 """_summary_

37

38 Parameters

39 ----------

40 polynomial : Vector

41 _description_

42

43 Returns

44 -------

45 Vector

46 _description_

47 """

48 return self.Zm.rem(polynomial, self.poly_modulus)

49

50 @validate_aliases

51 def add(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:

52 """_summary_

53

54 Parameters

55 ----------

56 polynomial_a : Vector

57 _description_

58 polynomial_b : Vector

59 _description_

60

61 Returns

62 -------

63 Vector

64 _description_

65 """

66 return self.reduce(self.Zm.add(polynomial_a, polynomial_b))

67

68 @validate_aliases

69 def sub(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:

70 """_summary_

71

72 Parameters

73 ----------

74 polynomial_a : Vector

75 _description_

76 polynomial_b : Vector

77 _description_

78

79 Returns

80 -------

81 Vector

82 _description_

83 """

84 return self.reduce(self.Zm.sub(polynomial_a, polynomial_b))

85

86 @validate_aliases

87 def mul(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:

88 """_summary_

89

90 Parameters

91 ----------

92 polynomial_a : Vector

93 _description_

94 polynomial_b : Vector

95 _description_

96

97 Returns

98 -------

99 Vector

100 _description_

101 """

102 return self.reduce(self.Zm.mul(polynomial_a, polynomial_b))

103

104 @validate_aliases

105 def inv(self, polynomial: Vector) -> Vector:

106 """_summary_

107

108 Parameters

109 ----------

110 polynomial : Vector

111 _description_

112

113 Returns

114 -------

115 Vector

116 _description_

117

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")

125

126 gcd, u, _ = self.Zm.eea(polynomial, self.poly_modulus)

127

128 c = modinv(gcd, self.int_modulus)

129

130 return self.reduce(u * c)

131

132

133 def construct_ring(p: str, N: int, q: int) -> ModIntPolyQuotientRing:

134 """_summary_

135

136 Parameters

137 ----------

138 p : str

139 _description_

140 N : int

141 _description_

142 q : int

143 _description_

144

145 Returns

146 -------

147 ModIntPolyQuotientRing

148 _description_

149

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}")

167

168 return ModIntPolyQuotientRing(g, q)