#![macro_use]
use std::cmp::Ordering;
use std::slice::Iter;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
pub use List::{Cons, Nil};
#[macro_export]
macro_rules! ls[
[] => (List::Nil);
[$x:expr] => (List::Cons($x, Box::new(List::Nil)));
[$x:expr, $($xs:expr),+] => (List::Cons($x, Box::new(ls![$($xs),+])));
];
impl<T: Clone> From<Iter<'_, T>> for List<T> {
fn from(iter: Iter<'_, T>) -> Self {
let mut list = ls![];
for x in iter {
list = list.append(x.clone());
}
list
}
}
impl<T: Clone> From<&[T]> for List<T> {
fn from(array: &[T]) -> Self {
List::from(array.iter()).map(|x| x)
}
}
impl<T: Eq> List<T> {
pub fn new<A>() -> Self {
Nil
}
pub fn is_empty(&self) -> bool {
self.eq(&Nil)
}
pub fn null(&self) -> bool {
self.is_empty()
}
pub fn len(&self) -> u32 {
fn aux<T>(xs: &List<T>, size: u32) -> u32 {
match *xs {
Nil => size,
Cons(_, ref tail) => aux(tail, size + 1),
}
};
aux(self, 0)
}
}
impl<T: Clone> List<T> {
pub fn prepend(self, x: T) -> Self {
Cons(x, Box::new(self))
}
pub fn append(self, item: T) -> Self {
match self {
Nil => ls![item],
Cons(head, tail) => tail.append(item).prepend(head),
}
}
pub fn concat(self, other: Self) -> Self {
match self {
Nil => other,
Cons(head, tail) => tail.concat(other).prepend(head),
}
}
pub fn get(&self, index: u32) -> Option<&T> {
match self {
Nil => None,
Cons(head, _) if index == 0 => Some(head),
Cons(_, ref tail) => tail.get(index - 1),
}
}
pub fn head(&self) -> Option<&T> {
self.get(0)
}
pub fn tail(&self) -> Self {
match self {
Nil => (*self).clone(),
Cons(_, ref tail) => unsafe {
let t = (*tail).clone();
(*Box::into_raw(t)).clone()
},
}
}
pub fn foldl<R>(self, func: impl Fn(R, T) -> R + Copy, init: R) -> R {
match self {
Nil => init,
Cons(x, tail) => tail.foldl(func, func(init, x)),
}
}
pub fn foldr<R>(self, func: impl Fn(T, R) -> R + Copy, init: R) -> R {
match self {
Nil => init,
Cons(x, tail) => func(x, tail.foldr(func, init)),
}
}
pub fn reverse(self) -> List<T> {
fn aux<T>(xs: List<T>, ys: List<T>) -> List<T> {
match ys {
Nil => xs,
Cons(y, tail) => aux(Cons(y, Box::new(xs)), *tail),
}
};
aux(Nil, self)
}
pub fn map<R>(self, func: impl Fn(T) -> R + Copy) -> List<R> {
match self {
Nil => Nil,
Cons(x, tail) => Cons(func(x), Box::new(tail.map(func))),
}
}
pub fn filter(self, func: impl Fn(&T) -> bool + Copy) -> Self {
match self {
Nil => self,
Cons(x, tail) => {
if func(&x) {
Cons(x, Box::new(tail.filter(func)))
} else {
tail.filter(func)
}
}
}
}
fn chop(self, ign: u32) -> Self {
if ign == 0 {
self
} else {
match self {
Cons(_, tx) => tx.chop(ign - 1),
_ => panic!("TODO: meaningful error message"),
}
}
}
pub fn qsort_by(self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy) -> Self {
match self {
Nil => self,
Cons(head, tail) => {
let smaller = tail
.clone()
.filter(|x| cmpfn(x, &head) == Ordering::Less)
.qsort_by(cmpfn);
let bigger = tail
.filter(|x| cmpfn(x, &head) != Ordering::Less)
.qsort_by(cmpfn);
smaller.append(head).concat(bigger)
}
}
}
}
impl<T> List<T>
where
T: Copy,
T: Ord,
{
pub fn qsort(self) -> Self {
self.qsort_by(|x, y| x.cmp(y))
}
pub fn sort(&self) -> Self {
self.sort_by(|x, y| x.cmp(y))
}
pub fn sort_by(&self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy) -> Self {
let ln = self.len();
if ln <= 1 {
return self.clone();
}
let l1 = self.clone().chop(ln / 2);
let l2 = self.clone().reverse().chop(ln - (ln / 2));
l1.sort_by(cmpfn).merge(cmpfn, &l2.sort_by(cmpfn))
}
pub fn partition(self, p: fn(T) -> bool) -> (Self, Self) {
fn aux<T: Clone + Copy + Eq + Ord>(
yes: List<T>,
no: List<T>,
p: fn(T) -> bool,
xs: List<T>,
) -> (List<T>, List<T>) {
match xs {
Nil => (yes, no),
Cons(x, tx) => {
if p(x) {
aux(yes.append(x), no, p, *tx)
} else {
aux(yes, no.append(x), p, *tx)
}
}
}
};
aux(Nil, Nil, p, self)
}
pub fn merge(&self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy, ys: &List<T>) -> Self {
match (self, ys) {
(Nil, l) => l.clone(),
(l, Nil) => l.clone(),
(Cons(x, tx), Cons(y, ty)) => {
let cmp = cmpfn(x, y);
if cmp == Ordering::Equal || cmp == Ordering::Less {
Cons(*x, Box::new(ys.merge(cmpfn, &**tx)))
} else {
Cons(*y, Box::new(self.merge(cmpfn, &**ty)))
}
}
}
}
}