#ifndef INCLUDED_BOBCAT_BINOPSBASE_
#define INCLUDED_BOBCAT_BINOPSBASE_

#include <utility>

namespace FBB
{

//////////////////////////////////////////////////////////////////
    // The recursively defined BinopsBase0 class: receives Binops, so a static
    // cast becomes available to reach the basic arithmetic classes

    // The general declaration:
    //
template <class Binops, class Derived, int ...ops>
class BinopsBase0;

    // The terminating definition:
    //
template <class Binops, class Derived>
class BinopsBase0<Binops, Derived>
{};

template <class Derived, int ...ops>
class BinopsBase;

//////////////////////////////////////////////////////////////////

template <class Binops, class Derived>
struct Mul
{
    Derived &operator*=(Derived const &rhs) &; 
    Derived &&operator*=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Mul<Binops, Derived>::operator*=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.mulWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Mul<Binops, Derived>::operator*=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).mulWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator*(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.mulWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator*(Derived &&lhs, Derived const &rhs)
{
    lhs.mulWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '*', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Mul<Binops, Derived>
{};

template <class Binops, class Derived>
struct Div
{
    Derived &operator/=(Derived const &rhs) &; 
    Derived &&operator/=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Div<Binops, Derived>::operator/=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.divWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Div<Binops, Derived>::operator/=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).divWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator/(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.divWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator/(Derived &&lhs, Derived const &rhs)
{
    lhs.divWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '/', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Div<Binops, Derived>
{};
template <class Binops, class Derived>
struct Mod
{
    Derived &operator%=(Derived const &rhs) &; 
    Derived &&operator%=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Mod<Binops, Derived>::operator%=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.modWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Mod<Binops, Derived>::operator%=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).modWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator%(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.modWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator%(Derived &&lhs, Derived const &rhs)
{
    lhs.modWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '%', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Mod<Binops, Derived>
{};
template <class Binops, class Derived>
struct Add
{
    Derived &operator+=(Derived const &rhs) &; 
    Derived &&operator+=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Add<Binops, Derived>::operator+=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.addWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Add<Binops, Derived>::operator+=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).addWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator+(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.addWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator+(Derived &&lhs, Derived const &rhs)
{
    lhs.addWrap(rhs);
    return std::move(lhs);
}


template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '+', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Add<Binops, Derived>
{};



template <class Binops, class Derived>
struct Sub
{
    Derived &operator-=(Derived const &rhs) &; 
    Derived &&operator-=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Sub<Binops, Derived>::operator-=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.subWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Sub<Binops, Derived>::operator-=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).subWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator-(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.subWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator-(Derived &&lhs, Derived const &rhs)
{
    lhs.subWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '-', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Sub<Binops, Derived>
{};





template <class Binops, class Derived>
struct Shl
{
    Derived &operator<<=(Derived const &rhs) &; 
    Derived &&operator<<=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Shl<Binops, Derived>::operator<<=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.shlWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Shl<Binops, Derived>::operator<<=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).shlWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator<<(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.shlWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator<<(Derived &&lhs, Derived const &rhs)
{
    lhs.shlWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '<', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Shl<Binops, Derived>
{};
template <class Binops, class Derived>
struct Shr
{
    Derived &operator>>=(Derived const &rhs) &; 
    Derived &&operator>>=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Shr<Binops, Derived>::operator>>=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.shrWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Shr<Binops, Derived>::operator>>=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).shrWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator>>(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.shrWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator>>(Derived &&lhs, Derived const &rhs)
{
    lhs.shrWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '>', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Shr<Binops, Derived>
{};
template <class Binops, class Derived>
struct And
{
    Derived &operator&=(Derived const &rhs) &; 
    Derived &&operator&=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &And<Binops, Derived>::operator&=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.andWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&And<Binops, Derived>::operator&=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).andWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator&(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.andWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator&(Derived &&lhs, Derived const &rhs)
{
    lhs.andWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '&', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public And<Binops, Derived>
{};
template <class Binops, class Derived>
struct Or
{
    Derived &operator|=(Derived const &rhs) &; 
    Derived &&operator|=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Or<Binops, Derived>::operator|=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.orWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Or<Binops, Derived>::operator|=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).orWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator|(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.orWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator|(Derived &&lhs, Derived const &rhs)
{
    lhs.orWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '|', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Or<Binops, Derived>
{};
template <class Binops, class Derived>
struct Xor
{
    Derived &operator^=(Derived const &rhs) &; 
    Derived &&operator^=(Derived const &rhs) &&;
};

template <class Binops, class Derived>
Derived &Xor<Binops, Derived>::operator^=(Derived const &rhs) &
{
    Derived tmp{static_cast<Derived &>(*this)};
    tmp.xorWrap(rhs);
    static_cast<Derived &>(*this).swap(tmp);
    return static_cast<Derived &>(*this);
}

template <class Binops, class Derived>
Derived &&Xor<Binops, Derived>::operator^=(Derived const &rhs) &&
{
    static_cast<Derived &>(*this).xorWrap(rhs);
    return std::move(static_cast<Derived &>(*this));
}

template <class Derived>
Derived operator^(Derived const &lhs, Derived const &rhs)
{
    Derived ret{lhs};
    ret.xorWrap(rhs);
    return ret;
}

template <class Derived>
Derived operator^(Derived &&lhs, Derived const &rhs)
{
    lhs.xorWrap(rhs);
    return std::move(lhs);
}

template <class Binops, class Derived, int ...ops>
class BinopsBase0<Binops, Derived, '^', ops...>
:
    public BinopsBase0<Binops, Derived, ops...>,
    public Xor<Binops, Derived>
{};

    // The class BinopsBase befriends all arithmetic classes, and implements
    // wrappers for the arithmetic functions potentially defined in
    // Derived.
    // The arithmetic classes call BinopsBase' functions, which in turn call
    // Derived's functions. This way Derived only needs to implement the
    // necessary functions. Since the other functions aren't called, they
    // arent't instantiated so there are no linker-errors
    //
template <class Derived, int ...ops>
class BinopsBase:
    public BinopsBase0<BinopsBase<Derived, ops...>, Derived, ops...>
{
    #include "friends.f"

    #include "wraps.f"
};

    
template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::addWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).add(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::mulWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).mul(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::divWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).div(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::modWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).mod(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::subWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).sub(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::shlWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).shl(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::shrWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).shr(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::andWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).and_(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::orWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).or_(rhs);
}

template <class Derived, int ...ops>
inline void BinopsBase<Derived, ops...>::xorWrap(Derived const &rhs)
{
    static_cast<Derived &>(*this).xor_(rhs);
}


} // FBB        
#endif
