use reify_reflect_core::{Reflect, RuntimeValue};
use std::marker::PhantomData;
#[derive(Default, Debug, Clone, Copy)]
pub struct Z;
#[derive(Default, Debug, Clone, Copy)]
pub struct S<N>(pub PhantomData<N>);
pub trait Nat {
fn to_u64() -> u64;
}
impl Nat for Z {
fn to_u64() -> u64 {
0
}
}
impl<N: Nat> Nat for S<N> {
fn to_u64() -> u64 {
1 + N::to_u64()
}
}
impl Reflect for Z {
type Value = RuntimeValue;
fn reflect() -> Self::Value {
RuntimeValue::Nat(0)
}
}
impl<N: Nat> Reflect for S<N> {
type Value = RuntimeValue;
fn reflect() -> Self::Value {
RuntimeValue::Nat(<S<N> as Nat>::to_u64())
}
}
pub trait Add<Rhs> {
type Result: Nat;
}
impl<N: Nat> Add<N> for Z {
type Result = N;
}
impl<M: Nat + Add<N>, N: Nat> Add<N> for S<M>
where
<M as Add<N>>::Result: Nat,
{
type Result = S<<M as Add<N>>::Result>;
}
pub trait Mul<Rhs> {
type Result: Nat;
}
impl<N: Nat> Mul<N> for Z {
type Result = Z;
}
impl<M, N> Mul<N> for S<M>
where
M: Nat + Mul<N>,
N: Nat + Add<<M as Mul<N>>::Result>,
<M as Mul<N>>::Result: Nat,
<N as Add<<M as Mul<N>>::Result>>::Result: Nat,
{
type Result = <N as Add<<M as Mul<N>>::Result>>::Result;
}
pub trait Lt<Rhs> {
const VALUE: bool;
}
impl Lt<Z> for Z {
const VALUE: bool = false;
}
impl<N: Nat> Lt<S<N>> for Z {
const VALUE: bool = true;
}
impl<M: Nat> Lt<Z> for S<M> {
const VALUE: bool = false;
}
impl<M: Nat + Lt<N>, N: Nat> Lt<S<N>> for S<M> {
const VALUE: bool = <M as Lt<N>>::VALUE;
}
pub type N0 = Z;
pub type N1 = S<N0>;
pub type N2 = S<N1>;
pub type N3 = S<N2>;
pub type N4 = S<N3>;
pub type N5 = S<N4>;
pub type N6 = S<N5>;
pub type N7 = S<N6>;
pub type N8 = S<N7>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_reflects_to_nat_0() {
assert_eq!(Z::reflect(), RuntimeValue::Nat(0));
}
#[test]
fn successor_reflects_correctly() {
assert_eq!(S::<Z>::reflect(), RuntimeValue::Nat(1));
assert_eq!(<S<S<Z>>>::reflect(), RuntimeValue::Nat(2));
assert_eq!(<S<S<S<Z>>>>::reflect(), RuntimeValue::Nat(3));
}
#[test]
fn nat_to_u64() {
assert_eq!(Z::to_u64(), 0);
assert_eq!(<S<Z>>::to_u64(), 1);
assert_eq!(<S<S<S<S<S<Z>>>>>>::to_u64(), 5);
}
#[test]
fn type_aliases() {
assert_eq!(N0::to_u64(), 0);
assert_eq!(N1::to_u64(), 1);
assert_eq!(N5::to_u64(), 5);
assert_eq!(N8::to_u64(), 8);
}
#[test]
fn addition() {
assert_eq!(<<Z as Add<N3>>::Result as Nat>::to_u64(), 3);
assert_eq!(<<N2 as Add<N3>>::Result as Nat>::to_u64(), 5);
assert_eq!(<<N1 as Add<N0>>::Result as Nat>::to_u64(), 1);
}
#[test]
fn multiplication() {
assert_eq!(<<Z as Mul<N3>>::Result as Nat>::to_u64(), 0);
assert_eq!(<<N2 as Mul<N3>>::Result as Nat>::to_u64(), 6);
assert_eq!(<<N1 as Mul<N5>>::Result as Nat>::to_u64(), 5);
assert_eq!(<<N3 as Mul<N1>>::Result as Nat>::to_u64(), 3);
}
#[test]
#[allow(clippy::assertions_on_constants)]
fn less_than() {
assert!(!<Z as Lt<Z>>::VALUE);
assert!(<Z as Lt<S<Z>>>::VALUE);
assert!(!<S<Z> as Lt<Z>>::VALUE);
assert!(<N2 as Lt<N5>>::VALUE);
assert!(!<N5 as Lt<N2>>::VALUE);
assert!(!<N3 as Lt<N3>>::VALUE);
}
#[test]
fn reflect_returns_runtime_value() {
assert_eq!(N5::reflect(), RuntimeValue::Nat(5));
assert_eq!(N0::reflect(), RuntimeValue::Nat(0));
}
}