aurum-numeric 0.2.0

Numeric traits
Documentation
// Copyright (c) 2016-2017 <daggerbot@gmail.com>
// This software is available under the terms of the zlib license.
// See COPYING.md for more information.

use std::{i8, i16, i32, i64, isize};
use std::error::Error;
use std::fmt::{self, Display, Formatter};

/// Error which occurs when an operation causes integer overflow.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IntOverflow;

const INT_OVERFLOW_STR: &'static str = "integer overflow";

impl Display for IntOverflow {
    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
        f.write_str(INT_OVERFLOW_STR)
    }
}

impl Error for IntOverflow {
    fn description (&self) -> &str { INT_OVERFLOW_STR }
}

/// Error which occurs when an operation causes integer underflow.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IntUnderflow;

const INT_UNDERFLOW_STR: &'static str = "integer underflow";

impl Display for IntUnderflow {
    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
        f.write_str(INT_UNDERFLOW_STR)
    }
}

impl Error for IntUnderflow {
    fn description (&self) -> &str { INT_UNDERFLOW_STR }
}

/// Error which occurs when an operation causes integer overflow or underflow.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IntRangeError {
    Overflow,
    Underflow,
}

impl IntRangeError {
    fn as_str (self) -> &'static str {
        match self {
            IntRangeError::Overflow => INT_OVERFLOW_STR,
            IntRangeError::Underflow => INT_UNDERFLOW_STR,
        }
    }
}

impl Display for IntRangeError {
    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl Error for IntRangeError {
    fn description (&self) -> &str { self.as_str() }
}

/// Error which may occur on division by zero.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DivideByZero;

const DIVIDE_BY_ZERO_STR: &'static str = "divide by zero";

impl Display for DivideByZero {
    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
        f.write_str(DIVIDE_BY_ZERO_STR)
    }
}

impl Error for DivideByZero {
    fn description (&self) -> &str { DIVIDE_BY_ZERO_STR }
}

/// Fallible addition trait.
pub trait TryAdd<RHS = Self> {
    type Output;
    type Err;

