From 667e32694e43f98c949805a3417972318fa25d03 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 11 Mar 2018 15:36:31 -0700 Subject: [PATCH] Start including both signed and unsigned numbers, and starting building in Signed traits. The latter seems much harder (and wordier) than it should be. --- src/cryptonum/builder.rs | 72 +++++++ src/cryptonum/mod.rs | 20 +- src/cryptonum/signed.rs | 431 ++++++++++++++++++++++++++++++++++++++ src/cryptonum/unsigned.rs | 14 ++ 4 files changed, 523 insertions(+), 14 deletions(-) create mode 100644 src/cryptonum/signed.rs create mode 100644 src/cryptonum/unsigned.rs diff --git a/src/cryptonum/builder.rs b/src/cryptonum/builder.rs index ff5a622..ecbaae2 100644 --- a/src/cryptonum/builder.rs +++ b/src/cryptonum/builder.rs @@ -992,3 +992,75 @@ macro_rules! opers3 { } } } + +macro_rules! math_operator { + ($cl: ident, $fn: ident, $asn: ident) => { + impl $cl for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign + MulAssign + DivAssign, + { + type Output = Signed; + + fn $fn(self, rhs: Signed) -> Signed + { + let mut res = self.clone(); + res.$asn(rhs); + res + } + } + + impl<'a,T> $cl<&'a Signed> for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign + MulAssign + DivAssign, + T: AddAssign<&'a T> + SubAssign<&'a T>, + T: MulAssign<&'a T> + DivAssign<&'a T> + { + type Output = Signed; + + fn $fn(self, rhs: &'a Signed) -> Signed + { + let mut res = self.clone(); + res.$asn(rhs); + res + } + } + + impl<'a,T> $cl for &'a Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign + MulAssign + DivAssign, + T: AddAssign<&'a T> + SubAssign<&'a T>, + T: MulAssign<&'a T> + DivAssign<&'a T> + { + type Output = Signed; + + fn $fn(self, rhs: &'a Signed) -> Signed + { + let mut res = self.clone(); + res.$asn(rhs); + res + } + } + + impl<'a,T> $cl> for &'a Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign + MulAssign + DivAssign, + T: AddAssign<&'a T> + SubAssign<&'a T>, + T: MulAssign<&'a T> + DivAssign<&'a T> + { + type Output = Signed; + + fn $fn(self, rhs: Signed) -> Signed + { + let mut res = self.clone(); + res.$asn(rhs); + res + } + } + } +} + + diff --git a/src/cryptonum/mod.rs b/src/cryptonum/mod.rs index 12ac9cb..5e3fd59 100644 --- a/src/cryptonum/mod.rs +++ b/src/cryptonum/mod.rs @@ -8,19 +8,11 @@ mod core; mod builder; //mod extended_math; // mod primes; +mod signed; mod traits; +mod unsigned; -use self::core::*; -use self::traits::*; -use std::cmp::Ordering; -use std::fmt::{Debug,Error,Formatter}; -use std::ops::*; - -construct_unsigned!(U512, BarretMu512, u512, 8); -construct_unsigned!(U1024, BarretMu1024, u1024, 16); -construct_unsigned!(U2048, BarretMu2048, u2048, 32); -construct_unsigned!(U3072, BarretMu3072, u3072, 48); -construct_unsigned!(U4096, BarretMu4096, u4096, 64); -construct_unsigned!(U7680, BarretMu7680, u7680, 120); -construct_unsigned!(U8192, BarretMu8192, u8192, 128); -construct_unsigned!(U15360, BarretMu15360, u15360, 240); +// pub use self::extended_math::{modexp,modinv,extended_euclidean,egcd}; +// pub use self::primes::{probably_prime}; +pub use self::signed::{Signed}; +pub use self::unsigned::{U512,U1024,U2048,U3072,U4096,U7680,U8192,U15360}; diff --git a/src/cryptonum/signed.rs b/src/cryptonum/signed.rs new file mode 100644 index 0000000..2c64b28 --- /dev/null +++ b/src/cryptonum/signed.rs @@ -0,0 +1,431 @@ +use cryptonum::traits::*; +use std::cmp::Ordering; +use std::fmt::{Debug,Error,Formatter}; +use std::ops::*; + +pub struct Signed { + positive: bool, + value: T +} + +impl Signed { + pub fn new(v: T) -> Signed { + Signed{ positive: true, value: v } + } + + pub fn abs(&self) -> T + where T: Clone + { + self.value.clone() + } + + pub fn is_positive(&self) -> bool + where T: CryptoNumBase + { + self.positive && !self.value.is_zero() + } + + pub fn is_negative(&self) -> bool + where T: CryptoNumBase + { + !self.positive && !self.value.is_zero() + } + + pub fn negate(&mut self) + { + self.positive = !self.positive; + } +} + +impl CryptoNumBase for Signed { + fn zero() -> Signed { + Signed{ positive: true, value: T::zero() } + } + fn max_value() -> Signed { + Signed{ positive: true, value: T::max_value() } + } + fn is_zero(&self) -> bool { + self.value.is_zero() + } + fn is_odd(&self) -> bool { + self.value.is_odd() + } + fn is_even(&self) -> bool { + self.value.is_even() + } + fn from_u8(x: u8) -> Signed { + Signed{ positive: true, value: T::from_u8(x) } + } + fn to_u8(&self) -> u8 { + self.value.to_u8() + } + fn from_u16(x: u16) -> Signed { + Signed{ positive: true, value: T::from_u16(x) } + } + fn to_u16(&self) -> u16 { + self.value.to_u16() + } + fn from_u32(x: u32) -> Signed { + Signed{ positive: true, value: T::from_u32(x) } + } + fn to_u32(&self) -> u32 { + self.value.to_u32() + } + fn from_u64(x: u64) -> Signed { + Signed{ positive: true, value: T::from_u64(x) } + } + fn to_u64(&self) -> u64 { + self.value.to_u64() + } +} + +impl CryptoNumFastMod for Signed { + type BarrettMu = T::BarrettMu; + + fn barrett_mu(&self) -> Option { + if self.positive { + self.value.barrett_mu() + } else { + None + } + } + + fn fastmod(&self, mu: &T::BarrettMu) -> Signed { + Signed{ positive: self.positive, value: self.value.fastmod(&mu) } + } +} + +impl Clone for Signed { + fn clone(&self) -> Signed { + Signed{ positive: self.positive, value: self.value.clone() } + } +} + +impl<'a,T: PartialEq> PartialEq<&'a Signed> for Signed { + fn eq(&self, other: &&Signed) -> bool { + (self.positive == other.positive) && (self.value == other.value) + } +} + +impl<'a,T: PartialEq> PartialEq> for &'a Signed { + fn eq(&self, other: &Signed) -> bool { + (self.positive == other.positive) && (self.value == other.value) + } +} + +impl PartialEq for Signed { + fn eq(&self, other: &Signed) -> bool { + (self.positive == other.positive) && (self.value == other.value) + } +} + +impl Eq for Signed {} + +impl Debug for Signed { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + if self.positive { + f.write_str("+")?; + } else { + f.write_str("-")?; + } + self.value.fmt(f) + } +} + +impl Ord for Signed { + fn cmp(&self, other: &Signed) -> Ordering { + match (self.positive, other.positive) { + (true, true) => self.value.cmp(&other.value), + (true, false) => Ordering::Greater, + (false, true) => Ordering::Less, + (false, false) => + self.value.cmp(&other.value).reverse() + } + } +} + +impl PartialOrd for Signed { + fn partial_cmp(&self, other: &Signed) -> Option{ + Some(self.cmp(other)) + } +} + +//------------------------------------------------------------------------------ + +impl Neg for Signed { + type Output = Signed; + + fn neg(self) -> Signed { + Signed { + positive: !self.positive, + value: self.value.clone() + } + } +} + +impl<'a,T: Clone> Neg for &'a Signed { + type Output = Signed; + + fn neg(self) -> Signed { + Signed { + positive: !self.positive, + value: self.value.clone() + } + } +} + +//------------------------------------------------------------------------------ + +impl AddAssign for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign, +{ + fn add_assign(&mut self, other: Signed) { + match (self.positive, other.positive, self.value.cmp(&other.value)) { + // if the signs are the same, we maintain the sign and just increase + // the magnitude + (x, y, _) if x == y => + self.value.add_assign(other.value), + // if the signs are different and the numbers are equal, we just set + // this to zero. However, we actually do the subtraction to make the + // timing roughly similar. + (_, _, Ordering::Equal) => { + self.positive = true; + self.value.sub_assign(other.value); + } + // if the signs are different and the first one is less than the + // second, then we flip the sign and subtract. + (_, _, Ordering::Less) => { + self.positive = !self.positive; + let temp = self.value.clone(); + self.value = other.value.clone(); + self.value.sub_assign(temp); + } + // if the signs are different and the first one is greater than the + // second, then we leave the sign and subtract. + (_, _, Ordering::Greater) => { + self.value.sub_assign(other.value); + } + } + } +} + +impl<'a,T> AddAssign<&'a Signed> for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign, + T: AddAssign<&'a T> + SubAssign<&'a T> +{ + fn add_assign(&mut self, other: &'a Signed) { + match (self.positive, other.positive, self.value.cmp(&other.value)) { + // if the signs are the same, we maintain the sign and just increase + // the magnitude + (x, y, _) if x == y => + self.value.add_assign(&other.value), + // if the signs are different and the numbers are equal, we just set + // this to zero. However, we actually do the subtraction to make the + // timing roughly similar. + (_, _, Ordering::Equal) => { + self.positive = true; + self.value.sub_assign(&other.value); + } + // if the signs are different and the first one is less than the + // second, then we flip the sign and subtract. + (_, _, Ordering::Less) => { + self.positive = !self.positive; + let temp = self.value.clone(); + self.value = other.value.clone(); + self.value.sub_assign(temp); + } + // if the signs are different and the first one is greater than the + // second, then we leave the sign and subtract. + (_, _, Ordering::Greater) => { + self.value.sub_assign(&other.value); + } + } + } +} + +math_operator!(Add,add,add_assign); + +//------------------------------------------------------------------------------ + +impl SubAssign for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign, +{ + fn sub_assign(&mut self, other: Signed) { + let mut other2 = other.clone(); + other2.positive = !other.positive; + self.add_assign(other2); + } +} + +impl<'a,T> SubAssign<&'a Signed> for Signed + where + T: Clone + Ord, + T: AddAssign + SubAssign, + T: AddAssign<&'a T> + SubAssign<&'a T> +{ + fn sub_assign(&mut self, other: &'a Signed) { + let mut other2 = other.clone(); + other2.positive = !other.positive; + self.add_assign(other2); + } +} + +math_operator!(Sub,sub,sub_assign); + +//------------------------------------------------------------------------------ + +impl MulAssign for Signed + where + T: MulAssign +{ + fn mul_assign(&mut self, other: Signed) { + self.positive = !(self.positive ^ other.positive); + self.value *= other.value; + } +} + +impl<'a,T> MulAssign<&'a Signed> for Signed + where + T: MulAssign + MulAssign<&'a T> +{ + fn mul_assign(&mut self, other: &'a Signed) { + self.positive = !(self.positive ^ other.positive); + self.value *= &other.value; + } +} + +math_operator!(Mul,mul,mul_assign); + +//------------------------------------------------------------------------------ + +impl DivAssign for Signed + where + T: DivAssign +{ + fn div_assign(&mut self, other: Signed) { + self.positive = !(self.positive ^ other.positive); + self.value /= other.value; + } +} + +impl<'a,T> DivAssign<&'a Signed> for Signed + where + T: DivAssign + DivAssign<&'a T> +{ + fn div_assign(&mut self, other: &'a Signed) { + self.positive = !(self.positive ^ other.positive); + self.value /= &other.value; + } +} + +math_operator!(Div,div,div_assign); + +//------------------------------------------------------------------------------ + +#[cfg(test)] +mod tests { + use cryptonum::unsigned::U512; + use quickcheck::{Arbitrary,Gen}; + use std::cmp::{max,min}; + use super::*; + + impl Arbitrary for Signed { + fn arbitrary(g: &mut G) -> Signed { + let value = T::arbitrary(g); + if value.is_zero() { + Signed { + positive: true, + value: value + } + } else { + Signed { + positive: g.gen_weighted_bool(2), + value: value + } + } + } + } + + quickcheck! { + fn double_negation(x: Signed) -> bool { + &x == (- (- &x)) + } + } + + quickcheck! { + fn add_associates(x: Signed, y: Signed, z: Signed) + -> bool + { + let mut a = x.clone(); + let mut b = y.clone(); + let mut c = z.clone(); + + // we shift these right because rollover makes for weird behavior + a.value >>= 2; + b.value >>= 2; + c.value >>= 2; + + (&a + (&b + &c)) == ((&a + &b) + &c) + } + fn add_commutes(x: Signed, y: Signed) -> bool { + (&x + &y) == (&y + &x) + } + fn add_identity(x: Signed) -> bool { + let zero = Signed{ positive: true, value: U512::zero() }; + (&x + &zero) == &x + } + } + + quickcheck! { + fn sub_is_add_negation(x: Signed, y: Signed) -> bool { + (&x - &y) == (&x + (- &y)) + } + } + + quickcheck! { + fn mul_associates(x: Signed, y: Signed, z: Signed) + -> bool + { + let mut a = x.clone(); + let mut b = y.clone(); + let mut c = z.clone(); + + // we shift these right because rollover makes for weird behavior + a.value >>= 258; + b.value >>= 258; + c.value >>= 258; + + (&a * (&b * &c)) == ((&a * &b) * &c) + } + fn mul_commutes(x: Signed, y: Signed) -> bool { + (&x * &y) == (&y * &x) + } + fn mul_identity(x: Signed) -> bool { + let one = Signed{ positive: true, value: U512::from_u8(1) }; + (&x * &one) == &x + } + } + + quickcheck! { + fn add_mul_distribution(x:Signed,y:Signed,z:Signed) + -> bool + { + let mut a = x.clone(); + let mut b = y.clone(); + let mut c = z.clone(); + + // we shift these right because rollover makes for weird behavior + a.value >>= 258; + b.value >>= 258; + c.value >>= 258; + + (&a * (&b + &c)) == ((&a * &b) + (&a * &c)) + } + } +} diff --git a/src/cryptonum/unsigned.rs b/src/cryptonum/unsigned.rs new file mode 100644 index 0000000..066e096 --- /dev/null +++ b/src/cryptonum/unsigned.rs @@ -0,0 +1,14 @@ +use cryptonum::core::*; +use cryptonum::traits::*; +use std::cmp::Ordering; +use std::fmt::{Debug,Error,Formatter}; +use std::ops::*; + +construct_unsigned!(U512, BarretMu512, u512, 8); +construct_unsigned!(U1024, BarretMu1024, u1024, 16); +construct_unsigned!(U2048, BarretMu2048, u2048, 32); +construct_unsigned!(U3072, BarretMu3072, u3072, 48); +construct_unsigned!(U4096, BarretMu4096, u4096, 64); +construct_unsigned!(U7680, BarretMu7680, u7680, 120); +construct_unsigned!(U8192, BarretMu8192, u8192, 128); +construct_unsigned!(U15360, BarretMu15360, u15360, 240);