#include <iostream>

class Int17Matrix3D {
public:

  Int17Matrix3D(int x, int y, int z);

  Int17Matrix3D(const Int17Matrix3D& other);

  ~Int17Matrix3D();

  Int17Matrix3D& operator=(int32_t value);  

  Int17Matrix3D& operator=(const Int17Matrix3D& other);

  static Int17Matrix3D make_array(int x, int y, int z);

  void SetValueAtCurrentIndex(int32_t value);

  class Proxy3D;
  class Proxy2D;
  class Proxy1D;

  Proxy1D operator[](int x);

  Int17Matrix3D operator+(const Int17Matrix3D& other);

  Int17Matrix3D operator-(const Int17Matrix3D& other);

  Int17Matrix3D operator*(int32_t mult);

  bool operator==(const Int17Matrix3D& other) const;

  bool operator!=(const Int17Matrix3D& other) const;

  friend std::istream& operator>>(std::istream& stream, Int17Matrix3D& array);

private:
  const int8_t kNumLenght_ = 17;
  int32_t width_;
  int32_t height_;
  int32_t depth_;
  int32_t* array_;

  void copy(int32_t* array1, const int32_t* array2, int size);

  Int17Matrix3D& SetValueAtIndex(int x, int y, int z, int32_t value);

  int32_t GetValueAtIndex(int x, int y, int z) const;

  bool TakeBit(const int32_t* value, uint32_t bit_position) const;

  Int17Matrix3D& ClearBit(int32_t* value, int32_t bit_position);

  Int17Matrix3D& SetBit(int32_t* value, int32_t bit_position);

  int GetIndex(int x, int y, int z) const;
};

class Int17Matrix3D::Proxy3D {
    Int17Matrix3D& parentMatrix_;
    int x_, y_, z_;

public:
    Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z);

    Proxy3D& operator=(int value);

    operator int() const {
        return parentMatrix_.GetValueAtIndex(x_, y_, z_);
    }
};

class Int17Matrix3D::Proxy2D {
  Int17Matrix3D& parentMatrix_;
  int x_, y_;

public:
  Proxy2D(Int17Matrix3D& parentMatrix_, int x, int y) : parentMatrix_(parentMatrix_), x_(x), y_(y) {}

  Proxy3D operator[](int z);
};

class Int17Matrix3D::Proxy1D {
  Int17Matrix3D& parentMatrix_;
  int x_;

public:
  Proxy1D(Int17Matrix3D& parentMatrix_, int x) : parentMatrix_(parentMatrix_), x_(x) {}

  Proxy2D operator[](int y);
};



#include <iostream>
#include "Int17Matrix3D.h"

Int17Matrix3D::Int17Matrix3D(int x, int y, int z) : width_(x), height_(y), depth_(z), array_(new int32_t[kNumLenght_ * x * y * z]){}

Int17Matrix3D::Int17Matrix3D(const Int17Matrix3D& other) : width_(other.width_), height_(other.height_), depth_(other.depth_), array_(new int32_t[kNumLenght_ * width_ * height_ * depth_]) {
    copy(array_, other.array_, kNumLenght_ * width_ * height_ * depth_);
}
Int17Matrix3D::~Int17Matrix3D() { delete[] array_; }

Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}

Int17Matrix3D& Int17Matrix3D::operator=(const Int17Matrix3D& other) {
  if (this != &other) {
    delete[] array_;
    width_ = other.width_;
    height_ = other.height_;
    depth_ = other.depth_;
    array_ = new int32_t[kNumLenght_ * width_ * height_ * depth_];
    copy(array_, other.array_, kNumLenght_ * width_ * height_ * depth_);
  }
  return *this;
}

Int17Matrix3D::Proxy3D& Int17Matrix3D::Proxy3D::operator=(int value) {
  parentMatrix_.SetValueAtIndex(x_, y_, z_, value);
  return *this;
}

Int17Matrix3D Int17Matrix3D::make_array(int x, int y, int z) {
  return Int17Matrix3D(x, y, z);
}

void Int17Matrix3D::copy(int32_t* array1, const int32_t* array2, int size) {
  for (int i = 0; i < size; ++i) {
    array1[i] = array2[i];
  }
}

Int17Matrix3D::Proxy1D Int17Matrix3D::operator[](int x) {
  if (x < 0 || x >= this->width_) {
    throw std::out_of_range("Index out of range in x-dimension");
  }
  return Proxy1D(*this, x);
}

Int17Matrix3D::Proxy2D Int17Matrix3D::Proxy1D::operator[](int y) {
  if (y < 0 || y >= parentMatrix_.height_) {
    throw std::out_of_range("Index out of range in y-dimension");
  }
  return Proxy2D(parentMatrix_, x_, y);
}

Int17Matrix3D::Proxy3D Int17Matrix3D::Proxy2D::operator[](int z) {
  if (z < 0 || z >= parentMatrix_.depth_) {
    throw std::out_of_range("Index out of range in z-dimension");
  }
  return Proxy3D(parentMatrix_, x_, y_, z);
}

Int17Matrix3D& Int17Matrix3D::SetValueAtIndex(int x, int y, int z, int32_t value) {
  int bit_index = GetIndex(x, y, z);
  for (int i = 0; i < kNumLenght_; ++i) {
    if (value & (1 << i)) {
      SetBit(array_, bit_index);
    } else {
      ClearBit(array_, bit_index);
    }
    bit_index++;
  }
  return *this;
}

