#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <ctime>


using namespace std;

//==============================ИСКЛЮЧЕНИЯ======================================

class ZeroException {
    string exc;

    friend ostream& operator<<(ostream& os, const ZeroException& vi) {
        os << "исключение " << vi.exc;
    }
public:

    ZeroException() {
        this->exc = "ZeroException";
    }
};

class NegativeException {
    string exc;

    friend ostream& operator<<(ostream& os, const NegativeException& vi) {
        os << "исключение " << vi.exc;
    }
public:

    NegativeException() {
        this->exc = "NegativeException";
    }
};

class InvalidAdress {
    string exc;

    friend ostream& operator<<(ostream& os, const InvalidAdress& vi) {
        os << "исключение " << vi.exc;
    }
public:

    InvalidAdress() {
        this->exc = "InvalidAdress";
    }
};
//==============================================================================

/**
 * <b>Класс - Вектор</b>
 *========================
 *@autor Arkanium77
 */
class Vector {
    int *v; // массив координат
    int n; //размерность пространствa

public:

    Vector(int n = 2) {
        if (n <= 0)throw NegativeException();
        v = new int[n];
        this->n = n;
        for (int i = 0; i < this->n; i++) {
            v[i] = 0;
        }
    }

    Vector(int *m, int n = 2) {
        if (n <= 0)throw NegativeException();
        v = new int[n];

        for (int i = 0; i < n; i++) {
            v[i] = m[i];
        }
        this->n = n;
    }

    Vector(int x, int y) {
        v = new int[2];

        v[0] = x;
        v[1] = y;
        this->n = 2;
    }

    Vector(const Vector& vect) {
        v = new int[vect.n];
        n = vect.n;
        for (int i = 0; i < n; i++) {
            v[i] = vect.v[i];
        }
    }

    ~Vector() {
        //cout << "Деструктор. Освобождение памяти, занимаемой объектом" << endl;
        delete [] v;
    }

    //=========================================================ФУНКЦИИ==========

    /**
     * <b>Заполнить вектор случайными числами
     * @return Вектор, заполненный случайными числами
     */
    Vector random() {
        //srand(time(0));
        Vector a = Vector(n);
        for (int i = 0; i < n; i++) {
            a.v[i] = rand() % 200 - 100;

        }
        //cout<<a;
        return a;
    }

    /**
     * <b>Равенство векторов</b>
     * @param b - второй вектор для сравнения
     * @return <code>true</code> - если равны. <br> Иначе - <code>false</code>
     */
    bool operator==(Vector b) {
        if (b.n != this->n)return false;
        for (int i = 0; i < n; i++) {
            if (this->v[i] != b.v[i])return false;
        }
        return true;
    }

    /**
     * <b>Неравенство векторов</b>
     * @param b - второй вектор для сравнения
     * @return <code>true</code> - если НЕ равны. <br> Иначе - <code>false</code>
     */
    bool operator!=(Vector b) {
        //Vector a=this;
        //Проблема была в том, что если писать this вместо *this всё ломалось из-за плохого приведения типов. Решение было неочевидно для меня.
        return !(*this == b);
        /*
        if (b.n != this->n)return true;
        for (int i = 0; i < n; i++) {
            if (this->v[i] != b.v[i])return true;
        }
        return false;
         */
    }


    //=======================================================ОПЕРАТОРЫ==========

    /**
     * <b>Переопределённый оператор =</b><br>
     * @param b Вектор
     * @return Ничего не возвращает. Изменяет объект от которого вызвана на копия
     */
    Vector& operator=(Vector b) {
        if(*this==b)return *this;
        if(b.n!=this->n){
            delete []this->v;
            this->n=b.n;
            this->v=new int [this->n];
        }
        this->copy(b.v);
        return *this;
    }
    /*
    void operator=(Vector b) {
        //this->v = &(*(Vector(b).v));
        //this->n = b.n;
        this->n=b.n;
        this->copy(b.v);
    }*/

    /**
     * <b>Переопределённый оператор []</b><br>
     * Аналог функции getV()
     * @param x - позиция
     * @return координату с заданой позиции
     */
    int operator[](int x) {
        return this->getV(x);
    }

    /**
     * <b>Переопределённый оператор (a,b)</b><br>
     * Аналог функции setV()
     * @param pos - позиция
     * @param obj - новая координата
     * @return ничего. Просто изменяет координату на выбранной позиции.
     */
    void operator()(int pos, int obj) {
        this->setV(pos, obj);
    }

    /**
     * <b>Переопределённый оператор -=</b>
     * @param b - вычитаемый вектор
     * @return Изменяет объект от которого был вызван
     */
    void operator-=(Vector b) {
        if (this->n < b.n) {
            int *ar = this->v;
            *this = Vector(b.n);
            this->copy(ar);
        }
        for (int i = 0; i<this->n; i++) {
            this->v[i] -= b.v[i];
        }
    }

    Vector operator-(Vector b) {

        Vector u = Vector(this->n);
        u.copy(this->v);
        u -= b;
        return u;
    }

