Sort of get fractions to work from signed or unsigned numbers
This commit is contained in:
parent
fb87807227
commit
aae00e2031
85
src/num.rs
85
src/num.rs
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user