Newer
Older
Import / research / reflection / examples / test.cpp
///////////////////////////////////////////////////////////////////////////////
// COPYRIGHT HEADER ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//
//  C++ Views and Pipes
//  by John Ryland
//  (c) Copyright 2019
//


///////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <sstream>
#include <array>
#include <regex>


///////////////////////////////////////////////////////////////////////////////
// TYPE TRAITS ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Container Traits ///////////////////////////////////////////////////////////

template <typename T, typename _ = void>
struct has_container_traits : std::false_type {};


template <typename... Ts>
struct has_container_traits_helper {};


template <typename T>
struct has_container_traits<T, std::conditional_t<false, has_container_traits_helper<
       // Const Container Trails:
       typename T::value_type,
       typename T::const_iterator,
       typename T::const_reverse_iterator,
       decltype(std::declval<T>().crbegin()),
       decltype(std::declval<T>().crend()),
       decltype(std::declval<T>().cbegin()),
       decltype(std::declval<T>().cend())
>, void>> : public std::true_type {};


template <typename Container,
          typename std::enable_if<has_container_traits<Container>{}, bool>::type = true>
struct is_container
{
  using type = bool;
};


template <typename Container>
using is_container_t = typename is_container<Container>::type;


// View Traits ////////////////////////////////////////////////////////////////

template <typename T, typename _ = void>
struct has_view_traits : std::false_type {};


template <typename... Ts>
struct has_view_traits_helper {};


/*
template <typename T>
class view             // Not actually needed, but defines the expected interface of a view
{
public:
  // It is expected that it should be possible to go to the first or last, and from there
  // to request the next or previous, and also to then move in either direction.
  using element_type = T;
  virtual bool first() = 0;        // sets current to first (returns false if none)
  virtual bool last() = 0;         // sets current to last (returns false if none)
  virtual bool next() = 0;         // moves current to next (returns false if current is last)
  virtual bool previous() = 0;     // moves current to previous (returns false if current is first)
  virtual const T& current() = 0;  // returns current (defaults to first)
};
*/


template <typename T>
struct has_view_traits<T, std::conditional_t<false, has_view_traits_helper<
       // View Traits:
       typename T::element_type,
       decltype(std::declval<T>().first()),
       decltype(std::declval<T>().last()),
       decltype(std::declval<T>().next()),
       decltype(std::declval<T>().previous()),
       decltype(std::declval<T>().current())
>, void>> : public std::true_type {};


template <typename View,
          typename std::enable_if<has_view_traits<View>{}, bool>::type = true>
struct is_view
{
  using type = bool;
};


template <typename View>
using is_view_t = typename is_view<View>::type;


///////////////////////////////////////////////////////////////////////////////
// VIEW TYPES /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Container View /////////////////////////////////////////////////////////////

template <typename Container, typename BaseType, is_container_t<BaseType> = true>
class container_wrapper_view
{
public:
  container_wrapper_view(Container a_container)
    : m_container(a_container)
  {
  }

  // implements View
  using element_type = typename BaseType::value_type;
  bool first()         { m_it = m_container.cbegin();          return m_it != m_container.cend(); }
  bool last()          { m_it = std::prev(m_container.cend()); return m_it != std::prev(m_container.cbegin()); }
  bool next()          { m_it = std::next(m_it);               return m_it != m_container.cend(); }
  bool previous()      { m_it = std::prev(m_it);               return m_it != std::prev(m_container.cbegin()); }
  const element_type& current() { return *m_it; }
private:
  Container                                 m_container;
  typename BaseType::const_iterator         m_it;
};


// Range View /////////////////////////////////////////////////////////////////

template <typename T>
class range_view
{
public:
  range_view(T start, T end)
    : m_start(start)
    , m_end(end)
  {
  }

  // implements View
  using element_type = T;
  bool first()         { m_current = m_start; return m_start <= m_end; }
  bool last()          { m_current = m_end;   return m_start <= m_end; }
  bool next()          { ++m_current; return m_current <= m_end;       }
  bool previous()      { --m_current; return m_current >= m_start;     }
  const T& current()   { return m_current; }
private:
  T m_current;
  T m_start;
  T m_end;
};


