//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// $Header: $
// $NoKeywords: $
//
// Program calculates binned histogram of data
//=============================================================================



#include <stdio.h>
#include <stdlib.h>

#include "CardStats.h"


//-----------------------------------------------------------------------------
// Main stats loop
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
	if ( argc != 3 ) // a unix style usage string
	{
		printf ( "Usage: <SearchString> <Filename>\n" );
		printf ( "Output file will be <filename>.<searchstring>.txt" );
		return 0;
	}

	char* pszCardtype = argv[1]; // video card type we are looking for
	char* pszFilename = argv[2]; // file containing the data

	CUtlBuffer buf( 0, 0, true );
	int nFileSize = LoadFileIntoBuffer(buf, pszFilename);

	ParseHeader ( buf );

	CUtlBuffer output ( 0, 255, true );

	int binwidth=100; 
	// video card stats over time
	//TimeSeriesVCard ( buf, output, pszCardtype, nFileSize, binwidth );
	// cpu type stats over time
	//TimeSeriesCPU ( buf, output, pszCardtype, nFileSize, binwidth );
	// CPU vs Videocard
	//ParseFile ( buf, output, pszCardtype, nFileSize, binwidth, 0);
	// CPU vs Memory
	//ParseFile2 ( buf, output, pszCardtype, nFileSize, binwidth );
	//CPU vs network speed
	//ParseFile3 ( buf, output, pszCardtype, nFileSize, binwidth );
	// histogram of video card distribution
	HistogramVidCards ( buf, output, pszCardtype, nFileSize, binwidth );
	//HistogramNetSpeed ( buf, output, pszCardtype, nFileSize, binwidth );
	//HistogramRam ( buf, output, pszCardtype, nFileSize, binwidth );
	//HistogramCPU ( buf, output, pszCardtype, nFileSize, binwidth );
	WriteOutputToFile ( output, pszFilename, pszCardtype );
	
	return 0;
}

void HistogramCPU( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
   ParseFile ( inbuf, outbuf, "", size, binwidth, 1 );
}

//-----------------------------------------------------------------------------
// Reads entire file contents in to utlbuffer
// input-buffer to load fileinto, filename
// output-filesize
//-----------------------------------------------------------------------------

int LoadFileIntoBuffer(CUtlBuffer &buf, char *pszFilename)
{
	// Open the file
	FILE *fh = fopen ( pszFilename, "rb" );
	if (fh == NULL)
	{
		printf ("Unable to open datafile. Check path/name.");
		exit( 0 );
	}

	fseek( fh, 0, SEEK_END );
	int nFileSize = ftell( fh );
	fseek ( fh, 0, SEEK_SET );

	// Read the file in one gulp
	buf.EnsureCapacity( nFileSize );
	int result=fread( buf.Base(), sizeof( char ), nFileSize, fh );
	fclose( fh );

	return nFileSize;

}

//-----------------------------------------------------------------------------
// Writes the contents of the utlbuffer to file
// datafile name becomes name.card.txt for results
//-----------------------------------------------------------------------------

void WriteOutputToFile( CUtlBuffer &buf, char *pszFilename, char *pszCardtype )
{
	char pszOutFilename[500]; 
	strncpy( pszOutFilename, pszFilename, strlen( pszFilename )-3 );
	pszOutFilename[strlen(pszFilename)-3]='\0';
	strcat ( pszOutFilename, pszCardtype );
	strcat ( pszOutFilename, ".txt\0" );

	FILE *fh = fopen ( pszOutFilename, "w" );
	if ( fh == NULL )
	{
		printf ( "Unable to open outputfile." );
		exit(0);
	}

	fwrite ( buf.Base(), sizeof( char ), buf.TellPut(), fh ); 
	fclose( fh );
}

//-----------------------------------------------------------------------------
// Strips off header fields from datafile
//-----------------------------------------------------------------------------

void ParseHeader( CUtlBuffer &buf )
{
	// remove first line of data (field labels)
	char pszTrash[100];
	buf.Scanf( "%s ", pszTrash);
	buf.Scanf( "%s ", pszTrash);
	buf.Scanf( "%s ", pszTrash);
	buf.Scanf( "%s ", pszTrash);
	buf.Scanf( "%s ", pszTrash);
	buf.Scanf( "%s \n", pszTrash);	
}

//-----------------------------------------------------------------------------
// Counts number of users that have a given cpu bin speed for videocard string
//
// Parses buffer into fields and puts results into outbuf
// size is size of the file read in
//-----------------------------------------------------------------------------

