Messy work in progress
This commit is contained in:
parent
d20cb06c87
commit
fb87807227
34
src/num.rs
34
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<U=Unsigned>: Int {
|
||||
fn is_neg(self) -> bool;
|
||||
|
||||
fn to_unsigned<U>(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<U=$un_type> for $type {
|
||||
fn is_neg(self) -> bool {
|
||||
self < 0
|
||||
}
|
||||
|
||||
fn to_unsigned<U>(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
|
||||
<U>::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 {
|
||||
|
168
src/rational.rs
168
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<T: Unsigned = usize> {
|
||||
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<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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Unsigned> Frac<T> {
|
||||
/// 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<T: Unsigned> Frac<T> {
|
||||
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<T: Unsigned + Mul<Output = T>> Mul for Frac<T> {
|
||||
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
|
||||
if self.sign != rhs.sign {
|
||||
Self::new_neg(numer, denom)
|
||||
} else {
|
||||
Self::new(numer, denom)
|
||||
Self::new_signed(numer, denom, sign)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Unsigned + Mul<Output = T>> Div for Frac<T> {
|
||||
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<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> Add for Frac<T> {
|
||||
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 {
|
||||
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<T: Unsigned + Sub<Output = T>> Sub for Frac<T> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user