// Match View ////////////////////////////////////////////////////////////////

template <typename ParentView, typename Functor, is_view_t<ParentView> = true>
class match_view
{
public:
  using element_type = typename ParentView::element_type;
  match_view(const ParentView& a_parentView, const Functor& a_match)
    : m_parentView(a_parentView)
    , m_match(a_match)
  {
  }

  // implements View
  bool first()                  { if (!m_parentView.first())    return false; return nextValid();     }
  bool last()                   { if (!m_parentView.last())     return false; return previousValid(); }
  bool next()                   { if (!m_parentView.next())     return false; return nextValid();     }
  bool previous()               { if (!m_parentView.previous()) return false; return previousValid(); }
  const element_type& current() { return m_parentView.current(); }
private:
  bool nextValid()              { while (!m_match(m_parentView.current())) { if (!m_parentView.next())     return false; } return true; }
  bool previousValid()          { while (!m_match(m_parentView.current())) { if (!m_parentView.previous()) return false; } return true; }
  ParentView    m_parentView;
  Functor       m_match;
};


// Filter View ////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class filter_view : public match_view<ParentView, std::function<bool(typename ParentView::element_type)>>
{
public:
  // inherits View
  using element_type = typename ParentView::element_type;
  using function_type = std::function<bool(element_type)>;

  filter_view(const ParentView& a_parentView, const function_type& a_filter)
    : match_view<ParentView, function_type>(a_parentView, [a_filter](element_type a_item) { return !a_filter(a_item); })
  {
  }
};


// Transform View /////////////////////////////////////////////////////////////

template <typename ParentView, typename Functor, typename ReturnType, is_view_t<ParentView> = true>
class transform_view
{
public:
  using parent_element_type = typename ParentView::element_type;
  transform_view(const ParentView& a_parentView, const Functor& a_transform)
    : m_parentView(a_parentView)
    , m_transform(a_transform)
  {
  }

  // implements View
  using element_type = ReturnType;
  bool first()                  { return m_parentView.first(); }
  bool last()                   { return m_parentView.last(); }
  bool next()                   { return m_parentView.next(); }
  bool previous()               { return m_parentView.previous(); }
  element_type current()        { return m_transform(m_parentView.current()); }   // NOTE: Transformed elements are returned by value
private:
  ParentView       m_parentView;
  Functor          m_transform;
};


// Reverse View ///////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class reverse_view
{
public:
  reverse_view(const ParentView& view)
    : m_parent(view)
  {
  }

  // implements View
  using element_type = typename ParentView::element_type;
  bool first()                  { return m_parent.last(); }
  bool last()                   { return m_parent.first(); }
  bool next()                   { return m_parent.previous(); }
  bool previous()               { return m_parent.next(); }
  const element_type& current() { return m_parent.current(); }
private:
  element_type m_current;
  ParentView   m_parent;
};


// Skip View //////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class skip_view
{
public:
  using element_type = typename ParentView::element_type;
  skip_view(const ParentView& a_parentView, size_t a_count)
    : m_parentView(a_parentView)
    , m_count(a_count)
  {
  }

  // implements View
  bool first()                  { if (!m_parentView.first()) return false; return nextValid();     }
  bool last()                   { if (!m_parentView.last())  return false; return previousValid(); }
  bool next()                   { return m_parentView.next();     }
  bool previous()               { return m_parentView.previous(); }
  const element_type& current() { return m_parentView.current();  }
private:
  bool nextValid()              { while (m_count >= 1) { if (!m_parentView.next())     return false; m_count--; } return true; }
  bool previousValid()          { while (m_count >= 1) { if (!m_parentView.previous()) return false; m_count--; } return true; }
  ParentView      m_parentView;
  size_t          m_count;
};


