/*****************************************************************************
 * $Id: TransformVertex1_speed.cc,v 1.9 2006/07/23 02:09:11 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Tests the speed of variants of the TransformVertex1() templates.
 *			Unsuprisingly, the reference variant is faster than the pointer one.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define TESTING 1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <sys/times.h>
#include <fcntl.h>
#include <limits.h>

#include <iostream>
#include <iomanip>
using namespace std;

// Amount of vertex transformations per test.
const int LOOP_CNT = 100*1000*1000;

typedef float fp;
fp eye_m[16];

enum { Xx, Xy, Xz, Xs,
       Yx, Yy, Yz, Ys,
       Zx, Zy, Zz, Zs,
       Ox, Oy, Oz, Os }; /*origin*/

struct vertexWorld
{
	fp	x;
	fp	y;
	fp	z;
};
struct vertexEye
{
	fp	x;
	fp	y;
	fp	z;
};

// TransformVertex1() using pointers.
static inline void
TransformVertex1( vertexEye* v2, const vertexWorld* v, const fp m[16] )
{
	// Variants of TransformVertex1() only differ in types (same calculation).
	fp x = v->x + m[Ox];
	fp y = v->y + m[Oy];
	fp z = v->z + m[Oz];
	v2->x = x * m[Xx] + y * m[Xy] + z * m[Xz];
	v2->y = x * m[Yx] + y * m[Yy] + z * m[Yz];
	v2->z = x * m[Zx] + y * m[Zy] + z * m[Zz];
}

// TransformVertex1() using refs.
static inline void
TransformVertex1( vertexEye& v2, const vertexWorld& v, const fp m[16] )
{
	// Variants of TransformVertex1() only differ in types (same calculation).
	fp x = v.x + m[Ox];
	fp y = v.y + m[Oy];
	fp z = v.z + m[Oz];
	v2.x = x * m[Xx] + y * m[Xy] + z * m[Xz];
	v2.y = x * m[Yx] + y * m[Yy] + z * m[Yz];
	v2.z = x * m[Zx] + y * m[Zy] + z * m[Zz];
}

// TransformVertex1() using refs and without passing m[].
static inline void
TransformVertex1( vertexEye& v2, const vertexWorld& v )
{
	// Variants of TransformVertex1() only differ in types (same calculation).
	fp x = v.x + eye_m[Ox];
	fp y = v.y + eye_m[Oy];
	fp z = v.z + eye_m[Oz];
	v2.x = x * eye_m[Xx] + y * eye_m[Xy] + z * eye_m[Xz];
	v2.y = x * eye_m[Yx] + y * eye_m[Yy] + z * eye_m[Yz];
	v2.z = x * eye_m[Zx] + y * eye_m[Zy] + z * eye_m[Zz];
}

////////////////////////////////////////////////////////////////////////////////

// TransformVertex1() using pointers.
void
Test1( void )
{
	vertexWorld v;
	vertexEye v2;
	fp m[16];
	for ( int i = 0; i < 16; ++i ) m[i] = 1.0;

	for ( int i = 0; i < LOOP_CNT; ++i )
	{
		v.x = 10.0;
		v.y = 10.0;
		v.z = 10.0;
		TransformVertex1( &v2, &v, m );
	}
}

// TransformVertex1() using refs.
void
Test2( void )
{
	vertexWorld v;
	vertexEye v2;
	fp m[16];
	for ( int i = 0; i < 16; ++i ) m[i] = 1.0;

	for ( int i = 0; i < LOOP_CNT; ++i )
	{
		v.x = 10.0;
		v.y = 10.0;
		v.z = 10.0;
		TransformVertex1( v2, v, m );
	}
}

// TransformVertex1() using refs and without passing m[].
void
Test3( void )
{
	vertexWorld v;
	vertexEye v2;
	for ( int i = 0; i < 16; ++i ) eye_m[i] = 1.0; // eye

	for ( int i = 0; i < LOOP_CNT; ++i )
	{
		v.x = 10.0;
		v.y = 10.0;
		v.z = 10.0;
		TransformVertex1( v2, v );
	}
}

char*
Measure( void(*Test)(void ) )
{
	char* results = (char*) malloc( 1000 );
#if __linux__
	// POSIX code that measures the time the process consumed in user-mode and kernel mode.
	struct tms time1;
	times( &time1 );

	Test();

	struct tms time2;
	times( &time2 );
	int clocksPerSec	= sysconf( _SC_CLK_TCK );
	float sec_user_sys	= float(time2.tms_utime+time2.tms_stime - time1.tms_utime+time1.tms_stime) / float(clocksPerSec);
	float sec_user		= float(time2.tms_utime                 - time1.tms_utime                ) / float(clocksPerSec);
	sprintf( results, "------------------------------------\n"
	                  "secs (user)     = %f\n"
	                  "secs (user+sys) = %f\n"
	                  "------------------------------------\n",
                      sec_user, sec_user_sys );
#else

//#error
	cerr << "### Linux is required ### \n";
	exit(1);

#endif /*__linux__*/
	return results;
}

int
main( int a, char** b )
{
	char* results3 = Measure( Test3 );
	char* results2 = Measure( Test2 );
	char* results1 = Measure( Test1 );
	cerr << "TransformVertex1() using pointers:\n" << results1 << endl;
	cerr << "TransformVertex1() using refs:\n"     << results2 << endl;
	cerr << "TransformVertex1() using refs without passing m[]:\n" << results3 << endl;
	return 0;
}
