#include <iostream>

class Int17Matrix3D {
public:

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

  Int17Matrix3D(const Int17Matrix3D& other);

  ~Int17Matrix3D();

  Int17Matrix3D& operator=(int32_t value);

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

  // Int17Matrix3D& operator[](int index);

  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);

  void SetValueAtCurrentIndex(int32_t value);

  Int17Matrix3D operator+(const Int17Matrix3D& other);

  Int17Matrix3D operator-(const Int17Matrix3D& other);

  Int17Matrix3D operator*(int32_t other);

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 std::ostream& operator<<(std::ostream& stream, const Int17Matrix3D& array);

  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"

class ProxyInt {
public:
    ProxyInt(Int17Matrix3D& matrix, int x, int y) : matrix_(matrix), x_(x), y_(y) {}

    int32_t& operator[](int z);

private:
    Int17Matrix3D& matrix_;
    int x_, y_;
};

class Proxy {
public:
    Proxy(Int17Matrix3D& matrix, int x) : matrix_(matrix), x_(x) {}

    ProxyInt operator[](int y) {
        return ProxyInt(matrix_, x_, y);
    }

private:
    Int17Matrix3D& matrix_;
    int x_;
};


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_ + 31) / 32]) {
    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);
}

int32_t Int17Matrix3D::GetValueAt(int x, int y, int z) const{
    int index = GetIndex(x, y, z);
    // Проверьте диапазон для x, y и z перед доступом к массиву
    return ToDecimal(array_, index); // Может потребоваться изменить остальную логику 
}

Proxy Int17Matrix3D::operator[](int x) {
    return Proxy(*this, x);
}

int32_t& ProxyInt::operator[](int z) {
    return matrix_.GetValueAt(x_, y_, z);
}

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;
}

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

void Int17Matrix3D::SetValueAtCurrentIndex(int32_t value) {
    (*this)[current_x_][current_y_][current_z_] = 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 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 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->ToDecimal(this->array_, index) + other.ToDecimal(other.array_, index);
      }
    }
  }

  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->ToDecimal(this->array_, index) - other.ToDecimal(other.array_, index);
      }
    }
  }

  return result;
}

Int17Matrix3D Int17Matrix3D::operator*(int32_t other) {
  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->ToDecimal(this->array_, index) * other;
      }
    }
  }

  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& matrix) {
  int32_t value = matrix.ToDecimal(matrix.array_, matrix.current_index_);
  stream << value;
  return stream;
}

std::istream& operator>>(std::istream& stream, Int17Matrix3D& array) {
  int32_t value;
  stream >> value;

  if (!stream) return stream;

  array.SetValueAtCurrentIndex(value);

  return stream;
}



[  7%] Building CXX object lib/CMakeFiles/Int17Matrix3D.dir/Int17Matrix3D.cpp.o
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:58:22: error: out-of-line definition of 'operator[]' does not match any declaration in 'Int17Matrix3D'
Proxy Int17Matrix3D::operator[](int x) {
                     ^~~~~~~~
/Users/alex/labwork5-SPLOIT47/lib/Int17Matrix3D.cpp:63:12: error: non-const lvalue reference to type 'int32_t' (aka 'int') cannot bind to a temporary of type 'int32_t'
    return matrix_.GetValueAt(x_, y_, z);
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 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