#include <mof/math/vector.hpp>
#include <mof/math/matrix.hpp>
#include <mof/util/multimedia_timer.hpp>
#include <iostream>
#include <memory>

const int REPEAT_COUNT = 100000000;

namespace 
{
//{{{ shared_type
	class shared_type
	{
		std::shared_ptr<int> p_; 
	public:

		shared_type()
		{
			p_ = std::make_shared<int>(1);
		}
	};
//}}}
//{{{ raw_type
	template <size_t Dim>
	class raw_type
	{
		float data[Dim];
	public:
		// ランタイムチェックに引っかからないように
		raw_type()
		{
		}
	};
//}}}
	mof::math::vector2 unused1;// block compile optimation
	raw_type<2>        unused2;// block compile optimation
	mof::math::vector3 unused3;// block compile optimation
	raw_type<3>        unused4;// block compile optimation
	mof::math::matrix2 unused5;// block compile optimation
	raw_type<9>        unused6;// block compile optimation
	mof::math::matrix3 unused7;// block compile optimation
	raw_type<16>       unused8;// block compile optimation
	shared_type        unused9;// block compile optimation
//{{{ copy_test
	template <typename T>
	int copy_test(T& unused) {
		using namespace mof::util;
		T v;
		multimedia_timer timer;
		for (int i = 0; i < REPEAT_COUNT; i++) unused = v;
		return timer.get_time_in_micro_sec();
	}
//}}}
//{{{ add_test
	template <typename T>
	int add_test(T& unused) {
		using namespace mof::util;
		T x;
		multimedia_timer timer;
		for (int i = 0; i < REPEAT_COUNT; i++) unused = x + unused;
		return timer.get_time_in_micro_sec();
	}
//}}}
//{{{ multiply_test
	template <typename Matrix, typename Vector>
	int multiply_test(Vector& unused) {
		using namespace mof::util;
		Matrix M;
		Vector v;
		multimedia_timer timer;
		for (int i = 0; i < REPEAT_COUNT; i++) unused = M * unused;
		return timer.get_time_in_micro_sec();
	}
//}}}
}

void copy_tests();
void add_tests();
void multiply_tests();

//{{{ main
int main()
{
	std::cout << REPEAT_COUNT << " times repeating" << std::endl;
	copy_tests();
	add_tests();
	multiply_tests();
	return 0;
}
//}}}
//{{{ copy_tests
void copy_tests()
{
	using namespace std;
	using namespace mof::math;

	cout << "***copy performance test***" << endl;
	
	cout << "vector2 --- " ;
	cout << copy_test<vector2>(unused1) << "[micro sec]" << endl;
	
	cout << "raw type2 --- " ;
	cout << copy_test<raw_type<2>>(unused2) << "[micro sec]" << endl;

	cout << "vector3 --- " ;
	cout << copy_test<vector3>(unused3) << "[micro sec]" << endl;
	
	cout << "raw type3 --- " ;
	cout << copy_test<raw_type<3>>(unused4) << "[micro sec]" << endl;

	cout << "matrix2 --- " ;
	cout << copy_test<matrix2>(unused5) << "[micro sec]" << endl;

	cout << "raw type9 --- " ;
	cout << copy_test<raw_type<9>>(unused6) << "[micro sec]" << endl;

	cout << "matrix3 --- " ;
	cout << copy_test<matrix3>(unused7) << "[micro sec]" << endl;

	cout << "raw type16 --- " ;
	cout << copy_test<raw_type<16>>(unused8) << "[micro sec]" << endl;
	
	cout << "shared type --- " ;
	cout << copy_test<shared_type>(unused9) << "[micro sec]" << endl;
}
//}}}
//{{{ add_tests
void add_tests()
{
	using namespace std;
	using namespace mof::math;

	cout << "***add performance test***" << endl;

	cout << "vector2 + vector2 --- " ;
	cout << add_test<vector2>(unused1) << "[micro sec]" << endl;
	
	cout << "vector3 + vector3 --- " ;
	cout << add_test<vector3>(unused3) << "[micro sec]" << endl;

	cout << "matrix2 + matrix2 --- " ;
	cout << add_test<matrix2>(unused5) << "[micro sec]" << endl;

	cout << "matrix3 + matrix3 --- " ;
	cout << add_test<matrix3>(unused7) << "[micro sec]" << endl;
}
//}}}
//{{{ multiply_tests
void multiply_tests()
{
	using namespace std;
	using namespace mof::math;

	cout << "***multiply performance test***" << endl;

	cout << "matrix2 * vector2 --- " ;
	cout << multiply_test<matrix2, vector2>(unused1) << "[micro sec]" << endl;
	cout << "matrix3 * vector3 --- " ;
	cout << multiply_test<matrix3, vector3>(unused3) << "[micro sec]" << endl;
}
//}}}
