@@ -4,6 +4,10 @@ pub use core::ops::{Add, AddAssign, Mul, Neg, Shr, ShrAssign, Sub, SubAssign};
4
4
5
5
use crypto_bigint:: Integer ;
6
6
use group:: Group ;
7
+ use subtle:: { Choice , ConditionallySelectable , CtOption } ;
8
+
9
+ #[ cfg( feature = "alloc" ) ]
10
+ use alloc:: vec:: Vec ;
7
11
8
12
/// Perform an inversion on a field element (i.e. base field element or scalar)
9
13
pub trait Invert {
@@ -25,6 +29,122 @@ pub trait Invert {
25
29
}
26
30
}
27
31
32
+ /// Perform a batched inversion on a sequence of field elements (i.e. base field elements or scalars)
33
+ /// at an amortized cost that should be practically as efficient as a single inversion.
34
+ pub trait BatchInvert < FieldElements > : Invert {
35
+ /// The output of batch inversion. A container of field elements.
36
+ type Output ;
37
+
38
+ /// Invert a batch of field elements.
39
+ fn batch_invert ( field_elements : FieldElements ) -> <Self as BatchInvert < FieldElements > >:: Output ;
40
+ }
41
+
42
+ impl < const N : usize , T > BatchInvert < & [ T ; N ] > for T
43
+ where
44
+ T : Invert < Output = CtOption < Self > >
45
+ + Mul < Self , Output = Self >
46
+ + Copy
47
+ + Default
48
+ + ConditionallySelectable ,
49
+ {
50
+ type Output = CtOption < [ Self ; N ] > ;
51
+
52
+ fn batch_invert ( field_elements : & [ Self ; N ] ) -> <Self as BatchInvert < & [ T ; N ] > >:: Output {
53
+ let mut field_elements_multiples = [ Self :: default ( ) ; N ] ;
54
+ let mut field_elements_multiples_inverses = [ Self :: default ( ) ; N ] ;
55
+ let mut field_elements_inverses = [ Self :: default ( ) ; N ] ;
56
+
57
+ let inversion_succeeded = invert_batch_internal (
58
+ field_elements,
59
+ & mut field_elements_multiples,
60
+ & mut field_elements_multiples_inverses,
61
+ & mut field_elements_inverses,
62
+ ) ;
63
+
64
+ CtOption :: new ( field_elements_inverses, inversion_succeeded)
65
+ }
66
+ }
67
+
68
+ #[ cfg( feature = "alloc" ) ]
69
+ impl < T > BatchInvert < & [ T ] > for T
70
+ where
71
+ T : Invert < Output = CtOption < Self > >
72
+ + Mul < Self , Output = Self >
73
+ + Copy
74
+ + Default
75
+ + ConditionallySelectable ,
76
+ {
77
+ type Output = CtOption < Vec < Self > > ;
78
+
79
+ fn batch_invert ( field_elements : & [ Self ] ) -> <Self as BatchInvert < & [ T ] > >:: Output {
80
+ let mut field_elements_multiples: Vec < Self > = vec ! [ Self :: default ( ) ; field_elements. len( ) ] ;
81
+ let mut field_elements_multiples_inverses: Vec < Self > =
82
+ vec ! [ Self :: default ( ) ; field_elements. len( ) ] ;
83
+ let mut field_elements_inverses: Vec < Self > = vec ! [ Self :: default ( ) ; field_elements. len( ) ] ;
84
+
85
+ let inversion_succeeded = invert_batch_internal (
86
+ field_elements,
87
+ field_elements_multiples. as_mut ( ) ,
88
+ field_elements_multiples_inverses. as_mut ( ) ,
89
+ field_elements_inverses. as_mut ( ) ,
90
+ ) ;
91
+
92
+ CtOption :: new (
93
+ field_elements_inverses. into_iter ( ) . collect ( ) ,
94
+ inversion_succeeded,
95
+ )
96
+ }
97
+ }
98
+
99
+ /// Implements "Montgomery's trick", a trick for computing many modular inverses at once.
100
+ ///
101
+ /// "Montgomery's trick" works by reducing the problem of computing `n` inverses
102
+ /// to computing a single inversion, plus some storage and `O(n)` extra multiplications.
103
+ ///
104
+ /// See: https://iacr.org/archive/pkc2004/29470042/29470042.pdf section 2.2.
105
+ fn invert_batch_internal <
106
+ T : Invert < Output = CtOption < T > > + Mul < T , Output = T > + Default + ConditionallySelectable ,
107
+ > (
108
+ field_elements : & [ T ] ,
109
+ field_elements_multiples : & mut [ T ] ,
110
+ field_elements_multiples_inverses : & mut [ T ] ,
111
+ field_elements_inverses : & mut [ T ] ,
112
+ ) -> Choice {
113
+ let batch_size = field_elements. len ( ) ;
114
+ if batch_size == 0
115
+ || batch_size != field_elements_multiples. len ( )
116
+ || batch_size != field_elements_multiples_inverses. len ( )
117
+ {
118
+ return Choice :: from ( 0 ) ;
119
+ }
120
+
121
+ field_elements_multiples[ 0 ] = field_elements[ 0 ] ;
122
+ for i in 1 ..batch_size {
123
+ // $ a_n = a_{n-1}*x_n $
124
+ field_elements_multiples[ i] = field_elements_multiples[ i - 1 ] * field_elements[ i] ;
125
+ }
126
+
127
+ field_elements_multiples[ batch_size - 1 ]
128
+ . invert ( )
129
+ . map ( |multiple_of_inverses_of_all_field_elements| {
130
+ field_elements_multiples_inverses[ batch_size - 1 ] =
131
+ multiple_of_inverses_of_all_field_elements;
132
+ for i in ( 1 ..batch_size) . rev ( ) {
133
+ // $ a_{n-1} = {a_n}^{-1}*x_n $
134
+ field_elements_multiples_inverses[ i - 1 ] =
135
+ field_elements_multiples_inverses[ i] * field_elements[ i] ;
136
+ }
137
+
138
+ field_elements_inverses[ 0 ] = field_elements_multiples_inverses[ 0 ] ;
139
+ for i in 1 ..batch_size {
140
+ // $ {x_n}^{-1} = a_{n}^{-1}*a_{n-1} $
141
+ field_elements_inverses[ i] =
142
+ field_elements_multiples_inverses[ i] * field_elements_multiples[ i - 1 ] ;
143
+ }
144
+ } )
145
+ . is_some ( )
146
+ }
147
+
28
148
/// Linear combination.
29
149
///
30
150
/// This trait enables crates to provide an optimized implementation of
0 commit comments