// Take View //////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class take_view
{
public:
  using element_type = typename ParentView::element_type;
  take_view(const ParentView& a_parentView, size_t a_count)
    : m_parentView(a_parentView)
    , m_count(a_count)
  {
  }

  // implements View
  bool first()                  { return m_parentView.first(); }
  bool last()                   {
                                  size_t count = m_count - 1;
                                  if (count <= 0 || !m_parentView.first()) return false;
                                  while (count)
                                  {
                                    if (!m_parentView.next()) return false;
                                    count--;
                                  }
                                  return true;
                                } 
  bool next()                   { m_count--; return (m_count > 0) ? m_parentView.next()     : false; }
  bool previous()               { m_count--; return (m_count > 0) ? m_parentView.previous() : false; }
  const element_type& current() { return m_parentView.current();  }
private:
  ParentView      m_parentView;
  size_t          m_count;
};


///////////////////////////////////////////////////////////////////////////////
// WRAPPERS ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Container Wrapper //////////////////////////////////////////////////////////

// creates a const reference container view
template <typename Container, is_container_t<Container> = true>
auto container_view(const Container& a_container)
{
  return container_wrapper_view<const Container&, Container>(a_container);
}


// creates a container view by value
template <typename Container, is_container_t<Container> = true>
auto container_copy_view(const Container& a_container)
{
  return container_wrapper_view<Container, Container>(a_container);
}


// Range Wrapper //////////////////////////////////////////////////////////////

template <typename T>
auto range(T start, T end)
{
  return range_view<T>(start, end);
}


// Array Wrapper //////////////////////////////////////////////////////////////

template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
  return std::array<std::remove_cv_t<T>, N>{ { a[I]... } };
}

  
template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&&a)[N], std::index_sequence<I...>)
{
  return std::array<std::remove_cv_t<T>, N>{ { std::move(a[I])... } };
}
 
 
template <class T, std::size_t N>
constexpr auto to_array(T (&a)[N])
{
  return to_array_impl(a, std::make_index_sequence<N>{});
}


template <class T, std::size_t N>
constexpr auto to_array(T (&&a)[N])
{
  return to_array_impl(std::move(a), std::make_index_sequence<N>{});
}


// Print Wrapper /////////////////////////////////////////////////////////////

class print_class
{
  // No state, just the class as a tag
};


// Reverse Wrapper ////////////////////////////////////////////////////////////

class reverse_class
{
  // No state, just the class as a tag
};


// Match Wrapper //////////////////////////////////////////////////////////////

template <typename Functor>
class match_class
{
public:
  match_class(Functor&& a_m)
    : m_m(a_m)
  {
  }
  Functor m_m;
};


// Filter Wrapper /////////////////////////////////////////////////////////////

template <typename Functor>
class filter_class
{
public:
  filter_class(Functor&& a_f)
    : m_f(a_f)
  {
  }
  Functor m_f;
};


// Transform Wrapper //////////////////////////////////////////////////////////

template <typename Functor>
class transform_class
{
public:
  transform_class(Functor&& t)
    : m_t(t)
  {
  }
  Functor m_t;
};


///////////////////////////////////////////////////////////////////////////////
// COMMANDS ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Print Command //////////////////////////////////////////////////////////////

print_class print;


// Reverse Command ////////////////////////////////////////////////////////////

reverse_class reverse;


// Match Command //////////////////////////////////////////////////////////////

template <typename Functor>
auto match(Functor&& a_m)
{
  return match_class<Functor>(std::forward<Functor>(a_m));
}


// Grep Command ///////////////////////////////////////////////////////////////

// Match a regular expression
auto grep(const std::string& a_re)
{
  std::regex re(a_re);
  auto f = [re](const std::string& a_s) { return std::regex_search(a_s, re); };
  return match_class<decltype(f)>(std::move(f));
}


// Filter Command /////////////////////////////////////////////////////////////

template <typename Functor>
auto filter(Functor&& a_f)
{
  return filter_class<Functor>(std::forward<Functor>(a_f));
}


// Take Command ///////////////////////////////////////////////////////////////

