
#ifndef COMPARISON_BENCHMARKS_BASE_MATRIX_H
#define COMPARISON_BENCHMARKS_BASE_MATRIX_H

#include <vector>
#include <algorithm>
#include <iostream>

namespace comparison_benchmarks {
namespace base {
namespace matrix {

const int MATRIX_SIZE = 128;

const int NUM_ITERATIONS = 5000;
const int WARMUP_ITERATIONS = 1000;

template<typename T>
class matrix {
 public:
  size_t size_;
  std::vector<T> data_;

  T &data_at(size_t row, size_t column) {
    return data_[row * size_ + column];
  }
  const T &data_at(size_t row, size_t column) const {
    return data_[row * size_ + column];
  }

  explicit matrix(size_t size) : size_{size}, data_(size * size) {
    for (int i = 0; i < size_; i++) {
      for (int j = 0; j < size_; j++) {
        data_at(i, j) = i;
      }
    }
  }

  virtual void multiply(const matrix<T> &a, const matrix<T> &b) {
    for (int i = 0; i < size_; i++) {
      multiply_column(i, a, b);
    }
  }

 protected:
  void multiply_column(int i, const matrix<T> &a, const matrix<T> &b) {
    for (int j = 0; j < size_; ++j) {
      data_at(i, j) = 0;
    }
    for (int k = 0; k < size_; ++k) {
      for (int j = 0; j < size_; ++j) {
        data_at(i, j) += a.data_at(i, k) * b.data_at(i, k);
      }
    }
  }
};

template<typename T, int SIZE>
std::ostream &operator<<(std::ostream &strm, const matrix<T> &matrix) {
  for (int i = 0; i < SIZE; i++) {
    for (int j = 0; j < SIZE; j++) {
      strm << matrix.data_at(i, j) << "\t";
    }
    strm << std::endl;
  }

  return strm;
}

}
}
}

#endif //COMPARISON_BENCHMARKS_BASE_MATRIX_H
