#ifndef COMPARISON_BENCHMARKS_BASE_MATRIX_DIV_CONQUER_H
#define COMPARISON_BENCHMARKS_BASE_MATRIX_DIV_CONQUER_H

#include <array>

namespace comparison_benchmarks {
namespace base {
namespace matrix_div_conquer {

const int MATRIX_SIZE = 128;
const int CUTOFF_SIZE = 8;

const int NUM_ITERATIONS = 100;
const int WARMUP_ITERATIONS = 10;

// Helpers to directly index into blocked matrices
const size_t MAX_SIZE = 128;
std::array<std::array<size_t, MAX_SIZE>, MAX_SIZE> BLOCK_LOOKUP; // ROW, COLUMN
void fill_block_lookup(size_t size = MAX_SIZE) {
  if (size <= 1) {
    BLOCK_LOOKUP[0][0] = 0;
    return;
  }

  fill_block_lookup(size / 2);

  size_t elements_per_quarter = (size / 2) * (size / 2);
  for (size_t row = 0; row < size / 2; row++) {
    for (size_t column = 0; column < size / 2; column++) {
      BLOCK_LOOKUP[row][size / 2 + column] = BLOCK_LOOKUP[row][column] + elements_per_quarter;
      BLOCK_LOOKUP[size / 2 + row][column] = BLOCK_LOOKUP[row][column] + 2 * elements_per_quarter;
      BLOCK_LOOKUP[size / 2 + row][size / 2 + column] = BLOCK_LOOKUP[row][column] + 3 * elements_per_quarter;
    }
  }
}

class blocked_matrix_view {
 public:
  blocked_matrix_view(double *data, size_t size) : data_{data}, size_{size} {}

  void fill_default_data() {
    for (size_t row = 0; row < size_; row++) {
      for (size_t column = 0; column < size_; column++) {
        at(row, column) = row;
      }
    }
  }

  blocked_matrix_view quadrant_1_1() {
    size_t elements_per_quarter = (size_ / 2) * (size_ / 2);
    return blocked_matrix_view(data_ + 0 * elements_per_quarter, size_ / 2);
  }
  blocked_matrix_view quadrant_1_2() {
    size_t elements_per_quarter = (size_ / 2) * (size_ / 2);
    return blocked_matrix_view(data_ + 1 * elements_per_quarter, size_ / 2);
  }
  blocked_matrix_view quadrant_2_1() {
    size_t elements_per_quarter = (size_ / 2) * (size_ / 2);
    return blocked_matrix_view(data_ + 2 * elements_per_quarter, size_ / 2);
  }
  blocked_matrix_view quadrant_2_2() {
    size_t elements_per_quarter = (size_ / 2) * (size_ / 2);
    return blocked_matrix_view(data_ + 3 * elements_per_quarter, size_ / 2);
  }

  double &at(size_t row, size_t column) {
    return data_[BLOCK_LOOKUP[row][column]];
  }

  double *get_data() {
    return data_;
  }

 private:
  double *data_;
  size_t size_;
};

void multiply_naive(size_t size, blocked_matrix_view &result, blocked_matrix_view &a, blocked_matrix_view &b) {
  for (size_t i = 0; i < size; i++) {
    for (size_t j = 0; j < size; j++) {
      result.at(i, j) = 0;
    }
    for (size_t j = 0; j < size; j++) {
      for (size_t k = 0; k < size; k++) {
        result.at(i, j) += a.at(i, k) * b.at(k, j);
      }
    }
  }
}

}
}
}

#endif // COMPARISON_BENCHMARKS_BASE_MATRIX_DIV_CONQUER_H