class take
{
public:
  take(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Head Command ///////////////////////////////////////////////////////////////

using head = take;


// First Command //////////////////////////////////////////////////////////////

using first = take;


// Last Command ///////////////////////////////////////////////////////////////

class last
{
public:
  last(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Tail Command ///////////////////////////////////////////////////////////////

using tail = last;


// Skip Command ///////////////////////////////////////////////////////////////

class skip
{
public:
  skip(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Drop Command ///////////////////////////////////////////////////////////////

using drop = skip;


// Transform Command //////////////////////////////////////////////////////////

template <typename Functor>
auto transform(Functor&& a_f)
{
  return transform_class<Functor>(std::forward<Functor>(a_f));
}


// Convert Command ////////////////////////////////////////////////////////////

template <typename Functor>
auto convert(Functor&& a_f)
{
  return transform_class<Functor>(std::forward<Functor>(a_f));
}


// Join Command ///////////////////////////////////////////////////////////////

class join
{
public:
  join(const std::string& s)
    : m_s(s)
  {
  }
  std::string m_s;
};


// Split Command //////////////////////////////////////////////////////////////

class split
{
public:
  split(const std::string& a_delimiters)
    : m_delimiters(a_delimiters)
  {
  }
  std::string m_delimiters;
};


///////////////////////////////////////////////////////////////////////////////
// PIPES //////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Container Pipe /////////////////////////////////////////////////////////////
  
template <typename Container, typename View, is_container_t<Container> = true>
auto operator|(const Container& c, const View& v)
{
  return container_view(c) | v;
}


// Array Pipe /////////////////////////////////////////////////////////////////

template <typename ArrayT, std::size_t ArrayN, typename View>
auto operator|(const ArrayT (&a)[ArrayN], const View& v)
{
  auto c = to_array(a);
  return container_view(c) | v;
}


// Reverse Pipe ///////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const reverse_class& r)
{
  return reverse_view<View>(v1);
}


// Match Pipe ////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const match_class<Functor>& m)
{
  return match_view<View, Functor>(v1, m.m_m);
}


// Filter Pipe ////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const filter_class<Functor>& f)
{
  return filter_view<View>(v1, f.m_f);
}


// Take Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const take& t)
{
  return take_view<View>(v1, t.m_count);
}


// Last Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const last& l)
{
  return v1 | reverse | take(l.m_count) | reverse;
}


// Skip Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const skip& s)
{
  return skip_view<View>(v1, s.m_count);
}


// Transform Pipe /////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const transform_class<Functor>& t)
{
  return transform_view<View, Functor, decltype(t.m_t((typename View::element_type){}))>(v1, t.m_t);
}


// Join Pipe //////////////////////////////////////////////////////////////////

std::string operator|(const std::string& s, const join& j)
{
  return s;
}


std::string operator|(const char* s, const join& j)
{
  return s;
}


template <typename View, is_view_t<View> = true>
std::string operator|(const View& c, const join& j)
{
  std::stringstream ss;
  std::string deliminator = j.m_s;
  apply(c, [&ss, deliminator](const auto& a, bool f, bool l) { ss << a; if (!l) ss << deliminator; } );
  return ss.str();
}


// Split Pipe /////////////////////////////////////////////////////////////////

// // Input can be a string or a view
// template <typename View>
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const split& s)
{
  // https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
  std::string str = v1 | join("");
  std::string delimiters = s.m_delimiters;
  std::vector<std::string> tokens;
  std::size_t start = 0, end, length = str.length();
  while (length && start < length + 1)
  {
    end = str.find_first_of(delimiters, start);
    if (end == std::string::npos)
    {
      end = length;
    }
    std::string token = std::string(str.data() + start, end - start);
    tokens.push_back(token);
    start = end + 1;
  }

  // No warnings from the compiler that this will blow up
  // it takes a reference to the tokens from the stack
  // Now changed to a container_copy_view so it doesn't blow up
  // and works correctly, but might not be as efficient as possible.
  // Ideally the compiler should give a warning. Even better the
  // language should be designed so you can't even make incorrect
  // programs like that.
  return container_copy_view<std::vector<std::string>>(tokens);
  // return container_view<std::vector<std::string>>(tokens);
}


// It should spit out string_spans which are views in to the original string - don't need to copy the string
/*
// implements RangeView
template <typename ParentView>
class split_string_view
{
public:
  using element_type = typename ParentView::element_type;
  split_string_view(const std::string& a_parentView, const std::string& a_delimiters = " ")
    : m_parentView(a_parentView)
    , m_delimiteres(a_delimiters)
  {
  }

  bool first()                  { if (!m_parentView.first())    return false; return nextValid();     }
  bool last()                   { if (!m_parentView.last())     return false; return previousValid(); }
  bool next()                   { if (!m_parentView.next())     return false; return nextValid();     }
  bool previous()               { if (!m_parentView.previous()) return false; return previousValid(); }
  const element_type& current() { return m_parentView.current(); }
private:
  bool nextValid()              { while (!m_filter(m_parentView.current())) { if (!m_parentView.next())     return false; } return true; }
  bool previousValid()          { while (!m_filter(m_parentView.current())) { if (!m_parentView.previous()) return false; } return true; }

  const std::string&       m_parentView;
  const std::string&       m_delimiters;
};
*/


// Print Pipe /////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
void operator|(const View& v1, const print_class& r)
{
  apply(v1, [](const auto& a, bool f, bool l) { std::cout << a; if (!l) std::cout << ","; } );
  std::cout << std::endl;
}


void operator|(const std::string& s, const print_class& r)
{
  std::cout << s << std::endl;
}


// Apply Pipe /////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
void for_each(const View& a_iterable, Functor&& f)
{
  View it = a_iterable; // takes a copy of the view, views should be cheap to copy
  if (it.first())
  {
    do
    {
      f(it.current());
    } while (it.next());
  }
}


template <typename View, typename Functor, is_view_t<View> = true>
void apply(const View& a_iterable, Functor&& f)
{
  bool first = true;
  bool last = false;
  View it = a_iterable; // takes a copy of the view, views should be cheap to copy
  if (it.first())
  {
    do
    {
      auto current = it.current();
      last = !it.next();
      f(current, first, last);
      first = false;
    } while (!last);
  }
}


template <typename View, is_view_t<View> = true>
void operator|(const View& v, const std::function<void(typename View::element_type)>& f)
{
  for_each(v, f);
}


template <typename Container, is_container_t<Container> = true>
void operator|(const Container& c, const std::function<void(typename Container::value_type)>& f)
{
  std::for_each(c.begin(), c.end(), f);
}


// Generic Pipe ///////////////////////////////////////////////////////////////

/*

template <typename View1, typename View2>
auto pipe(const View1& in, const View2& next)
{
  return true;
}

template <typename View1, typename View2>
auto operator|(const View1& in, const View2& next)
{
  return pipe(in, next);
}


template <typename R, typename View1, typename View2>
R pipe(const View1& in, const View2& next)
{
  static_assert(false, "no specialization found");
}

*/


void demo1()
{
  std::cout << "Demo" << std::endl;

  auto strings = to_array({"one","two","three","four","five"});

  std::cout << "Printing strings" << std::endl;
  strings | print;

  std::cout << "Reversing strings" << std::endl;
  strings | reverse | print;

  std::cout << "Matching strings with 'o'" << std::endl;
  strings | match([](std::string a){ return a.find("o") != std::string::npos; }) | print;

  std::cout << "Grepping strings for 'one|two|four'" << std::endl;
  strings | grep("one|two|four") | print;

  std::cout << "Filtering strings without 'o'" << std::endl;
  strings | filter([](std::string a){ return a.find("o") == std::string::npos; }) | print;

  std::cout << "Taking 3 strings" << std::endl;
  strings | take(3) | print;
  
  std::cout << "Head 3 strings" << std::endl;
  strings | head(3) | print;

  std::cout << "First 3 strings" << std::endl;
  strings | first(3) | print;

  std::cout << "Last 3 strings" << std::endl;
  strings | last(3) | print;

  std::cout << "Tail 3 strings" << std::endl;
  strings | tail(3) | print;

  std::cout << "Skipping 2 strings" << std::endl;
  strings | skip(2) | print;

  std::cout << "Dropping 2 strings" << std::endl;
  strings | drop(2) | print;

  std::cout << "Transforming strings to lengths" << std::endl;
  strings | transform([](std::string a) { return a.length(); }) | print;

  std::cout << "Converting strings to lengths" << std::endl;
  strings | convert([](std::string a) { return a.size(); }) | print;

  std::cout << "Joining strings with '.'" << std::endl;
  strings | join(".") | print;

  std::cout << "Splitting strings with '.'" << std::endl;
  strings | join(".") | split(".") | print;
}


void demo2()
{
  // Functions
  auto printer = [](auto x) { std::cout << x << ","; };
  auto newline = []() { std::cout << std::endl; };

  // Input
  std::vector<int> v = {1,2,3,4,5};

  // Normal way
  for (auto i : v)
    printer(i);
  std::cout << std::endl;

  // C++11 functional way
  std::for_each(v.begin(), v.end(), printer);
  std::cout << std::endl;

  // Improved way
  v | printer;
  newline();

  // Best way
  v | print;

  // More interesting options 
  v | reverse | print;
}


//auto test = [](auto testCond, auto expect) { std::cout << std::endl; };
#define CHECK_EQUAL(lhs, rhs)      assert((lhs) == (rhs)); 


void test()
{
  CHECK_EQUAL(range(10,15) | join(","),            "10,11,12,13,14,15");
  CHECK_EQUAL(range(10,15) | reverse | join(","),  "15,14,13,12,11,10");
}


void test_utf8()
{
  // UTF-8 test (4 characters, 8 bytes)
  char str[] = "αγρω";
  printf("%ld %ld\n", sizeof(str), strlen(str));
  
  // Latin-1 (4 characters, 4 bytes)
  char str2[] = "1234";
  printf("%ld %ld\n", sizeof(str2), strlen(str2));
}


int main()
{
  test_utf8();
  test();

  demo1();
  demo2();


  std::stringstream ss;
  auto printer = [](auto x) { std::cout << x << ","; };
  auto newline = []() { std::cout << std::endl; };
  auto odd = [](int x) -> bool { return (x & 1) == 1; };
  auto as_string = [](auto x) -> std::string { return std::to_string(x); };

  
  int v2[] = {1,2,3,4,5};
  v2 | print;

  int v3[]{1,2,3,4,5};
  v3 | print;
  
  auto v4 = to_array<int>({1,2,3,4,5});
  v4 | print;
  
  to_array({1,2,3,4,5}) | print;

  std::string("13,15,66,42,23") | split(",") | join(".") | print;
  "13,15,66,42,23" | split(",") | join(".") | print;
  "13,15,66,42,23" | split(",") | grep("3") | join(".") | print;

  auto sl = "13,15,66,42,23,123" | split(",");
  auto s = sl | join(".");
  std::cout << s << std::endl;
  newline();

  range(10,15) | print;
  
  std::cout << (range(10,15) | transform(as_string) | join(",")) << std::endl;
 

  for_each(range(10,15), printer);
  newline();
  for_each(reverse_view<range_view<int>>(range(10,15)), printer);
  newline();
/*
  for_each(filter_view(reverse_view(range(10,15)), odd), printer);
  newline();
  for_each(filter_view(reverse_view(range(9,15)), odd), printer);
  newline();
  for_each(filter_view(range(9,15), odd), printer);
  newline();
  for_each(reverse_view(range(9,15)), printer);
  newline();
  for_each(filter_view(reverse_view(range(9,14)), odd), printer);
  newline();
  for_each(filter_view(reverse_view(range(10,14)), odd), printer);
  newline();
  for_each(reverse_view(range(10,14)) | filter(odd), printer);
  newline();
*/
  for_each(range(10,14) | reverse | filter(odd), printer);
  newline();

  
  std::vector<int> v = {1,2,3,4,5};
  container_view(v) | print;
  container_view(v) | match(odd) | print;
  container_view(v) | filter(odd) | print;
  container_view(v) | reverse | print;
  container_view(v) | reverse | reverse | print;
  container_view(v) | reverse | reverse | filter(odd) | reverse | reverse | print;
  container_view(v) | reverse | filter(odd) | reverse | print;
  container_view(v) | filter(odd) | reverse | print;
  container_view(v) | reverse | filter(odd) | print;

  
  range(10,14) | reverse | filter(odd) | print;
  range(10,15) | reverse | print;
  range(10,15) | reverse | reverse | print;
  range(10,15) | filter(odd) | print;
  range(10,15) | reverse | filter(odd) | print;
  range(10,15) | reverse | filter(odd) | reverse | print;
  range(10,14) | reverse | filter(odd) | print;
  range(1,101) | reverse | filter(odd) | take(5) | print;
  range(1,101) | reverse | filter(odd) | head(5) | print;
  range(1,101) | reverse | filter(odd) | first(5) | print;
  range(1,101) | reverse | first(5) | reverse | print;
  range(1,101) | last(5) | print;
  range(1,101) | tail(5) | print;
  range(1,6) | print;
  range(1,6) | skip(3) | print;

  auto filt = [](std::string a){ return a == "BAD"; };

  to_array({"five","BAD","four","three","BAD","two","one"}) | reverse | filter(filt) | print;

  to_array({"one","two","three","four","five"}) | reverse | filter([](std::string a){ return a == "two"; }) | print;

}


#if 0


template <typename T>
class iterator
{
public:
  explicit iterator(T v = 0) : m_v(v)   { }
  iterator& operator++()                { m_v++; return *this; }
  iterator operator++(int)              { m_v++; return *this; }
  bool operator==(iterator other) const { return m_v == other.m_v; }
  bool operator!=(iterator other) const { return !(*this == other); }
  const T& operator*() const            { return m_v; }
private:
  T m_v;
};


template <typename T>
class reverse_iterator
{
public:
  explicit reverse_iterator(T v = 0) : m_v(v)   { }
  reverse_iterator& operator++()                { m_v--; return *this; }
  reverse_iterator operator++(int)              { m_v--; return *this; }
  bool operator==(reverse_iterator other) const { return m_v == other.m_v; }
  bool operator!=(reverse_iterator other) const { return !(*this == other); }
  const T& operator*() const                    { return m_v; }
private:
  T m_v;
};


class range // : public View<int>
{
public:
  range(int start, int end)
    : m_start(start)
    , m_end(end)
  {
  }

  iterator<int> begin() const          { return iterator<int>(m_start);   }
  iterator<int> end() const            { return iterator<int>(m_end + 1); }
  reverse_iterator<int> rbegin() const { return reverse_iterator<int>(m_end);       }
  reverse_iterator<int> rend() const   { return reverse_iterator<int>(m_start - 1); }
private:
  int m_start;
  int m_end;
};


template <typename View>
class reverse_view
{
public:
  reverse_view(const View& view)
    : m_view(view)
  {
  }

  auto begin() const  { return m_view.rbegin(); }
  auto end() const    { return m_view.rend();   }

  auto rbegin() const { return m_view.begin();  }
  auto rend() const   { return m_view.end();    }
private:
  const View& m_view;
};


template <typename BaseIter>
class filter_iterator
{
public:
  explicit filter_iterator(BaseIter it, BaseIter end, const std::function<bool(int)>& f)
    : m_it(it)
    , m_end(end)
    , m_f(f)
  {
    next();
  }
  void next()                                  { while (!m_f(*m_it) && m_it != m_end) m_it++; }
  filter_iterator& operator++()                { m_it++; next(); return *this; }
  filter_iterator operator++(int)              { m_it++; next(); return *this; }
  bool operator==(filter_iterator other) const { return m_it == other.m_it; }
  bool operator!=(filter_iterator other) const { return !(*this == other); }
  const auto& operator*() const                { return *m_it; }
private:
  std::function<bool(int)> m_f;
  BaseIter m_it;
  BaseIter m_end;
  //T m_v;
};


template <typename View>
class filter_view
{
public:
  filter_view(const View& view, const std::function<bool(int)>& f)
    : m_view(view)
    , m_f(f)
  {
  }

  auto begin() const { return filter_iterator(m_view.begin(), m_view.end(), m_f); }
  auto end() const   { return filter_iterator(m_view.end(), m_view.begin(), m_f); }

  auto rbegin() const { return filter_iterator(m_view.rbegin(), m_view.rend(), m_f); }
  auto rend() const   { return filter_iterator(m_view.rend(), m_view.rbegin(), m_f); }
private:
  const View& m_view;
  std::function<bool(int)> m_f;
};


#endif