/* original code copyright 2004 Christopher W. Cowell-Shah http://www.cowell-shah.com/research/benchmark/code */
/* other code portions copyright http://dada.perl.it/shootout/ and Doug Bagley http://www.bagley.org/~doug/shootout */
/* combined, modified and fixed by Thomas Bruckschlegel - http://www.tommti-systems.com */

//#include "stdafx.h"
#include <time.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <list>
#include <list>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;


int Hi = 0, Lo = 0;

double intArithmetic(int);
double doubleArithmetic(double, double);
double longArithmetic(long long, long long);
double trig(double);
double io(int);

double array(int n);
double except(int volatile N);
double hashtest(int n);
double hashes(int n);
double hs(int N);
double ls(int n);
double mm(int n);
double nl(int n);
double sc(int n);

int main()
{
	int intMax =		1000000000; // 1B
	double doubleMin =  10000000000.0; // 10B
	double doubleMax =	11000000000.0; // 11B
	long long longMin = 10000000000; // 10B
	long long longMax = 11000000000; // 11B
	double trigMax =	10000000; // 10M
	int ioMax =			1000000; // 1M

	printf("Start C++ benchmark\n");

    long intArithmeticTime = (long)intArithmetic(intMax);
    long doubleArithmeticTime = (long)doubleArithmetic(doubleMin, doubleMax);
    long longArithmeticTime = (long)longArithmetic(longMin, longMax);
    long trigTime = (long)trig(trigMax);
    long ioTime = (long)io(ioMax);
	
	int n=10;
	long arrayTime = (long)array(n*10000);
	long ExceptionTime = (long)except(n*100000);
	long hashtestTime = (long)hashtest(n*10000);
	long hashesTime = (long)hashes(n*100);
	long hsTime = (long)hs(n*100000);
	long lsTime = (long)ls(n*10);
	long mmTime = (long)mm(n*10000);     
	long nlTime = (long)nl(n*4);                
	long scTime = (long)sc(n*1000000);

	long totalTime =intArithmeticTime + doubleArithmeticTime + longArithmeticTime + trigTime + ioTime
					+arrayTime
					+ExceptionTime
					+hashtestTime
					+hashesTime
					+hsTime 
					+lsTime 
					+mmTime 
					+nlTime 
					+scTime; 


	printf("Total elapsed time: %d ms\n", totalTime);
	
	printf("Stop C++ benchmark\n");
	getch();
    return 0;
}

//////////////
double intArithmetic(int intMax)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	int intResult = 1;
	int i = 1;
	while (i < intMax)
	{
		intResult -= i++;
		intResult += i++;
		intResult *= i++;
		intResult /= i++;
	}

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Int arithmetic elapsed time: %1.0f ms with intMax of %ld\n", elapsedTime, intMax);
	printf(" i: %d\n", i);
	printf(" intResult: %d\n", intResult);
	return elapsedTime;
}


//////////////
double doubleArithmetic(double doubleMin, double doubleMax)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	double doubleResult = doubleMin;
	double i = doubleMin;
	while (i < doubleMax)
	{
		doubleResult -= i++;
		doubleResult += i++;
		doubleResult *= i++;
		doubleResult /= i++;
	}

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Double arithmetic elapsed time: %1.0f ms with doubleMin %.15f, doubleMax %.15f\n", elapsedTime, doubleMin, doubleMax);
	printf(" i: %f\n", i);
	printf(" doubleResult: %.15f\n", doubleResult);
	return elapsedTime;
}



//////////////
double longArithmetic(long long longMin, long long longMax)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	long long longResult = longMin;
	long long i = longMin;
	while (i < longMax)
	{
		longResult -= i++;
		longResult += i++;
		longResult *= i++;
		longResult /= i++;
	}

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Long arithmetic elapsed time: %1.0f ms with longMax %I64d\n", elapsedTime, longMax);
	printf(" i: %I64d\n", i);
	printf(" longResult: %I64d\n", longResult);
	return elapsedTime;
}


//////////////
double trig(double trigMax)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	double sine;
	double cosine;
	double tangent;
	double logarithm;
	double squareRoot;

	double i = 0.0;
	while (i < trigMax)
	{
		sine = sin(i);
		cosine = cos(i);
		tangent = tan(i);
		logarithm = log10(i);
		squareRoot = sqrt(i);
		i++;
	}

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Trig elapsed time: %1.0f ms with max of %1.0f\n", elapsedTime, trigMax);
	printf(" i: %f\n", i);
	printf(" sine: %.15f\n", sine);
	printf(" cosine: %.15f\n", cosine);
	printf(" tangent: %.15f\n", tangent);
	printf(" logarithm: %.15f\n", logarithm);
	printf(" squareRoot: %.15f\n", squareRoot);
	return elapsedTime;
}


