#include <iostream>

class Proxy;
class ConstProxy;

class Int17Matrix3D {
public:

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

  Int17Matrix3D(const Int17Matrix3D& other);

  ~Int17Matrix3D();

  Int17Matrix3D& operator=(int32_t value);

  Int17Matrix3D& operator[](int index);

  int32_t operator[](int index) const;

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

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

  Int17Matrix3D& operator=(const Int17Matrix3D& other);

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

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

private:
  const int8_t kNumLenght_ = 17;
  int32_t current_index_;
  int32_t max_index_;
  int32_t width_;
  int32_t height_;
  int32_t depth_;
  int32_t* array_;
  int32_t current_x_ = 0;
  int32_t current_y_ = 0;
  int32_t current_z_ = 0;

  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;

  int32_t ToDecimal(const int32_t* array_, int32_t current_index_) const;

  friend Int17Matrix3D operator+(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs);

  friend Int17Matrix3D operator-(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs);

  friend Int17Matrix3D operator*(const Int17Matrix3D& lhs, const int32_t& rhs);

  friend std::ostream& operator<<(std::ostream& stream, const Int17Matrix3D& array);

  friend bool operator==(const Int17Matrix3D& lhs, int32_t rhs);

  friend bool operator==(int32_t lhs, const Int17Matrix3D& rhs);

  friend bool operator==(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs);

  friend bool operator!=(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs);
};



#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& 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 Int17Matrix3D::make_array(int x, int y, int z) {
  return Int17Matrix3D(x, y, z);
}

Int17Matrix3D& Int17Matrix3D::operator[](int index) {
  static int8_t counter = 0;
  if (counter == 0) {
    current_x_ = index;
  } else if (counter == 1) {
    current_y_ = index;
  } else {
    current_z_ = index;
  }

  if (counter == 2) {
    current_index_ = GetIndex(current_x_, current_y_, current_z_);
    counter = -1;
  }

  ++counter;

  if (current_x_ >= width_ || current_y_ >= height_ || current_z_ >= depth_) {
    throw std::out_of_range("Indices are out of bounds"); 
  }

  return *this;
}

int Int17Matrix3D::operator[](int index) const {
  return ToDecimal(array_, current_index_);
}

Int17Matrix3D& Int17Matrix3D::operator=(int32_t value) {
  int first_bit_index = current_index_ * kNumLenght_;

  for (int i = 0; i < kNumLenght_; ++i) {
    if (value & (1 << i)) {
      SetBit(array_, first_bit_index + i);
    } else {
      ClearBit(array_, first_bit_index + i);
    }
  }
  return *this;
}

Int17Matrix3D& Int17Matrix3D::operator()(int x, int y, int z) {
  if (x >= width_ || y >= height_ || z >= depth_) {
    throw std::out_of_range("Indices are out of bounds"); 
  }
  current_index_ = GetIndex(x, y, z);
  return *this;
}

int32_t Int17Matrix3D::operator()(int x, int y, int z) const {
  int index = GetIndex(x, y, z);
  return ToDecimal(array_, index);
}

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

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 x + y * (width_ * kNumLenght_) + z * (width_ * height_ * kNumLenght_);
}

int32_t Int17Matrix3D::ToDecimal(const int32_t* array_, int32_t current_index_) const {
  int first_bit_index = current_index_ * kNumLenght_;
  int32_t decimal_value = 0;
  int32_t exp = 1;

  for (int i = 0; i < kNumLenght_; ++i) {
    if (TakeBit(array_, first_bit_index + i)) {
      decimal_value += exp;
    }
    exp <<= 1;
  }

  return decimal_value;
}

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

  Int17Matrix3D result(lhs.width_, lhs.height_, lhs.depth_);

  for (int z = 0; z < lhs.depth_; ++z) {
    for (int y = 0; y < lhs.height_; ++y) {
      for (int x = 0; x < lhs.width_; ++x) {
        int index = lhs.GetIndex(x, y, z);

        result.array_[result.GetIndex(x, y, z)] = lhs.array_[lhs.GetIndex(x, y, z)] + rhs.array_[rhs.GetIndex(x, y, z)];
      }
    }
  }

  return result;
}

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

  Int17Matrix3D result(lhs.width_, lhs.height_, lhs.depth_);

  for (int z = 0; z < lhs.depth_; ++z) {
    for (int y = 0; y < lhs.height_; ++y) {
      for (int x = 0; x < lhs.width_; ++x) {
        int index = lhs.GetIndex(x, y, z);

        result.array_[result.GetIndex(x, y, z)] = lhs.array_[lhs.GetIndex(x, y, z)] - rhs.array_[rhs.GetIndex(x, y, z)];
      }
    }
  }

  return result;
}

Int17Matrix3D operator*(const Int17Matrix3D& lhs, const int32_t& rhs) {
  Int17Matrix3D result(lhs.width_, lhs.height_, lhs.depth_);

  for (int z = 0; z < lhs.depth_; ++z) {
    for (int y = 0; y < lhs.height_; ++y) {
      for (int x = 0; x < lhs.width_; ++x) {
        int index = lhs.GetIndex(x, y, z);

        result.array_[result.GetIndex(x, y, z)] = lhs.array_[lhs.GetIndex(x, y, z)] * rhs;
      }
    }
  }

  return result;
}

bool operator==(const Int17Matrix3D& lhs, int32_t rhs) {
  return lhs.ToDecimal(lhs.array_, lhs.current_index_) == rhs;
}

bool operator==(int32_t lhs, const Int17Matrix3D& rhs) {
  return lhs == rhs.ToDecimal(rhs.array_, rhs.current_index_);
}

bool operator==(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs) {
  return lhs.ToDecimal(lhs.array_, lhs.current_index_) == rhs.ToDecimal(rhs.array_, rhs.current_index_);
}

bool operator!=(const Int17Matrix3D& lhs, const Int17Matrix3D& rhs) {
  return !(lhs == rhs);
}

std::ostream& operator<<(std::ostream& stream, const Int17Matrix3D& array) {

}


реалиузй операторы >> и << для одного элемента