#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>

using namespace std;

#define FORWARD_ASSIGN(from, to) \
    from(to const& other) : to(other) {} \
    from const& operator=(from const& other) { to::operator=(other); return *this; }

#define FORWARD_ARITHMETIC_OPS(from, to) \
    from& operator+=(from const& other) { \
        to::operator+=(other); return *this; \
    } \
    from& operator-=(from const& other) { \
        to::operator-=(other); return *this; \
    } \
    from& operator*=(from const& other) { \
        to::operator*=(other); return *this; \
    } \
    from& operator/=(from const& other) { \
        to::operator/=(other); return *this; \
    } \
    friend from operator+(from const& lhs, from const& rhs) { \
        return operator+(lhs.operator to(), rhs.operator to()); \
    } \
    friend from operator-(from const& lhs, from const& rhs) { \
        return operator-(lhs.operator to(), rhs.operator to()); \
    } \
    friend from operator*(from const& lhs, from const& rhs) { \
        return operator*(lhs.operator to(), rhs.operator to()); \
    } \
    friend from operator/(from const& lhs, from const& rhs) { \
        return operator/(lhs.operator to(), rhs.operator to()); \
    }

//folgendes wäre Teil der Standardlibrary
//nur minimale features implementiert (zeitgründe)
class Integer {
public:
    typedef int BaseType;
    int value;

    Integer(int value=0) : value(value) {}
    Integer(Integer const& other) : value(other.value) {}

    Integer& operator=(Integer const& other) {
        value=other.value;
        return *this;
    }

    Integer& operator+=(Integer const& other) {
        value+=other.value;
        return *this;
    }
    Integer& operator-=(Integer const& other) {
        value-=other.value;
        return *this;
    }
    Integer& operator*=(Integer const& other) {
        value*=other.value;
        return *this;
    }
    Integer& operator/=(Integer const& other) {
        value/=other.value;
        return *this;
    }

};

Integer operator+(Integer const& lhs, Integer const& rhs) {
    return Integer(lhs.value+rhs.value);
}
Integer operator-(Integer const& lhs, Integer const& rhs) {
    return Integer(lhs.value-rhs.value);
}
Integer operator*(Integer const& lhs, Integer const& rhs) {
    return Integer(lhs.value*rhs.value);
}
Integer operator/(Integer const& lhs, Integer const& rhs) {
    return Integer(lhs.value/rhs.value);
}


class PositiveInteger : protected Integer {
public:
    typedef int BaseType;

    PositiveInteger(int value=0) : Integer(value) { if(value<0) throw "Negative Value"; }
    PositiveInteger(Integer const& other) : Integer(other) { if(value<0) throw "Negative Value"; }

    PositiveInteger& operator=(Integer const& other) {
        if(other.value < 0) throw "Negative Value";
        value=other.value;
        return *this;
    }

    operator Integer() const { return *this; }
    FORWARD_ARITHMETIC_OPS(PositiveInteger, Integer)
};




template<typename T>
class Vector {
private:
    vector<T> impl;

public:
    void add(T const& val) {
        impl.push_back(val);
    }

    template<typename F>
    void for_each(F f) {
        for(auto& obj : impl) {
            f(obj);
        }
    }
};

template<typename T>
class ChangeObserver : public Vector<function<void(T const&, T const&)> > {
public:
    void change(T const& oldVal, T const& newVal) {
        this->for_each([&](function<void(T const&, T const&)> f) { f(oldVal, newVal); });
    }
};

template<typename ValueType>
class ChangeObservableArithmeticType : protected ValueType, public ChangeObserver<ValueType> {
public:
    typedef typename ValueType::BaseType BaseType;
    ChangeObservableArithmeticType(typename ValueType::BaseType value=0) : ValueType(value) {}
    ChangeObservableArithmeticType(ChangeObservableArithmeticType const& other) : ValueType(other) {}
    ChangeObservableArithmeticType(ValueType const& other) : ValueType(other) {}

    ChangeObservableArithmeticType& operator=(ChangeObservableArithmeticType const& other) {
        this->change(*this, other);
        this->value=other.value;
        return *this;
    }

    operator ValueType() const {
        return *this;
    }

    FORWARD_ARITHMETIC_OPS(ChangeObservableArithmeticType, ValueType)
};



class Radius : public ChangeObservableArithmeticType<PositiveInteger> {
public:
    Radius() {}
    Radius(ChangeObservableArithmeticType::BaseType value) : ChangeObservableArithmeticType(value) {}
    PositiveInteger const& asPositiveInteger() const {
        return *this;
    }

    FORWARD_ASSIGN(Radius, ChangeObservableArithmeticType)
};

class Punkt : public Integer {
public:
    FORWARD_ASSIGN(Punkt, Integer)
    Punkt(int val) : Integer(val) {}
};

class PunktX : public Punkt { public: FORWARD_ASSIGN(PunktX, Punkt) PunktX(int val):Punkt(val){} };
class PunktY : public Punkt { public: FORWARD_ASSIGN(PunktY, Punkt) PunktY(int val):Punkt(val){} };

class PunktXY : public PunktX, public PunktY {
public:
    PunktXY(int x=0, int y=0) : PunktX(x), PunktY(y) {}
};

class Startpunkt : public PunktXY { public: FORWARD_ASSIGN(Startpunkt, PunktXY) };
class Endpunkt : public PunktXY { public: FORWARD_ASSIGN(Endpunkt, PunktXY) };

class Kreis : public Radius, public PunktXY {
public:
    Kreis(int r) : Radius(r) {
        Radius::add([&](::PositiveInteger const&, ::PositiveInteger const&) {
            redraw();
        });

        redraw();
    }

    void redraw() {
        cout<<"Radius changed, Kreis redrawn\n";
    }
};

class Laenge : public Integer { public: FORWARD_ASSIGN(Laenge, Integer) };

class Strecke : public Startpunkt, public Endpunkt {
public:
    Strecke() : Startpunkt(PunktXY(0,0)), Endpunkt(PunktXY(0,0)) {}
};

class Rechteck : public PunktXY, public Laenge {
public:
    Rechteck(int laenge) : Laenge(laenge) {}
};

void verdoppleRadius(Radius& r) {
    r=r*2;
}

void printKoordinaten(PunktXY const& p) {
    cout<<"("<<p.PunktX::value<<"/"<<p.PunktY::value<<")\n";
}

void wierdShit(Kreis& k, Laenge& l) {
    l=l*static_cast<PunktX&>(k)+static_cast<PunktY&>(k);
}

int main() {

    Kreis k(3);
    Rechteck r(12);
    Strecke s;

    k.PunktXY::operator=(PunktXY(10,20));
    r.PunktXY::operator=(PunktXY(1,2));
    s.Startpunkt::operator=(PunktXY(5,7));
    s.Endpunkt::operator=(PunktXY(15,17));

    //2x redraw ausführen:
    k.Radius::operator=(7);

    try {
        //force error
        k.Radius::operator=(-7);
    } catch(...) {
        cout<<"Error\n";
    }

    verdoppleRadius(k);

    printKoordinaten(k);
    printKoordinaten(r);
    printKoordinaten(static_cast<Startpunkt&>(s));



    return 0;
}

