typerat 0.0.4

Type-level rational numbers based on `typenum`.
Documentation
//! **Development in progress**
//!
//! Type-level rational numbers based on [typenum].
//!
//! [typenum]: https://crates.io/crates/typenum

#![no_std]
#![cfg_attr(test, recursion_limit = "256")]

use core::marker::PhantomData;
use typenum::Gcd;

#[doc(no_inline)]
pub use typenum::consts::*;

#[doc(no_inline)]
pub use typenum::{Equal, Greater, Less, NInt, PInt};

mod uint;
pub use uint::NonZeroUnsigned;

mod int;
pub use int::Integer;

mod marker;
pub use marker::{NonNegative, NonZero, NotOne};

/// Type-level integers usable as denominators of type-level rational numbers.
///
/// # Examples
///
/// ```
/// use typerat::*;
///
/// fn is_denominator<D: Denominator>() -> bool {
///     true
/// }
///
/// assert!(is_denominator::<P1>());
/// assert!(is_denominator::<P2>());
/// ```
///
/// ```compile_fail
/// # use typerat::*;
/// #
/// # fn is_denominator<D: Denominator>() -> bool {
/// #     true
/// # }
/// #
/// assert!(is_denominator::<Z0>());
/// ```
///
/// ```compile_fail
/// # use typerat::*;
/// #
/// # fn is_denominator<D: Denominator>() -> bool {
/// #     true
/// # }
/// #
/// assert!(is_denominator::<N1>());
/// ```
pub trait Denominator: Integer + NonNegative + NonZero {}

impl<D> Denominator for D where D: Integer + NonNegative + NonZero {}

/// Type-level integers usable as numerators of type-level rational numbers with denominator `D`.
///
/// # Examples
///
/// ```
/// use typerat::*;
///
/// fn is_numerator_for_denominator_2<N: Numerator<P2>>() -> bool {
///     true
/// }
///
/// assert!(is_numerator_for_denominator_2::<P1>());
/// assert!(is_numerator_for_denominator_2::<N1>());
/// assert!(is_numerator_for_denominator_2::<P3>());
/// assert!(is_numerator_for_denominator_2::<N3>());
/// ```
///
/// ```compile_fail
/// # use typerat::*;
/// #
/// # fn is_numerator_for_denominator_2<N: Numerator<P2>>() -> bool {
/// #     true
/// # }
/// #
/// assert!(is_numerator_for_denominator_2::<P2>());
/// ```
///
/// ```compile_fail
/// # use typerat::*;
/// #
/// # fn is_numerator_for_denominator_2<N: Numerator<P2>>() -> bool {
/// #     true
/// # }
/// #
/// assert!(is_numerator_for_denominator_2::<N2>());
/// ```
///
/// ```compile_fail
/// # use typerat::*;
/// #
/// # fn is_numerator_for_denominator_2<N: Numerator<P2>>() -> bool {
/// #     true
/// # }
/// #
/// assert!(is_numerator_for_denominator_2::<P4>());
/// ```
pub trait Numerator<D = P1>: Integer + Gcd<D, Output = P1> {}

impl<N, D> Numerator<D> for N
where
    N: Integer + Gcd<D, Output = P1>,
    D: Denominator,
{
}

/// `Q<N, D>` represents a type-level rational number with numerator `N` and denominator `D`.
#[derive(Debug)]
pub struct Q<N, D = P1>(PhantomData<(N, D)>)
where
    N: Numerator<D>,
    D: Denominator;

impl<N, D> Q<N, D>
where
    N: Numerator<D>,
    D: Denominator,
{
    const SELF: Self = Q(PhantomData);

    /// Constructs a new instance of this type-level rational number.
    pub const fn new() -> Self {
        Self::SELF
    }

    /// Borrows an instance of this type-level rational number.
    pub const fn borrow<'a>() -> &'a Self {
        &Self::SELF
    }
}

impl<N, D> Clone for Q<N, D>
where
    N: Numerator<D>,
    D: Denominator,
{
    fn clone(&self) -> Self {
        *self
    }
}

impl<N, D> Copy for Q<N, D>
where
    N: Numerator<D>,
    D: Denominator,
{
}

impl<N, D> Default for Q<N, D>
where
    N: Numerator<D>,
    D: Denominator,
{
    fn default() -> Self {
        Self::new()
    }
}

mod rational;
pub use rational::Rational;

mod ops;
pub use ops::Recip;

mod cmp;
pub use cmp::{Cmp, Ordering};

mod fmt;

#[cfg(test)]
mod tests {
    use super::*;
    use char_buf::CharBuf;
    use core::fmt::Write;

    macro_rules! format {
        ($($arg:tt)*) => {
            {
                let mut w = CharBuf::<1024>::new();
                write!(w, $($arg)*).unwrap();
                w
            }
        };
    }

    #[test]
    fn test_q() {
        let a = Q::<P1, P2>::default();
        let b = a.clone();
        let c: PhantomData<(P1, P2)> = PhantomData;
        let d = Q::<P1, P2>::borrow();

        assert_eq!(a, b);
        assert_eq!(&a, d);
        assert_eq!(format!("{:?}", a), format!("Q({:?})", c));
    }
}