1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use crate::Group;
use ark_ff::{BigInteger, PrimeField};
use ark_std::vec::Vec;

/// A helper type that contains all the context required for computing
/// a window NAF multiplication of a group element by a scalar.
pub struct WnafContext {
    pub window_size: usize,
}

impl WnafContext {
    /// Constructs a new context for a window of size `window_size`.
    ///
    /// # Panics
    ///
    /// This function will panic if not `2 <= window_size < 64`
    pub fn new(window_size: usize) -> Self {
        assert!(window_size >= 2);
        assert!(window_size < 64);
        Self { window_size }
    }

    pub fn table<G: Group>(&self, mut base: G) -> Vec<G> {
        let mut table = Vec::with_capacity(1 << (self.window_size - 1));
        let dbl = base.double();

        for _ in 0..(1 << (self.window_size - 1)) {
            table.push(base);
            base += &dbl;
        }
        table
    }

    /// Computes scalar multiplication of a group element `g` by `scalar`.
    ///
    /// This method uses the wNAF algorithm to perform the scalar
    /// multiplication; first, it uses `Self::table` to calculate an
    /// appropriate table of multiples of `g`, and then uses the wNAF
    /// algorithm to compute the scalar multiple.
    pub fn mul<G: Group>(&self, g: G, scalar: &G::ScalarField) -> G {
        let table = self.table(g);
        self.mul_with_table(&table, scalar).unwrap()
    }

    /// Computes scalar multiplication of a group element by `scalar`.
    /// `base_table` holds precomputed multiples of the group element; it can be
    /// generated using `Self::table`. `scalar` is an element of
    /// `G::ScalarField`.
    ///
    /// Returns `None` if the table is too small.
    pub fn mul_with_table<G: Group>(&self, base_table: &[G], scalar: &G::ScalarField) -> Option<G> {
        if 1 << (self.window_size - 1) > base_table.len() {
            return None;
        }
        let scalar_wnaf = scalar.into_bigint().find_wnaf(self.window_size).unwrap();

        let mut result = G::zero();

        let mut found_non_zero = false;

        for n in scalar_wnaf.iter().rev() {
            if found_non_zero {
                result.double_in_place();
            }

            if *n != 0 {
                found_non_zero = true;

                if *n > 0 {
                    result += &base_table[(n / 2) as usize];
                } else {
                    result -= &base_table[((-n) / 2) as usize];
                }
            }
        }

        Some(result)
    }
}