use std::marker::PhantomData;
pub trait NumType: Into<i64> + Into<i32> + Into<i16> + Into<i8> + Into<isize> {
#[inline(always)] fn new() -> Self;
}
pub trait PosType: NumType + Into<u64> + Into<u32> + Into<u16> + Into<u8> + Into<usize> {}
pub trait NegType: NumType {}
#[allow(dead_code)]
#[derive(Copy,Clone,PartialEq,Eq,PartialOrd,Ord)]
pub struct Zero;
#[allow(dead_code)]
#[derive(Copy,Clone,PartialEq,Eq,PartialOrd,Ord)]
pub struct Succ<N> {
phantom: PhantomData<N>
}
#[allow(dead_code)]
#[derive(Copy,Clone,PartialEq,Eq,PartialOrd,Ord)]
pub struct Pred<N> {
phantom: PhantomData<N>
}
impl NumType for Zero {
#[inline(always)] fn new() -> Self { Zero }
}
impl PosType for Zero {}
impl NegType for Zero {}
impl<N: NumType> NumType for Succ<N> {
#[inline(always)] fn new() -> Self { Succ { phantom: PhantomData } }
}
impl<N: NumType> NumType for Pred<N> {
#[inline(always)] fn new() -> Self { Pred { phantom: PhantomData } }
}
impl<N: PosType> PosType for Succ<N> {}
impl<N: NegType> NegType for Pred<N> {}
macro_rules! impl_into_signed {
($($ity:ty)+) => ($(
impl<N: NumType> Into<$ity> for Succ<N> {
#[inline(always)] fn into(self) -> $ity { Into::<$ity>::into(N::new()) + 1 }
}
impl<N: NumType> Into<$ity> for Pred<N> {
#[inline(always)] fn into(self) -> $ity { Into::<$ity>::into(N::new()) - 1 }
}
impl Into<$ity> for Zero {
#[inline(always)] fn into(self) -> $ity { 0 }
}
)+)
}
macro_rules! impl_into_unsigned {
($($ity:ty)+) => ($(
impl<N: PosType> Into<$ity> for Succ<N> {
#[inline(always)] fn into(self) -> $ity { Into::<$ity>::into(N::new()) + 1 }
}
impl Into<$ity> for Zero {
#[inline(always)] fn into(self) -> $ity { 0 }
}
)+)
}
impl_into_signed!(i64 i32 i16 i8 isize);
impl_into_unsigned!(u64 u32 u16 u8 usize);
pub trait Neg: NumType {
type Out: NumType;
}
impl Neg for Zero { type Out = Zero; }
impl<A: PosType, B: NegType> Neg for Succ<A> where A: Neg<Out=B> { type Out = Pred<B>; }
impl<A: NegType, B: PosType> Neg for Pred<A> where A: Neg<Out=B> { type Out = Succ<B>; }
pub trait Incr: NumType {
type Out: NumType;
}
impl Incr for Zero { type Out = Succ<Zero>; }
impl<A: PosType> Incr for Succ<A> { type Out = Succ<Succ<A>>; }
impl<A: NegType> Incr for Pred<A> { type Out = A; }
pub trait Decr: NumType {
type Out: NumType;
}
impl Decr for Zero { type Out = Pred<Zero>; }
impl<A: PosType> Decr for Succ<A> { type Out = A; }
impl<A: NegType> Decr for Pred<A> { type Out = Pred<Pred<A>>; }
pub trait Add<RHS>: NumType {
type Out: NumType;
}
impl<RHS: NumType> Add<RHS> for Zero { type Out = RHS; }
impl<A: PosType, RHS, B: NumType> Add<RHS> for Succ<A> where RHS: Incr<Out=B>, A: Add<B> { type Out = A::Out; }
impl<A: NegType, RHS, B: NumType> Add<RHS> for Pred<A> where RHS: Decr<Out=B>, A: Add<B> { type Out = A::Out; }
pub trait Sub<RHS>: NumType {
type Out: NumType;
}
impl<A, RHS, B: NumType> Sub<RHS> for A where RHS: Neg<Out=B>, A: Add<B> { type Out = A::Out; }
pub trait Halve: NumType {
type Out: NumType;
}
impl Halve for Zero { type Out = Zero; }
impl<A: PosType, B: NumType> Halve for Succ<Succ<A>> where A: Halve<Out=B> { type Out = Succ<B>; }
impl<A: NegType, B: NumType> Halve for Pred<Pred<A>> where A: Halve<Out=B> { type Out = Pred<B>; }
pub trait Mul<RHS>: NumType {
type Out: NumType;
}
impl<N: NumType> Mul<N> for Zero { type Out = Zero; }
impl<A: PosType, RHS, B: NumType> Mul<RHS> for Succ<A> where A: Mul<RHS, Out=B>, RHS: Add<B> { type Out = RHS::Out; }
impl<A: NegType, RHS, B, C: NumType> Mul<RHS> for Pred<A> where A: Mul<RHS, Out=C>, RHS: Neg<Out=B>, B: Add<C> { type Out = B::Out; }
pub trait Div<RHS>: NumType {
type Out: NumType;
}
impl<A: PosType> Div<Succ<A>> for Zero { type Out = Zero; }
impl<A: NegType> Div<Pred<A>> for Zero { type Out = Zero; }
impl<A: NumType, B: NumType, C: NumType> Div<Succ<B>> for Succ<A> where A: Sub<B, Out=C>, C: Div<Succ<B>> { type Out = Succ<C::Out>; }
impl<N: NegType, NN: NegType, P: PosType, PP: PosType> Div<Pred<NN>> for Pred<N>
where N: Neg<Out=P>, NN: Neg<Out=PP>, Succ<P>: Div<Succ<PP>> { type Out = <Succ<P> as Div<Succ<PP>>>::Out; }
impl<P: NumType, N: NegType, PP: NumType, PPP: NumType> Div<Pred<N>> for Succ<P>
where N: Neg<Out=PP>, Succ<P>: Div<Succ<PP>, Out=Succ<PPP>>, Succ<PPP>: Neg { type Out = <Succ<PPP> as Neg>::Out; }
impl<P: NumType, N: NegType, PP: NumType, PPP: NumType> Div<Succ<P>> for Pred<N>
where N: Neg<Out=PP>, Succ<PP>: Div<Succ<P>, Out=Succ<PPP>>, Succ<PPP>: Neg { type Out = <Succ<PPP> as Neg>::Out; }
pub type P1 = Succ<Zero>;
pub type P2 = Succ<P1>;
pub type P3 = Succ<P2>;
pub type P4 = Succ<P3>;
pub type P5 = Succ<P4>;
pub type P6 = Succ<P5>;
pub type P7 = Succ<P6>;
pub type P8 = Succ<P7>;
pub type P9 = Succ<P8>;
pub type N1 = Pred<Zero>;
pub type N2 = Pred<N1>;
pub type N3 = Pred<N2>;
pub type N4 = Pred<N3>;
pub type N5 = Pred<N4>;
pub type N6 = Pred<N5>;
pub type N7 = Pred<N6>;
pub type N8 = Pred<N7>;
pub type N9 = Pred<N8>;
type Plus5<N> = Succ<Succ<Succ<Succ<Succ<N>>>>>;
type Plus10<N> = Plus5<Plus5<N>>;
type Plus50<N> = Plus10<Plus10<Plus10<Plus10<Plus10<N>>>>>;
#[test]
fn zero_sized() {
use std::mem::size_of;
assert_eq!(size_of::<Zero>(), 0);
assert_eq!(size_of::<P1>(), 0);
assert_eq!(size_of::<N1>(), 0);
assert_eq!(size_of::<Plus50<Zero>>(), 0);
}
#[test]
fn into_number() {
assert_eq!(0, Zero::new().into());
assert_eq!(-3, N3::new().into());
assert_eq!(2, P2::new().into());
assert_eq!(2i8, P2::new().into());
assert_eq!(2u64, P2::new().into());
assert_eq!(2u8, P2::new().into());
type P63 = Plus10<Plus50<P3>>;
assert_eq!(63, P63::new().into());
}
#[test]
fn operations() {
fn neg<A: NumType, Out: NumType>() -> i32 where A: Neg<Out=Out> {
Out::new().into()
}
fn add<A: NumType, B: NumType, Out: NumType>() -> i32 where A: Add<B, Out=Out> {
Out::new().into()
}
fn sub<A: NumType, B: NumType, Out: NumType>() -> i32 where A: Sub<B, Out=Out> {
Out::new().into()
}
fn halve<A: NumType, Out: NumType>() -> i32 where A: Halve<Out=Out> {
Out::new().into()
}
assert_eq!(-5, neg::<P5,_>());
assert_eq!( 5, neg::<N5,_>());
assert_eq!( 0, neg::<Zero,_>());
assert_eq!( 5, add::<P2,P3,_>());
assert_eq!(-1, sub::<P2,P3,_>());
assert_eq!( 2, halve::<P4,_>());
assert_eq!(-25, neg::<Plus5<Plus10<Plus10<Zero>>>,_>());
assert_eq!( 45, sub::<Plus50<Zero>, P5,_>());
assert_eq!( 50, halve::<Plus50<Plus50<Zero>>,_>());
}
#[test]
fn division() {
fn div<A: NumType, B: NumType, Out: NumType>() -> i32 where A: Div<B, Out=Out> {
Out::new().into()
}
assert_eq!(0, div::<Zero,P1,_>());
assert_eq!(1, div::<P4,P4,_>());
assert_eq!(2, div::<P4,P2,_>());
assert_eq!(4, div::<P4,P1,_>());
assert_eq!(1, div::<N4,N4,_>());
assert_eq!(2, div::<N4,N2,_>());
assert_eq!(4, div::<N4,N1,_>());
assert_eq!(-1, div::<N4,P4,_>());
assert_eq!(-2, div::<N4,P2,_>());
assert_eq!(-4, div::<N4,P1,_>());
assert_eq!(-1, div::<P4,N4,_>());
assert_eq!(-2, div::<P4,N2,_>());
assert_eq!(-4, div::<P4,N1,_>());
assert_eq!( 2, div::<Plus10<Plus10<Zero>>,Plus10<Zero>,_>());
assert_eq!(10, div::<Plus10<Plus10<Zero>>,P2,_>());
assert_eq!( 4, div::<Plus10<Plus10<Zero>>,P5,_>());
}
#[test]
fn multiplication() {
fn mul<A: NumType, B: NumType, Out: NumType>() -> i32 where A: Mul<B, Out=Out> {
Out::new().into()
}
assert_eq!(0, mul::<Zero,Zero,_>());
assert_eq!(0, mul::<P1,Zero,_>());
assert_eq!(0, mul::<Zero,P1,_>());
assert_eq!(1, mul::<P1,P1,_>());
assert_eq!(2, mul::<P2,P1,_>());
assert_eq!(2, mul::<P1,P2,_>());
assert_eq!(4, mul::<P2,P2,_>());
assert_eq!(-1, mul::<P1,N1,_>());
assert_eq!(-2, mul::<P2,N1,_>());
assert_eq!(-2, mul::<P1,N2,_>());
assert_eq!(-4, mul::<P2,N2,_>());
assert_eq!(-1, mul::<N1,P1,_>());
assert_eq!(-2, mul::<N2,P1,_>());
assert_eq!(-2, mul::<N1,P2,_>());
assert_eq!(-4, mul::<N2,P2,_>());
assert_eq!(1, mul::<N1,N1,_>());
assert_eq!(2, mul::<N2,N1,_>());
assert_eq!(2, mul::<N1,N2,_>());
assert_eq!(4, mul::<N2,N2,_>());
assert_eq!(25, mul::<P5,P5,_>());
assert_eq!(25, mul::<N5,N5,_>());
}