#include "cblas.h"
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

string expect = R"(7 10 15 22 )";

#define M 2
#define N 2
#define K 2

void setup() {
  // put your setup code here, to run once:
  // Define scalar factor alpha for matrix multiplication, here set to 1
  float alpha = 1;

  // Define scalar factor beta for matrix multiplication, here set to 0
  float beta = 0;

  // Define leading dimension of matrix A. In row-major storage, lda represents the number of columns of A
  int lda = K;

  // Define leading dimension of matrix B. In row-major storage, ldb represents the number of columns of B
  int ldb = N;

  // Define leading dimension of matrix C. In row-major storage, ldc represents the number of columns of C
  int ldc = N;

  // Define matrix A stored as a 1D array in row-major order
  float A[M*K] = {1, 2, 3, 4};
  /* Actual 2x2 matrix A:
    1 2
    3 4
  */

  // Define matrix B stored as a 1D array in row-major order
  float B[K*N] = {1, 2, 3, 4};
  /* Actual 2x2 matrix B:
    1 2
    3 4
  */

  // Define matrix C to store the result of matrix multiplication
  float C[M*N];

  // Perform matrix multiplication using OpenBLAS cblas_sgemm:
  // C = alpha * A * B + beta * C
  // Parameters:
  // - CblasRowMajor: matrices stored in row-major order
  // - CblasNoTrans: do not transpose A
  // - CblasNoTrans: do not transpose B
  // - M: number of rows of A
  // - N: number of columns of B
  // - K: number of columns of A (equal to number of rows of B)
  // - alpha: scalar multiplier for A*B
  // - A: input matrix A
  // - lda: leading dimension of A
  // - B: input matrix B
  // - ldb: leading dimension of B
  // - beta: scalar multiplier for C
  // - C: output matrix
  // - ldc: leading dimension of C
  cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
              M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);

  // Create a stringstream to capture the output of matrix C
  stringstream ss;
  // Save the current standard output buffer
  streambuf *buffer = cout.rdbuf();
  // Redirect standard output to stringstream
  cout.rdbuf(ss.rdbuf());

  // Iterate through each element of matrix C and print it
  for (int i = 0; i < M*N; i++) {
      cout << C[i] << " ";
  }

  // Restore the original standard output buffer
  cout.rdbuf(buffer);
  // Convert stringstream contents to a string
  string s(ss.str());

  // Print separator line
  cout << "*********************************************************" << endl;
  // Print result header
  cout << "This is the result:" << endl;
  // Print the computed result of matrix C
  cout << s << endl;
  // Print separator line
  cout << "*********************************************************" << endl;
  // Print reference header
  cout << "This is the reference:" << endl;
  // Print expected reference result
  cout << expect << endl;

  // Compare the actual result with the reference result
  if (expect == s)
      // If they match, print test pass message
      cout << "{Test PASS}." << endl;
  else
      // If they differ, print test fail message
      cout << "{Test FAIL}." << endl;
}

void loop() {
  // put your main code here, to run repeatedly:

}