void ParseFile( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth, int nofilter)
{

	// Each bin is how many computers with the given card had cpus > cpubin 
	// and less than the next bin so cpu 200 covers cpu's from 200 to 299
	CUtlVector<int> nCpuList;
	CUtlVector<int> nQuantity;
	for ( int i=0; i <= 2400 ; i+=binwidth)	 // a reasonable cpu range 
	{
		nCpuList.AddToTail(i);
		nQuantity.AddToTail(0);
	}

	int totalUsers=0;
	while ( inbuf.TellGet() < size )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[300];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d ", pszVersion, &nNetSpeed, &nRam, &nCpu );
		
		char pszCard[300]; 
		char chSentinel = ' ';
		i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
		}
		pszCard[i] = '\0';
		
		if (nRead != 4)
			continue;

		// if card is our type
		if ( nofilter || stristr ( pszCard, pszSearchString ) != NULL )
		{	  
			InsertResult(nCpu, nCpuList, nQuantity);
		}
		totalUsers++;
	}

	if (nCpuList.Size()>65000)
	{
		printf ("Too many points, increase bin width\n");
	}
	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Cpu\tQuantity\n" ); // headers
	for ( i=0; i < nCpuList.Size(); ++i )
	{
		outbuf.Printf ( "%d\t%d\n", nCpuList[i], nQuantity[i] );
		total+=nQuantity[i];
	}

	printf ("Users in this subset %d\n", total);
	printf ("Percent of dataset %.2f", ((float)total/(float)totalUsers)*100);
}


//-----------------------------------------------------------------------------
// given cpu bin, plots number of users in a given memory bin 
//
// Parses buffer into fields and puts results into outbuf
// size is size of the file read in
//-----------------------------------------------------------------------------

#define NMEMBINS 6
//#define NCPUBINS 48	  // binwidth 50
#define NCPUBINS 24 // binwidth 100

void ParseFile2( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
	int membinwidth=500;
	binwidth=100;

	// Each bin is how many computers with the given card had cpus > cpubin 
	// and less than the next bin so cpu 200 covers cpu's from 200 to 299
	CUtlVector<int> nCpuList;
	CUtlVector<int> nMemList;
	int nQuantity[NMEMBINS][NCPUBINS];
	for (int i=0; i < NMEMBINS; ++i)
	{
		for (int j=0; j< NCPUBINS; ++j)
			nQuantity[i][j]=0;
	}

	for ( i=0; i <= 2400 ; i+=binwidth)	 // a reasonable cpu range 
	{
		nCpuList.AddToTail(i);
	}

	nMemList.AddToTail(0);
	int basemem=64;
	while ( basemem < 2050 )
	{
		nMemList.AddToTail(basemem);
		basemem<<=1;
	}

	int totalUsers=0;
	int maxram=0;
	while ( inbuf.TellGet() < size )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		// scan through the rest of the junk
		char chSentinel = ' ';
		i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			++i;
		}
		// determine cpu bin
		if (nCpu < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nCpu > nCpuList[nCpuList.Size()-1] )
			continue;
		i=0;
		while ( nCpu > nCpuList[i] )
		{
			++i;
		}
		int cpuIndex = i-1;
		assert(i<nCpuList.Size());
		// determine memory bin
		if (nRam < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nRam > nMemList[nMemList.Size()-1] )
			continue;
		i=0;
		while ( nRam > nMemList[i] )
		{
			++i;
		}
		int memIndex = i-1;
		assert(i<nMemList.Size());
		// insert data 
		assert(memIndex<NMEMBINS);
		assert(cpuIndex<NCPUBINS);
		(nQuantity[memIndex][cpuIndex])++;

		totalUsers++;
		//printf ("%d\n", totalUsers);
		if (nRam> maxram)
			maxram=nRam;
	}

	if (nCpuList.Size()>65000)
	{
		printf ("Too many points, increase bin width\n");
	}
	printf("TotalUsers %d\n", totalUsers);
	printf("Max ram %d\n", maxram);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Cpu" ); // headers
	for (int j=1; j < nMemList.Size(); ++j)
			outbuf.Printf ("\tMemoryBin%d", nMemList[j]);
	outbuf.Printf ("\n");

	for ( i=0; i < NCPUBINS; ++i )
	{
		outbuf.Printf ( "%d", nCpuList[i]);
		for (j=0; j < NMEMBINS; ++j)
			outbuf.Printf ("\t%d", nQuantity[j][i]);
		outbuf.Printf ("\n");
	}
}



