value 0.1.1

Type-level constants for generic contexts
Documentation
//! Type-level constants for generic contexts.
//!
//! These types implement [`From`], [`PartialEq`], and [`PartialOrd`] so that
//! generic code can check if a value is zero, etc.
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(non_camel_case_types, non_upper_case_globals)]
#![forbid(missing_docs, unconditional_recursion)]
#![cfg_attr(test, deny(warnings))]

// this makes me upset
#[cfg(feature = "std")]
extern crate core;

use core::{cmp::Ordering, fmt};

macro_rules! const_type {
    ($(
        $(#[$attr:meta])*
        pub type $struct:ident;
    )*) => {$(
        $(#[$attr])*
        #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
        pub struct $struct;
        impl fmt::Display for $struct {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Display::fmt(&f64::from(*self), f)
            }
        }
    )*};
}

macro_rules! const_type_impls {
    ($($struct:ident => $val:expr, $($t:ident)*;)*) => {$($(
        impl From<$struct> for $t {
            #[inline]
            fn from(_: $struct) -> $t { $val as $t }
        }
        impl PartialEq<$struct> for $t {
            #[inline]
            fn eq(&self, _: &$struct) -> bool { *self == ($val as $t) }
        }
        #[allow(unused_comparisons)]
        impl PartialOrd<$struct> for $t {
            #[inline]
            fn partial_cmp(&self, _: &$struct) -> Option<Ordering> {
                self.partial_cmp(&($val as $t))
            }
        }
        impl PartialEq<$t> for $struct {
            #[inline]
            fn eq(&self, rhs: &$t) -> bool { ($val as $t) == *rhs }
        }
        #[allow(unused_comparisons)]
        impl PartialOrd<$t> for $struct {
            #[inline]
            fn partial_cmp(&self, rhs: &$t) -> Option<Ordering> {
                ($val as $t).partial_cmp(rhs)
            }
        }
    )*)*};
}

const_type!(
    /// Zero.
    pub type _0;

    /// One.
    pub type _1;

    /// Two.
    pub type _2;

    /// Eighth turn in degrees.
    pub type _45;

    /// Quarter turn in degrees.
    pub type _90;

    /// Half turn in degrees.
    pub type _180;

    /// Three-quarters turn in degrees.
    pub type _270;

    /// Full turn in degrees.
    pub type _360;

    /// Eighth circumference of unit circle (pi/4, tau/8).
    pub type _45_rad;

    /// Quarter circumference of unit circle (pi/2, tau/4).
    pub type _90_rad;

    /// Half circumference of unit circle (pi, tau/2).
    pub type _180_rad;

    /// Three-quarters circumference of unit circle (3pi/2, 3tau/4).
    pub type _270_rad;

    /// Circumference of unit circle (2pi, tau).
    pub type _360_rad;
);

/// pi/4.
pub const _pi_4: _45_rad = _45_rad;

/// pi/2.
pub const _pi_2: _90_rad = _90_rad;

/// pi.
pub const _pi: _180_rad = _180_rad;

/// 3pi/2.
pub const _3pi_2: _270_rad = _270_rad;

/// 2pi.
pub const _2pi: _360_rad = _360_rad;

/// tau/8.
pub const _tau_8: _45_rad = _45_rad;

/// tau/4.
pub const _tau_4: _90_rad = _90_rad;

/// tau/2.
pub const _tau_2: _180_rad = _180_rad;

/// 3tau/4.
pub const _3tau_4: _270_rad = _270_rad;

/// tau.
pub const _tau: _360_rad = _360_rad;

const_type_impls!(
    _0 => 0, i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _1 => 1, i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _2 => 2, i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _45 => 45, i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _90 => 90, i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _180 => 180, u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _270 => 270, i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _360 => 360, i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64;
    _45_rad => 0.78539816339744830961566084581987572, f32 f64;
    _90_rad => 1.57079632679489661923132169163975144, f32 f64;
    _180_rad => 3.14159265358979323846264338327950288, f32 f64;
    _270_rad => 4.71238898038468985769396507491925432, f32 f64;
    _360_rad => 6.28318530717958647692528676655900576, f32 f64;
);

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! verify_equal {
        ($struct:ident == $val:expr; $($t:ident)*) => {
            [
                $val as f32,
                $($t::from($struct) as f32),*
            ].windows(2).for_each(|a| assert!(a[0] == a[1]))
        }
    }

    #[test]
    fn basic_ints() {
        verify_equal!(_0 == 0; i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_1 == 1; i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_2 == 2; i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
    }

    #[test]
    fn degrees() {
        verify_equal!(_45 == 45; i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_90 == 90; i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_180 == 180; u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_270 == 270; i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
        verify_equal!(_360 == 360; i16 u16 i32 u32 i64 u64 i128 u128 isize usize f32 f64);
    }

    #[test]
    fn pi_radians() {
        use core::f64::consts::PI;
        verify_equal!(_pi_4 == PI / 4.0; f32 f64);
        verify_equal!(_pi_2 == PI / 2.0; f32 f64);
        verify_equal!(_pi == PI; f32 f64);
        verify_equal!(_3pi_2 == 3.0 * PI / 2.0; f32 f64);
        verify_equal!(_2pi == 2.0 * PI; f32 f64);
    }

    #[test]
    fn tau_radians() {
        use core::f64::consts::PI;
        const TAU: f64 = PI * 2.0;
        verify_equal!(_tau_8 == TAU / 8.0; f32 f64);
        verify_equal!(_tau_4 == TAU / 4.0; f32 f64);
        verify_equal!(_tau_2 == TAU / 2.0; f32 f64);
        verify_equal!(_3tau_4 == 3.0 * TAU / 4.0; f32 f64);
        verify_equal!(_tau == TAU; f32 f64);
    }
}