    /**
     * <b>Переопределённый оператор +=</b>
     * @param b - вектор-второе слогаемое
     * @return Изменяет объект от которого был вызван
     */
    void operator+=(Vector b) {
        if (this->n < b.n) {
            int *ar = this->v;
            *this = Vector(b.n);
            this->copy(ar);
        }
        for (int i = 0; i<this->n; i++) {
            this->v[i] += b.v[i];
        }
        /*
        if (b.n >= this->n) {
            for (int i = 0; i<this->n; i++) {
                this->v[i] += b.v[i];
            }
        } else {
            for (int i = 0; i < b.n; i++) {
                this->v[i] += b.v[i];
            }
        }*/
    }

    Vector operator+(Vector b) {

        Vector u = Vector(this->n);
        u.copy(this->v);
        u += b;
        return u;
    }

    /**
     * <b>Переопределённый оператор *=</b>
     * @param a - Число-множитель
     * @return Изменяет объект от которого был вызван
     */
    void operator*=(int a) {
        for (int i = 0; i<this->n; i++) {
            this->v[i] *= a;
        }
    }

    /**
     * <b>Переопределённый оператор *</b>
     * @param a - Число-множитель
     * @return Новый вектор
     */
    Vector operator*(int a) {
        //cout<<endl<<"============"<<endl;
        //cout<<this->n<<endl;
        //int gg=this->n;
        //Vector t=Vector(*this->v,gg);//Почему-то он выделяет память только под 2 ячейки массива. Независимо от способа задания.
        //cout<<t.n<<endl<<endl;
        Vector u = Vector(this->n);
        u.copy(this->v);
        u *= a;
        return u;
    }

    /**
     * <b>Переопределённый оператор /=</b>
     * @param a - Число-делитель
     * @throws ZeroException - ошибка деления на 0
     * @return Изменяет объект от которого был вызван
     */
    void operator/=(int a) {
        if (a == 0) {//perror("Деление на ноль");
            throw ZeroException();
        }
        for (int i = 0; i<this->n; i++) {
            this->v[i] /= a;
        }
    }

    /**
     * <b>Переопределённый оператор /</b>
     * @param a - Число-делитель
     * @throws ZeroException - ошибка деления на 0
     * @return Новый вектор
     */
    Vector operator/(int a) {
        Vector u = Vector(this->n);
        u.copy(this->v);
        u /= a;
        return u;
    }

    /**
     * <b>Переопределённый оператор вывода</b>
     * @param os поток вывода.
     * @param vi Вектор
     * @return изменённый поток вывода
     */
    friend ostream& operator<<(ostream& os, const Vector& vi) {
        //os << "v << " << endl;
        //string c = "";
        for (int i = 0; i < vi.n; i++) {
            os << vi.v[i] << " ";
        }
        // os << "v <<  -2  " << endl;
        return os;
    }

protected:

    /**
     * <b>Получить координату по позиции</b>
     * @param pos - позиция.
     * @return Координату
     */
    int getV(int pos) {
        if (pos < 0 || pos >= this->n)throw InvalidAdress();
        return v[pos];
    }

    /**
     * <b>Изменить координату</b>
     * @param pos - позиция координаты
     * @param obj - новая координата
     */
    void setV(int pos, int obj) {
        if (pos < 0 || pos >= n)throw InvalidAdress();
        this->v[pos] = obj;
    }

    /**
     * <b>Перенос массива</b>
     * Техническая функция. ВАЖНО: Размер this.v должен совпадать с размером переносимого массива
     * @param v переносимый массив
     */
    void copy(int *v) {
        int *ar = new int [this->n];
        for (int i = 0; i<this->n; i++) {
            ar[i] = v[i];
        }
        this->v = ar;
        delete []ar;
    }
};


//======================================================================

int main() {
    setlocale(0, "");
    srand(time(0));
    cout << "Russian  Русский";
    Vector v = Vector(4);
    Vector v2 = Vector(4);
    v2(0, 16);
    v2(1, 2);
    v2(2, 2);
    v2(3, 1);
    v(0, 0);
    v(1, 3);
    v(2, 2);
    v(3, 1);
    cout << endl << endl << v;
    cout << endl << endl << v2;
    v=v2;
    v2 *= 2;
    cout << endl << endl << v2;
    v2 = (v2 * 2);
    cout << endl << endl << v2;

    //========================ИСКЛЮЧЕНИЯ=======================================
    try {
        v /= 0;
    } catch (ZeroException a) {
        cout << endl << a << endl;
    }
    //=========================================================================
    cout << endl << "v:" << v << endl;
    v += v2;
    cout << "v[0]=" << v[0] << endl;
    cout << endl << v;
    v = v.random();
    cout << endl << "!" << endl << v;

    int m[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    v = Vector(m, 9); //Интересный случай. Почему-то с запуском нового конструктора длина вектора не меняется.
    Vector v3 = Vector(m, 9); //Vector(v);
    cout << endl << "v3:" << v << "-> " << v3 << endl << "v3[5]=" << v3[5];
    v = v3.random();
    cout << endl << "v3:" << v << "-> " << v3 << endl << "v3[5]=" << v3[5];
    v = v3.random();
    v3 = v3.random();
    cout << endl << "v3:" << v << "-> " << v3 << endl << "v3[5]=" << v3[5];
    if (v != v3)cout << endl << "Бинго";
    v = v3;
    if (v == v3)cout << " X2";
    return 0;
}