//-----------------------------------------------------------------------------
// given cpu bin, plots number of users in a given network speed bin 
//
// Parses buffer into fields and puts results into outbuf
// size is size of the file read in
//-----------------------------------------------------------------------------

#define NNETBINS 9

void ParseFile3( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
	binwidth=100;

	// Each bin is how many computers with the given card had cpus > cpubin 
	// and less than the next bin so cpu 200 covers cpu's from 200 to 299
	CUtlVector<int> nCpuList;
	CUtlVector<int> nNetList;
	int nQuantity[NNETBINS][NCPUBINS];
	for (int i=0; i < NNETBINS; ++i)
	{
		for (int j=0; j< NCPUBINS; ++j)
			nQuantity[i][j]=0;
	}

	for ( i=0; i <= 2400 ; i+=binwidth)	 // a reasonable cpu range 
	{
		nCpuList.AddToTail(i);
	}

	nNetList.AddToTail(0);
	nNetList.AddToTail(28);
	nNetList.AddToTail(33);
	nNetList.AddToTail(56);
	nNetList.AddToTail(112);
	nNetList.AddToTail(256);
	nNetList.AddToTail(512);
	nNetList.AddToTail(1000);
	nNetList.AddToTail(2000);

	int totalUsers=0;
	int maxspeed=0;
	while ( inbuf.TellGet() < size )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		// scan through the rest of the junk
		char chSentinel = ' ';
		i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			++i;
		}
		// determine cpu bin
		if (nCpu < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nCpu > nCpuList[nCpuList.Size()-1] )
			continue;
		i=0;
		while ( nCpu > nCpuList[i] )
		{
			++i;
		}
		int cpuIndex = i-1;
		assert(i<nCpuList.Size());
		// determine netspeed bin
		if (nNetSpeed < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nNetSpeed > nNetList[nNetList.Size()-1] )
			continue;
		i=0;
		while ( nNetSpeed > nNetList[i] )
		{
			++i;
		}
		int netIndex = i;
		assert(i<nNetList.Size());
		// insert data 
		assert(netIndex<NNETBINS);
		assert(cpuIndex<NCPUBINS);
		(nQuantity[netIndex][cpuIndex])++;

		totalUsers++;
		//printf ("%d\n", totalUsers);
		if (nNetSpeed> maxspeed)
			maxspeed=nNetSpeed;
	}

	if (nCpuList.Size()>65000)
	{
		printf ("Too many points, increase bin width\n");
	}
	printf("TotalUsers %d\n", totalUsers);
	printf("Max ram %d\n", maxspeed);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Cpu" ); // headers
	for (int j=0; j < nNetList.Size()-1; ++j)
			outbuf.Printf ("\t%d-%d", nNetList[j], nNetList[j+1]);
	outbuf.Printf ("\n");

	for ( i=0; i < NCPUBINS; ++i )
	{
		outbuf.Printf ( "%d", nCpuList[i]);
		for (j=0; j < NNETBINS; ++j)
			outbuf.Printf ("\t%d", nQuantity[j][i]);
		outbuf.Printf ("\n");
	}
}



//-----------------------------------------------------------------------------
// given time bin, plots number of users in a given cpu TYPE over time,
// as the numbers came into the server
//
// Parses buffer into fields and puts results into outbuf
// size is size of the file read in
//-----------------------------------------------------------------------------

#define CPUBINS 3	  // 0=AMD, 1=Intel, 2=Unknown
#define MAXUSERS 750000

