From 9c4ea7ae26a0d148014cd7ef0412325ba38fc7de Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 23 Mar 2018 22:12:34 -0700 Subject: [PATCH] Start working variable-length numbers. --- src/cryptonum/conversions.rs | 29 ++++++ src/cryptonum/mod.rs | 165 +++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/cryptonum/conversions.rs create mode 100644 src/cryptonum/mod.rs diff --git a/src/cryptonum/conversions.rs b/src/cryptonum/conversions.rs new file mode 100644 index 0000000..ea579c8 --- /dev/null +++ b/src/cryptonum/conversions.rs @@ -0,0 +1,29 @@ +macro_rules! define_from +{ + ($type: ident, $base: ident) => { + impl From<$base> for $type { + fn from(x: $base) -> $type { + if x == 0 { + UCN{ contents: Vec::new() } + } else { + UCN{ contents: vec![x as u64] } + } + } + } + } +} + +macro_rules! define_into +{ + ($type: ident, $base: ident) => { + impl Into<$base> for $type { + fn into(self) -> $base { + if self.contents.is_empty() { + 0 + } else { + self.contents[0] as $base + } + } + } + } +} diff --git a/src/cryptonum/mod.rs b/src/cryptonum/mod.rs new file mode 100644 index 0000000..389e7b9 --- /dev/null +++ b/src/cryptonum/mod.rs @@ -0,0 +1,165 @@ +#[macro_use] +mod conversions; + +use num::{BigUint,ToPrimitive,Zero}; +use std::cmp::Ordering; + +/// In case you were wondering, it stands for "Unsigned Crypto Num". +#[derive(Clone,Debug,PartialEq,Eq)] +pub struct UCN { + contents: Vec +} + +//------------------------------------------------------------------------------ +// +// Conversions to/from crypto nums. +// +//------------------------------------------------------------------------------ + +define_from!(UCN, u8); +define_from!(UCN, u16); +define_from!(UCN, u32); +define_from!(UCN, u64); +define_into!(UCN, u8); +define_into!(UCN, u16); +define_into!(UCN, u32); +define_into!(UCN, u64); + +impl From for UCN { + fn from(mut x: BigUint) -> UCN { + let mut dest = Vec::new(); + let mask = BigUint::from(0xFFFFFFFFFFFFFFFF as u64); + + while !x.is_zero() { + match (&x & &mask).to_u64() { + None => + panic!("Can't use BigUint in From"), + Some(val) => + dest.push(val) + } + x >>= 64; + } + + UCN{ contents: dest } + } +} + +impl Into for UCN { + fn into(self) -> BigUint { + let mut result = BigUint::zero(); + + for part in self.contents.iter().rev() { + result <<= 64; + result += BigUint::from(*part); + } + + result + } +} + +//------------------------------------------------------------------------------ +// +// Comparisons +// +//------------------------------------------------------------------------------ + +impl PartialOrd for UCN { + fn partial_cmp(&self, other: &UCN) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for UCN { + fn cmp(&self, other: &UCN) -> Ordering { + let mut iter_left = self.contents.iter(); + let mut iter_right = other.contents.iter(); + + loop { + match (iter_left.next(), iter_right.next()) { + (None, None) => return Ordering::Equal, + (Some(_), None) => return Ordering::Greater, + (None, Some(_)) => return Ordering::Less, + (Some(x), Some(y)) => + match x.cmp(y) { + Ordering::Equal => continue, + result => return result + } + } + } + } +} + + +//------------------------------------------------------------------------------ +// +// Tests! +// +//------------------------------------------------------------------------------ + +#[cfg(test)] +mod test { + use quickcheck::{Arbitrary,Gen}; + use super::*; + + #[test] + #[allow(overflowing_literals)] + fn test_builders() { + assert_eq!(UCN{ contents: vec![] }, + UCN::from(0 as u8)); + assert_eq!(UCN{ contents: vec![0x7F] }, + UCN::from(0x7F as u8)); + assert_eq!(UCN{ contents: vec![0x7F7F] }, + UCN::from(0x7F7F as u16)); + assert_eq!(UCN{ contents: vec![0xCA5CADE5] }, + UCN::from(0xCA5CADE5 as u32)); + assert_eq!(UCN{ contents: vec![0xFFFFFFFFFFFFFFFF] }, + UCN::from(0xFFFFFFFFFFFFFFFF as u64)); + assert_eq!(UCN{ contents: vec![0x00000000FFFFFFFF] }, + UCN::from(0xFFFFFFFFFFFFFFFF as u32)); + } + + quickcheck! { + fn builder_u8_upgrade_u16(x: u8) -> bool { + UCN::from(x) == UCN::from(x as u16) + } + fn builder_u16_upgrade_u32(x: u16) -> bool { + UCN::from(x) == UCN::from(x as u32) + } + fn builder_u32_upgrade_u64(x: u32) -> bool { + UCN::from(x) == UCN::from(x as u64) + } + fn builder_u8_roundtrips(x: u8) -> bool { + let thereback: u8 = UCN::from(x).into(); + x == thereback + } + fn builder_u16_roundtrips(x: u16) -> bool { + let thereback: u16 = UCN::from(x).into(); + x == thereback + } + fn builder_u32_roundtrips(x: u32) -> bool { + let thereback: u32 = UCN::from(x).into(); + x == thereback + } + fn builder_u64_roundtrips(x: u64) -> bool { + let thereback: u64 = UCN::from(x).into(); + x == thereback + } + } + + quickcheck! { + fn u64_comparison_sane(x: u64, y: u64) -> bool { + let ucnx = UCN::from(x); + let ucny = UCN::from(y); + ucnx.cmp(&ucny) == x.cmp(&y) + } + fn longer_is_greater(x: u64, y: u64) -> bool { + if x == 0 { + true + } else { + let ucnx = UCN{ contents: vec![x, 1] }; + let ucny = UCN::from(y); + ucnx.cmp(&ucny) == Ordering::Greater + } + } + } +}