/**
 * Ben Sowell
 * 6-23-06
 */

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

#define NUM_HEURISTICS 4
#define NUM_REPETITIONS 1

struct Bidder
{
	double budget;
	
	/* T is the percentage of the budget spent so far. 
	 Since this depends on the algorithm, we store an a 
	 array with a separate entry for each algorithm. */
	double T[NUM_HEURISTICS];
};

struct Bidder *bidders;
int *subsetArray;
int numBidders;

/* File to write data to if specified
  on the command line */
FILE *out;
int output;

enum MODE_TYPE {MEHTA, MAX_BUDGET_LEFT, MIN_BUDGET_LEFT, MAX_BID};


/**
 * Generates the specified number of bidders with budgets
 * chosen from a uniform random distribution between 
 * minBudget and maxBudget.  Also initializes the subsetArray, 
 * which  will be used to generate a random subset of bidders. 
 * if output is 1, then each budget will be written to the out 
 * file. 
 *
 * @param  num  The number of bidders to generate.
 * @param  minBudget  The smallest possible budget.
 * @param  maxBudget  The largest possible budget.
 */
void generateBidders(int num, double minBudget, double maxBudget)
{
	numBidders = num;

	if(output)
		fprintf(out, "%d\n", numBidders);

	int i, j;
	for(i = 0; i < num; i++)
	{
		bidders[i].budget = minBudget +  (maxBudget - minBudget) * 
				(double) random() / (double) (RAND_MAX + 1.0);
			
		for(j = 0; j < NUM_HEURISTICS; j++)
			bidders[i].T[j] = 0;
		
		subsetArray[i] = i;

		if(output)
			fprintf(out, "%g\n", bidders[i].budget);
	}
}


/**
 * Generates a random bidder id that is distinct from
 * all previously generated ids. This is done by generating
 * a random subset of integers between 0 and numBidders - 1. 
 *
 * @param  index  The index of the subset element we wish to access.  
 *				   (which is generated on the fly).
 * @return  A random bidder ID. 
 */
int getRandomBidderID(int index)
{	
	int swapNum = (int) (numBidders - index) *
		(double) random() / (double) (RAND_MAX + 1.0) + index;
	
	int temp = subsetArray[index];
	subsetArray[index] = subsetArray[swapNum];
	subsetArray[swapNum] = temp;
	
	return subsetArray[index];
}

/**
 * Computes the revenue generated by a single query using NUM_HEURISTICS
 * different algorithms, and updates the appropriate bidders. If in is 
 * NULL, then numBids bidders are randomly selected each bidding random
 * amount between minBidSize and maxBidSize. Otherwise, the individual
 * bids are read from in. 
 *
 * @param  in  The file to read from if applicable. 
 * @param  numBids  The number of bidders bidding on this query. 
 * @param  minBidSize  The minimum possible bid size. 
 *								(Not used if in is not NULL)
 * @param  maxBidSize  The maximum possible bid size. 
 *								(Not used if in is not NULL)
 * @param  revenue  An array containing the revenue so far for each algorithm. 
 */
