Allow comparing size of Bigints, and allow comparing BigInts to number primitives
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
This commit is contained in:
parent
3f6071d196
commit
e54485f12b
181
src/bigint.rs
181
src/bigint.rs
@ -13,6 +13,7 @@ use alloc::string::*;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
use core::cmp::{Ordering, PartialOrd, PartialEq};
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::mem::replace;
|
use core::mem::replace;
|
||||||
use core::ops::{
|
use core::ops::{
|
||||||
@ -131,7 +132,7 @@ impl BigInt {
|
|||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_digit_count(a: &Self, b: &Self) -> usize {
|
fn get_ceil_digit_count(a: &Self, b: &Self) -> usize {
|
||||||
let a_digits = a.inner.len();
|
let a_digits = a.inner.len();
|
||||||
let b_digits = b.inner.len();
|
let b_digits = b.inner.len();
|
||||||
|
|
||||||
@ -164,6 +165,38 @@ impl BigInt {
|
|||||||
Positive
|
Positive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normal primitive multiplication
|
||||||
|
fn prim_mul(self, rhs: Self, digits: usize) -> Self {
|
||||||
|
let mut out = BigInt::with_capacity(digits);
|
||||||
|
|
||||||
|
let mut carry = 0usize;
|
||||||
|
for i in 0..digits {
|
||||||
|
let a = *self.inner.get(i).unwrap_or(&0usize);
|
||||||
|
let b = *rhs.inner.get(i).unwrap_or(&0usize);
|
||||||
|
|
||||||
|
if a == 0 || b == 0 {
|
||||||
|
out.inner.push(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (res, overflowed) = a.overflowing_mul(b);
|
||||||
|
|
||||||
|
if overflowed {
|
||||||
|
todo!()
|
||||||
|
} else {
|
||||||
|
let (res, overflowed) = res.overflowing_add(carry);
|
||||||
|
|
||||||
|
out.inner.push(res);
|
||||||
|
carry = if overflowed { 1 } else { 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.sign = Self::get_sign(self, rhs, FracOp::Other);
|
||||||
|
out.shrink_to_fit();
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for BigInt {
|
impl Add for BigInt {
|
||||||
@ -178,7 +211,7 @@ impl Add for BigInt {
|
|||||||
return self - -rhs;
|
return self - -rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
let digits = Self::get_digit_count(&self, &rhs) + 1;
|
let digits = Self::get_ceil_digit_count(&self, &rhs) + 1;
|
||||||
let mut out = BigInt::with_capacity(digits);
|
let mut out = BigInt::with_capacity(digits);
|
||||||
|
|
||||||
let mut carry = 0usize;
|
let mut carry = 0usize;
|
||||||
@ -217,7 +250,7 @@ impl Sub for BigInt {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
let digits = Self::get_digit_count(&self, &rhs);
|
let digits = Self::get_ceil_digit_count(&self, &rhs);
|
||||||
let mut out = BigInt::with_capacity(digits);
|
let mut out = BigInt::with_capacity(digits);
|
||||||
|
|
||||||
// Handle cases where addition makes more sense
|
// Handle cases where addition makes more sense
|
||||||
@ -233,16 +266,17 @@ impl Sub for BigInt {
|
|||||||
let b = *rhs.inner.get(i).unwrap_or(&0usize);
|
let b = *rhs.inner.get(i).unwrap_or(&0usize);
|
||||||
|
|
||||||
if a >= borrow && (a - borrow) >= b {
|
if a >= borrow && (a - borrow) >= b {
|
||||||
|
// This is the easy way, no additional borrowing or underflow
|
||||||
let res = a - b - borrow;
|
let res = a - b - borrow;
|
||||||
|
|
||||||
out.inner.push(res);
|
out.inner.push(res);
|
||||||
borrow = 0;
|
borrow = 0;
|
||||||
} else {
|
} else {
|
||||||
// To prevent subtraction overflow, the max borrowed
|
// To prevent overflow, the max borrowed value is
|
||||||
// value is usize::MAX. The rest of the borrowed value
|
// usize::MAX (place-value - 1). The rest of the borrowed value
|
||||||
// will be added on afterwords.
|
// will be added on afterwords.
|
||||||
// In base ten, this would be like:
|
// In base ten, this would be like:
|
||||||
// 18 - 9 = 9-9 + 9
|
// 15 - 8 = (9 - 8) + (5 + 1)
|
||||||
let rem = (a + 1) - borrow;
|
let rem = (a + 1) - borrow;
|
||||||
let res = (core::usize::MAX - b) + rem;
|
let res = (core::usize::MAX - b) + rem;
|
||||||
out.inner.push(res);
|
out.inner.push(res);
|
||||||
@ -263,36 +297,12 @@ impl Mul for BigInt {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
let input_digits = Self::get_ceil_digit_count(&self, &rhs);
|
||||||
|
|
||||||
// Multiplication can result in twice the number of digits
|
// Multiplication can result in twice the number of digits
|
||||||
let digits = Self::get_digit_count(&self, &rhs) * 2;
|
let out_digits = Self::get_ceil_digit_count(&self, &rhs) * 2;
|
||||||
let mut out = BigInt::with_capacity(digits);
|
|
||||||
|
|
||||||
let mut carry = 0usize;
|
self.prim_mul(rhs, out_digits)
|
||||||
for i in 0..digits {
|
|
||||||
let a = *self.inner.get(i).unwrap_or(&0usize);
|
|
||||||
let b = *rhs.inner.get(i).unwrap_or(&0usize);
|
|
||||||
|
|
||||||
if a == 0 || b == 0 {
|
|
||||||
out.inner.push(0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (res, overflowed) = a.overflowing_mul(b);
|
|
||||||
|
|
||||||
if overflowed {
|
|
||||||
todo!()
|
|
||||||
} else {
|
|
||||||
let (res, overflowed) = res.overflowing_add(carry);
|
|
||||||
|
|
||||||
out.inner.push(res);
|
|
||||||
carry = if overflowed { 1 } else { 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.sign = Self::get_sign(self, rhs, FracOp::Other);
|
|
||||||
out.shrink_to_fit();
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +387,48 @@ impl Not for BigInt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for BigInt {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
// The signs differ
|
||||||
|
if self.sign != other.sign {
|
||||||
|
// If the signs are different, the magnitude doesn't matter
|
||||||
|
// unless the value is zero on both sides
|
||||||
|
return if self.eq(&0) && other.eq(&0) {
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
} else {
|
||||||
|
self.sign.partial_cmp(&other.sign)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything is the same
|
||||||
|
if self.inner == other.inner {
|
||||||
|
return Some(Ordering::Equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of place values differs
|
||||||
|
if self.inner.len() != other.inner.len() {
|
||||||
|
return if self.inner.len() > other.inner.len() {
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
} else {
|
||||||
|
Some(Ordering::Less)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the sign is the same, and the number of place values is equal,
|
||||||
|
// so compare the individual place values (from greatest to least) until they
|
||||||
|
// are different. At this point, the digits can not all be equal.
|
||||||
|
for i in (0usize..self.inner.len()).rev() {
|
||||||
|
if self.inner[i] < other.inner[i] {
|
||||||
|
return Some(Ordering::Less);
|
||||||
|
} else if self.inner[i] > other.inner[i] {
|
||||||
|
return Some(Ordering::Greater);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_from_smaller {
|
macro_rules! impl_from_smaller {
|
||||||
($(($s: ty, $u: ty)),* ) => {
|
($(($s: ty, $u: ty)),* ) => {
|
||||||
$(
|
$(
|
||||||
@ -436,11 +488,45 @@ macro_rules! impl_from_larger {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_ord_literal {
|
||||||
|
($(($($prim: ty),+), $base: ty), *) => {
|
||||||
|
$(
|
||||||
|
$(
|
||||||
|
impl PartialEq<$prim> for BigInt {
|
||||||
|
fn eq(&self, other: &$prim) -> bool {
|
||||||
|
self == &BigInt::from(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<BigInt> for $prim {
|
||||||
|
fn eq(&self, other: &BigInt) -> bool {
|
||||||
|
&BigInt::from(*self) == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<$prim> for BigInt {
|
||||||
|
fn partial_cmp(&self, other: &$prim) -> Option<Ordering> {
|
||||||
|
self.partial_cmp(&BigInt::from(*other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<BigInt> for $prim {
|
||||||
|
fn partial_cmp(&self, other: &BigInt) -> Option<Ordering> {
|
||||||
|
(&BigInt::from(*self)).partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
impl_from_larger!((i64, u64), (i128, u128));
|
impl_from_larger!((i64, u64), (i128, u128));
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
impl_from_smaller!((i8, u8), (i16, u16), (i32, u32));
|
impl_from_smaller!((i8, u8), (i16, u16), (i32, u32));
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
impl_ord_literal!((i8,u8,i16,u16,i32,u32,i64,u64), u32);
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
static BITS: usize = 32;
|
static BITS: usize = 32;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
@ -448,6 +534,8 @@ impl_from_larger!((i128, u128));
|
|||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
impl_from_smaller!((i8, u8), (i16, u16), (i32, u32), (i64, u64));
|
impl_from_smaller!((i8, u8), (i16, u16), (i32, u32), (i64, u64));
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
impl_ord_literal!((i8,u8,i16,u16,i32,u32,i64,u64), u32);
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
static BITS: usize = 64;
|
static BITS: usize = 64;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -699,6 +787,31 @@ mod tests {
|
|||||||
assert_eq!(b.inner[0], core::usize::MAX);
|
assert_eq!(b.inner[0], core::usize::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_partial_eq() {
|
||||||
|
let a = 12345u16;
|
||||||
|
let b = BigInt::from(a);
|
||||||
|
|
||||||
|
assert!(a.eq(&b));
|
||||||
|
assert!(b.eq(&a));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_partial_ord() {
|
||||||
|
let a = 12345u32;
|
||||||
|
let b = BigInt::from(a);
|
||||||
|
let c = 3u8;
|
||||||
|
|
||||||
|
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
|
||||||
|
assert_eq!(c.partial_cmp(&b), Some(Ordering::Less));
|
||||||
|
assert_eq!(b.partial_cmp(&c), Some(Ordering::Greater));
|
||||||
|
|
||||||
|
assert!(big_int!(-32) < big_int!(3));
|
||||||
|
assert!(big_int!(3) > big_int!(-32));
|
||||||
|
assert!(big_int!(152) > big_int!(132));
|
||||||
|
assert_eq!(big_int!(123), big_int!(123));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from() {
|
fn test_from() {
|
||||||
// Signed numbers
|
// Signed numbers
|
||||||
|
27
src/num.rs
27
src/num.rs
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Home to the numeric trait chain of doom, aka `Unsigned`
|
//! Home to the numeric trait chain of doom, aka `Unsigned`
|
||||||
#![allow(unused_comparisons)]
|
#![allow(unused_comparisons)]
|
||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min, Ordering};
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::ops::{
|
use core::ops::{
|
||||||
@ -55,6 +55,21 @@ impl Not for Sign {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Sign {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match self {
|
||||||
|
Self::Positive => match other {
|
||||||
|
Self::Positive => Some(Ordering::Equal),
|
||||||
|
Self::Negative => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Self::Negative => match other {
|
||||||
|
Self::Positive => Some(Ordering::Less),
|
||||||
|
Self::Negative => Some(Ordering::Equal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Native number type
|
/// Native number type
|
||||||
pub trait Num:
|
pub trait Num:
|
||||||
Add
|
Add
|
||||||
@ -303,6 +318,16 @@ mod tests {
|
|||||||
|
|
||||||
let ns = !s;
|
let ns = !s;
|
||||||
assert_eq!(ns, Sign::Negative);
|
assert_eq!(ns, Sign::Negative);
|
||||||
|
|
||||||
|
let a = Sign::Negative;
|
||||||
|
let b = Sign::Positive;
|
||||||
|
let c = Sign::Negative;
|
||||||
|
let d = Sign::Positive;
|
||||||
|
|
||||||
|
assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
|
||||||
|
assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
|
||||||
|
assert_eq!(a.partial_cmp(&c), Some(Ordering::Equal));
|
||||||
|
assert_eq!(b.partial_cmp(&d), Some(Ordering::Equal));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user