@@ -103,10 +103,10 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
103103}
104104
105105/// Computes the sum of all the powers of \\(x\\) \\(S(n) = (x^0 + \dots + x^{n-1})\\)
106- /// using \\(O(\lg n)\\) multiplications and additions . Length \\(n\\) is not considered secret
107- /// and algorithm is fastest when \\(n\\) is the power of two.
106+ /// using \\(O(\lg n)\\) multiplications. Length \\(n\\) is not considered secret
107+ /// and algorithm is fastest when \\(n\\) is the power of two (\\(2\lg n + 1\\) multiplications) .
108108///
109- /// ### Algorithm overview
109+ /// ### Algorithm description
110110///
111111/// First, let \\(n\\) be a power of two.
112112/// Then, we can divide the polynomial in two halves like so:
@@ -127,9 +127,26 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
127127/// s_i &= s_{i-1} + x^{2^{i-1}} s_{i-1}
128128/// \end{aligned}
129129/// \\]
130- /// This representation allows us to square \\(x\\) only \\(\lg n\\) times.
130+ /// This representation allows us to do only \\(2 \cdot \lg n\\) multiplications:
131+ /// squaring \\(x\\) and multiplying it by \\(s_{i-1}\\) at each iteration.
131132///
132- /// Lets apply this to \\(n\\) which is not a power of two (\\(2^{k-1} < n < 2^k\\)) which can be represented in binary using
133+ /// Lets apply this to \\(n\\) which is not a power of two. The intuition behind the generalized
134+ /// algorithm is to combine all intermediate power-of-two-degree polynomials corresponding to the
135+ /// bits of \\(n\\) that are equal to 1.
136+ ///
137+ /// 1. Represent \\(n\\) in binary.
138+ /// 2. For each bit which is set (from the lowest to the highest):
139+ /// 1. Compute a corresponding power-of-two-degree polynomial using the above algorithm.
140+ /// Since we can reuse all intermediate polynomials, this adds no overhead to computing
141+ /// a polynomial for the highest bit.
142+ /// 2. Multiply the polynomial by the next power of \\(x\\), relative to the degree of the
143+ /// already computed result. This effectively _offsets_ the polynomial to a correct range of
144+ /// powers, so it can be added directly with the rest.
145+ /// The next power of \\(x\\) is computed along all the intermediate polynomials,
146+ /// by multiplying it by power-of-two power of \\(x\\) computed in step 2.1.
147+ /// 3. Add to the result.
148+ ///
149+ /// (\\(2^{k-1} < n < 2^k\\)) which can be represented in binary using
133150/// bits \\(b_i\\) in \\(\\{0,1\\}\\):
134151/// \\[
135152/// n = b_0 2^0 + \dots + b_{k-1} 2^{k-1}
@@ -151,16 +168,16 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
151168/// \\]
152169pub fn sum_of_powers ( x : & Scalar , mut n : usize ) -> Scalar {
153170 let mut result = Scalar :: zero ( ) ;
154- let mut f = Scalar :: one ( ) ; // power of x to offset subsequent polynomials based on lower bits of n.
155- let mut s = Scalar :: one ( ) ; // power-of-two polynomial: 1, 1+x, 1+x+x^2+x^3, ...
156- let mut p = * x; // x , x^2, x^4, ..., x^{2^i}
171+ let mut f = Scalar :: one ( ) ; // next- power-of- x to offset subsequent polynomials based on preceding bits of n.
172+ let mut s = Scalar :: one ( ) ; // power-of-two polynomials: ( 1, 1+x, 1+x+x^2+x^3, 1+ ...+x^7, , 1+...+x^15, ...)
173+ let mut p = * x; // power-of-two powers of x: (x , x^2, x^4, ..., x^{2^i})
157174 while n > 0 {
158175 // take a bit from n
159176 let bit = n & 1 ;
160177 n = n >> 1 ;
161178
162179 if bit == 1 {
163- // bits of `n` are not secret, so it's okay to be vartime because of `n` value .
180+ // `n` is not secret, so it's okay to be vartime on bits of `n`.
164181 result += f * s;
165182 if n > 0 { // avoid multiplication if no bits left
166183 f = f * p;
0 commit comments