use std::ops::Add;
use typenum::{Add1, UTerm, Unsigned, B1};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Nil;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Cons<H, T> {
pub head: H,
pub tail: T,
}
pub fn cons<H, T>(head: H, tail: T) -> Cons<H, T> {
Cons { head, tail }
}
pub trait PushFront<H> {
type Output;
fn push_front(self, head: H) -> Self::Output;
}
impl<NewH, H, T> PushFront<NewH> for Cons<H, T> {
type Output = Cons<NewH, Self>;
fn push_front(self, head: NewH) -> Self::Output {
cons(head, self)
}
}
impl<NewH> PushFront<NewH> for Nil {
type Output = Cons<NewH, Nil>;
fn push_front(self, head: NewH) -> Self::Output {
cons(head, self)
}
}
pub trait PushBack<U> {
type Output;
fn push_back(self, elem: U) -> Self::Output;
}
impl<U> PushBack<U> for Nil {
type Output = Cons<U, Nil>;
fn push_back(self, elem: U) -> Cons<U, Nil> {
cons(elem, Nil)
}
}
impl<U, H, T> PushBack<U> for Cons<H, T>
where
T: PushBack<U>,
{
type Output = Cons<H, T::Output>;
fn push_back(self, elem: U) -> Cons<H, T::Output> {
cons(self.head, self.tail.push_back(elem))
}
}
pub trait Append<List> {
type Appended;
fn append(self, list: List) -> Self::Appended;
}
impl<List> Append<List> for Nil {
type Appended = List;
fn append(self, list: List) -> List {
list
}
}
impl<List, H, T> Append<List> for Cons<H, T>
where
T: Append<List>,
{
type Appended = Cons<H, T::Appended>;
fn append(self, list: List) -> Cons<H, T::Appended> {
cons(self.head, self.tail.append(list))
}
}
pub trait Len {
type Len: Unsigned;
fn is_empty() -> bool {
Self::len() == 0
}
fn len() -> usize {
Self::Len::to_usize()
}
}
impl Len for Nil {
type Len = UTerm;
}
impl<Head, Tail> Len for Cons<Head, Tail>
where
Tail: Len,
<Tail as Len>::Len: Add<B1>,
<<Tail as Len>::Len as Add<B1>>::Output: Unsigned,
{
type Len = Add1<<Tail as Len>::Len>;
}
#[macro_export]
macro_rules! length {
($list:ty) => {
<<$list as $crate::cons::Len>::Len as typenum::Unsigned>::USIZE
};
}