//////////////
double io(int ioMax)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	ofstream ostream("C:\\TestC.txt");
	int i = 0;
	while (i++ < ioMax)
	{
		ostream << "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefgh\n";
	}
	ostream.close();

	char readLine[100];
	ifstream istream ("C:\\TestC.txt");
	i = 0;
	istream.width( 100 );
	while (i++ < ioMax)
	{
		istream >> readLine;
	}
	istream.close();

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("I/O elapsed time: %1.0f ms with max of %ld\n", elapsedTime, ioMax);
	printf(" i: %d\n", i);
	printf(" readLine: %s\n", readLine);
	
	return elapsedTime;
}
///// update
//////////////
double array(int n)
{
    int i, k, *x, *y;
    
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();
	x = new int[n];
	y = new int[n];

	for (i = 0; i < n; i++) {
    x[i] = i + 1;
	y[i] = 0;
    }
    for (k=0; k<1000; k++) {
    for (i = n-1; i >= 0; i--) {
        y[i] += x[i];
    }
    }

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("array elapsed time: %1.0f ms - %ld %ld\n", elapsedTime, y[0], y[n-1]);

    delete[] x;
    delete[] y;

    return elapsedTime;
}

static int count = 0;
class HiException
{
};

class LoException
{
};


void BlowUp(int n) 
{
	if ((n & 1) == 0) 
	{
		throw LoException();
	} 
	else 
	{
		throw HiException();
	}
}	

void LoFunction(int n)
{
	try 
	{
		BlowUp(n);
	} 
	catch (LoException) 
	{
		Lo++;
	}
}

void HiFunction(int n) 
{
	try 
	{
		LoFunction(n);
	} 
	catch (HiException) 
	{
		Hi++;
	}
}

void SomeFunction(int n) 
{
	HiFunction(n);
}


double except(int N)
{

	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	while (N) {
    SomeFunction(N);
	N--;
    }
    //printf("Exceptions: HI=%d / LO=%d\n", HI, LO);

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Exceptions elapsed time: %1.0f ms - HI=%d / LO=%d\n", elapsedTime, Hi, Lo);

	return elapsedTime;
}



//////////////
double hashtest(int n)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	map <string, int > X;
	int c = 0;
    
    if(n < 1) n = 1;

	for(int i=1; i<=n; i++) {
		std::ostringstream oss;
		oss << i;
		X.insert( map <string, int> :: value_type ( oss.str() , i ) );
    }
  
    for(int i=n; i>0; i--) {
		std::ostringstream oss;
		oss << i;
		if(X[oss.str()]!=0) c++;
    }
	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("HashMap elapsed time: %1.0f ms - %d\n", elapsedTime, c);

    return elapsedTime;
}

//////////////
double hashes(int n)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	map <string, int > hash1;
	map <string, int > hash2;
	int c = 0;
    
	for(int i=0; i<10000; i++)
	{
		std::ostringstream oss;
		oss << i;
		hash1.insert( map <string, int> :: value_type ( string("foo_")+oss.str() , i ) );
	}

	map <string, int> :: iterator h1_it;
	map <string, int> :: iterator h2_it;
	for(int i=0; i<n; i++)
	{
		for ( h1_it = hash1.begin( ) ; h1_it != hash1.end( ); h1_it++)
		{
			h2_it=hash2.find( h1_it->first );
			if(hash2.end()!=h2_it)
			{
				int v1 = (int) h1_it->second;
				int v2 = (int) h2_it->second;                
				h2_it->second =  v1 + v2;
			}
			else
			{
				hash2.insert( map <string, int> :: value_type ( h1_it->first , h1_it->second ) );
			}
		}
	}
//	printf("%d\" \"%d\" \"%d\" \"%d\n", hash1["foo_1"], hash1["foo_9999"], hash2["foo_1"], hash2["foo_9999"]);
	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("HashMaps elapsed time: %1.0f ms - %d\" \"%d\" \"%d\" \"%d\n", elapsedTime, hash1["foo_1"], hash1["foo_9999"], hash2["foo_1"], hash2["foo_9999"]);
    return elapsedTime;
}

#define IM 139968
#define IA   3877
#define IC  29573

double gen_random(double max) {
    static long last = 42;
    return( max * (last = (last * IA + IC) % IM) / IM );
}

void heapsort(int n, double *ra) {
    int i, j;
    int ir = n;
    int l = (n >> 1) + 1;
    double rra;

    for (;;) {
    if (l > 1) {
        rra = ra[--l];
    } else {
        rra = ra[ir];
        ra[ir] = ra[1];
        if (--ir == 1) {
        ra[1] = rra;
        return;
        }
    }
    i = l;
    j = l << 1;
    while (j <= ir) {
        if (j < ir && ra[j] < ra[j+1]) { ++j; }
        if (rra < ra[j]) {
        ra[i] = ra[j];
        j += (i = j);
        } else {
        j = ir + 1;
        }
    }
    ra[i] = rra;
    }
}

//////////////
double hs(int N)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	double *ary;
    int i;
    
    ary = new double[N+1];
    for (i=1; i<=N; i++) {
    ary[i] = gen_random(1);
    }

    heapsort(N, ary);

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Heapsort elapsed time: %1.0f ms - %.10g\n", elapsedTime, ary[N]);

    delete[] ary;
	return elapsedTime;
}


