#include <iostream>

template<typename C, typename T>
struct caster;

template<typename C>
struct caster<C, long>
{
  static long cast(const C& c) { return c.as_long(); }
};

template<typename C>
struct caster<C, double>
{
  static double cast(const C& c) { return c.as_double(); }
};

template<typename C, typename T>
struct ops_base
{
  friend bool operator==(const ops_base& l, const ops_base& r)
  {
    const C& cl = static_cast<const C&>(l);
    const C& cr = static_cast<const C&>(r);
    return (caster<C, T>::cast(cl) == caster<C, T>::cast(cr));
  }

  friend bool operator==(const ops_base& l, T r)
  {
    const C& cl = static_cast<const C&>(l);
    return (caster<C, T>::cast(cl) == r);
  }

  friend bool operator==(T l, const ops_base& r)
  {
    const C& cr = static_cast<const C&>(r);
    return (l == caster<C, T>::cast(cr));
  }

  friend bool operator!=(const ops_base& l, const ops_base& r)
  { return !(l == r); }

  friend bool operator!=(const ops_base& l, T r)
  { return !(l == r); }

  friend bool operator!=(T l, const ops_base& r)
  { return !(l == r); }

  friend bool operator<(const ops_base& l, const ops_base& r)
  {
    const C& cl = static_cast<const C&>(l);
    const C& cr = static_cast<const C&>(r);
    return (caster<C, T>::cast(cl) < caster<C, T>::cast(cr));
  }

  friend bool operator<(const ops_base& l, T r)
  {
    const C& cl = static_cast<const C&>(l);
    return (caster<C, T>::cast(cl) < r);
  }

  friend bool operator<(T l, const ops_base& r)
  {
    const C& cr = static_cast<const C&>(r);
    return (l < caster<C, T>::cast(cr));
  }

  friend bool operator>(const ops_base& l, const ops_base& r)
  {
    const C& cl = static_cast<const C&>(l);
    const C& cr = static_cast<const C&>(r);
    return (caster<C, T>::cast(cl) > caster<C, T>::cast(cr));
  }

  friend bool operator>(const ops_base& l, T r)
  {
    const C& cl = static_cast<const C&>(l);
    return (caster<C, T>::cast(cl) > r);
  }

  friend bool operator>(T l, const ops_base& r)
  {
    const C& cr = static_cast<const C&>(r);
    return (l > caster<C, T>::cast(cr));
  }

  friend bool operator<=(const ops_base& l, const ops_base& r)
  { return !(l > r); }

  friend bool operator<=(const ops_base& l, T r)
  { return !(l > r); }

  friend bool operator<=(T l, const ops_base& r)
  { return !(l > r); }

  friend bool operator>=(const ops_base& l, const ops_base& r)
  { return !(l < r); }

  friend bool operator>=(const ops_base& l, T r)
  { return !(l < r); }

  friend bool operator>=(T l, const ops_base& r)
  { return !(l < r); }
};

template<typename C, typename T>
struct io_base
{
  friend std::ostream& operator<<(std::ostream& out, const io_base& self)
  {
    const C& c = static_cast<const C&>(self);
    return out << caster<C, T>::cast(c);
  }
};

struct Long : ops_base<Long, long>, io_base<Long, long>
{
  Long(long val) : value_(val) { }

  long as_long() const { return value_; }

private:
  long value_;
};

struct Float : ops_base<Float, double>, io_base<Float, double>
{
  Float(double val) : value_(val) { }

  double as_double() const { return value_; }

private:
  double value_;
};

int main(int argc, char** argv)
{
  {
    Long l { 5 };
    Long r { 6 };

    std::cout << "[Long, Long]" << std::endl;
    std::cout << l << " == " << r << " : " << (l == r) << std::endl;
    std::cout << l << " != " << r << " : " << (l != r) << std::endl;
    std::cout << l << " < " << r << " : " << (l < r) << std::endl;
    std::cout << l << " > " << r << " : " << (l > r) << std::endl;
    std::cout << l << " <= " << r << " : " << (l <= r) << std::endl;
    std::cout << l << " >= " << r << " : " << (l >= r) << std::endl;
  }
  {
    Long l { 5 };
    long r { 6 };

    std::cout << "[Long, long]" << std::endl;
    std::cout << l << " == " << r << " : " << (l == r) << std::endl;
    std::cout << l << " != " << r << " : " << (l != r) << std::endl;
    std::cout << l << " < " << r << " : " << (l < r) << std::endl;
    std::cout << l << " > " << r << " : " << (l > r) << std::endl;
    std::cout << l << " <= " << r << " : " << (l <= r) << std::endl;
    std::cout << l << " >= " << r << " : " << (l >= r) << std::endl;
  }

  {
    Float l { 5 };
    Float r { 6 };

    std::cout << "[Float, Float]" << std::endl;
    std::cout << l << " == " << r << " : " << (l == r) << std::endl;
    std::cout << l << " != " << r << " : " << (l != r) << std::endl;
    std::cout << l << " < " << r << " : " << (l < r) << std::endl;
    std::cout << l << " > " << r << " : " << (l > r) << std::endl;
    std::cout << l << " <= " << r << " : " << (l <= r) << std::endl;
    std::cout << l << " >= " << r << " : " << (l >= r) << std::endl;
  }
  {
    Float l { 5 };
    float r { 6 };

    std::cout << "[Float, float]" << std::endl;
    std::cout << l << " == " << r << " : " << (l == r) << std::endl;
    std::cout << l << " != " << r << " : " << (l != r) << std::endl;
    std::cout << l << " < " << r << " : " << (l < r) << std::endl;
    std::cout << l << " > " << r << " : " << (l > r) << std::endl;
    std::cout << l << " <= " << r << " : " << (l <= r) << std::endl;
    std::cout << l << " >= " << r << " : " << (l >= r) << std::endl;
  }
}
