#include <iostream>
#include <deque>

using namespace std;

template <class T>
struct Operation
{
    virtual ~Operation() {}

    virtual Operation * Clone() const = 0;

    virtual T ApplyDirect (T value) const = 0;
    virtual T ApplyInverse(T value) const = 0;
};

template <class T>
struct Add : Operation<T>
{
    T amount;

    Add(T amount_) : amount(amount_) {}

    Add * Clone() const { return new Add(amount); }

    T ApplyDirect (T value) const { return value + amount; }
    T ApplyInverse(T value) const { return value - amount; }
};

template <class T>
struct Mul : Operation<T>
{
    T amount;

    Mul(T amount_) : amount(amount_) {}

    Mul * Clone() const { return new Mul(amount); }

    T ApplyDirect (T value) const { return value * amount; }
    T ApplyInverse(T value) const { return value / amount; }
};

template <class T, unsigned N>
class MassiveValArray
{
    deque<T> data;

    deque<unsigned> changedElements;
    deque<Operation<T>*> operations;

    void makeSomeCalculations()
    {
        if (changedElements.empty()) return;

        static const unsigned elementsChangedPerCall = 3;

        for (unsigned i = 0; i < elementsChangedPerCall; ++ i)
        {
            unsigned index = changedElements[0] + i;

            if (index >= N) break;

            data[index] = operations[0]->ApplyDirect(data[index]);
        }

        changedElements[0] += elementsChangedPerCall;

        if (changedElements[0] >= N)
        {
            changedElements.pop_front();

            delete operations[0];
            operations.pop_front();
        }
    }

public:

    MassiveValArray() : data(N) {}
    MassiveValArray(T initialValue) : data(N, initialValue) {}

    ~MassiveValArray() { for (unsigned i = 0; i < operations.size(); ++ i) delete operations[i]; }

    T Get(unsigned index)
    {
        if (operations.empty()) return data[index];

        makeSomeCalculations();

        T ret = data[index];

        for (unsigned i = 0, size = operations.size(); i < size; ++ i)
            if (changedElements[i] <= index)
                ret = operations[i]->ApplyDirect(ret);

        return ret;
    }

    void Set(unsigned index, T value)
    {
        if (operations.empty()) { data[index] = value; return; }

        makeSomeCalculations();

        for (unsigned i = operations.size() - 1; i + 1 > 0; -- i)
            if (changedElements[i] <= index)
                value = operations[i]->ApplyInverse(value);

        data[index] = value;
    }

    void ApplyToAll(const Operation<T> & op)
    {
        operations.push_back(op.Clone());
        changedElements.push_back(0);

        makeSomeCalculations();
    }

    ostream & Print()
    {
        for (unsigned i = 0; i < N; ++ i)
            cout << Get(i) << " ";

        return cout << endl;
    }

    ostream & PrintOpInfo() const
    {
        unsigned opCount = operations.size();

        cout << "stacked ops : " << opCount << endl;

        if (opCount > 0)
            cout << "front op completion : "
                 << changedElements[0] * 100 / N
                 << "%" << endl;

        return cout;
    }
};

int main()
{
    MassiveValArray<double, 35> arr(1);

    arr.Print();

    arr.ApplyToAll(Mul<double>(2.0)); arr.ApplyToAll(Add<double>(1.0));
    arr.ApplyToAll(Mul<double>(2.0)); arr.ApplyToAll(Add<double>(3.0));

    arr.Set(0, 0.0); arr.Set(1, 1.0); arr.Set(2, 2.0); arr.Set(3, 3.0); arr.Set(4, 4.0); arr.Set(5, 5.0);

    cout << "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" << endl;

    arr.PrintOpInfo();

    cout << "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" << endl;

    arr.Print() << endl; arr.PrintOpInfo();

    cout << "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" << endl;

    arr.Print() << endl; arr.PrintOpInfo();
}