#include <cstddef>
#include <iostream>
#include <iomanip>

class Matrix {
  protected:
    // Out proxy class.
    // Visible to children, for implementing.
    struct SubscriptProxy {
        virtual operator double&() = 0;
        virtual operator double*() = 0;
        virtual double& operator=(double) = 0;
        virtual double& operator[](size_t) = 0;
        virtual ~SubscriptProxy() = default;
    };

  public:
    virtual SubscriptProxy& operator[](size_t i) = 0;
    virtual ~Matrix() = default;
    
    virtual void out(std::ostream& str) const = 0;
    friend std::ostream& operator<<(std::ostream& str, const Matrix& m) {
        m.out(str);
        return str;
    }
};
std::ostream& operator<<(std::ostream& str, const Matrix& m);

// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
    double arr[5];
    
    // Proxy for single element.
    class OneDProxy : public SubscriptProxy {
        double& elem;

        operator double*() override { return &elem; }
        double& operator[](size_t) override { return elem; }
      public:
        OneDProxy(double& e) : elem(e) {}
      
        operator double&() override { return elem; }
        double& operator=(double d) override {
            elem = d;
            return elem;
        }
    };
    
  public:
    OneDMatrix() : arr{0} {}
  
    // operator[] maintains a static pointer, to keep the return value alive and guarantee
    //  proper cleanup.
    SubscriptProxy& operator[](size_t i) override {
        static OneDProxy* ret = nullptr;
        
        if (ret) { delete ret; }
        ret = new OneDProxy(arr[i]);
        return *ret;
    }
    
    void out(std::ostream& str) const override {
        for (size_t i = 0; i < 5; i++) {
            str << std::setw(4) << arr[i] << ' ';
        }
        str << std::endl;
    }
};

// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
    double arr[3][4];
    
    // Proxy for array.
    class TwoDProxy : public SubscriptProxy {
        double* elem;
        
        operator double&() override { return elem[0]; }
        double& operator=(double) override { return elem[0]; }
      public:
        TwoDProxy(double* e) : elem(e) {}
        operator double*() override { return elem; }
        double& operator[](size_t i) override { return elem[i]; }
    };
    
  public:
    TwoDMatrix() : arr{{0}} {}

    // operator[] maintains a static pointer, to keep the return value alive and guarantee
    //  proper cleanup.
    SubscriptProxy& operator[](size_t i) override {
        static TwoDProxy* ret = nullptr;
        
        if (ret) { delete ret; }
        ret = new TwoDProxy(arr[i]);
        return *ret;
    }
    
    void out(std::ostream& str) const override {
        for (size_t i = 0; i < 3; i++) {
            for (size_t j = 0; j < 4; j++) {
                str << std::setw(4) << arr[i][j] << ' ';
            }
            str << '\n';
        }
    }
};

int main() {
    // By pointer.
    Matrix* one = new OneDMatrix;
    Matrix* two = new TwoDMatrix;
    
    (*one)[2] = 3;
    std::cout << *one << std::endl;
    delete one;
    
    // -----
    
    (*two)[0][1] = 1;
    (*two)[1][0] = 10;
    (*two)[2][2] = 100;
    std::cout << *two << std::endl;
    delete two;
    
    // -----
    
    //Directly.
    OneDMatrix oneone;
    oneone[2] = -3;
    std::cout << oneone << std::endl;
    
    TwoDMatrix twotwo;
    twotwo[1][0] = -1;
    twotwo[0][1] = -10;
    twotwo[2][2] = -100;
    std::cout << twotwo << std::endl;
}