#define lSIZE 10000

int ListTest()
{
	// create a list of integers (Li1) from 1 to SIZE
	list<int> Li1;
	for (int i = 1; i < lSIZE + 1; i++)
	{		
		Li1.push_back((int)i);	//addlast
	}
	// copy the list to Li2 (not by individual items)
	list<int> Li2=Li1;
	list<int> Li3;
	// remove each individual item from left side of Li2 and
	// append to right side of Li3 (preserving order)
	while (!Li2.empty())
	{
		Li3.push_back(Li2.front()); //addlast
		Li2.erase(Li2.begin());
	}
	// Li2 must now be empty
	// remove each individual item from right side of Li3 and
	// append to right side of Li2 (reversing list)
	while (!Li3.empty())
	{
		Li2.push_back( Li3.back() ); //addlast
		//Li3.remove(Li3.back());
		Li3.erase(--Li3.end());
	}
	// Li3 must now be empty
	// reverse Li1
	list<int> tmp;
	while (!Li1.empty())
	{
		tmp.push_front(Li1.front()); //addfirst
		//Li1.remove(Li1.front());
		Li1.erase(Li1.begin());
	}
	Li1 = tmp;
	// check that first item is now lSIZE
	if ( (int)(Li1.front()) != lSIZE )
	{
		printf("first item of Li1 != lSIZE");
		return (0);
	}

	// compare Li1 and Li2 for equality
	if(Li1!=Li2)
	{
		printf("Li1 and Li2 differ");
		return (0);
	}
	// return the length of the list
	return (Li1.size());
}
//////////////
double ls(int n)
{
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	int result = 0;
    while(n--) result = ListTest();
//    printf("%d\n", result);
	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("List elapsed time: %1.0f ms - %d\n", elapsedTime, result);
    return elapsedTime;
}

#define mSIZE 30

int **mkmatrix(int rows, int cols) {
    int i, j, count = 1;
    int **m = (int **) malloc(rows * sizeof(int *));
    for (i=0; i<rows; i++) {
    m[i] = (int *) malloc(cols * sizeof(int));
    for (j=0; j<cols; j++) {
        m[i][j] = count++;
    }
    }
    return(m);
}

void zeromatrix(int rows, int cols, int **m) {
    int i, j;
    for (i=0; i<rows; i++)
    for (j=0; j<cols; j++)
        m[i][j] = 0;
}

void freematrix(int rows, int **m) {
    while (--rows > -1) { free(m[rows]); }
    free(m);
}

int **mmult(int rows, int cols, int **m1, int **m2, int **m3) {
    int i, j, k, val;
    for (i=0; i<rows; i++) {
    for (j=0; j<cols; j++) {
        val = 0;
        for (k=0; k<cols; k++) {
        val += m1[i][k] * m2[k][j];
        }
        m3[i][j] = val;
    }
    }
    return(m3);
}

//////////////
double mm(int n){
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();

	int i;
    
    int **m1 = mkmatrix(mSIZE, mSIZE);
    int **m2 = mkmatrix(mSIZE, mSIZE);
    int **mm = mkmatrix(mSIZE, mSIZE);

    for (i=0; i<n; i++) {
    mm = mmult(mSIZE, mSIZE, m1, m2, mm);
    }
    //printf("%d %d %d %d\n", mm[0][0], mm[2][3], mm[3][2], mm[4][4]);

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Matrix Multiply elapsed time: %1.0f ms - %d %d %d %d\n", elapsedTime, mm[0][0], mm[2][3], mm[3][2], mm[4][4]);

	freematrix(mSIZE, m1);
    freematrix(mSIZE, m2);
    freematrix(mSIZE, mm);
    return elapsedTime;
};

//////////////
double nl(int n) {
	double elapsedTime;
	clock_t stopTime;
	clock_t startTime = clock();
	int a, b, c, d, e, f;
	int x=0;
    
    for (a=0; a<n; a++)
		for (b=0; b<n; b++)
			for (c=0; c<n; c++)
				for (d=0; d<n; d++)
					for (e=0; e<n; e++)
						for (f=0; f<n; f++)
							x+=a+b+c+d+e+f;

	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("Nested Loop elapsed time: %1.0f ms %d\n", elapsedTime, x);
    return elapsedTime;
}


//////////////
double sc(int n) {
	double elapsedTime;
	wchar_t *test=L"hello";
	clock_t stopTime;
	clock_t startTime = clock();

	int i, buflen = 8;
    wchar_t *strbuf = new wchar_t[n*8];
    strbuf[0]='\0';
	
	int i3=0;
	for (i=0; i<n; i++)
	{
		for (int i2=0; i2<wcslen(test); i2++)
		{
			strbuf[i3]=test[i2];
			i3++;
		}
	}
	stopTime = clock();
	elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);
	printf("String Concat. (fixed) elapsed time: %1.0f ms\n", elapsedTime);//, strbuf);
	delete strbuf;

    return elapsedTime;
}
