83 lines
3.3 KiB
Rust
83 lines
3.3 KiB
Rust
/// Squaring of large numbers.
|
|
pub trait Square<Output> {
|
|
fn square(&self) -> Output;
|
|
}
|
|
|
|
macro_rules! square_impls {
|
|
($name: ident, $bigger: ident, $size: expr) => {
|
|
impl Square<$bigger> for $name {
|
|
fn square(&self) -> $bigger {
|
|
let mut w = [0; $size/32];
|
|
let t = $size / 64;
|
|
|
|
for i in 0..t {
|
|
let x128 = self.value[i] as u128;
|
|
let mut uvb = (w[2*i] as u128) + (x128 * x128);
|
|
w[2*i] = uvb & 0xFFFFFFFFFFFFFFFF;
|
|
let mut c = uvb >> 64;
|
|
for j in (i+1)..t {
|
|
let xj128 = self.value[j] as u128;
|
|
let xi128 = self.value[i] as u128;
|
|
// this first product is safely 128 bits or less,
|
|
// because the input arguments are both 64 bits.
|
|
let xij128 = xj128 * xi128;
|
|
// this next bit may overflow, but will do so by exactly
|
|
// one bit.
|
|
let twoxij128 = xij128 << 1;
|
|
let carried_shl = (xij128 & (1 << 127)) != 0;
|
|
// this next bit may *also* overflow, but should also do
|
|
// so by no more than one bit.
|
|
let (new,carry1) = twoxij128.overflowing_add(c);
|
|
// ditto ...
|
|
let wij = w[i+j];
|
|
let (uvb2,carry2) = new.overflowing_add(wij as u128);
|
|
// for the value we're going to save for this digit, we
|
|
// only care about the low bits, so we can forget about
|
|
// the carry stuff.
|
|
w[i+j] = uvb2 & 0xFFFFFFFFFFFFFFFF;
|
|
// for c, though, we do care about the carries, above.
|
|
// Fortunately, they were both by only one bit, so we
|
|
// should be able to just back-fix them.
|
|
c = uvb2 >> 64;
|
|
if carried_shl { c += 1 << 64; }
|
|
if carry1 { c += 1 << 64; }
|
|
if carry2 { c += 1 << 64; }
|
|
}
|
|
w[i+t] = c;
|
|
}
|
|
let mut res = $bigger::zero();
|
|
for i in 0..w.len() { res.value[i] = w[i] as u64; }
|
|
res
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
macro_rules! generate_square_tests {
|
|
($name: ident, $lname: ident, $dbl: ident) => {
|
|
#[test]
|
|
fn $lname() {
|
|
generate_square_tests!(body $name, $lname, $dbl);
|
|
}
|
|
};
|
|
(ignore $name: ident, $lname: ident, $dbl: ident) => {
|
|
#[test]
|
|
#[ignore]
|
|
fn $lname() {
|
|
generate_square_tests!(body $name, $lname, $dbl);
|
|
}
|
|
};
|
|
(body $name: ident, $lname: ident, $dbl: ident) => {
|
|
let fname = build_test_path("square", stringify!($name));
|
|
run_test(fname.to_string(), 2, |case| {
|
|
let (neg0, abytes) = case.get("a").unwrap();
|
|
let (neg1, rbytes) = case.get("r").unwrap();
|
|
assert!(!neg0 && !neg1);
|
|
|
|
let a = $name::from_bytes(abytes);
|
|
let r = $dbl::from_bytes(rbytes);
|
|
assert_eq!(r, a.square());
|
|
});
|
|
};
|
|
} |