From fb8780722705a6852ef5305405ca71def65573bf Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Fri, 14 Feb 2020 17:24:51 -0500 Subject: [PATCH] Messy work in progress --- src/num.rs | 34 +++++++++- src/rational.rs | 166 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 182 insertions(+), 18 deletions(-) diff --git a/src/num.rs b/src/num.rs index 67e892e..f39326c 100644 --- a/src/num.rs +++ b/src/num.rs @@ -1,4 +1,5 @@ //! Numeric Helper Traits +use std::convert::TryFrom; use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, @@ -41,6 +42,15 @@ pub trait Unsigned: Int { /// Find the least common multiple of two numbers fn lcm(a: Self, b: Self) -> Self; + + fn is_signed(self) -> bool; +} + +/// A Trait representing signed integer primitives +pub trait Signed: Int { + fn is_neg(self) -> bool; + + fn to_unsigned(self) -> U; } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -143,9 +153,29 @@ macro_rules! impl_unsigned { }; } -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); +macro_rules! impl_signed { + ($(($type: ty, $un_type: ty)),* ) => { + $( + impl Signed for $type { + fn is_neg(self) -> bool { + self < 0 + } + + fn to_unsigned(self) -> U { + // Converting from signed to unsigned should always be safe + // when using the absolute value, especially since I'm converting + // between the same bit size + ::try_from(self).unwrap() + } + } + )* + } +} + +impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, 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_signed!((i8,u8),(i16,u16),(i32,u32),(i64,u64),(i128,u128),(isize,usize)); #[cfg(test)] mod tests { diff --git a/src/rational.rs b/src/rational.rs index 2f1100a..ee49d97 100644 --- a/src/rational.rs +++ b/src/rational.rs @@ -12,18 +12,78 @@ //! * SubAssign use crate::num::*; -use std::ops::{Mul, Neg}; +use std::ops::{Add, Div, Mul, Neg, Sub}; -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Frac { numer: T, denom: T, sign: Sign, } +#[macro_export] +macro_rules! frac { + ($n:literal / $d:literal) => { + Frac::new_conv($n, $d) + }; +} + +/* macro_rules! impl_from_signed { + ($(($in_type: ty, $out_type: ty)),* ) => { + $( + impl Frac<$in_type> { + pub fn new(n: $in_type, d: $in_type) -> Frac<$out_type> { + // 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 = $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 { + 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 Frac { + 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) + } +} + impl Frac { /// Create a new rational number - pub fn new(n: T, d: T) -> Self { + pub fn new(n: T, d: T, s: Sign) -> Self { if d.is_zero() { panic!("Fraction can not have a zero denominator"); } @@ -31,18 +91,21 @@ impl Frac { Frac { numer: n, denom: d, - sign: Sign::Positive, + sign: s, } .reduce() } - pub fn new_neg(n: T, d: T) -> Self { - let mut frac = Frac::new(n, d); - frac.sign = Sign::Negative; - - frac + /// Determine the output sign given the two input signs + fn get_sign(a: Self, b: Self) -> Sign { + if a.sign != b.sign { + Sign::Negative + } else { + Sign::Positive + } } + /// Convert the fraction to its simplest form fn reduce(mut self) -> Self { let gcd = T::gcd(self.numer, self.denom); self.numer /= gcd; @@ -58,13 +121,68 @@ impl> Mul for Frac { fn mul(self, rhs: Self) -> Self { let numer = self.numer * rhs.numer; let denom = self.denom * rhs.denom; + let sign = Self::get_sign(self, rhs); - // Figure out the sign + Self::new_signed(numer, denom, sign) + } +} + +impl> Div for Frac { + type Output = Self; + + fn div(self, rhs: Self) -> Self { + let numer = self.numer * rhs.denom; + let denom = self.denom * rhs.numer; + let sign = Self::get_sign(self, rhs); + + Self::new_signed(numer, denom, sign) + } +} + +impl + Sub + Mul> Add for Frac { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + let a = self; + let b = rhs; + + // If the sign of one input differs, + // subtraction is equivalent if self.sign != rhs.sign { - Self::new_neg(numer, denom) - } else { - Self::new(numer, denom) + if a.numer > b.numer { + return a - b + } else if a.numer < b.numer { + return b - a + } } + + // Find a common denominator if needed + if a.denom != b.denom { + // Let's just use the simplest method, rather than + // worrying about reducing to the least common denominator + let numer = (a.numer * b.denom) + (b.numer * a.denom); + let denom = a.denom * b.denom; + let sign = Self::get_sign(a, b); + + return Self::new_signed(numer, denom, sign); + } + + let numer = a.numer + b.numer; + let denom = self.denom; + let sign = Self::get_sign(a, b); + + Self::new_signed(numer, denom, sign) + } +} + +impl> Sub for Frac { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + let a = self; + let b = rhs; + + unimplemented!() } } @@ -85,11 +203,27 @@ mod tests { #[test] fn mul_test() { - let frac1 = Frac::new(1u8, 3u8); - let frac2 = Frac::new(2u8, 3u8); + let frac1 = Frac::new(1u8, 3u8, Sign::Positive); + let frac2 = Frac::new(2u8, 3u8, Sign::Positive); - let expected = Frac::new(2u8, 9u8); + let expected = Frac::new(2u8, 9u8, Sign::Positive); assert_eq!(frac1 * frac2, expected); } + + #[test] + fn add_test() { + assert_eq!(frac!(5u8 / 6), frac!(1 / 3) + frac!(1 / 2)); + } + + #[test] + fn macro_test() { + let frac1 = frac!(1u8 / 3); + let frac2 = Frac::new(1u8, 3, Sign::Positive); + assert_eq!(frac1, frac2); + + let frac1 = -frac!(1u8 / 2); + let frac2 = Frac::new(1u8, 2, Sign::Negative); + assert_eq!(frac1, frac2); + } }