    fn try_add (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_add_unsigned {
    ($($ty:ty),*) => { $(
        impl TryAdd for $ty {
            type Output = $ty;
            type Err = IntOverflow;

            fn try_add (self, rhs: $ty) -> Result<$ty, IntOverflow> {
                match self.checked_add(rhs) {
                    None => Err(IntOverflow),
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

macro_rules! impl_try_add_signed {
    ($($ty:ty),*) => { $(
        impl TryAdd for $ty {
            type Output = $ty;
            type Err = IntRangeError;

            fn try_add (self, rhs: $ty) -> Result<$ty, IntRangeError> {
                match self.checked_add(rhs) {
                    None => {
                        if rhs > 0 {
                            Err(IntRangeError::Overflow)
                        } else {
                            Err(IntRangeError::Underflow)
                        }
                    },
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

impl_try_add_unsigned!(u8, u16, u32, u64, usize);
impl_try_add_signed!(i8, i16, i32, i64, isize);

#[test]
fn test_try_add () {
    assert_eq!(254u8.try_add(1), Ok(255u8));
    assert_eq!(255u8.try_add(1), Err(IntOverflow));
    assert_eq!(126i8.try_add(1), Ok(127i8));
    assert_eq!(127i8.try_add(1), Err(IntRangeError::Overflow));
    assert_eq!((-127i8).try_add(-1), Ok(-128i8));
    assert_eq!((-128i8).try_add(-1), Err(IntRangeError::Underflow));
}

/// Fallible subtraction trait.
pub trait TrySub<RHS = Self> {
    type Output;
    type Err;

    fn try_sub (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_sub_unsigned {
    ($($ty:ty),*) => { $(
        impl TrySub for $ty {
            type Output = $ty;
            type Err = IntUnderflow;

            fn try_sub (self, rhs: $ty) -> Result<$ty, IntUnderflow> {
                match self.checked_sub(rhs) {
                    None => Err(IntUnderflow),
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

macro_rules! impl_try_sub_signed {
    ($($ty:ty),*) => { $(
        impl TrySub for $ty {
            type Output = $ty;
            type Err = IntRangeError;

            fn try_sub (self, rhs: $ty) -> Result<$ty, IntRangeError> {
                match self.checked_sub(rhs) {
                    None => {
                        if rhs > 0 {
                            Err(IntRangeError::Underflow)
                        } else {
                            Err(IntRangeError::Overflow)
                        }
                    },
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

impl_try_sub_unsigned!(u8, u16, u32, u64, usize);
impl_try_sub_signed!(i8, i16, i32, i64, isize);

#[test]
fn test_try_sub () {
    assert_eq!(1u8.try_sub(1), Ok(0u8));
    assert_eq!(0u8.try_sub(1), Err(IntUnderflow));
    assert_eq!((-127i8).try_sub(1), Ok(-128i8));
    assert_eq!((-128i8).try_sub(1), Err(IntRangeError::Underflow));
    assert_eq!(126i8.try_sub(-1), Ok(127i8));
    assert_eq!(127i8.try_sub(-1), Err(IntRangeError::Overflow));
}

//TODO

/// Fallible multiplication trait.
pub trait TryMul<RHS = Self> {
    type Output;
    type Err;

    fn try_mul (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_mul_unsigned {
    ($($ty:ty),*) => { $(
        impl TryMul for $ty {
            type Output = $ty;
            type Err = IntOverflow;

            fn try_mul (self, rhs: $ty) -> Result<$ty, IntOverflow> {
                match self.checked_mul(rhs) {
                    None => Err(IntOverflow),
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

macro_rules! impl_try_mul_signed {
    ($($ty:ty),*) => { $(
        impl TryMul for $ty {
            type Output = $ty;
            type Err = IntRangeError;

            fn try_mul (self, rhs: $ty) -> Result<$ty, IntRangeError> {
                match self.checked_mul(rhs) {
                    None => {
                        if (self > 0) == (rhs > 0) {
                            Err(IntRangeError::Overflow)
                        } else {
                            Err(IntRangeError::Underflow)
                        }
                    },
                    Some(n) => Ok(n),
                }
            }
        }
    )* };
}

impl_try_mul_unsigned!(u8, u16, u32, u64, usize);
impl_try_mul_signed!(i8, i16, i32, i64, isize);

#[test]
fn test_try_mul () {
    assert_eq!(85u8.try_mul(3), Ok(255u8));
    assert_eq!(64u8.try_mul(4), Err(IntOverflow));
    assert_eq!(63i8.try_mul(2), Ok(126i8));
    assert_eq!(64i8.try_mul(2), Err(IntRangeError::Overflow));
    assert_eq!(64i8.try_mul(-2), Ok(-128i8));
    assert_eq!(65i8.try_mul(-2), Err(IntRangeError::Underflow));
    assert_eq!((-64i8).try_mul(2), Ok(-128i8));
    assert_eq!((-65i8).try_mul(2), Err(IntRangeError::Underflow));
    assert_eq!((-63i8).try_mul(-2), Ok(126i8));
    assert_eq!((-64i8).try_mul(-2), Err(IntRangeError::Overflow));
}

/// Fallible division trait.
pub trait TryDiv<RHS = Self> {
    type Output;
    type Err;

    fn try_div (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_div {
    ($($ty:ty),*) => { $(
        impl TryDiv for $ty {
            type Output = $ty;
            type Err = DivideByZero;

            fn try_div (self, rhs: $ty) -> Result<$ty, DivideByZero> {
                if rhs == 0 {
                    Err(DivideByZero)
                } else {
                    Ok(self / rhs)
                }
            }
        }
    )* };
}

impl_try_div!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);

#[test]
fn test_try_div () {
    assert_eq!(255u8.try_div(3), Ok(85u8));
    assert_eq!(255u8.try_div(0), Err(DivideByZero));
}

/// Fallible division remainder trait.
pub trait TryRem<RHS = Self> {
    type Output;
    type Err;

    fn try_rem (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_rem {
    ($($ty:ty),*) => { $(
        impl TryRem for $ty {
            type Output = $ty;
            type Err = DivideByZero;

            fn try_rem (self, rhs: $ty) -> Result<$ty, DivideByZero> {
                if rhs == 0 {
                    Err(DivideByZero)
                } else {
                    Ok(self % rhs)
                }
            }
        }
    )* };
}

impl_try_rem!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);

#[test]
fn test_try_rem () {
    assert_eq!(11u8.try_rem(5), Ok(1u8));
    assert_eq!(11u8.try_rem(0), Err(DivideByZero));
}

/// Fallible negation trait.
pub trait TryNeg {
    type Output;
    type Err;

    fn try_neg (self) -> Result<Self::Output, Self::Err>;
}

macro_rules! impl_try_neg {
    ($($ty:ident),*) => { $(
        impl TryNeg for $ty {
            type Output = $ty;
            type Err = IntOverflow;

            fn try_neg (self) -> Result<$ty, IntOverflow> {
                if self == $ty::MIN {
                    Err(IntOverflow)
                } else {
                    Ok(-self)
                }
            }
        }
    )* };
}

impl_try_neg!(i8, i16, i32, i64, isize);

#[test]
fn test_try_neg () {
    assert_eq!((-127i8).try_neg(), Ok(127i8));
    assert_eq!((-128i8).try_neg(), Err(IntOverflow));
}