Skip to main content

cupcake/integer_arith/
butterfly.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6#[cfg(test)]
7use super::{SuperTrait};
8
9use super::{ArithUtils};
10
11// (X, Y) -> (X+Y, W(X-Y)) mod q 
12#[allow(non_snake_case)]
13pub fn inverse_butterfly<T>(X: &mut T, Y: &mut T, W: &T, q: &T) where T: ArithUtils<T>{
14    let temp  = T::sub_mod(X,Y, q);
15    *X = T::add_mod(X, Y, q);
16    *Y = T::mul_mod(W, &temp, q);
17}
18
19// (X, Y) -> (X+WY, X-WY) mod q 
20#[allow(non_snake_case)]
21pub fn butterfly<T>(X: &mut T, Y: &mut T, W: &T, q: &T) where T: ArithUtils<T>{
22    let temp  = T::mul_mod(Y, W, q);
23    *Y = T::sub_mod(X, &temp, q);
24    *X = T::add_mod(X, &temp, q);
25}
26
27// (X, Y) -> (X+WY, X-WY)
28// 0 <= X, Y < 4q => (0 <= X', Y' < 4q)
29#[allow(non_snake_case)]
30#[cfg(test)]
31pub fn lazy_butterfly<T>(X: &mut T, Y: &mut T, W: u64, Wprime: u64, q: &T, twoq: u64) where T: SuperTrait<T>{
32    let mut xx = X.rep(); 
33    if xx > twoq{
34        xx -= twoq; 
35    }
36    let _qq = super::util::mul_high_word(Wprime, Y.rep());
37    let quo = W.wrapping_mul(Y.rep()) - _qq.wrapping_mul(q.rep());
38    // X += quo;
39    *X = T::from(xx + quo); 
40    // Y = (x + 2q - quo);
41    *Y = T::from(xx + twoq - quo); 
42}
43
44#[allow(clippy::many_single_char_names)]
45pub fn lazy_butterfly_u64(mut x: u64, y:u64, w: u64, wprime: u64, q: u64, twoq: u64) -> (u64, u64){
46    // let twoq = 0; 
47    if x > twoq{
48        x -= twoq; 
49    }
50    let _qq = super::util::mul_high_word(wprime, y);
51    let wy = w.wrapping_mul(y); 
52    let qqq = _qq.wrapping_mul(q); 
53    let quo; 
54    if wy >= qqq { 
55        quo = wy - qqq; 
56    } 
57    else{
58        quo = u64::MAX - qqq + wy + 1; 
59    }
60    (x + quo, x + twoq - quo)
61}
62
63#[allow(clippy::many_single_char_names)]
64pub fn lazy_inverse_butterfly_u64(x: u64, y:u64, w: u64, wprime: u64, q: u64, twoq: u64) -> (u64, u64){
65    let mut xx = x+y;
66    	
67    if xx > twoq {
68        xx -= twoq; 
69    }
70    let t = twoq - y + x; 
71    let quo = super::util::mul_high_word(wprime, t); 
72    let wt = w.wrapping_mul(t); 
73    let qquo = quo.wrapping_mul(q); 
74    let yy; 
75    if wt >= qquo { 
76        yy = wt - qquo; 
77    } 
78    else{
79        yy = u64::MAX - qquo + wt + 1; 
80    }
81    (xx, yy)
82}
83
84// (X,Y) -> (X+Y, W(X-Y)) mod q
85// 0 <= X, Y < 2q  ==> 0 <= X', Y' < 2q 
86#[allow(non_snake_case)]
87#[cfg(test)]
88pub(crate) fn lazy_inverse_butterfly<T>(X: &mut T, Y: &mut T, W: u64, Wprime: u64, q: &T) where T: SuperTrait<T>{
89    let mut xx = X.rep() + Y.rep(); 
90    	
91    let twoq = 2*q.rep();
92    if xx > twoq {
93        xx -= twoq; 
94    }
95    let t = twoq - Y.rep() + X.rep(); 
96    let quo = super::util::mul_high_word(Wprime, t); 
97    let yy = W.wrapping_mul(t) - quo.wrapping_mul(q.rep()); 
98    *X = T::from(xx); 
99    *Y = T::from(yy);
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use super::super::scalar::Scalar;
106
107    fn butterfly_for_test(arr: [u64;4]) -> [u64; 2] {
108      let mut X:Scalar = Scalar::from(arr[0]);
109      let mut Y:Scalar = Scalar::from(arr[1]);
110      let W:Scalar = Scalar::from(arr[2]);
111      let q:Scalar = Scalar::new_modulus(arr[3]);
112
113      butterfly(&mut X, &mut Y, &W, &q);
114      [X.into(), Y.into()]
115    }
116
117    fn inverse_butterfly_for_test(arr: [u64;4]) -> [u64; 2] {
118      let mut X:Scalar = Scalar::from(arr[0]);
119      let mut Y:Scalar = Scalar::from(arr[1]);
120      let W:Scalar = Scalar::from(arr[2]);
121      let q:Scalar = Scalar::new_modulus(arr[3]);
122
123      inverse_butterfly(&mut X, &mut Y, &W, &q);
124      [X.into(), Y.into()]
125    }
126
127    fn lazy_butterfly_for_test(arr: [u64;4]) -> [u64; 2] {
128      let mut X:Scalar = Scalar::from(arr[0]);
129      let mut Y:Scalar = Scalar::from(arr[1]);
130      let W = arr[2];
131      let q:Scalar = Scalar::new_modulus(arr[3]);
132      //  W′ = ⌊W β/p⌋, 0 < W′ < β
133      let Wprime: u64 = super::super::util::compute_harvey_ratio(W, q.rep());
134      let twoq = q.rep() << 1; 
135
136      lazy_butterfly(&mut X, &mut Y, W, Wprime, &q, twoq);
137      [X.into(), Y.into()]
138    }
139
140    fn lazy_inverse_butterfly_for_test(arr: [u64;4]) -> [u64; 2] {
141      let mut X:Scalar = Scalar::from(arr[0]);
142      let mut Y:Scalar = Scalar::from(arr[1]);
143      let W = arr[2];
144      let q:Scalar = Scalar::new_modulus(arr[3]);
145      //  W′ = ⌊W β/p⌋, 0 < W′ < β
146      let Wprime: u64 = super::super::util::compute_harvey_ratio(W, q.rep());
147
148      lazy_inverse_butterfly(&mut X, &mut Y, W, Wprime, &q);
149      [X.into(), Y.into()]
150    }
151
152    macro_rules! lazy_butterfly_tests {
153      ($($name:ident: $value:expr,)*) => {
154      $(
155          #[test]
156          fn $name() {
157              let input = $value;
158              let butterfly_out = butterfly_for_test(input); 
159              let output = lazy_butterfly_for_test(input);
160              println!("{:?}", butterfly_out); 
161              println!("{:?}", output); 
162              assert!(output[0] < 4*input[3]); 
163              assert!(output[1] < 4*input[3]); 
164              assert_eq!((output[1] - butterfly_out[1]) % input[3], 0); 
165              assert_eq!((output[0] - butterfly_out[0]) % input[3], 0); 
166          }
167        )*
168      }
169    }
170
171    macro_rules! lazy_inverse_butterfly_tests {
172      ($($name:ident: $value:expr,)*) => {
173      $(
174          #[test]
175          fn $name() {
176              let input = $value;
177              let butterfly_out = inverse_butterfly_for_test(input); 
178              let output = lazy_inverse_butterfly_for_test(input);
179              println!("{:?}", butterfly_out); 
180              println!("{:?}", output); 
181              assert!(output[0] < 2*input[3]); 
182              assert!(output[1] < 2*input[3]); 
183              assert_eq!((output[1] - butterfly_out[1]) % input[3], 0); 
184              assert_eq!((output[0] - butterfly_out[0]) % input[3], 0); 
185          }
186        )*
187      }
188    }
189
190    macro_rules! inverse_butterfly_tests {
191        ($($name:ident: $value:expr,)*) => {
192        $(
193            #[test]
194            fn $name() {
195                let (input, expected) = $value;
196                assert_eq!(expected, inverse_butterfly_for_test(input));
197            }
198          )*
199        }
200      }
201
202    macro_rules! butterfly_tests {
203      ($($name:ident: $value:expr,)*) => {
204      $(
205          #[test]
206          fn $name() {
207              let (input, expected) = $value;
208              assert_eq!(expected, butterfly_for_test(input));
209          }
210        )*
211      }
212    }
213
214  butterfly_tests! {
215    butterfly_0: ([0u64, 1u64, 0u64, 100u64], [0u64, 0u64]),
216    butterfly_1: ([1u64, 1u64, 1u64, 100u64], [2u64, 0u64]),
217    butterfly_2: ([50u64, 50u64, 1u64, 100u64], [0u64, 0u64]),
218    butterfly_3: ([1u64, 1u64, 50u64, 100u64], [51u64, 51u64]),
219  }
220
221  inverse_butterfly_tests! {
222    inverse_butterfly_0: ([0u64, 1u64, 0u64, 100u64], [1u64, 0u64]),
223    inverse_butterfly_1: ([1u64, 1u64, 1u64, 100u64], [2u64, 0u64]),
224    inverse_butterfly_2: ([50u64, 50u64, 1u64, 100u64], [0u64, 0u64]),
225    inverse_butterfly_3: ([2u64, 1u64, 50u64, 100u64], [3u64, 50u64]),
226  }
227
228  lazy_butterfly_tests! {
229    lazy_butterfly_0: ([0u64, 1u64, 0u64, 100u64]),
230    lazy_butterfly_1: ([1u64, 1u64, 1u64, 100u64]),
231    lazy_butterfly_2: ([50u64, 50u64, 1u64, 100u64]),
232    lazy_butterfly_3: ([1u64, 1u64, 50u64, 100u64]),
233  }
234
235  lazy_inverse_butterfly_tests! {
236    lazy_inverse_butterfly_0: ([0u64, 1u64, 0u64, 100u64]),
237    lazy_inverse_butterfly_1: ([1u64, 1u64, 1u64, 100u64]),
238    lazy_inverse_butterfly_2: ([50u64, 50u64, 1u64, 100u64]),
239    lazy_inverse_butterfly_3: ([1u64, 1u64, 50u64, 100u64]),
240  }
241}