diff --git a/src/bigint.rs b/src/bigint.rs index 6ac5767..61f865f 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -1,57 +1,23 @@ +#![allow(unused_variables)] //! \[WIP\] Arbitrarily large integers -//! -//! Traits to implement: -//! * Neg -//! * Rem -//! * RemAssign -//! * Sub -//! * SubAssign use crate::num::*; use core::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Sub, SubAssign, }; +use core::usize; +use std::convert::TryInto; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct BigInt { inner: Vec, sign: Sign, } -macro_rules! impl_from_smaller { - ($($Type: ty),* ) => { - $( - impl From<$Type> for BigInt { - fn from(n: $Type) -> Self { - let mut new = Self::default(); - new.inner[0] = n as usize; - - new - } - } - )* - } -} - -#[cfg(target_pointer_width = "16")] -impl_from_smaller!(u8, u16); -#[cfg(target_pointer_width = "16")] -static BITS:usize = 16; - -#[cfg(target_pointer_width = "32")] -impl_from_smaller!(u8, u16, u32); -#[cfg(target_pointer_width = "32")] -static BITS:usize = 32; - -#[cfg(target_pointer_width = "64")] -impl_from_smaller!(u8, u16, u32, u64); -#[cfg(target_pointer_width = "64")] -static BITS:usize = 64; - impl Default for BigInt { fn default() -> Self { Self { inner: vec![0], - sign: Sign::Positive, + sign: Sign::default(), } } } @@ -60,7 +26,7 @@ impl From for BigInt { fn from(n: usize) -> Self { Self { inner: vec![n], - sign: Sign::Positive, + sign: Sign::default(), } } } @@ -78,14 +44,52 @@ impl From for BigInt { } impl BigInt { + /// Create a new Bigint, of value 0 + /// + /// The various `From` implementations are more useful in most cases pub fn new() -> Self { Self::default() } - pub fn shrink_to_fit(&mut self) { - todo!(); + fn new_empty() -> Self { + Self { + inner: Vec::new(), + sign: Sign::Positive, + } } + /// Remove digits that are zero from the internal representation. + /// + /// Similar to 007 -> 7 in base 10 + pub fn trim_zeros(&mut self) { + let current_len = self.inner.len(); + if current_len < 2 { + return + } + + let mut trailing_zeros = 0usize; + for val in self.inner.iter().rev() { + if *val != 0 { + break + } + + trailing_zeros += 1; + } + + let new_len = current_len - trailing_zeros; + + self.inner.truncate(new_len); + } + + /// Remove unused digits, and shrink the internal vector + pub fn shrink_to_fit(&mut self) { + self.trim_zeros(); + self.inner.shrink_to_fit(); + } + + /// Convert a `&str` or a `String` representing a number in the specified radix to a Bigint. + /// + /// For radix 10, use the `from` associated function instead. pub fn from_str_radix(s: T, radix: usize) -> BigInt { todo!(); } @@ -96,7 +100,7 @@ impl Add for BigInt { fn add(self, rhs: Self) -> Self::Output { // @TODO: handle signs - let mut out = BigInt::default(); + let mut out = BigInt::new_empty(); let u_digits = self.inner.len(); let v_digits = rhs.inner.len(); @@ -114,22 +118,14 @@ impl Add for BigInt { let (res, overflowed) = a.overflowing_add(b); if overflowed { - if i == 0 { - out.inner[i] = res + carry; - } else { - out.inner.push(res + carry); - } + out.inner.push(res + carry); carry = 1; - } else { - if res < core::usize::MAX { - out.inner.push(res + carry); - carry = 0; - } else { - out.inner.push(0usize); - carry = 1; - } - + } else if res < core::usize::MAX { + out.inner.push(res + carry); carry = 0; + } else { + out.inner.push(0usize); + carry = 1; } } @@ -141,7 +137,7 @@ impl Sub for BigInt { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - unimplemented!() + todo!() } } @@ -149,7 +145,7 @@ impl Mul for BigInt { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - unimplemented!() + todo!() } } @@ -157,7 +153,7 @@ impl Div for BigInt { type Output = Self; fn div(self, rhs: Self) -> Self::Output { - unimplemented!() + todo!() } } @@ -165,7 +161,7 @@ impl Rem for BigInt { type Output = Self; fn rem(self, rhs: Self) -> Self::Output { - unimplemented!() + todo!() } } @@ -202,6 +198,7 @@ impl RemAssign for BigInt { impl Neg for BigInt { type Output = Self; + /// Flip the sign of the current `BigInt` value fn neg(self) -> Self::Output { let mut output = self.clone(); output.sign = !output.sign; @@ -213,6 +210,7 @@ impl Neg for BigInt { impl Not for BigInt { type Output = Self; + /// Do a bitwise negation of every digit's value fn not(self) -> Self::Output { let mut flipped: Vec = Vec::with_capacity(self.inner.len()); @@ -228,6 +226,79 @@ impl Not for BigInt { } } +macro_rules! impl_from_smaller { + ($(($s: ty, $u: ty)),* ) => { + $( + impl From<$s> for BigInt { + /// Create a `BigInt` from a signed integer primitive + fn from(n: $s) -> Self { + let sign = if n < 0 { Sign::Negative } else { Sign::Positive }; + let n = n.abs(); + let raw: usize = <$s>::try_into(n).unwrap(); + + Self { + inner: vec![raw], + sign, + } + } + } + + impl From<$u> for BigInt { + /// Create a `BigInt` from an unsigned integer primitive + fn from(n: $u) -> Self { + let mut new = Self::new_empty(); + new.inner.push(n as usize); + + new + } + } + )* + } +} + +macro_rules! impl_from_larger { + ($(($s: ty, $u: ty)),* ) => { + $( + impl From<$s> for BigInt { + /// Create a `BigInt` from a signed integer primitive + fn from(n: $s) -> Self { + todo!(); + } + } + + impl From<$u> for BigInt { + /// Create a `BigInt` from an unsigned integer primitive + fn from(n: $u) -> Self { + use core::usize::MAX; + + let base_usize_value = n / MAX as $u; + let rem = n % MAX as $u; + + if base_usize_value == 0 { + Self::from(rem as usize) + } else { + todo!(); + } + } + } + )* + }; +} + +#[cfg(target_pointer_width = "32")] +impl_from_larger!((i64, u64), (i128, u128)); +#[cfg(target_pointer_width = "32")] +impl_from_smaller!((i8, u8), (i16, u16), (i32, u32)); +#[cfg(target_pointer_width = "32")] +static BITS:usize = 32; + +#[cfg(target_pointer_width = "64")] +impl_from_larger!((i128, u128)); +#[cfg(target_pointer_width = "64")] +impl_from_smaller!((i8, u8), (i16, u16), (i32, u32), (i64, u64)); +#[cfg(target_pointer_width = "64")] +static BITS:usize = 64; + #[cfg(test)] mod tests { use super::*; @@ -285,4 +356,13 @@ mod tests { ); assert_eq!(a.inner[1], 1usize, "most significant place should be 1"); } + + #[test] + fn test_from() { + // Signed numbers + assert_eq!(-BigInt::from(2), BigInt::from(-2)); + + // Larger than usize + assert_eq!(BigInt::from(45u128), BigInt::from(45usize)); + } }