@@ -200,6 +200,87 @@ impl Fp6 {
200200 self . c0 . is_zero ( ) & self . c1 . is_zero ( ) & self . c2 . is_zero ( )
201201 }
202202
203+ /// Returns `c = self * b`.
204+ ///
205+ /// Implements the full-tower interleaving strategy from
206+ /// [ePrint 2022-376](https://eprint.iacr.org/2022/367).
207+ #[ inline]
208+ fn mul_interleaved ( & self , b : & Self ) -> Self {
209+ // The intuition for this algorithm is that we can look at F_p^6 as a direct
210+ // extension of F_p^2, and express the overall operations down to the base field
211+ // F_p instead of only over F_p^2. This enables us to interleave multiplications
212+ // and reductions, ensuring that we don't require double-width intermediate
213+ // representations (with around twice as many limbs as F_p elements).
214+
215+ // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is
216+ // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully
217+ // expanded multiplication is given by (2022-376 §5):
218+ //
219+ // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1
220+ // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0.
221+ // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1)
222+ // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1).
223+ //
224+ // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0
225+ // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1.
226+ // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1)
227+ // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1).
228+ //
229+ // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1
230+ // - a_2,0 b_2,1 - a_2,1 b_2,0.
231+ // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1)
232+ // - a_2,1(b_2,0 + b_2,1).
233+ //
234+ // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0
235+ // + a_2,0 b_2,0 - a_2,1 b_2,1
236+ // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1)
237+ // + a_2,1(b_2,0 - b_2,1).
238+ //
239+ // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1.
240+ // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0.
241+ //
242+ // Each of these is a "sum of products", which we can compute efficiently.
243+
244+ let a = self ;
245+ let b10_p_b11 = b. c1 . c0 + b. c1 . c1 ;
246+ let b10_m_b11 = b. c1 . c0 - b. c1 . c1 ;
247+ let b20_p_b21 = b. c2 . c0 + b. c2 . c1 ;
248+ let b20_m_b21 = b. c2 . c0 - b. c2 . c1 ;
249+
250+ Fp6 {
251+ c0 : Fp2 {
252+ c0 : Fp :: sum_of_products (
253+ [ a. c0 . c0 , -a. c0 . c1 , a. c1 . c0 , -a. c1 . c1 , a. c2 . c0 , -a. c2 . c1 ] ,
254+ [ b. c0 . c0 , b. c0 . c1 , b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11] ,
255+ ) ,
256+ c1 : Fp :: sum_of_products (
257+ [ a. c0 . c0 , a. c0 . c1 , a. c1 . c0 , a. c1 . c1 , a. c2 . c0 , a. c2 . c1 ] ,
258+ [ b. c0 . c1 , b. c0 . c0 , b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11] ,
259+ ) ,
260+ } ,
261+ c1 : Fp2 {
262+ c0 : Fp :: sum_of_products (
263+ [ a. c0 . c0 , -a. c0 . c1 , a. c1 . c0 , -a. c1 . c1 , a. c2 . c0 , -a. c2 . c1 ] ,
264+ [ b. c1 . c0 , b. c1 . c1 , b. c0 . c0 , b. c0 . c1 , b20_m_b21, b20_p_b21] ,
265+ ) ,
266+ c1 : Fp :: sum_of_products (
267+ [ a. c0 . c0 , a. c0 . c1 , a. c1 . c0 , a. c1 . c1 , a. c2 . c0 , a. c2 . c1 ] ,
268+ [ b. c1 . c1 , b. c1 . c0 , b. c0 . c1 , b. c0 . c0 , b20_p_b21, b20_m_b21] ,
269+ ) ,
270+ } ,
271+ c2 : Fp2 {
272+ c0 : Fp :: sum_of_products (
273+ [ a. c0 . c0 , -a. c0 . c1 , a. c1 . c0 , -a. c1 . c1 , a. c2 . c0 , -a. c2 . c1 ] ,
274+ [ b. c2 . c0 , b. c2 . c1 , b. c1 . c0 , b. c1 . c1 , b. c0 . c0 , b. c0 . c1 ] ,
275+ ) ,
276+ c1 : Fp :: sum_of_products (
277+ [ a. c0 . c0 , a. c0 . c1 , a. c1 . c0 , a. c1 . c1 , a. c2 . c0 , a. c2 . c1 ] ,
278+ [ b. c2 . c1 , b. c2 . c0 , b. c1 . c1 , b. c1 . c0 , b. c0 . c1 , b. c0 . c0 ] ,
279+ ) ,
280+ } ,
281+ }
282+ }
283+
203284 #[ inline]
204285 pub fn square ( & self ) -> Self {
205286 let s0 = self . c0 . square ( ) ;
@@ -244,38 +325,7 @@ impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
244325
245326 #[ inline]
246327 fn mul ( self , other : & ' b Fp6 ) -> Self :: Output {
247- let aa = self . c0 * other. c0 ;
248- let bb = self . c1 * other. c1 ;
249- let cc = self . c2 * other. c2 ;
250-
251- let t1 = other. c1 + other. c2 ;
252- let tmp = self . c1 + self . c2 ;
253- let t1 = t1 * tmp;
254- let t1 = t1 - bb;
255- let t1 = t1 - cc;
256- let t1 = t1. mul_by_nonresidue ( ) ;
257- let t1 = t1 + aa;
258-
259- let t3 = other. c0 + other. c2 ;
260- let tmp = self . c0 + self . c2 ;
261- let t3 = t3 * tmp;
262- let t3 = t3 - aa;
263- let t3 = t3 + bb;
264- let t3 = t3 - cc;
265-
266- let t2 = other. c0 + other. c1 ;
267- let tmp = self . c0 + self . c1 ;
268- let t2 = t2 * tmp;
269- let t2 = t2 - aa;
270- let t2 = t2 - bb;
271- let cc = cc. mul_by_nonresidue ( ) ;
272- let t2 = t2 + cc;
273-
274- Fp6 {
275- c0 : t1,
276- c1 : t2,
277- c2 : t3,
278- }
328+ self . mul_interleaved ( other)
279329 }
280330}
281331
0 commit comments