int32_t Int17Matrix3D::GetValueAtIndex(int x, int y, int z) const {
  int32_t value = 0;
  int bit_index = GetIndex(x, y, z);
  for (int i = 0; i < kNumLenght_; ++i) {
    if (TakeBit(array_, bit_index)) {
      value |= (1 << i);
    }
    bit_index++;
  }
  return value;
}

bool Int17Matrix3D::TakeBit(const int32_t* value, uint32_t bit_position) const {
  int array_index = bit_position / 32;
  int bit_index = bit_position % 32;
  return ((value[array_index] >> bit_index) & 1) != 0;
}

Int17Matrix3D& Int17Matrix3D::ClearBit(int32_t* value, int32_t bit_position) {
  value[bit_position / 32] &= ~(1 << (bit_position % 32));
  return *this;
}

Int17Matrix3D& Int17Matrix3D::SetBit(int32_t* value, int32_t bit_position) {
  value[bit_position / 32] |= (1 << (bit_position % 32));
  return *this;
}

int Int17Matrix3D::GetIndex(int x, int y, int z) const {
  return (z * width_ * height_ + y * width_ + x) * kNumLenght_; 
}

Int17Matrix3D Int17Matrix3D::operator+(const Int17Matrix3D& other) {
  if (this->width_ != other.width_ || this->height_ != other.height_ || this->depth_ != other.depth_) {
    throw std::out_of_range("Arrays must be with the same size");
  }

  Int17Matrix3D result(this->width_, this->height_, this->depth_);

  for (int z = 0; z < this->depth_; ++z) {
    for (int y = 0; y < this->height_; ++y) {
      for (int x = 0; x < this->width_; ++x) {
        int index = this->GetIndex(x, y, z);
        result[x][y][z] = this->GetValueAtIndex(x, y, z) + other.GetValueAtIndex(x, y, z);
      }
    }
  }
  return result;
}

Int17Matrix3D Int17Matrix3D::operator-(const Int17Matrix3D& other) {
  if (this->width_ != other.width_ || this->height_ != other.height_ || this->depth_ != other.depth_) {
    throw std::out_of_range("Arrays must be with the same size");
  }

  Int17Matrix3D result(this->width_, this->height_, this->depth_);

  for (int z = 0; z < this->depth_; ++z) {
    for (int y = 0; y < this->height_; ++y) {
      for (int x = 0; x < this->width_; ++x) {
        int index = this->GetIndex(x, y, z);
        result[x][y][z] = this->GetValueAtIndex(x, y, z) - other.GetValueAtIndex(x, y, z);
      }
    }
  }
  return result;
}

Int17Matrix3D Int17Matrix3D::operator*(int32_t mult) {
  Int17Matrix3D result(this->width_, this->height_, this->depth_);

  for (int z = 0; z < this->depth_; ++z) {
    for (int y = 0; y < this->height_; ++y) {
      for (int x = 0; x < this->width_; ++x) {
        int index = this->GetIndex(x, y, z);
        result[x][y][z] = this->GetValueAtIndex(x, y, z) * mult;
      }
    }
  }
  return result;
}

bool Int17Matrix3D::operator==(const Int17Matrix3D& other) const{
  for (int z = 0; z < this->depth_; ++z) {
    for (int y = 0; y < this->height_; ++y) {
      for (int x = 0; x < this->width_; ++x) {
        int index = this->GetIndex(x, y, z);
        if (this->GetValueAtIndex(x, y, z) != other.GetValueAtIndex(x, y, z)) return false;
      }
    }
  }
  return true;
}

bool Int17Matrix3D::operator!=(const Int17Matrix3D& other) const{
  return !(operator==(other));
}

std::istream& operator>>(std::istream& stream, Int17Matrix3D& matrix) {
    int32_t value;
    for (int z = 0; z < matrix.depth_; ++z) {
        for (int y = 0; y < matrix.height_; ++y) {
            for (int x = 0; x < matrix.width_; ++x) {
                if (!(stream >> value)) {
                    return stream;
                }
                matrix.SetValueAtIndex(x, y, z, value);
            }
        }
    }
    return stream;
}

[  7%] Building CXX object lib/CMakeFiles/Int17Matrix3D.dir/Int17Matrix3D.cpp.o
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:37: error: expected ')'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                    ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:23: note: to match this '('
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                      ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:24: error: no matching constructor for initialization of 'Int17Matrix3D::Proxy3D'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                       ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.h:60:22: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
class Int17Matrix3D::Proxy3D {
                     ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.h:60:22: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were provided
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.h:65:5: note: candidate constructor not viable: requires 4 arguments, but 0 were provided
    Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z);
    ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:75: error: expected ';' after top level declarator
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                          ^
                                                                          ;
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:77: error: unknown type name 'parentMatrix_'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                            ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:110: error: unknown type name 'x'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                                                             ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:117: error: unknown type name 'y'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                                                                    ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:124: error: unknown type name 'z'
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                                                                           ^
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:11:126: error: expected ';' after top level declarator
Int17Matrix3D::Proxy3D(Int17Matrix3D& parentMatrix_, int x, int y, int z) : parentMatrix_(parentMatrix_), x_(x), y_(y), z_(z) {}
                                                                                                                             ^
                                                                                                                             ;
8 errors generated.
make[2]: *** [lib/CMakeFiles/Int17Matrix3D.dir/Int17Matrix3D.cpp.o] Error 1
make[1]: *** [lib/CMakeFiles/Int17Matrix3D.dir/all] Error 2
make: *** [all] Error 2