void TimeSeriesCPU( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
	binwidth=600;

	// unequally sized bins

	// Each bin is how many computers with the given card had cpus > cpubin 
	// and less than the next bin so cpu 200 covers cpu's from 200 to 299
	CUtlVector<int> nTimeList;
	int nQuantity[30][CPUBINS];
	for (int i=0; i < 30; ++i)
	{
		for (int j=0; j< CPUBINS; ++j)
			nQuantity[i][j]=0;
	}
					
	nTimeList.AddToTail(0);
	int bin=10000;
	for (i=bin; i<=MAXUSERS; i=bin)
	{
		nTimeList.AddToTail(i);
		bin<<=1;
	}
	nTimeList.AddToTail(i);

	// for unequal bins
	int currentTimeBin=1;

	int totalUsers=0;
	while ( (inbuf.TellGet() < size) )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		char pszCard[300]; 
		char chSentinel = ' ';
		i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
			if (i >= 300)
				break;
		}
		// check for those blasted hackers
		if (i>= 300)
		{
			// read rest
			while (( chSentinel != '\n' ))
			{
				chSentinel = inbuf.GetChar();
			}
			continue;
		}

		pszCard[i] = '\0';

		int cpuIndex=2; // default is unknown
		if ( stristr ( pszCard, "SSE" ) != NULL )
		{
		   cpuIndex=1; // intel
		}
		else if ( stristr ( pszCard, "KNI" ) != NULL )
		{
			cpuIndex=1; // intel
		}
		else if ( stristr ( pszCard, "3DNOW" ) != NULL )
		{
			cpuIndex=0;
		}

		// update nTimeList bin	if necessary
		if (nTimeList[currentTimeBin] <= totalUsers)
		{
			currentTimeBin++;
		}

		nQuantity[currentTimeBin][cpuIndex]++;
		assert(nQuantity[currentTimeBin][cpuIndex]>0);

		totalUsers++;
	}

	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "CPU" ); // headers
	for (int j=1; j < nTimeList.Size(); ++j)
			outbuf.Printf ("\tUsersSoFar%d", nTimeList[j]);
	outbuf.Printf ("\n");

	for ( i=0; i < CPUBINS; ++i )
	{
		outbuf.Printf ( "-");
		for (j=1; j < nTimeList.Size(); ++j)  // use NTIMEBINS for equal sized bins
			outbuf.Printf ("\t%d", nQuantity[j][i]);
		outbuf.Printf ("\n");
	}
}



//-----------------------------------------------------------------------------
// given time bin, plots number of users in a given video card TYPE over time,
// as the numbers came into the server
//
// Parses buffer into fields and puts results into outbuf
// size is size of the file read in
//-----------------------------------------------------------------------------

// 0=RIVA TNT
// 1=GeForce2 MX
// 2=Microsoft Corporation GDI Generic
// 3=GeForce2 GTS
// 4=Voodoo 3
// 5=Intel 810
// 6=GeForce3

#define CARDBINS 8	  

void TimeSeriesVCard( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
	// unequally sized bins
	CUtlVector<int> nTimeList;
	int nQuantity[30][CARDBINS];
	for (int i=0; i < 30; ++i)
	{
		for (int j=0; j< CARDBINS; ++j)
			nQuantity[i][j]=0;
	}
					
	nTimeList.AddToTail(0);
	int bin=10000;
	for (i=bin; i<=MAXUSERS; i=bin)
	{
		nTimeList.AddToTail(i);
		bin<<=1;
	}
	nTimeList.AddToTail(i);

	//int currentTimeBin=1;

	// for unequal bins
	int currentTimeBin=1;
	int numTimeBins=nTimeList.Size();

	int totalUsers=0;
	while ( (inbuf.TellGet() < size) )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		char pszCard[300]; 
		char chSentinel = ' ';
		i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
			if (i >= 300)
				break;
		}
		// check for those blasted hackers
		if (i>= 300)
		{
			// read rest
			while (( chSentinel != '\n' ))
			{
				chSentinel = inbuf.GetChar();
			}
			continue;
		}

		pszCard[i] = '\0';
		
		// 0=RIVA TNT
		// 1=GeForce2 MX
		// 2=Microsoft Corporation GDI Generic
		// 3=GeForce2 GTS
		// 4=Voodoo 3
		// 5=Intel 810
		// 6=GeForce3
		// 7=All others
		int cardIndex=7; // default is other
		if ( stristr ( pszCard, "RIVA TNT2" ) != NULL )
		{
			//printf ("%s", pszCard);
			cardIndex=0; 
		}
		else if ( stristr ( pszCard, "GeForce2 MX" ) != NULL )
		{
			cardIndex=1; 
		}
		else if ( stristr ( pszCard, "Microsoft Corporation GDI Generic" ) != NULL )
		{
			cardIndex=2;
		}
		else if ( stristr ( pszCard, "GeForce2 GTS" ) != NULL )
		{
			cardIndex=3;
		}

		else if ( stristr ( pszCard, "Voodoo3" ) != NULL )
		{
			cardIndex=4;
		}

		else if ( stristr ( pszCard, "Intel 810" ) != NULL )
		{
			cardIndex=5;
		}

		else if ( stristr ( pszCard, "GeForce3" ) != NULL )
		{
			cardIndex=6;
		}

		// update nTimeList bin	if necessary
		if (nTimeList[currentTimeBin] <= totalUsers)
		{
			currentTimeBin++;
		}

		nQuantity[currentTimeBin][cardIndex]++;

		totalUsers++;
	}

	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Video Card" ); // headers
	for (int j=1; j < nTimeList.Size(); ++j)
			outbuf.Printf ("\tUsersSoFar%d", nTimeList[j]);
	outbuf.Printf ("\n");

	for ( i=0; i < CARDBINS; ++i )
	{
		outbuf.Printf ( "-");
		for (j=1; j < numTimeBins; ++j)  // use NTIMEBINS for equal sized bins
			outbuf.Printf ("\t%d", nQuantity[j][i]);
		outbuf.Printf ("\n");
	}
}


