/// 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());
        });
    };
}