#include <pgs/pgs.hpp>
#include <gtest/gtest.h>
#include <sstream>
#include <iostream>
namespace {
template <class T> struct cons_t;
struct nil_t {};
bool operator==(nil_t const&, nil_t const&) {
return true;
}
template <class T>
template <class L>
struct list_value_type;
template <class T>
struct list_value_type<list<T>> { typedef T type; };
template <class L>
using list_value_type_t = typename list_value_type<L>::type;
template <class T>
struct cons_t {
T hd;
list<T> tl;
template <class U, class V>
cons_t (U&& hd, V&& tl) :
hd {std::forward<U> (hd)}, tl {std::forward<V>(tl)} {
}
};
template <class T>
bool operator== (cons_t<T> const& l, cons_t<T> const& r) {
return l.hd == r.hd && l.tl == r.tl;
}
template <class T>
bool operator!= (cons_t<T> const& l, cons_t<T> const& r) {
return !(l == r);
}
template <class T>
constexpr list<T> nil () {
}
template <class U, class V>
inline list<decay_t<U>> cons (U&& hd, V&& tl) {
using T = decay_t<U>;
}
template <class T>
inline T const& hd (list<T> const& l) {
return l.template match <T const&> (
[] (nil_t const&) -> T const& {
[] (cons_t<T> const& n) -> T const& {
return n.hd; }
);
}
template <class T>
inline list<T> const& tl (list<T> const& l) {
return l.template match <list<T> const&> (
[] (nil_t const&) -> list<T> const& {
[] (cons_t<T> const& n) -> list<T> const& {
return n.tl; }
);
}
template <class F, class AccT, class T>
AccT
fold_left (F f, AccT
const& acc, list<T>
const& l) {
return l.template match<AccT>(
[=](nil_t){ return acc; },
[=](cons_t<T> const& x) {
);
}
template <class F, class T, class AccT>
AccT fold_right (F f, list<T> const& l, AccT const& acc) {
return l.template match<AccT>(
[=](nil_t){ return acc; },
[=](cons_t<T> const& x) {
return f (x.hd, (fold_right (f, x.tl, acc))); }
);
}
template<class T>
inline list<T> rev (list<T> const& l) {
[](list<T> const& acc, T const& x) { return cons (x, acc); }
, nil<T>()
, l
);
}
template <class T>
inline int length (list<T> const& l) {
return fold_left ([](
int acc, T
const&) {
return ++acc; }, 0, l);
}
template <class T>
return l.template match<T const&>(
[](nil_t) -> T const& {
[=](cons_t<T> const& x) -> T const& { return i == 0 ? x.hd : nth (x.tl, i - 1); }
);
}
namespace list_detail {
list<int> range_aux (list<int> const& acc, int const s, int e) {
if (s >= e) return acc;
return range_aux (cons (s, acc), s + 1, e);
};
}
return rev (list_detail::range_aux (nil<int> (), begin, end));
};
template <class T>
list<T> append (list<T> const& l, list<T> const& r) {
return fold_right (
[](T const& x, list<T> const& acc) -> list<T> { return cons (x, acc); }
, l, r);
}
template <class T>
inline list<T> unit (T&& a) {
return cons (std::forward<T>(a), nil<T>());
}
template <class T, class F>
auto operator * (list<T> const& a, F k) -> decltype (k (hd (a))) {
using result_t = decltype (k (hd (a)));
using t = list_value_type_t<result_t>;
return a.template match<result_t>(
[](nil_t const&) { return nil<t>(); },
[=](cons_t<T> const& x) { return append (k (x.hd), x.tl * k); }
);
}
template <class T>
list<T> join (list<list<T>> const& z) {
return z * [](list<T> const& m) { return m; };
}
template <class T, class F>
list<T> map (F f, list<T> const& m) {
return m * [=](T const& a) { return unit (f (a)); };
}
}
ASSERT_EQ (rev (rg (1, 4)), cons (3, cons (2, cons (1, nil<int> ()))));
ASSERT_EQ (append (rg (1, 4), rg (4, 8)), rg (1, 8));
ASSERT_EQ (join (cons (rg (1, 3), cons (rg (3, 5), nil<list<int>>()))) , rg (1, 5));
list<int> l = rg (1, 3);
list<int> m =
map ([](int m) { return m * m; }, rg (1, 3));
ASSERT_EQ (m, cons (1, cons (4, nil<int>())));
auto n =
l * [&m](int x) {
return m * [=](int y) {
};
}