/*
// traditional "naiive" loops
forall( [M], [N], [P] )
void matmul( array(float, M, P) & src1,
			 array(float, P, N) & src2, 
			 array(float, M, N) & tgt ) {
	for (i; M) for (j; N) {
		tgt[i][j] = 0;
		for (k; P)
			tgt[i][j] += src1[i][k] * src2[k][j];
	}
}
*/

#if defined BADBOUNDS
#define BOUND(X) 17
#else
#define BOUND(X) X
#endif

#include <vector>
using namespace std;


#ifndef TESTCASE
#define TESTCASE 1
#endif

#if TESTCASE==1

float f( vector<float> & a ) {
	float result = 0;
	for( int i = 0; i < BOUND(a.size()); i++ ) {
		result += a.at(i);
		// hillarious that, while writing THIS VERY DEMO, on first go, I actaully wrote it a[i]
	}
	return result;
}

#ifdef WITHHARNESS
#include <iostream>
int main( int argc, char ** argv ) {
	vector<float> v(5);
	v.at(0) = 3.14;
	v.at(1) = 3.14;
	v.at(2) = 3.14;
	v.at(3) = 3.14;
	v.at(4) = 3.14;

	float answer = f(v);

	cout << "answer: " << answer << endl;

}
#endif 

// g++ array-boundcheck-removal-stdvec.cpp -DWITHHARNESS -DBADBOUNDS
// ./a.out
// Aborted: terminate called after throwing an instance of 'std::out_of_range'


// g++ array-boundcheck-removal-stdvec.cpp -DWITHHARNESS
// ./a.out
// answer: 15.7

// g++ array-boundcheck-removal-stdvec.cpp -S -O2 -DBADBOUNDS
// loop has cmp-jmp
// jmp target has call _ZSt24__throw_out_of_range_fmtPKcz@PLT

// g++ array-boundcheck-removal-stdvec.cpp -S -O2
// loop is clean


#elif TESTCASE==2

//#include <cassert>
#define assert(prop) if (!prop) return

typedef vector<vector<float> > mat;

void matmul( mat & a, mat & b, mat & rslt ) {
	size_t m = rslt.size();
	assert( m == a.size() );
	size_t p = b.size();
	for ( int i = 0; i < BOUND(m); i++ ) {
		assert( p == a.at(i).size() );
		size_t n = rslt.at(i).size();
		for ( int j = 0; j < BOUND(n); j++ ) {
			rslt.at(i).at(j) = 0.0;
			for ( int k = 0; k < BOUND(p); k++ ) {
				assert(b.at(k).size() == n); // asking to check it too often
				rslt.at(i).at(j) += a.at(i).at(k) * b.at(k).at(j);
			}
		}
	}
}

#ifdef WITHHARNESS
#include <iostream>
int main( int argc, char ** argv ) {
	mat a(5, vector<float>(6));
	mat b(6, vector<float>(7));
	mat r(5, vector<float>(7));
	matmul(a, b, r);
}
#endif 

// (modify the declarations in main to be off by one, each of the 6 values at a time)
// g++ array-boundcheck-removal-stdvec.cpp -DWITHHARNESS -DTESTCASE=2
// ./a.out
// (see an assertion failure)

// g++ array-boundcheck-removal-stdvec.cpp -DWITHHARNESS -DTESTCASE=2
// ./a.out
// (runs fine)

// g++ array-boundcheck-removal-stdvec.cpp -S -O2 -DTESTCASE=2
// hypothesis: loop bound checks are clean because the assertions took care of it
// actual so far:  both assertions and bound checks survived

#endif