//-----------------------------------------------------------------------------
// Increment the proper bin's counter
//-----------------------------------------------------------------------------

void InsertResult( int nCpu, CUtlVector<int> &nCpuList, CUtlVector<int> &nQuantity )
{
	if (nCpu < 0) // handle corrupt data
		return;

	// handle outliers--bin is off the scale
	if ( nCpu > nCpuList[nCpuList.Size()-1] )
	{
		//nCpuList.AddToTail( nCpu );
		//nQuantity.AddToTail( 1 );
		return;
	}
	
	int i=0;
	while ( nCpu > nCpuList[i] )
	{
		++i;
	}
	nQuantity[i-1]++;		
}



void HistogramVidCards( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{

#define CARDHISTBINS 15
	// unequally sized bins
	int nQuantity[CARDHISTBINS];
	for (int j=0; j< CARDHISTBINS; ++j)
			nQuantity[j]=0;

	int totalUsers=0;
	while ( (inbuf.TellGet() < size) )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		char pszCard[300]; 
		char chSentinel = ' ';
		int i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
			if (i >= 300)
				break;
		}
		// check for those blasted hackers
		if (i>= 300)
		{
			// read rest
			while (( chSentinel != '\n' ))
			{
				chSentinel = inbuf.GetChar();
			}
			continue;
		}

		pszCard[i] = '\0';
		
		// 0=RIVA TNT2
		// 1=GeForce2 MX
		// 2=Microsoft Corporation GDI Generic
		// 3=GeForce2 GTS
		// 4=Voodoo 3
		// 5=Intel 810
		// 6=GeForce3
		// 7=Riva TNT
		// 8=GeForce 256
		// 9=Rage 128
		// 10=S3 Savage4
		// 11=SiS 630
		// 12=Radeon DDR
		// 13=Rage 128 Pro
		// 14=All others
		
		int cardIndex=14; // default is other
		if ( stristr ( pszCard, "RIVA TNT2" ) != NULL )
		{
			//printf ("%s", pszCard);
			cardIndex=0; 
		}
		else if ( stristr ( pszCard, "GeForce2 MX" ) != NULL )
		{
			cardIndex=1; 
		}
		else if ( stristr ( pszCard, "Microsoft Corporation GDI Generic" ) != NULL )
		{
			cardIndex=2;
		}
		else if ( stristr ( pszCard, "GeForce2 GTS" ) != NULL )
		{
			cardIndex=3;
		}

		else if ( stristr ( pszCard, "Voodoo3" ) != NULL )
		{
			cardIndex=4;
		}

		else if ( stristr ( pszCard, "Intel 810" ) != NULL )
		{
			cardIndex=5;
		}
		else if ( stristr ( pszCard, "GeForce3" ) != NULL )
		{
			cardIndex=6;
		}
		else if ( stristr ( pszCard, "Riva TNT" ) != NULL )
		{
				cardIndex=7;
		}
		else if ( stristr ( pszCard, "GeForce 256" ) != NULL )
		{
			cardIndex=8;
		}
		else if ( stristr ( pszCard, "Rage 128 Pro" ) != NULL )
		{
			cardIndex=13;
		}
		else if ( stristr ( pszCard, "Rage 128" ) != NULL )
		{
			cardIndex=9;
		}
		else if ( stristr ( pszCard, "S3 Savage4" ) != NULL )
		{
			cardIndex=10;
		}
		else if ( stristr ( pszCard, "SiS 630" ) != NULL )
		{
			cardIndex=11;
		}
		else if ( stristr ( pszCard, "Radeon DDR" ) != NULL )
		{
			cardIndex=12;
		}

		nQuantity[cardIndex]++;

		totalUsers++;
	}

	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Video Card" ); // headers
	outbuf.Printf ("\tNumber of Users\n");
	outbuf.Printf ("\n");

	for ( int i=0; i < CARDHISTBINS; ++i )
	{
		outbuf.Printf ( "-");
		outbuf.Printf ("\t%d", nQuantity[i]);
		outbuf.Printf ("\n");
	}
}


