@@ -66,14 +66,22 @@ impl Zeroize for FieldElement2625 {
6666 }
6767}
6868
69- impl < ' b > AddAssign < & ' b FieldElement2625 > for FieldElement2625 {
70- fn add_assign ( & mut self , _rhs : & ' b FieldElement2625 ) {
71- for i in 0 ..10 {
69+ impl FieldElement2625 {
70+ pub ( crate ) const fn const_add_assign ( & mut self , _rhs : & FieldElement2625 ) -> FieldElement2625 {
71+ let mut i = 0 ;
72+ while i < 10 {
7273 self . 0 [ i] += _rhs. 0 [ i] ;
74+ i += 1 ;
7375 }
7476 }
7577}
7678
79+ impl < ' b > AddAssign < & ' b FieldElement2625 > for FieldElement2625 {
80+ fn add_assign ( & mut self , _rhs : & ' b FieldElement2625 ) {
81+ self . const_add_assign ( _rhs)
82+ }
83+ }
84+
7785impl < ' a , ' b > Add < & ' b FieldElement2625 > for & ' a FieldElement2625 {
7886 type Output = FieldElement2625 ;
7987 fn add ( self , _rhs : & ' b FieldElement2625 ) -> FieldElement2625 {
@@ -121,109 +129,115 @@ impl<'b> MulAssign<&'b FieldElement2625> for FieldElement2625 {
121129 }
122130}
123131
132+ impl FieldElement2625 {
133+ #[ rustfmt:: skip] // keep alignment of z* calculations
134+ pub ( crate ) const fn const_mul ( & self , _rhs : & FieldElement2625 ) -> FieldElement2625 {
135+ /// Helper function to multiply two 32-bit integers with 64 bits
136+ /// of output.
137+ #[ inline( always) ]
138+ const fn m ( x : u32 , y : u32 ) -> u64 {
139+ ( x as u64 ) * ( y as u64 )
140+ }
141+
142+ // Alias self, _rhs for more readable formulas
143+ let x: & [ u32 ; 10 ] = & self . 0 ;
144+ let y: & [ u32 ; 10 ] = & _rhs. 0 ;
145+
146+ // We assume that the input limbs x[i], y[i] are bounded by:
147+ //
148+ // x[i], y[i] < 2^(26 + b) if i even
149+ // x[i], y[i] < 2^(25 + b) if i odd
150+ //
151+ // where b is a (real) parameter representing the excess bits of
152+ // the limbs. We track the bitsizes of all variables through
153+ // the computation and solve at the end for the allowable
154+ // headroom bitsize b (which determines how many additions we
155+ // can perform between reductions or multiplications).
156+
157+ let y1_19 = 19 * y[ 1 ] ; // This fits in a u32
158+ let y2_19 = 19 * y[ 2 ] ; // iff 26 + b + lg(19) < 32
159+ let y3_19 = 19 * y[ 3 ] ; // if b < 32 - 26 - 4.248 = 1.752
160+ let y4_19 = 19 * y[ 4 ] ;
161+ let y5_19 = 19 * y[ 5 ] ; // below, b<2.5: this is a bottleneck,
162+ let y6_19 = 19 * y[ 6 ] ; // could be avoided by promoting to
163+ let y7_19 = 19 * y[ 7 ] ; // u64 here instead of in m()
164+ let y8_19 = 19 * y[ 8 ] ;
165+ let y9_19 = 19 * y[ 9 ] ;
166+
167+ // What happens when we multiply x[i] with y[j] and place the
168+ // result into the (i+j)-th limb?
169+ //
170+ // x[i] represents the value x[i]*2^ceil(i*51/2)
171+ // y[j] represents the value y[j]*2^ceil(j*51/2)
172+ // z[i+j] represents the value z[i+j]*2^ceil((i+j)*51/2)
173+ // x[i]*y[j] represents the value x[i]*y[i]*2^(ceil(i*51/2)+ceil(j*51/2))
174+ //
175+ // Since the radix is already accounted for, the result placed
176+ // into the (i+j)-th limb should be
177+ //
178+ // x[i]*y[i]*2^(ceil(i*51/2)+ceil(j*51/2) - ceil((i+j)*51/2)).
179+ //
180+ // The value of ceil(i*51/2)+ceil(j*51/2) - ceil((i+j)*51/2) is
181+ // 1 when both i and j are odd, and 0 otherwise. So we add
182+ //
183+ // x[i]*y[j] if either i or j is even
184+ // 2*x[i]*y[j] if i and j are both odd
185+ //
186+ // by using precomputed multiples of x[i] for odd i:
187+
188+ let x1_2 = 2 * x[ 1 ] ; // This fits in a u32 iff 25 + b + 1 < 32
189+ let x3_2 = 2 * x[ 3 ] ; // iff b < 6
190+ let x5_2 = 2 * x[ 5 ] ;
191+ let x7_2 = 2 * x[ 7 ] ;
192+ let x9_2 = 2 * x[ 9 ] ;
193+
194+ let z0 = m ( x[ 0 ] , y[ 0 ] ) + m ( x1_2, y9_19) + m ( x[ 2 ] , y8_19) + m ( x3_2, y7_19) + m ( x[ 4 ] , y6_19) + m ( x5_2, y5_19) + m ( x[ 6 ] , y4_19) + m ( x7_2, y3_19) + m ( x[ 8 ] , y2_19) + m ( x9_2, y1_19) ;
195+ let z1 = m ( x[ 0 ] , y[ 1 ] ) + m ( x[ 1 ] , y[ 0 ] ) + m ( x[ 2 ] , y9_19) + m ( x[ 3 ] , y8_19) + m ( x[ 4 ] , y7_19) + m ( x[ 5 ] , y6_19) + m ( x[ 6 ] , y5_19) + m ( x[ 7 ] , y4_19) + m ( x[ 8 ] , y3_19) + m ( x[ 9 ] , y2_19) ;
196+ let z2 = m ( x[ 0 ] , y[ 2 ] ) + m ( x1_2, y[ 1 ] ) + m ( x[ 2 ] , y[ 0 ] ) + m ( x3_2, y9_19) + m ( x[ 4 ] , y8_19) + m ( x5_2, y7_19) + m ( x[ 6 ] , y6_19) + m ( x7_2, y5_19) + m ( x[ 8 ] , y4_19) + m ( x9_2, y3_19) ;
197+ let z3 = m ( x[ 0 ] , y[ 3 ] ) + m ( x[ 1 ] , y[ 2 ] ) + m ( x[ 2 ] , y[ 1 ] ) + m ( x[ 3 ] , y[ 0 ] ) + m ( x[ 4 ] , y9_19) + m ( x[ 5 ] , y8_19) + m ( x[ 6 ] , y7_19) + m ( x[ 7 ] , y6_19) + m ( x[ 8 ] , y5_19) + m ( x[ 9 ] , y4_19) ;
198+ let z4 = m ( x[ 0 ] , y[ 4 ] ) + m ( x1_2, y[ 3 ] ) + m ( x[ 2 ] , y[ 2 ] ) + m ( x3_2, y[ 1 ] ) + m ( x[ 4 ] , y[ 0 ] ) + m ( x5_2, y9_19) + m ( x[ 6 ] , y8_19) + m ( x7_2, y7_19) + m ( x[ 8 ] , y6_19) + m ( x9_2, y5_19) ;
199+ let z5 = m ( x[ 0 ] , y[ 5 ] ) + m ( x[ 1 ] , y[ 4 ] ) + m ( x[ 2 ] , y[ 3 ] ) + m ( x[ 3 ] , y[ 2 ] ) + m ( x[ 4 ] , y[ 1 ] ) + m ( x[ 5 ] , y[ 0 ] ) + m ( x[ 6 ] , y9_19) + m ( x[ 7 ] , y8_19) + m ( x[ 8 ] , y7_19) + m ( x[ 9 ] , y6_19) ;
200+ let z6 = m ( x[ 0 ] , y[ 6 ] ) + m ( x1_2, y[ 5 ] ) + m ( x[ 2 ] , y[ 4 ] ) + m ( x3_2, y[ 3 ] ) + m ( x[ 4 ] , y[ 2 ] ) + m ( x5_2, y[ 1 ] ) + m ( x[ 6 ] , y[ 0 ] ) + m ( x7_2, y9_19) + m ( x[ 8 ] , y8_19) + m ( x9_2, y7_19) ;
201+ let z7 = m ( x[ 0 ] , y[ 7 ] ) + m ( x[ 1 ] , y[ 6 ] ) + m ( x[ 2 ] , y[ 5 ] ) + m ( x[ 3 ] , y[ 4 ] ) + m ( x[ 4 ] , y[ 3 ] ) + m ( x[ 5 ] , y[ 2 ] ) + m ( x[ 6 ] , y[ 1 ] ) + m ( x[ 7 ] , y[ 0 ] ) + m ( x[ 8 ] , y9_19) + m ( x[ 9 ] , y8_19) ;
202+ let z8 = m ( x[ 0 ] , y[ 8 ] ) + m ( x1_2, y[ 7 ] ) + m ( x[ 2 ] , y[ 6 ] ) + m ( x3_2, y[ 5 ] ) + m ( x[ 4 ] , y[ 4 ] ) + m ( x5_2, y[ 3 ] ) + m ( x[ 6 ] , y[ 2 ] ) + m ( x7_2, y[ 1 ] ) + m ( x[ 8 ] , y[ 0 ] ) + m ( x9_2, y9_19) ;
203+ let z9 = m ( x[ 0 ] , y[ 9 ] ) + m ( x[ 1 ] , y[ 8 ] ) + m ( x[ 2 ] , y[ 7 ] ) + m ( x[ 3 ] , y[ 6 ] ) + m ( x[ 4 ] , y[ 5 ] ) + m ( x[ 5 ] , y[ 4 ] ) + m ( x[ 6 ] , y[ 3 ] ) + m ( x[ 7 ] , y[ 2 ] ) + m ( x[ 8 ] , y[ 1 ] ) + m ( x[ 9 ] , y[ 0 ] ) ;
204+
205+ // How big is the contribution to z[i+j] from x[i], y[j]?
206+ //
207+ // Using the bounds above, we get:
208+ //
209+ // i even, j even: x[i]*y[j] < 2^(26+b)*2^(26+b) = 2*2^(51+2*b)
210+ // i odd, j even: x[i]*y[j] < 2^(25+b)*2^(26+b) = 1*2^(51+2*b)
211+ // i even, j odd: x[i]*y[j] < 2^(26+b)*2^(25+b) = 1*2^(51+2*b)
212+ // i odd, j odd: 2*x[i]*y[j] < 2*2^(25+b)*2^(25+b) = 1*2^(51+2*b)
213+ //
214+ // We perform inline reduction mod p by replacing 2^255 by 19
215+ // (since 2^255 - 19 = 0 mod p). This adds a factor of 19, so
216+ // we get the bounds (z0 is the biggest one, but calculated for
217+ // posterity here in case finer estimation is needed later):
218+ //
219+ // z0 < ( 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 249*2^(51 + 2*b)
220+ // z1 < ( 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 154*2^(51 + 2*b)
221+ // z2 < ( 2 + 1 + 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 195*2^(51 + 2*b)
222+ // z3 < ( 1 + 1 + 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 118*2^(51 + 2*b)
223+ // z4 < ( 2 + 1 + 2 + 1 + 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 141*2^(51 + 2*b)
224+ // z5 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 82*2^(51 + 2*b)
225+ // z6 < ( 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 87*2^(51 + 2*b)
226+ // z7 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1*19 + 1*19 )*2^(51 + 2b) = 46*2^(51 + 2*b)
227+ // z6 < ( 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1*19 )*2^(51 + 2b) = 33*2^(51 + 2*b)
228+ // z7 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 )*2^(51 + 2b) = 10*2^(51 + 2*b)
229+ //
230+ // So z[0] fits into a u64 if 51 + 2*b + lg(249) < 64
231+ // if b < 2.5.
232+ FieldElement2625 :: reduce ( [ z0, z1, z2, z3, z4, z5, z6, z7, z8, z9] )
233+ }
234+ }
235+
124236impl < ' a , ' b > Mul < & ' b FieldElement2625 > for & ' a FieldElement2625 {
125237 type Output = FieldElement2625 ;
126238
127- #[ rustfmt:: skip] // keep alignment of z* calculations
128239 fn mul ( self , _rhs : & ' b FieldElement2625 ) -> FieldElement2625 {
129- /// Helper function to multiply two 32-bit integers with 64 bits
130- /// of output.
131- #[ inline( always) ]
132- fn m ( x : u32 , y : u32 ) -> u64 {
133- ( x as u64 ) * ( y as u64 )
134- }
135-
136- // Alias self, _rhs for more readable formulas
137- let x: & [ u32 ; 10 ] = & self . 0 ;
138- let y: & [ u32 ; 10 ] = & _rhs. 0 ;
139-
140- // We assume that the input limbs x[i], y[i] are bounded by:
141- //
142- // x[i], y[i] < 2^(26 + b) if i even
143- // x[i], y[i] < 2^(25 + b) if i odd
144- //
145- // where b is a (real) parameter representing the excess bits of
146- // the limbs. We track the bitsizes of all variables through
147- // the computation and solve at the end for the allowable
148- // headroom bitsize b (which determines how many additions we
149- // can perform between reductions or multiplications).
150-
151- let y1_19 = 19 * y[ 1 ] ; // This fits in a u32
152- let y2_19 = 19 * y[ 2 ] ; // iff 26 + b + lg(19) < 32
153- let y3_19 = 19 * y[ 3 ] ; // if b < 32 - 26 - 4.248 = 1.752
154- let y4_19 = 19 * y[ 4 ] ;
155- let y5_19 = 19 * y[ 5 ] ; // below, b<2.5: this is a bottleneck,
156- let y6_19 = 19 * y[ 6 ] ; // could be avoided by promoting to
157- let y7_19 = 19 * y[ 7 ] ; // u64 here instead of in m()
158- let y8_19 = 19 * y[ 8 ] ;
159- let y9_19 = 19 * y[ 9 ] ;
160-
161- // What happens when we multiply x[i] with y[j] and place the
162- // result into the (i+j)-th limb?
163- //
164- // x[i] represents the value x[i]*2^ceil(i*51/2)
165- // y[j] represents the value y[j]*2^ceil(j*51/2)
166- // z[i+j] represents the value z[i+j]*2^ceil((i+j)*51/2)
167- // x[i]*y[j] represents the value x[i]*y[i]*2^(ceil(i*51/2)+ceil(j*51/2))
168- //
169- // Since the radix is already accounted for, the result placed
170- // into the (i+j)-th limb should be
171- //
172- // x[i]*y[i]*2^(ceil(i*51/2)+ceil(j*51/2) - ceil((i+j)*51/2)).
173- //
174- // The value of ceil(i*51/2)+ceil(j*51/2) - ceil((i+j)*51/2) is
175- // 1 when both i and j are odd, and 0 otherwise. So we add
176- //
177- // x[i]*y[j] if either i or j is even
178- // 2*x[i]*y[j] if i and j are both odd
179- //
180- // by using precomputed multiples of x[i] for odd i:
181-
182- let x1_2 = 2 * x[ 1 ] ; // This fits in a u32 iff 25 + b + 1 < 32
183- let x3_2 = 2 * x[ 3 ] ; // iff b < 6
184- let x5_2 = 2 * x[ 5 ] ;
185- let x7_2 = 2 * x[ 7 ] ;
186- let x9_2 = 2 * x[ 9 ] ;
187-
188- let z0 = m ( x[ 0 ] , y[ 0 ] ) + m ( x1_2, y9_19) + m ( x[ 2 ] , y8_19) + m ( x3_2, y7_19) + m ( x[ 4 ] , y6_19) + m ( x5_2, y5_19) + m ( x[ 6 ] , y4_19) + m ( x7_2, y3_19) + m ( x[ 8 ] , y2_19) + m ( x9_2, y1_19) ;
189- let z1 = m ( x[ 0 ] , y[ 1 ] ) + m ( x[ 1 ] , y[ 0 ] ) + m ( x[ 2 ] , y9_19) + m ( x[ 3 ] , y8_19) + m ( x[ 4 ] , y7_19) + m ( x[ 5 ] , y6_19) + m ( x[ 6 ] , y5_19) + m ( x[ 7 ] , y4_19) + m ( x[ 8 ] , y3_19) + m ( x[ 9 ] , y2_19) ;
190- let z2 = m ( x[ 0 ] , y[ 2 ] ) + m ( x1_2, y[ 1 ] ) + m ( x[ 2 ] , y[ 0 ] ) + m ( x3_2, y9_19) + m ( x[ 4 ] , y8_19) + m ( x5_2, y7_19) + m ( x[ 6 ] , y6_19) + m ( x7_2, y5_19) + m ( x[ 8 ] , y4_19) + m ( x9_2, y3_19) ;
191- let z3 = m ( x[ 0 ] , y[ 3 ] ) + m ( x[ 1 ] , y[ 2 ] ) + m ( x[ 2 ] , y[ 1 ] ) + m ( x[ 3 ] , y[ 0 ] ) + m ( x[ 4 ] , y9_19) + m ( x[ 5 ] , y8_19) + m ( x[ 6 ] , y7_19) + m ( x[ 7 ] , y6_19) + m ( x[ 8 ] , y5_19) + m ( x[ 9 ] , y4_19) ;
192- let z4 = m ( x[ 0 ] , y[ 4 ] ) + m ( x1_2, y[ 3 ] ) + m ( x[ 2 ] , y[ 2 ] ) + m ( x3_2, y[ 1 ] ) + m ( x[ 4 ] , y[ 0 ] ) + m ( x5_2, y9_19) + m ( x[ 6 ] , y8_19) + m ( x7_2, y7_19) + m ( x[ 8 ] , y6_19) + m ( x9_2, y5_19) ;
193- let z5 = m ( x[ 0 ] , y[ 5 ] ) + m ( x[ 1 ] , y[ 4 ] ) + m ( x[ 2 ] , y[ 3 ] ) + m ( x[ 3 ] , y[ 2 ] ) + m ( x[ 4 ] , y[ 1 ] ) + m ( x[ 5 ] , y[ 0 ] ) + m ( x[ 6 ] , y9_19) + m ( x[ 7 ] , y8_19) + m ( x[ 8 ] , y7_19) + m ( x[ 9 ] , y6_19) ;
194- let z6 = m ( x[ 0 ] , y[ 6 ] ) + m ( x1_2, y[ 5 ] ) + m ( x[ 2 ] , y[ 4 ] ) + m ( x3_2, y[ 3 ] ) + m ( x[ 4 ] , y[ 2 ] ) + m ( x5_2, y[ 1 ] ) + m ( x[ 6 ] , y[ 0 ] ) + m ( x7_2, y9_19) + m ( x[ 8 ] , y8_19) + m ( x9_2, y7_19) ;
195- let z7 = m ( x[ 0 ] , y[ 7 ] ) + m ( x[ 1 ] , y[ 6 ] ) + m ( x[ 2 ] , y[ 5 ] ) + m ( x[ 3 ] , y[ 4 ] ) + m ( x[ 4 ] , y[ 3 ] ) + m ( x[ 5 ] , y[ 2 ] ) + m ( x[ 6 ] , y[ 1 ] ) + m ( x[ 7 ] , y[ 0 ] ) + m ( x[ 8 ] , y9_19) + m ( x[ 9 ] , y8_19) ;
196- let z8 = m ( x[ 0 ] , y[ 8 ] ) + m ( x1_2, y[ 7 ] ) + m ( x[ 2 ] , y[ 6 ] ) + m ( x3_2, y[ 5 ] ) + m ( x[ 4 ] , y[ 4 ] ) + m ( x5_2, y[ 3 ] ) + m ( x[ 6 ] , y[ 2 ] ) + m ( x7_2, y[ 1 ] ) + m ( x[ 8 ] , y[ 0 ] ) + m ( x9_2, y9_19) ;
197- let z9 = m ( x[ 0 ] , y[ 9 ] ) + m ( x[ 1 ] , y[ 8 ] ) + m ( x[ 2 ] , y[ 7 ] ) + m ( x[ 3 ] , y[ 6 ] ) + m ( x[ 4 ] , y[ 5 ] ) + m ( x[ 5 ] , y[ 4 ] ) + m ( x[ 6 ] , y[ 3 ] ) + m ( x[ 7 ] , y[ 2 ] ) + m ( x[ 8 ] , y[ 1 ] ) + m ( x[ 9 ] , y[ 0 ] ) ;
198-
199- // How big is the contribution to z[i+j] from x[i], y[j]?
200- //
201- // Using the bounds above, we get:
202- //
203- // i even, j even: x[i]*y[j] < 2^(26+b)*2^(26+b) = 2*2^(51+2*b)
204- // i odd, j even: x[i]*y[j] < 2^(25+b)*2^(26+b) = 1*2^(51+2*b)
205- // i even, j odd: x[i]*y[j] < 2^(26+b)*2^(25+b) = 1*2^(51+2*b)
206- // i odd, j odd: 2*x[i]*y[j] < 2*2^(25+b)*2^(25+b) = 1*2^(51+2*b)
207- //
208- // We perform inline reduction mod p by replacing 2^255 by 19
209- // (since 2^255 - 19 = 0 mod p). This adds a factor of 19, so
210- // we get the bounds (z0 is the biggest one, but calculated for
211- // posterity here in case finer estimation is needed later):
212- //
213- // z0 < ( 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 249*2^(51 + 2*b)
214- // z1 < ( 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 154*2^(51 + 2*b)
215- // z2 < ( 2 + 1 + 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 195*2^(51 + 2*b)
216- // z3 < ( 1 + 1 + 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 118*2^(51 + 2*b)
217- // z4 < ( 2 + 1 + 2 + 1 + 2 + 1*19 + 2*19 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 141*2^(51 + 2*b)
218- // z5 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1*19 + 1*19 + 1*19 + 1*19 )*2^(51 + 2b) = 82*2^(51 + 2*b)
219- // z6 < ( 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1*19 + 2*19 + 1*19 )*2^(51 + 2b) = 87*2^(51 + 2*b)
220- // z7 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1*19 + 1*19 )*2^(51 + 2b) = 46*2^(51 + 2*b)
221- // z6 < ( 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1*19 )*2^(51 + 2b) = 33*2^(51 + 2*b)
222- // z7 < ( 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 )*2^(51 + 2b) = 10*2^(51 + 2*b)
223- //
224- // So z[0] fits into a u64 if 51 + 2*b + lg(249) < 64
225- // if b < 2.5.
226- FieldElement2625 :: reduce ( [ z0, z1, z2, z3, z4, z5, z6, z7, z8, z9] )
240+ self . const_mul ( _rhs)
227241 }
228242}
229243
0 commit comments