Browse Source

utils: add faster Legendre Symbol

Lennart Braun 2 years ago
parent
commit
8f452f12a0
3 changed files with 76 additions and 55 deletions
  1. 1 0
      utils/Cargo.toml
  2. 19 4
      utils/benches/field.rs
  3. 56 51
      utils/src/field.rs

+ 1 - 0
utils/Cargo.toml

@@ -14,6 +14,7 @@ ff = { version = "0.13.0", features = ["derive"] }
 num = "0.4.0"
 rand = "0.8.5"
 rand_chacha = "0.3.1"
+rug = "1.19.0"
 
 [dev-dependencies]
 criterion = "0.4.0"

+ 19 - 4
utils/benches/field.rs

@@ -2,7 +2,7 @@ use bincode;
 use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
 use ff::Field;
 use rand::thread_rng;
-use utils::field::Fp;
+use utils::field::{legendre_symbol_exp, legendre_symbol_rug, Fp};
 
 const VEC_LENS: [usize; 4] = [1, 4, 16, 64];
 
@@ -35,8 +35,11 @@ pub fn bench_decode(c: &mut Criterion) {
                     bincode::encode_to_vec(&x, bincode::config::standard()).expect("encode failed");
                 b.iter(|| {
                     black_box(
-                        bincode::decode_from_slice::<Vec<Fp>, _>(&bytes, bincode::config::standard())
-                            .expect("decode failed"),
+                        bincode::decode_from_slice::<Vec<Fp>, _>(
+                            &bytes,
+                            bincode::config::standard(),
+                        )
+                        .expect("decode failed"),
                     )
                 });
             },
@@ -44,5 +47,17 @@ pub fn bench_decode(c: &mut Criterion) {
     }
 }
 
-criterion_group!(benches, bench_encode, bench_decode);
+pub fn bench_legendre_symbol(c: &mut Criterion) {
+    let mut g = c.benchmark_group("LegendreSymbol");
+    g.bench_function("exp", |b| {
+        let x = Fp::random(thread_rng());
+        b.iter(|| black_box(legendre_symbol_exp(x)));
+    });
+    g.bench_function("rug", |b| {
+        let x = Fp::random(thread_rng());
+        b.iter(|| black_box(legendre_symbol_rug(x)));
+    });
+}
+
+criterion_group!(benches, bench_encode, bench_decode, bench_legendre_symbol);
 criterion_main!(benches);

+ 56 - 51
utils/src/field.rs

@@ -4,6 +4,7 @@ use blake3;
 use ff::{Field, PrimeField};
 use num;
 use rand::{thread_rng, Rng};
+use rug;
 
 #[allow(non_upper_case_globals)]
 pub const p: u128 = 340282366920938462946865773367900766209;
@@ -61,7 +62,51 @@ pub trait LegendreSymbol: PrimeField {
     /// Return an arbitrary QNR.
     fn get_non_random_qnr() -> Self;
     /// Compute the Legendre Symbol (p/a)
-    fn legendre_symbol(a: Self) -> Self;
+    fn legendre_symbol(a: Self) -> i8;
+}
+
+pub fn legendre_symbol_exp(a: Fp) -> i8 {
+    // handle 65x even
+    let mut x = a;
+    for _ in 0..65 {
+        x = x.square();
+    }
+
+    // handle 1x odd
+    let mut y = x;
+    x = x.square();
+
+    // handle 2x even
+    x = x.square();
+    x = x.square();
+
+    // handle 59x odd
+    for _ in 0..58 {
+        y = x * y;
+        x = x.square();
+    }
+    let z = x * y;
+
+    debug_assert!(
+        (z == -Fp::ONE || z == Fp::ONE || z == Fp::ZERO) && (z != Fp::ZERO || a == Fp::ZERO)
+    );
+
+    if z == Fp::ONE {
+        1
+    } else if z == -Fp::ONE {
+        -1
+    } else if z == Fp::ZERO {
+        0
+    } else {
+        panic!("something went wrong during Legendre Symbol computation")
+    }
+}
+
+pub fn legendre_symbol_rug(a: Fp) -> i8 {
+    let bytes = a.to_le_bytes();
+    let a_int = rug::Integer::from_digits(&bytes, rug::integer::Order::LsfLe);
+    let p_int = rug::Integer::from(p);
+    a_int.legendre(&p_int) as i8
 }
 
 impl LegendreSymbol for Fp {
@@ -75,33 +120,8 @@ impl LegendreSymbol for Fp {
     }
 
     /// Compute the Legendre Symbol (p/a)
-    fn legendre_symbol(a: Self) -> Self {
-        // handle 65x even
-        let mut x = a;
-        for _ in 0..65 {
-            x = x.square();
-        }
-
-        // handle 1x odd
-        let mut y = x;
-        x = x.square();
-
-        // handle 2x even
-        x = x.square();
-        x = x.square();
-
-        // handle 59x odd
-        for _ in 0..58 {
-            y = x * y;
-            x = x.square();
-        }
-        let z = x * y;
-
-        assert!(
-            (z == -Fp::ONE || z == Fp::ONE || z == Fp::ZERO) && (z != Fp::ZERO || a == Fp::ZERO)
-        );
-
-        z
+    fn legendre_symbol(a: Self) -> i8 {
+        legendre_symbol_rug(a)
     }
 }
 
@@ -228,32 +248,17 @@ mod tests {
             315840344961299616831836711745928570660,
             340282366920938462946865773367900766208,
         ];
-        const OUTPUTS: [u128; 20] = [
-            0,
-            1,
-            1,
-            1,
-            1,
-            1,
-            340282366920938462946865773367900766208,
-            1,
-            1,
-            340282366920938462946865773367900766208,
-            1,
-            340282366920938462946865773367900766208,
-            1,
-            1,
-            1,
-            340282366920938462946865773367900766208,
-            340282366920938462946865773367900766208,
-            1,
-            1,
-            1,
+        const OUTPUTS: [i8; 20] = [
+            0, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1,
         ];
         for (&x, &y) in INPUTS.iter().zip(OUTPUTS.iter()) {
-            assert_eq!(Fp::legendre_symbol(Fp::from_u128(x)), Fp::from_u128(y));
+            assert_eq!(Fp::legendre_symbol(Fp::from_u128(x)), y);
+            assert_eq!(legendre_symbol_exp(Fp::from_u128(x)), y);
+            assert_eq!(legendre_symbol_rug(Fp::from_u128(x)), y);
         }
-        assert_eq!(Fp::legendre_symbol(Fp::get_non_random_qnr()), -Fp::ONE);
+        assert_eq!(Fp::legendre_symbol(Fp::get_non_random_qnr()), -1);
+        assert_eq!(legendre_symbol_exp(Fp::get_non_random_qnr()), -1);
+        assert_eq!(legendre_symbol_rug(Fp::get_non_random_qnr()), -1);
     }
 
     #[test]