#include <utility>
#include <concepts>

//comparable
template <typename Type>
concept Comparable =
    requires (Type lhs, Type rhs)
    {
        lhs == rhs;
        lhs != rhs;
    };

template <typename Type>
concept Incrementable =
    requires (Type type)
    {
        ++type;
        type++;
    };
//=

//dereference
template <typename Type>
concept Dereferenceable =
    requires(Type type)
    {
        { *type } -> std::same_as<typename Type::value_type &>;
    };
//=

//constderef
template <typename Type>
concept ConstDereferenceable =
    requires(Type const &type)
    {
        { *type } -> std::same_as<typename Type::value_type const &>;
    };
//=

struct InIt
{
    using value_type = int;

    InIt &operator++();
    InIt operator++(int);

    int const &operator*() const;
};

bool operator==(InIt const &lh, InIt const &rh);
bool operator!=(InIt const &lh, InIt const &rh);
int  operator-(InIt const &lh, InIt const &rh);


//initerator
template <typename Type>
concept InIterator =
    Comparable<Type> and Incrementable<Type> and
    ConstDereferenceable<Type>;

template <InIterator Type>
void inFun(Type tp)
{}
//=


struct OutIt: public InIt
{
    int &operator*();
};

//outiterator
template <typename Type>
concept OutIterator =
    Comparable<Type> and Incrementable<Type> and
    Dereferenceable<Type>;

template <OutIterator Type>
void outFun(Type tp)
{}

//=

//fwditerator
template <typename Type>
concept FwdIterator =
    InIterator<Type> and OutIterator<Type>;

struct FwdIt: public OutIt
{
    using OutIt::operator*;
    using InIt::operator*;
};

template <FwdIterator Type>
void fwdFun(Type tp)
{}

//=

//iterable
struct Iterable
{
    using value_type = int;

    Iterable &operator++();
    Iterable operator++(int);

    int const &operator*() const;
    int &operator*();
};

bool operator==(Iterable const &lh, Iterable const &rh);
bool operator!=(Iterable const &lh, Iterable const &rh);
int  operator-(Iterable const &lh, Iterable const &rh);
//=

//bidir
template <typename Type>
concept BiIterator =
    FwdIterator<Type> and
    requires(Type type)
    {
        --type;
        type--;
    };

template <typename Type>
concept RndIterator =
    BiIterator<Type>
    and
    requires(Type lhs, Type rhs)
    {
        lhs += 0;
        lhs -= 0;
        lhs + 0;
        lhs - 0;
        { lhs - rhs } -> std::same_as<int>;
    };
//=

struct BiIt: public FwdIt
{
    InIt &operator--();
    InIt operator--(int);
};

template <BiIterator Type>
void biFun(Type tp)
{}

//randomit
struct RndIt: public BiIt
{
    RndIt &operator+=(int);
    RndIt &operator-=(int);
};

RndIt &operator+(RndIt const &lh, int);
RndIt &operator-(RndIt const &lh, int);

template <RndIterator Type>
void rndFun(Type tp)
{}

int main()
{
    inFun(InIt{});
//  outFun(InIt{});         // WC: unsatisfied constraint
     outFun(OutIt{});
//    fwdFun(OutIt{});      // WC
     fwdFun(FwdIt{});

     inFun(Iterable{});
     outFun(Iterable{});
     fwdFun(Iterable{});

      biFun(BiIt{});
//     biFun(InIt{});       // WC
//     biFun(OutIt{});      // WC

     rndFun(RndIt{});
     biFun(RndIt{});         // OK: extra functions are fine
}
