## Flatten
В данном задании необходимо реализовать шаблонный класс `FlattenedVector<T>` в файлe flatten.h.
Данный класс принимает на вход объект `std::vector<std::vector<T>>` и имитирует работу с ним, как с объектом типа `std::vector<T>`.

Для этого требуется создать random access iterator, который предоставит весь требуемый функционал.
Чтобы ускорить время обращения к произвольному элементу, предлагается завести вектор префиксных сумм размеров подвекторов входного вектора. Теперь, чтобы найти в каком подмассиве находится элемент, достаточно бинарным поиском найти нужный подмассив, в котором сдвинуться на оставшееся число шагов.

Требования:
* Класс `FlattenedVector<T>` должен иметь random access iterator с временем адресации по индексу хотя бы `O(logN)`, где N - количество подмассивов.
* В остальном итератор должен соотвествовать требованиям [random access iterator](https://en.cppreference.com/w/cpp/iterator/random_access_iterator).
* `FlattenedVector<T>` не должен владеть входными данными, он просто предоставляет возможность итеррировать по внутренним элементам. Аналогично его итератор, тоже должен быть легким объектом, не хранящим в себе много данных.

Примеры использования смотрите в тестирующем коде, файл main.cpp. мой код #ifndef FLATTEN_H
#define FLATTEN_H

#include <vector>
#include <iterator>
#include <algorithm>

template <typename T>
class FlattenedVector {
public:
    using ContainerType = std::vector<std::vector<T>>;
    using SizeType = typename ContainerType::size_type;
    using ValueType = T;

    // Конструктор: при передаче контейнера, нужные типы будут выведены
    FlattenedVector(const ContainerType& data)
        : data_(data), totalSize_(0) {
        prefixSums_.reserve(data.size() + 1);
        prefixSums_.push_back(0);
        for (const auto& sub_vector : data) {
            totalSize_ += sub_vector.size();
            prefixSums_.push_back(totalSize_);
        }
    }

    class Iterator {
    public:
        using iterator_category = std::random_access_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;

        Iterator(const FlattenedVector* flattened, SizeType index)
            : flattened_(flattened), index_(index) {}

        reference operator*() const {
            return flattened_->getElement(index_);
        }

        reference operator[](difference_type n) const {
            return *(*this + n);
        }

        Iterator& operator++() {
            ++index_;
            return *this;
        }

        Iterator operator++(int) {
            Iterator temp = *this;
            ++(*this);
            return temp;
        }

        Iterator& operator--() {
            --index_;
            return *this;
        }

        Iterator operator--(int) {
            Iterator temp = *this;
            --(*this);
            return temp;
        }

        Iterator& operator+=(difference_type n) {
            index_ += n;
            return *this;
        }

        Iterator operator+(difference_type n) const {
            Iterator temp = *this;
            temp += n;
            return temp;
        }

        friend Iterator operator+(difference_type n, const Iterator& it) {
            return it + n;
        }

        Iterator& operator-=(difference_type n) {
            index_ -= n;
            return *this;
        }

        Iterator operator-(difference_type n) const {
            Iterator temp = *this;
            temp -= n;
            return temp;
        }

        difference_type operator-(const Iterator& other) const {
            return index_ - other.index_;
        }

        bool operator==(const Iterator& other) const {
            return index_ == other.index_;
        }

        bool operator!=(const Iterator& other) const {
            return !(*this == other);
        }

        bool operator<(const Iterator& other) const {
            return index_ < other.index_;
        }

        bool operator<=(const Iterator& other) const {
            return index_ <= other.index_;
        }

        bool operator>(const Iterator& other) const {
            return index_ > other.index_;
        }

        bool operator>=(const Iterator& other) const {
            return index_ >= other.index_;
        }

    private:
        const FlattenedVector* flattened_;
        SizeType index_;
    };

    Iterator begin() const {
        return Iterator(this, 0);
    }

    Iterator end() const {
        return Iterator(this, totalSize_);
    }

private:
    const ContainerType& data_;
    std::vector<SizeType> prefixSums_;
    SizeType totalSize_;

    T& getElement(SizeType flatIndex) const {
        auto it = std::upper_bound(prefixSums_.begin(), prefixSums_.end(), flatIndex);
        SizeType outerIndex = std::distance(prefixSums_.begin(), it) - 1;
        SizeType innerIndex = flatIndex - prefixSums_[outerIndex];
        return const_cast<T&>(data_[outerIndex][innerIndex]);
    }
};

#endif // FLATTEN_H