use core::ops::{Add, Sub};
use std::marker::PhantomData;
pub use typenum;
use typenum::{Bit, Diff, Sum, UInt, Unsigned, U0};
#[derive(Debug)]
pub struct Absorb<N>(PhantomData<N>);
#[derive(Debug)]
pub struct Squeeze<N>(PhantomData<N>);
pub trait IOWord: private::Sealed {}
impl<N: Unsigned> IOWord for Absorb<N> {}
impl<N: Unsigned> IOWord for Squeeze<N> {}
pub trait List: private::Sealed {
fn unit() -> Self;
fn is_empty() -> bool;
}
impl<Item: IOWord, Next: List> List for Cons<Item, Next> {
fn unit() -> Self {
Cons {
_phantom: PhantomData,
}
}
fn is_empty() -> bool {
false
}
}
impl List for Nil {
fn unit() -> Self {
Nil
}
fn is_empty() -> bool {
true
}
}
#[derive(Debug)]
pub struct Cons<Item, Next: List> {
_phantom: PhantomData<(Item, Next)>,
}
#[derive(Debug)]
pub struct Nil;
#[macro_export]
macro_rules! iopat {
() => { $crate::traits::Nil };
(...$rest:ty) => { $rest };
($a:ty) => { $crate::iopat![$a,] };
($a:ty, $($tok:tt)*) => {
$crate::traits::Cons<$a, $crate::iopat![$($tok)*]>
};
}
pub trait Normalize: List {
type Output: List;
}
pub type Norm<T> = <T as Normalize>::Output;
impl Normalize for Nil {
type Output = Nil;
}
impl<L: Normalize> Normalize for Cons<Squeeze<U0>, L> {
type Output = Norm<L>;
}
impl<L: Normalize> Normalize for Cons<Absorb<U0>, L> {
type Output = Norm<L>;
}
impl<U: Unsigned, B: Bit> Normalize for Cons<Absorb<UInt<U, B>>, Nil> {
type Output = Cons<Absorb<UInt<U, B>>, Nil>;
}
impl<U: Unsigned, B: Bit> Normalize for Cons<Squeeze<UInt<U, B>>, Nil> {
type Output = Cons<Squeeze<UInt<U, B>>, Nil>;
}
impl<U: Unsigned, B: Bit, M: Unsigned, T: List> Normalize
for Cons<Absorb<UInt<U, B>>, Cons<Absorb<M>, T>>
where
UInt<U, B>: Add<M>, Cons<Absorb<Sum<UInt<U, B>, M>>, T>: Normalize,
{
type Output = Norm<Cons<Absorb<Sum<UInt<U, B>, M>>, T>>;
}
impl<U: Unsigned, B: Bit, M: Unsigned, T: List> Normalize
for Cons<Squeeze<UInt<U, B>>, Cons<Squeeze<M>, T>>
where
UInt<U, B>: Add<M>, Cons<Squeeze<Sum<UInt<U, B>, M>>, T>: Normalize,
{
type Output = Norm<Cons<Squeeze<Sum<UInt<U, B>, M>>, T>>;
}
impl<U: Unsigned, B: Bit, U2: Unsigned, B2: Bit, T: List> Normalize
for Cons<Squeeze<UInt<U, B>>, Cons<Absorb<UInt<U2, B2>>, T>>
where
Cons<Absorb<UInt<U2, B2>>, T>: Normalize,
{
type Output = Cons<Squeeze<UInt<U, B>>, Norm<Cons<Absorb<UInt<U2, B2>>, T>>>;
}
impl<U: Unsigned, B: Bit, U2: Unsigned, B2: Bit, T: List> Normalize
for Cons<Absorb<UInt<U, B>>, Cons<Squeeze<UInt<U2, B2>>, T>>
where
Cons<Squeeze<UInt<U2, B2>>, T>: Normalize,
{
type Output = Cons<Absorb<UInt<U, B>>, Norm<Cons<Squeeze<UInt<U2, B2>>, T>>>;
}
impl<U: Unsigned, B: Bit, T: List> Normalize for Cons<Squeeze<UInt<U, B>>, Cons<Absorb<U0>, T>>
where
Cons<Squeeze<UInt<U, B>>, T>: Normalize,
{
type Output = Norm<Cons<Squeeze<UInt<U, B>>, T>>;
}
impl<U: Unsigned, B: Bit, T: List> Normalize for Cons<Absorb<UInt<U, B>>, Cons<Squeeze<U0>, T>>
where
Cons<Absorb<UInt<U, B>>, T>: Normalize,
{
type Output = Norm<Cons<Absorb<UInt<U, B>>, T>>;
}
pub trait Consume<Op: IOWord> {
type Output: List;
}
#[allow(dead_code)]
pub type Use<T, U> = <T as Consume<U>>::Output;
impl<N, T: List> Consume<Absorb<U0>> for Cons<Absorb<N>, T>
where
N: Unsigned,
{
type Output = Self;
}
impl<N, T: List> Consume<Squeeze<U0>> for Cons<Squeeze<N>, T>
where
N: Unsigned,
{
type Output = Self;
}
impl<U, B, N, T> Consume<Absorb<UInt<U, B>>> for Cons<Absorb<N>, T>
where
U: Unsigned,
B: Bit,
N: Unsigned,
T: List,
N: Sub<UInt<U, B>>, Cons<Absorb<Diff<N, UInt<U, B>>>, T>: Normalize,
{
type Output = Norm<Cons<Absorb<Diff<N, UInt<U, B>>>, T>>;
}
impl<U, B, N, T> Consume<Squeeze<UInt<U, B>>> for Cons<Squeeze<N>, T>
where
U: Unsigned,
B: Bit,
N: Unsigned,
T: List,
N: Sub<UInt<U, B>>, Cons<Squeeze<Diff<N, UInt<U, B>>>, T>: Normalize,
{
type Output = Norm<Cons<Squeeze<Diff<N, UInt<U, B>>>, T>>;
}
mod private {
pub trait Sealed {}
impl<N> Sealed for super::Absorb<N> {}
impl<N> Sealed for super::Squeeze<N> {}
impl Sealed for super::Nil {}
impl<H, T: super::List> Sealed for super::Cons<H, T> {}
}
#[cfg(test)]
mod tests {
use super::*;
use typenum::assert_type_eq;
use typenum::{U1, U2, U3, U4, U5, U6};
#[test]
fn normalizes() {
assert_type_eq!(Norm<iopat![Absorb<U2>, Absorb<U3>]>, iopat![Absorb<U5>]);
assert_type_eq!(Norm<iopat![Squeeze<U2>, Squeeze<U3>]>, iopat![Squeeze<U5>]);
assert_type_eq!(
Norm<iopat![Squeeze<U2>, Absorb<U3>]>,
iopat![Squeeze<U2>, Absorb<U3>]
);
assert_type_eq!(
Norm<iopat![Squeeze<U2>, Squeeze<U3>, Absorb<U1>]>,
iopat![Squeeze<U5>, Absorb<U1>]
);
assert_type_eq!(Norm<iopat![Absorb<U0>, Absorb<U3>]>, iopat![Absorb<U3>]);
assert_type_eq!(Norm<iopat![Absorb<U0>, Squeeze<U3>]>, iopat![Squeeze<U3>]);
assert_type_eq!(Norm<iopat![Squeeze<U0>, Squeeze<U3>]>, iopat![Squeeze<U3>]);
assert_type_eq!(Norm<iopat![Squeeze<U0>, Absorb<U3>]>, iopat![Absorb<U3>]);
assert_type_eq!(
Norm<iopat![Absorb<U3>, Squeeze<U0>, Absorb<U1>]>,
iopat![Absorb<U4>]
);
assert_type_eq!(
Norm<iopat![Squeeze<U3>, Absorb<U0>, Squeeze<U1>]>,
iopat![Squeeze<U4>]
);
}
#[test]
fn uses() {
assert_type_eq!(
Use<iopat![Absorb<U5>], Absorb<U2>>,
iopat![Absorb<U3>]
);
assert_type_eq!(
Use<iopat![Absorb<U5>, Squeeze<U2>], Absorb<U2>>,
iopat![Absorb<U3>, Squeeze<U2>]
);
assert_type_eq!(
Use<iopat![Squeeze<U5>], Squeeze<U2>>,
iopat![Squeeze<U3>]
);
assert_type_eq!(
Use<iopat![Squeeze<U5>, Absorb<U2>], Squeeze<U2>>,
iopat![Squeeze<U3>, Absorb<U2>]
);
assert_type_eq!(Use<iopat![Absorb<U5>], Absorb<U5>>, Nil);
assert_type_eq!(
Use<iopat![Absorb<U5>], Absorb<U0>>,
iopat![Absorb<U5>]
);
assert_type_eq!(
Use<iopat![Squeeze<U5>], Squeeze<U0>>,
iopat![Squeeze<U5>]
);
assert_type_eq!(
Use<iopat![Absorb<U3>, Squeeze<U2>], Absorb<U3>>,
iopat![Squeeze<U2>]
);
assert_type_eq!(
Use<Use<iopat![Squeeze<U3>, Absorb<U5>, Absorb<U1>], Squeeze<U3>>, Absorb<U6>>,
Nil
);
}
}