Barrett reduction!
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
use cryptonum::{U192, U256, U384, U512, U576,
|
||||
U1024, U2048, U3072, U4096, U8192,
|
||||
U15360};
|
||||
use cryptonum::addition::raw_addition;
|
||||
use cryptonum::comparison::{bignum_cmp,bignum_ge};
|
||||
use cryptonum::division::divmod;
|
||||
use cryptonum::multiplication::raw_multiplication;
|
||||
use cryptonum::subtraction::raw_subtraction;
|
||||
use std::cmp::{Ordering,min};
|
||||
use std::fmt;
|
||||
|
||||
macro_rules! generate_barrett_implementations {
|
||||
($bname: ident, $name: ident, $size: expr) => {
|
||||
pub struct $bname {
|
||||
pub(crate) k: usize,
|
||||
pub(crate) m: [u64; $size/32],
|
||||
pub(crate) mu: [u64; $size/32]
|
||||
}
|
||||
|
||||
@@ -32,25 +39,86 @@ macro_rules! generate_barrett_implementations {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl $bname {
|
||||
fn new(m: &$name) -> $bname {
|
||||
pub fn new(m: &$name) -> $bname {
|
||||
let mut b = [0; ($size/32) + 1];
|
||||
let mut widerm = [0; ($size/32) + 1];
|
||||
let mut quot = [0; ($size/32) + 1];
|
||||
let mut remndr = [0; ($size/32) + 1];
|
||||
let mut result = $bname{ mu: [0; $size/32] };
|
||||
let mut result = $bname{ k: 0,
|
||||
m: [0; $size/32],
|
||||
mu: [0; $size/32] };
|
||||
|
||||
b[$size/32] = 1;
|
||||
for (idx, val) in m.values.iter().enumerate() { widerm[idx] = *val; }
|
||||
for (idx, val) in m.values.iter().enumerate() {
|
||||
let x = *val;
|
||||
widerm[idx] = x;
|
||||
result.m[idx] = x;
|
||||
if x != 0 { result.k = idx; }
|
||||
}
|
||||
result.k += 1;
|
||||
b[result.k*2] = 1;
|
||||
divmod(&b, &widerm, &mut quot, &mut remndr);
|
||||
for (idx, val) in result.mu.iter_mut().enumerate() { *val = quot[idx]; }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn reduce(&self, x: &mut $name) {
|
||||
printvar("x", &x.values);
|
||||
printvar("m", &self.m);
|
||||
printvar("u", &self.mu);
|
||||
// 1. q1←⌊x/bk−1⌋, q2←q1 · μ, q3←⌊q2/bk+1⌋.
|
||||
let mut q1 = [0; $size/32];
|
||||
shiftr(&x.values, self.k - 1, &mut q1);
|
||||
let mut q2 = [0; $size/16];
|
||||
raw_multiplication(&q1, &self.mu, &mut q2);
|
||||
let mut q3 = [0; $size/16];
|
||||
shiftr(&q2, self.k + 1,&mut q3);
|
||||
// 2. r1←x mod bk+1, r2←q3 · m mod bk+1, r←r1 − r2.
|
||||
let mut r = [0; $size/16];
|
||||
let copylen = min(self.k+1, x.values.len());
|
||||
for i in 0..copylen { r[i] = x.values[i]; }
|
||||
let mut r2big = [0; $size/8];
|
||||
let mut mwider = [0; $size/16];
|
||||
for i in 0..$size/32 { mwider[i] = self.m[i]; }
|
||||
raw_multiplication(&q3, &mwider, &mut r2big);
|
||||
let mut r2 = [0; $size/16];
|
||||
for i in 0..self.k+1 { r2[i] = r2big[i]; }
|
||||
let went_negative = !bignum_ge(&r, &r2);
|
||||
raw_subtraction(&mut r, &r2);
|
||||
// 3. If r<0 then r←r+bk+1.
|
||||
if went_negative {
|
||||
let mut bk1 = [0; $size/32];
|
||||
bk1[self.k+1] = 1;
|
||||
raw_addition(&mut r, &bk1);
|
||||
}
|
||||
// 4. While r≥m do: r←r−m.
|
||||
while bignum_cmp(&r, &mwider) == Ordering::Greater {
|
||||
raw_subtraction(&mut r, &mwider);
|
||||
}
|
||||
// Copy it over.
|
||||
for (idx, val) in x.values.iter_mut().enumerate() {
|
||||
*val = r[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn printvar(name: &'static str, val: &[u64]) {
|
||||
print!("{}: 0x", name);
|
||||
for x in val.iter().rev() {
|
||||
print!("{:016X}", *x);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
|
||||
fn shiftr(x: &[u64], amt: usize, dest: &mut [u64])
|
||||
{
|
||||
for i in amt..x.len() {
|
||||
dest[i-amt] = x[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
generate_barrett_implementations!(BarrettU192, U192, 192);
|
||||
generate_barrett_implementations!(BarrettU256, U256, 256);
|
||||
@@ -78,13 +146,19 @@ macro_rules! generate_tests {
|
||||
fn $name() {
|
||||
let fname = format!("tests/math/barrett_gen{}.test",
|
||||
stringify!($name));
|
||||
run_test(fname.to_string(), 2, |case| {
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (neg0, mbytes) = case.get("m").unwrap();
|
||||
let (neg1, ubytes) = case.get("u").unwrap();
|
||||
let (neg2, kbytes) = case.get("k").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1);
|
||||
assert!(!neg0 && !neg1 && !neg2);
|
||||
let m = $name::from_bytes(mbytes);
|
||||
let mut u = $bname{ mu: [0; $size/32]};
|
||||
let mut kbig = [0; 1];
|
||||
raw_decoder(&kbytes, &mut kbig);
|
||||
let mut u = $bname{ k: kbig[0] as usize,
|
||||
m: [0; $size/32],
|
||||
mu: [0; $size/32]};
|
||||
raw_decoder(&mbytes, &mut u.m);
|
||||
raw_decoder(&ubytes, &mut u.mu);
|
||||
let r = $bname::new(&m);
|
||||
assert_eq!(u,r);
|
||||
@@ -92,6 +166,42 @@ macro_rules! generate_tests {
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod reduction {
|
||||
use cryptonum::encoding::{Decoder,raw_decoder};
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
$(
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $name() {
|
||||
let fname = format!("tests/math/barrett_reduce{}.test",
|
||||
stringify!($name));
|
||||
run_test(fname.to_string(), 5, |case| {
|
||||
let (neg0, mbytes) = case.get("m").unwrap();
|
||||
let (neg1, ubytes) = case.get("u").unwrap();
|
||||
let (neg2, kbytes) = case.get("k").unwrap();
|
||||
let (neg3, xbytes) = case.get("x").unwrap();
|
||||
let (neg4, rbytes) = case.get("r").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4);
|
||||
let mut kbig = [0; 1];
|
||||
raw_decoder(&kbytes, &mut kbig);
|
||||
let mut u = $bname{ k: kbig[0] as usize,
|
||||
m: [0; $size/32],
|
||||
mu: [0; $size/32]};
|
||||
raw_decoder(&mbytes, &mut u.m);
|
||||
raw_decoder(&ubytes, &mut u.mu);
|
||||
let mut x = $name::from_bytes(&xbytes);
|
||||
let r = $name::from_bytes(&rbytes);
|
||||
u.reduce(&mut x);
|
||||
assert_eq!(x, r);
|
||||
});
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,4 +215,4 @@ generate_tests!((BarrettU192, U192, 192),
|
||||
(BarrettU3072, U3072, 3072),
|
||||
(BarrettU4096, U4096, 4096),
|
||||
(BarrettU8192, U8192, 8192),
|
||||
(BarrettU15360, U15360, 15360));
|
||||
(BarrettU15360, U15360, 15360));
|
||||
Reference in New Issue
Block a user