Sort of get fractions to work from signed or unsigned numbers

This commit is contained in:
Timothy Warren 2020-02-14 23:41:14 -05:00
parent fb87807227
commit aae00e2031
2 changed files with 103 additions and 76 deletions

View File

@ -1,14 +1,38 @@
//! Numeric Helper Traits //! Numeric Helper Traits
use std::convert::TryFrom; #![allow(unused_comparisons)]
use std::ops::{ use core::convert::TryFrom;
use core::fmt::Debug;
use core::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
}; };
/// Native number type /// Native number type
pub trait Num: pub trait Num:
Add + AddAssign + Div + DivAssign + Mul + MulAssign + Rem + RemAssign + Copy + Sub + SubAssign Add
+ AddAssign
+ Debug
+ Div
+ DivAssign
+ Mul
+ MulAssign
+ Rem
+ RemAssign
+ PartialOrd
+ PartialEq
+ Copy
+ Sub
+ SubAssign
{ {
/// Is this number type signed?
fn is_signed(&self) -> bool {
true
}
}
/// Float primitive
pub trait Float: Num {
fn is_neg(self) -> bool;
} }
/// Integer primitive /// Integer primitive
@ -33,6 +57,9 @@ pub trait Int:
/// Is this a zero value? /// Is this a zero value?
fn is_zero(self) -> bool; fn is_zero(self) -> bool;
/// Is this number less than zero?
fn is_neg(self) -> bool;
} }
/// A Trait representing unsigned integer primitives /// A Trait representing unsigned integer primitives
@ -43,14 +70,16 @@ pub trait Unsigned: Int {
/// Find the least common multiple of two numbers /// Find the least common multiple of two numbers
fn lcm(a: Self, b: Self) -> Self; fn lcm(a: Self, b: Self) -> Self;
fn is_signed(self) -> bool; fn is_signed(self) -> bool {
false
}
} }
/// A Trait representing signed integer primitives /// A Trait representing signed integer primitives
pub trait Signed<U=Unsigned>: Int { pub trait Signed: Int {
fn is_neg(self) -> bool; type Un;
fn to_unsigned<U>(self) -> U; fn to_unsigned(self) -> Self::Un;
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -86,6 +115,18 @@ macro_rules! impl_num {
} }
} }
macro_rules! impl_float {
($( $Type: ty ),* ) => {
$(
impl Float for $Type {
fn is_neg(self) -> bool {
self < 0.0
}
}
)*
}
}
macro_rules! impl_int { macro_rules! impl_int {
($( $Type: ty ),* ) => { ($( $Type: ty ),* ) => {
$( $(
@ -97,6 +138,16 @@ macro_rules! impl_int {
fn max_value() -> $Type { fn max_value() -> $Type {
<$Type>::max_value() <$Type>::max_value()
} }
/// Is this number less than zero?
fn is_neg(self) -> bool {
if self.is_signed() == false {
false
} else {
self < 0
}
}
} }
)* )*
} }
@ -156,16 +207,14 @@ macro_rules! impl_unsigned {
macro_rules! impl_signed { macro_rules! impl_signed {
($(($type: ty, $un_type: ty)),* ) => { ($(($type: ty, $un_type: ty)),* ) => {
$( $(
impl Signed<U=$un_type> for $type { impl Signed for $type {
fn is_neg(self) -> bool { type Un = $un_type;
self < 0
}
fn to_unsigned<U>(self) -> U { fn to_unsigned(self) -> $un_type {
// Converting from signed to unsigned should always be safe // Converting from signed to unsigned should always be safe
// when using the absolute value, especially since I'm converting // when using the absolute value, especially since I'm converting
// between the same bit size // between the same bit size
<U>::try_from(self).unwrap() <$un_type>::try_from(self).unwrap()
} }
} }
)* )*
@ -173,9 +222,17 @@ macro_rules! impl_signed {
} }
impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, i64, u64, i128, u128, isize, usize); impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, i64, u64, i128, u128, isize, usize);
impl_float!(f32, f64);
impl_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); impl_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
impl_unsigned!(u8, u16, u32, u64, u128, usize); impl_unsigned!(u8, u16, u32, u64, u128, usize);
impl_signed!((i8,u8),(i16,u16),(i32,u32),(i64,u64),(i128,u128),(isize,usize)); impl_signed!(
(i8, u8),
(i16, u16),
(i32, u32),
(i64, u64),
(i128, u128),
(isize, usize)
);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -24,66 +24,36 @@ pub struct Frac<T: Unsigned = usize> {
#[macro_export] #[macro_export]
macro_rules! frac { macro_rules! frac {
($n:literal / $d:literal) => { ($n:literal / $d:literal) => {
Frac::new_conv($n, $d) frac($n, $d)
};
($n:literal / $d:literal) => {
frac($n, $d)
}; };
} }
/* macro_rules! impl_from_signed { /// Create a new rational number
($(($in_type: ty, $out_type: ty)),* ) => { pub fn frac<S: Signed + Signed<Un = U>, U: Unsigned>(n: S, d: S) -> Frac<U> {
$( // Converting from signed to unsigned should always be safe
impl Frac<$in_type> { // when using the absolute value, especially since I'm converting
pub fn new(n: $in_type, d: $in_type) -> Frac<$out_type> { // between the same bit size
// Converting from signed to unsigned should always be safe let mut sign = Sign::Positive;
// when using the absolute value, especially since I'm converting let numer = n.to_unsigned();
// between the same bit size let denom = d.to_unsigned();
let mut sign = $crate::num::Sign::Positive;
let numer = <$out_type>::try_from(n.abs()).unwrap();
let denom = <$out_type>::try_from(d.abs()).unwrap();
if n < 0 { if n.is_neg() {
sign = !sign; sign = !sign;
}
if d < 0 {
sign = !sign;
}
Frac::new_signed(numer, denom, sign)
}
fn new_signed(n: $in_type, d: $in_type, _: $crate::num::Sign) -> Frac<$out_type> {
Self::new(n, d)
}
}
)*
};
}
impl_from_signed!((i8, u8), (i16, u16), (i32, u32), (i64, u64), (i128, u128), (isize, usize)); */
impl<T: Signed, U: Unsigned> Frac<U> {
pub fn new_conv(n: T, d: T) -> Self {
// Converting from signed to unsigned should always be safe
// when using the absolute value, especially since I'm converting
// between the same bit size
let mut sign = Sign::Positive;
let numer:T::Un = n.to_unsigned();
let denom:T::Un = d.to_unsigned();
if n.is_neg() {
sign = !sign;
}
if d.is_neg() {
sign = !sign;
}
Self::new(numer, denom, sign)
} }
if d.is_neg() {
sign = !sign;
}
Frac { numer, denom, sign }.reduce()
} }
impl<T: Unsigned> Frac<T> { impl<T: Unsigned> Frac<T> {
/// Create a new rational number /// Create a new rational number
pub fn new(n: T, d: T, s: Sign) -> Self { pub fn new(n: T, d: T, s: Sign) -> Frac<T> {
if d.is_zero() { if d.is_zero() {
panic!("Fraction can not have a zero denominator"); panic!("Fraction can not have a zero denominator");
} }
@ -123,7 +93,7 @@ impl<T: Unsigned + Mul<Output = T>> Mul for Frac<T> {
let denom = self.denom * rhs.denom; let denom = self.denom * rhs.denom;
let sign = Self::get_sign(self, rhs); let sign = Self::get_sign(self, rhs);
Self::new_signed(numer, denom, sign) Self::new(numer, denom, sign)
} }
} }
@ -135,7 +105,7 @@ impl<T: Unsigned + Mul<Output = T>> Div for Frac<T> {
let denom = self.denom * rhs.numer; let denom = self.denom * rhs.numer;
let sign = Self::get_sign(self, rhs); let sign = Self::get_sign(self, rhs);
Self::new_signed(numer, denom, sign) Self::new(numer, denom, sign)
} }
} }
@ -150,9 +120,9 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> Add for
// subtraction is equivalent // subtraction is equivalent
if self.sign != rhs.sign { if self.sign != rhs.sign {
if a.numer > b.numer { if a.numer > b.numer {
return a - b return a - b;
} else if a.numer < b.numer { } else if a.numer < b.numer {
return b - a return b - a;
} }
} }
@ -164,14 +134,14 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> Add for
let denom = a.denom * b.denom; let denom = a.denom * b.denom;
let sign = Self::get_sign(a, b); let sign = Self::get_sign(a, b);
return Self::new_signed(numer, denom, sign); return Self::new(numer, denom, sign);
} }
let numer = a.numer + b.numer; let numer = a.numer + b.numer;
let denom = self.denom; let denom = self.denom;
let sign = Self::get_sign(a, b); let sign = Self::get_sign(a, b);
Self::new_signed(numer, denom, sign) Self::new(numer, denom, sign)
} }
} }
@ -213,17 +183,17 @@ mod tests {
#[test] #[test]
fn add_test() { fn add_test() {
assert_eq!(frac!(5u8 / 6), frac!(1 / 3) + frac!(1 / 2)); assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2));
} }
#[test] #[test]
fn macro_test() { fn macro_test() {
let frac1 = frac!(1u8 / 3); let frac1 = frac!(1 / 3);
let frac2 = Frac::new(1u8, 3, Sign::Positive); let frac2 = Frac::new(1u32, 3, Sign::Positive);
assert_eq!(frac1, frac2); assert_eq!(frac1, frac2);
let frac1 = -frac!(1u8 / 2); let frac1 = -frac!(1 / 2);
let frac2 = Frac::new(1u8, 2, Sign::Negative); let frac2 = Frac::new(1u32, 2, Sign::Negative);
assert_eq!(frac1, frac2); assert_eq!(frac1, frac2);
} }
} }