Move number traits into their own module

This commit is contained in:
Timothy Warren 2020-02-14 10:14:22 -05:00
parent 23d0ab75ec
commit 2e47d97cc7
4 changed files with 165 additions and 143 deletions

View File

@ -12,7 +12,7 @@
//! * RemAssign //! * RemAssign
//! * Sub //! * Sub
//! * SubAssign //! * SubAssign
use crate::{Sign, Unsigned}; use crate::num::*;
#[derive(Debug)] #[derive(Debug)]
pub struct BigInt { pub struct BigInt {

View File

@ -1,146 +1,9 @@
//! # Rusty Numbers
//!
//! Playin' with Numerics in Rust
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
pub mod bigint; pub mod bigint;
pub mod num;
pub mod rational; pub mod rational;
pub mod seq; pub mod seq;
/// A Trait representing unsigned integer primitives
pub trait Unsigned:
Add
+ AddAssign
+ BitAnd
+ BitAndAssign
+ BitOr
+ BitOrAssign
+ BitXor
+ BitXorAssign
+ Div
+ DivAssign
+ Mul
+ MulAssign
+ Rem
+ RemAssign
+ Copy
+ Shl
+ ShlAssign
+ Shr
+ ShrAssign
+ Sub
+ SubAssign
+ Eq
+ Ord
+ Not
{
/// Find the greatest common denominator of two numbers
fn gcd(a: Self, b: Self) -> Self;
/// Find the least common multiple of two numbers
fn lcm(a: Self, b: Self) -> Self;
/// The maximum value of the type
fn max_value() -> Self;
/// Is this a zero value?
fn is_zero(self) -> bool;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sign {
Positive,
Negative,
}
impl Default for Sign {
fn default() -> Self {
Sign::Positive
}
}
impl Not for Sign {
type Output = Sign;
fn not(self) -> Self::Output {
match self {
Self::Positive => Self::Negative,
Self::Negative => Self::Positive,
}
}
}
macro_rules! impl_unsigned {
($Type: ty) => {
impl Unsigned for $Type {
fn gcd(a: $Type, b: $Type) -> $Type {
if a == b {
return a;
} else if a == 0 {
return b;
} else if b == 0 {
return a;
}
let a_even = a % 2 == 0;
let b_even = b % 2 == 0;
if a_even {
if b_even {
// Both a & b are even
return Self::gcd(a >> 1, b >> 1) << 1;
} else if !b_even {
// b is odd
return Self::gcd(a >> 1, b);
}
}
// a is odd, b is even
if (!a_even) && b_even {
return Self::gcd(a, b >> 1);
}
if a > b {
return Self::gcd((a - b) >> 1, b);
}
Self::gcd((b - a) >> 1, a)
}
fn lcm(a: $Type, b: $Type) -> $Type {
if (a == 0 && b == 0) {
return 0;
}
a * b / Self::gcd(a, b)
}
fn max_value() -> $Type {
<$Type>::max_value()
}
fn is_zero(self) -> bool {
self == 0
}
}
};
}
impl_unsigned!(u8);
impl_unsigned!(u16);
impl_unsigned!(u32);
impl_unsigned!(u64);
impl_unsigned!(u128);
impl_unsigned!(usize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gcd() {
assert_eq!(u8::gcd(2, 2), 2);
assert_eq!(u16::gcd(36, 48), 12);
}
}

159
src/num.rs Normal file
View File

@ -0,0 +1,159 @@
//! Numeric Helper Traits
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
/// Native number type
pub trait Num:
Add + AddAssign + Div + DivAssign + Mul + MulAssign + Rem + RemAssign + Copy + Sub + SubAssign
{
}
/// Integer primitive
pub trait Int:
Num
+ BitAnd
+ BitAndAssign
+ BitOr
+ BitOrAssign
+ BitXor
+ BitXorAssign
+ Eq
+ Ord
+ Not
+ Shl
+ Shr
+ ShlAssign
+ ShrAssign
{
/// The maximum value of the type
fn max_value() -> Self;
/// Is this a zero value?
fn is_zero(self) -> bool;
}
/// A Trait representing unsigned integer primitives
pub trait Unsigned: Int {
/// Find the greatest common denominator of two numbers
fn gcd(a: Self, b: Self) -> Self;
/// Find the least common multiple of two numbers
fn lcm(a: Self, b: Self) -> Self;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sign {
Positive,
Negative,
}
impl Default for Sign {
fn default() -> Self {
Sign::Positive
}
}
impl Not for Sign {
type Output = Sign;
fn not(self) -> Self::Output {
match self {
Self::Positive => Self::Negative,
Self::Negative => Self::Positive,
}
}
}
macro_rules! impl_num {
($( $Type: ty ),* ) => {
$(
impl Num for $Type {
}
)*
}
}
macro_rules! impl_int {
($( $Type: ty ),* ) => {
$(
impl Int for $Type {
fn is_zero(self) -> bool {
self == 0
}
fn max_value() -> $Type {
<$Type>::max_value()
}
}
)*
}
}
macro_rules! impl_unsigned {
($($Type: ty),* ) => {
$(
impl Unsigned for $Type {
/// Implementation based on https://en.wikipedia.org/wiki/Binary_GCD_algorithm
fn gcd(a: $Type, b: $Type) -> $Type {
if a == b {
return a;
} else if a == 0 {
return b;
} else if b == 0 {
return a;
}
let a_even = a % 2 == 0;
let b_even = b % 2 == 0;
if a_even {
if b_even {
// Both a & b are even
return Self::gcd(a >> 1, b >> 1) << 1;
} else if !b_even {
// b is odd
return Self::gcd(a >> 1, b);
}
}
// a is odd, b is even
if (!a_even) && b_even {
return Self::gcd(a, b >> 1);
}
if a > b {
return Self::gcd((a - b) >> 1, b);
}
Self::gcd((b - a) >> 1, a)
}
fn lcm(a: $Type, b: $Type) -> $Type {
if (a == 0 && b == 0) {
return 0;
}
a * b / Self::gcd(a, b)
}
}
)*
};
}
impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, i64, u64, i128, u128, usize);
impl_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, usize);
impl_unsigned!(u8, u16, u32, u64, u128, usize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gcd() {
assert_eq!(u8::gcd(2, 2), 2);
assert_eq!(u16::gcd(36, 48), 12);
}
}

View File

@ -11,7 +11,7 @@
//! * Sub //! * Sub
//! * SubAssign //! * SubAssign
use crate::{Sign, Unsigned}; use crate::num::*;
use std::ops::{Mul, Neg}; use std::ops::{Mul, Neg};
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]