#ifndef __Lib_Output_HPP__
#define __Lib_Output_HPP__
#include <iosfwd>
#include <utility>
#include <tuple>
#include <string>
#include <sstream>
namespace std {
template<class... As>
std::ostream& operator<<(std::ostream& out, tuple<As...> const& self);
}
namespace Lib {
namespace Output {
template<class T>
struct Ptr { T self; };
template<class T, std::enable_if_t<!std::is_pointer_v<T>, bool> = true>
Ptr<T const&> ptr(T const& self) { return { .self = self, }; }
template<class T, std::enable_if_t<std::is_pointer_v<T>, bool> = true>
Ptr<T> ptr(T self) { return { .self = self, }; }
template<class T>
std::ostream& operator<<(std::ostream& out, Ptr<T*> const& self)
{ return self.self == nullptr ? out << "NULL" : out << *self.self; }
template<class T>
std::ostream& operator<<(std::ostream& out, Ptr<T> const& self)
{ return out << self.self; }
template<class T>
struct Repeat { T const& to_repeat; unsigned times; };
template<class T>
Repeat<T> repeat(T const& c, unsigned times)
{ return Repeat<T>{c, times}; }
template<class T>
std::ostream& operator<<(std::ostream& out, Repeat<T> const& self)
{ for (unsigned i = 0; i < self.times; i++) out << self.to_repeat; return out; };
template<class T>
struct Multiline {
T const& self;
unsigned indent;
static void outputIndent(std::ostream& out, unsigned indent);
};
template<class T>
Multiline<T> multiline(T const& self, unsigned indent = 0)
{ return { self, indent, }; }
template<class T>
void Multiline<T>::outputIndent(std::ostream& out, unsigned indent)
{ out << Output::repeat(" ", indent); };
template<class Sep, class Iter>
struct Interleaved { Sep const& sep; Iter iter; };
template<class Sep, class Iter>
struct Interleaved<Sep,Iter> interleaved(Sep const& s, Iter i)
{ return Interleaved<Sep, Iter>{s, std::move(i)}; }
template<class Sep, class Iter>
std::ostream& operator<<(std::ostream& out, Interleaved<Sep, Iter> self)
{
if (self.iter.hasNext()) {
out << self.iter.next();
while (self.iter.hasNext()) {
out << self.sep << self.iter.next();
}
}
return out;
}
template<class Iter>
auto commaSep(Iter i) { return Output::interleaved(", ", std::move(i)); }
struct Nothing {
friend std::ostream& operator<<(std::ostream& out, Nothing const& self)
{ return out << ""; }
};
struct Comma {
friend std::ostream& operator<<(std::ostream& out, Comma const& self)
{ return out << ", "; }
};
template<unsigned i, unsigned sz, class Tup, class Sep>
struct __TupleHelper
{
static void apply(std::ostream& out, Tup const& self)
{
out << Sep{} << std::get<i>(self);
__TupleHelper<i + 1, sz, Tup, Sep>::apply(out, self);
}
};
template<class Sep>
struct __TupleHelper<0, 0, std::tuple<>, Sep>
{ static void apply(std::ostream& out, std::tuple<> const& self) { } };
template<unsigned sz, class Tup, class Sep>
struct __TupleHelper<sz, sz, Tup, Sep>
{ static void apply(std::ostream& out, Tup const& self) { } };
template<unsigned sz, class Tup, class Sep>
struct __TupleHelper<0, sz, Tup, Sep>
{
static void apply(std::ostream& out, Tup const& self)
{
out << std::get<0>(self);
__TupleHelper<1, sz, Tup, Sep>::apply(out, self);
}
};
template<class... Elems>
struct OutputCat { std::tuple<Elems...> elems; };
template<class... Elems>
struct OutputCat<Elems...> catOwned(Elems&&... elems)
{ return OutputCat<Elems...>{std::tuple<Elems...>(std::move(elems)...)}; }
template<class... Elems>
struct OutputCat<Elems const&...> cat(Elems const&... elems)
{ return OutputCat<Elems const&...>{std::tie(elems...)}; }
template<class... As>
std::ostream& operator<<(std::ostream& out, OutputCat<As...> const& self)
{
__TupleHelper<0, std::tuple_size<std::tuple<As...>>::value, std::tuple<As...>, Nothing>::apply(out, self.elems);
return out;
}
template<class... As>
std::string toString(As const&... as) {
std::stringstream out;
out << cat(as...);
return out.str();
}
} }
namespace std {
template<class A, class B>
std::ostream& operator<<(std::ostream& out, pair<A,B> const& self)
{ return out << "(" << self.first << ", " << self.second << ")"; }
template<class... As>
std::ostream& operator<<(std::ostream& out, tuple<As...> const& self)
{
out << "(";
Lib::Output::__TupleHelper<0, std::tuple_size<std::tuple<As...>>::value, std::tuple<As...>, Lib::Output::Comma>::apply(out, self);
out << ")";
return out;
}
}
#endif