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

1from ..integer._modring import modinv 

2from ..typing import Vector, validate_aliases 

3from ._modpolyring import ModIntPolyRing 

4from ._poly import zero_poly 

5 

6 

7class 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 

133def 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)