void getQueryRevenue(FILE *in, int numBids, double minBidSize, 
					   double maxBidSize, double revenue[NUM_HEURISTICS])
{	
	int winningBidderID[NUM_HEURISTICS];
	double winningBid[NUM_HEURISTICS];
	
	/* Keeps track of whatever metric uses
	   to compare bidders */
	double winningTest[NUM_HEURISTICS];

	int i;
	for(i = 0; i < NUM_HEURISTICS; i++)
	{
		winningBidderID[i] = 0;
		winningBid[i] = 0;
		winningTest[i] = 0;
	}
	
	/* Since the min_budget_left algorithm
	   looks for a minimum, we must initialize the
	   min value to something large. */
	winningTest[MIN_BUDGET_LEFT] = 100000;

	int bidderID;
	double bid = 0.0;
	double bidTest = 0.0;
	struct Bidder currentBidder;

	if(output) 
		fprintf(out, "%d ", numBids);

	for(i = 0; i < numBids; i++)
	{
		/* If in is null, generate the bids at random, otherwise
		   read them from a file */
		if(in == NULL)
		{
			bidderID = getRandomBidderID(i);
			bid = (maxBidSize - minBidSize) * 
				(double) random() / (double) (RAND_MAX + 1.0) + minBidSize;
		}	
		else 
		{
			fscanf(in, "%d %lg", &bidderID, &bid);
		}
	
		currentBidder = bidders[bidderID];

		if(output)
			fprintf(out, "%d %g ", bidderID, bid);
		
		/* MEHTA algorithm */
		bidTest = bid * (1 - exp(currentBidder.T[MEHTA] - 1));
		if(bidTest > winningTest[MEHTA])
		{
			winningTest[MEHTA] = bidTest;
			winningBidderID[MEHTA] = bidderID;
			winningBid[MEHTA] = bid;
		}
		
		/* Max Bid algorithm */
		bidTest = currentBidder.budget - currentBidder.budget * 
										currentBidder.T[MAX_BID];
		if(bid > winningBid[MAX_BID] && bidTest >= bid)
		{
			winningTest[MAX_BID] = bidTest;
			winningBidderID[MAX_BID] = bidderID;
			winningBid[MAX_BID] = bid;
		}
		/* Break ties according to the max remaining budget */
		else if(bid == winningBid[MAX_BID] && bidTest > winningTest[MAX_BID] 
										   && bidTest >= bid)
		{
			winningBidderID[MAX_BID] = bidderID;
			winningTest[MAX_BID] = bidTest;
		}
		

		/* Max Budget left algorithm */
		bidTest = currentBidder.budget - currentBidder.budget * 
									currentBidder.T[MAX_BUDGET_LEFT];
		if(bidTest > winningTest[MAX_BUDGET_LEFT] && bidTest >= bid)
		{
			winningTest[MAX_BUDGET_LEFT] = bidTest;
			winningBidderID[MAX_BUDGET_LEFT] = bidderID;
			winningBid[MAX_BUDGET_LEFT] = bid;
		}
		
		/* Min Budget left algorithm */
		bidTest = currentBidder.budget - currentBidder.budget * 
									currentBidder.T[MIN_BUDGET_LEFT];
		
		if(bidTest < winningTest[MIN_BUDGET_LEFT] && bidTest >= bid)
		{
			winningTest[MIN_BUDGET_LEFT] = bidTest;
			winningBidderID[MIN_BUDGET_LEFT] = bidderID;
			winningBid[MIN_BUDGET_LEFT] = bid;
		}
			
	}
	
	if(output)
		fprintf(out, "\n");
	
	/* Update the T values for each winning bidder, and compute the revenues. */
	for(i = 0; i < NUM_HEURISTICS; i++)
	{
		bidders[winningBidderID[i]].T[i] += winningBid[i]/bidders[winningBidderID[i]].budget;
		revenue[i] += winningBid[i];
	}	
}


/**
 * Runs the AdWords algorithm for numQueries where
 * each query has a number of bids randomly chosen from
 * a uniform distribution between 0 and numBidders. Each 
 * bid is between min and max. 
 *
 * In order for this method to work properly, generateBidders
 * must have been called so that the bidders array is properly 
 * instantiated. 
 *
 * @param  numQueries The number of queries. 
 * @param  min  The minimum bid size. 
 * @param  max  The maximum bid size. 
 */
void runFromRandomQueries(int numQueries, double min, double max)
{
	int numBids, i;
	double revenue[NUM_HEURISTICS];
	
	for(i = 0; i < NUM_HEURISTICS; i++)
		revenue[i] = 0;
	
	if(output)
		fprintf(out, "%d\n", numQueries);

	for(i = 0; i < numQueries; i++)
	{
		numBids = (int) numBidders * (double) random() / (double) (RAND_MAX + 1.0);
		getQueryRevenue(NULL, numBids, min, max, revenue);
	}

 	for(i = 0; i < NUM_HEURISTICS; i++)
		printf("%g ", revenue[i]);
	printf("\n");
}

/**
 * Runs AdWords algorithm by using query data from the 
 * specifed file. This file should be in the form:
 *
 * n
 * b1 min1 max1
 * b2 min2 max2
 * b3 min3 max3
 * ...
 * where n is the total number of queries, bi is the number of 
 * bids for the ith query, and mini and maxi are the are the minimum
 * and maximum values for each bid. Each bid is randomly chosen
 * from a uniform random distribution between min and max. 
 *
 * In order for this method to work properly, generateBidders
 * must have been called so that the bidders array is properly 
 * instantiated. 
 *
 * @param  fileName  The file containing the query data. 
 */