void HistogramNetSpeed( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
#define NETHISTBINS 9
	// unequally sized bins
	int nQuantity[NETHISTBINS];
	for (int j=0; j< NETHISTBINS; ++j)
			nQuantity[j]=0;

	CUtlVector<int> nNetList;
	nNetList.AddToTail(0);
	nNetList.AddToTail(28);
	nNetList.AddToTail(33);
	nNetList.AddToTail(56);
	nNetList.AddToTail(112);
	nNetList.AddToTail(256);
	nNetList.AddToTail(512);
	nNetList.AddToTail(1100);
	nNetList.AddToTail(2000);

	int totalUsers=0;
	while ( (inbuf.TellGet() < size) )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		char pszCard[300]; 
		char chSentinel = ' ';
		int i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
			if (i >= 300)
				break;
		}
		// check for those blasted hackers
		if (i>= 300)
		{
			// read rest
			while (( chSentinel != '\n' ))
			{
				chSentinel = inbuf.GetChar();
			}
			continue;
		}

		pszCard[i] = '\0';
		
		if (nNetSpeed < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nNetSpeed > nNetList[nNetList.Size()-1] )
			continue;
		i=0;
		while ( nNetSpeed > nNetList[i] )
		{
			++i;
		}
		int netIndex = i;
		nQuantity[netIndex]++;

		totalUsers++;
	}

	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "Network Speed" ); // headers
	for (j=0; j < nNetList.Size()-1; ++j)
			outbuf.Printf ("\t%d-%d", nNetList[j], nNetList[j+1]);
	outbuf.Printf ("\n");

	for ( int i=0; i < NETHISTBINS; ++i )
	{
		outbuf.Printf ( "-");
		outbuf.Printf ("\t%d", nQuantity[i]);
		outbuf.Printf ("\n");
	}
}




void HistogramRam( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth)
{
#define RAMHISTBINS 20
	// unequally sized bins
	int nQuantity[RAMHISTBINS];
	for (int j=0; j< RAMHISTBINS; ++j)
			nQuantity[j]=0;

	CUtlVector<int> nRamList;
	nRamList.AddToTail(0);
	int basemem=16;
	while ( basemem < 2050 )
	{
		nRamList.AddToTail(basemem);
		basemem<<=1;
	}

	int totalUsers=0;
	while ( (inbuf.TellGet() < size) )
	{	
		// Now parse the utlbuffer
		// file has fields of version, netspeed, ram, cpu and card type
		char pszVersion[30];
		int nNetSpeed, nRam, nCpu;
		int nRead=inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu );

		char pszCard[300]; 
		char chSentinel = ' ';
		int i=0;
		while (( chSentinel != '\n' ))
		{
			chSentinel = inbuf.GetChar();
			pszCard[i] = chSentinel;
			++i;
			if (i >= 300)
				break;
		}
		// check for those blasted hackers
		if (i>= 300)
		{
			// read rest
			while (( chSentinel != '\n' ))
			{
				chSentinel = inbuf.GetChar();
			}
			continue;
		}

		pszCard[i] = '\0';
		
		if (nRam < 0) // handle corrupt data
			continue;
		// handle outliers--bin is off the scale
		if ( nRam > nRamList[nRamList.Size()-1] )
			continue;
		i=0;
		while ( nRam > nRamList[i] )
		{
			++i;
		}
		int ramIndex = i;
		nQuantity[ramIndex]++;

		totalUsers++;
	}

	printf("TotalUsers %d\n", totalUsers);
	
	// write results to an output buffer
	int total=0;
	outbuf.Printf ( "RAM" ); // headers
	for (j=0; j < nRamList.Size()-1; ++j)
			outbuf.Printf ("\t%d-%d", nRamList[j], nRamList[j+1]);
	outbuf.Printf ("\n");

	for ( int i=0; i < nRamList.Size(); ++i )
	{
		outbuf.Printf ( "-");
		outbuf.Printf ("\t%d", nQuantity[i]);
		outbuf.Printf ("\n");
	}
}