void runFromQueryFile(char* fileName)
{
	FILE *f = fopen(fileName, "r");
	int numQueries, numBids, i;
	double min, max;

	double revenue[NUM_HEURISTICS];
	for(i = 0; i < NUM_HEURISTICS; i++)
		revenue[i] = 0;

	fscanf(f, "%d\n", &numQueries);

	if(output)
		fprintf(out, "%d\n", numQueries);

	for(i = 0; i < numQueries; i++)
	{
		fscanf(f, "%d %lg %lg", &numBids, &min, &max);
		getQueryRevenue(NULL, numBids, min, max, revenue);
	}

	fclose(f);
	
 	for(i = 0; i < NUM_HEURISTICS; i++)
		printf("%g ", revenue[i]);
	printf("\n");
}

/**
 * Runs the AdWords algorithm where all data is specified in a file.
 * The file should be of the following form:
 * 
 * numBidders
 * budget1
 * budget2
 * ...
 * numQueries 
 * numBids bidder1 bid1 bidder2 bid2 ...
 * numBids bidder1 bid1 bidder2 bid2 ...
 * ...
 *
 * @param  fileName  The file containing the bidder and query data. 
 */
void runFromFile(char* fileName)
{
	FILE *f = fopen(fileName, "r");
	int numQueries, numBids, i, j;
	double budget;
	
	double revenue[NUM_HEURISTICS];
	for(i = 0; i < NUM_HEURISTICS; i++)
		revenue[i] = 0;

	/*Read the number of bidders and allocate
	  the appropriate amount of memory*/
	fscanf(f, "%d\n", &numBidders);
	bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders);


	for(i = 0; i < numBidders; i++)
	{
		fscanf(f, "%lg\n", &budget);
		bidders[i].budget = budget;
		
		for(j = 0; j < NUM_HEURISTICS; j++)
			bidders[i].T[j] = 0;
	}

 	fscanf(f, "%d\n", &numQueries);
	
	for(i = 0; i < numQueries; i++)
	{
		fscanf(f, "%d ", &numBids);
		getQueryRevenue(f, numBids, 0, 0, revenue);
	}
	
	fclose(f);
 	
	for(i = 0; i < NUM_HEURISTICS; i++)
		printf("%g ", revenue[i]);
	printf("\n");
}



int main(int argc, char *argv[])
{
	srandom(time(0));
	int startIndex = 0;
	
	/*
	 * If there is an additional parameter, then we 
	 * output the randomly generated data to a file.
	 */
	if(argc == 8 || argc == 6)
	{
		output = 1;
		out = fopen(argv[++startIndex], "w");
		argc--;
	}
	else
		output = 0;

	/*
	 * The only parameter is a fileName, so we call
	 * runFromFile. 
	 */
	if(argc == 2)
	{
		runFromFile(argv[++startIndex]);
	}
	/*
	 * The user specified, the number of bidders, min and max
	 * budget size, the number of queries, and min and max query
	 * size. We call runFromRandomQueries. 
	 */
	else if(argc == 7)
	{
		int numQueries;
		double minBudget, maxBudget, minBid, maxBid;

		sscanf(argv[++startIndex], "%d", &numBidders);
		sscanf(argv[++startIndex], "%lg", &minBudget);
		sscanf(argv[++startIndex], "%lg", &maxBudget);
		sscanf(argv[++startIndex], "%d", &numQueries);
		sscanf(argv[++startIndex], "%lg", &minBid);
		sscanf(argv[++startIndex], "%lg", &maxBid);

		bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders);
		subsetArray = (int*) malloc(sizeof(int) * numBidders);

		int i;
		for(i = 0; i < NUM_REPETITIONS; i++)
		{
			generateBidders(numBidders, minBudget, maxBudget);
			runFromRandomQueries(numQueries, minBid, maxBid);	
		}
			
	
	}

	/*
	 * The user specifed the number of bids, the min
	 * and max budgets, and a file name, so we call 
	 * runFromQueryFile. 
	 */
	else if(argc == 5)
	{
		double minBudget, maxBudget;

		char* fileName = argv[++startIndex];

		sscanf(argv[++startIndex], "%d", &numBidders);
		sscanf(argv[++startIndex], "%lg", &minBudget);
		sscanf(argv[++startIndex], "%lg", &maxBudget);
			 
		bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders);
		subsetArray = (int*) malloc(sizeof(int) * numBidders);

		int i;
		for(i = 0; i < NUM_REPETITIONS; i++)
		{

			generateBidders(numBidders, minBudget, maxBudget);
			runFromQueryFile(fileName);
		}
	
	
	}
	else
	{
		printf("Invalid arguments\n");
	}

	free(bidders);
	free(subsetArray);


	if(output)
			fclose(out);

	return 0;
}



