/*
 * Ben Sowell
 * 6-20-06
 */


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <unistd.h> //for getopt

struct Bidder
{
		double budget;
		double T;
};

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

/**
 * Generates the specified number of bidders with budgets
 * chosen from a uniform random distribution between 
 * minBudget and maxBudget.  Also initializes the subestArray, 
 *which  will be used to generate a random subset of bidders. 
 *
 * @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;

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


/**
 * Randomly generates num bids between min and max and
 * finds which one maximizes the tradeoff function as specifed
 * in "AdWords and Generalized On-line Matching" by Mehta et. al. 
 *
 * @param  num  The number of bids to generate.
 * @param  min  The minimum bid size. 
 * @param  max  The maximum bid size. 
 * @return  The revenue made from this query. 
 */
double getQueryRevenue(int num, double min, double max)
{
		int maxBidderID = 0, swapNum, temp;
		double bid = 0.0, maxBid = 0.0, bidTest = 0.0, maxBidTest = 0.0;

		int i;
		for(i = 0; i < num; i++)
		{
				swapNum = (int) (numBidders - i) *
						(double) rand() / (double) (RAND_MAX + 1.0) + i;

				temp = subsetArray[i];
				subsetArray[i] = subsetArray[swapNum];
				subsetArray[swapNum] = temp;
				
				bid = (max - min) *	(double) rand() / (double) (RAND_MAX + 1.0) + min;
				bidTest = bid * (1 - exp(bidders[subsetArray[i]].T - 1));

				if(bidTest > maxBidTest)
				{
						maxBidTest = bidTest;
						maxBidderID = subsetArray[i];
						maxBid = bid;
				}
		}
		
		bidders[maxBidderID].T +=  maxBid/bidders[maxBidderID].budget;
		return maxBid;
}

/**
 * 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. 
 * @return  The revenue from this run. 
 */
double runFromQueryFile(char* fileName)
{
   		FILE *f = fopen(fileName, "r");
		int numQueries, num;
		double min, max, revenue = 0.0;

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


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

/**
 * 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. 
 * @return  The revenue generated by this run. 
 */
double runFromFile(char* fileName)
{
		int bidderID, maxBidderID;
		double maxBid, bid, bidTest, maxBidTest;
		double revenue = 0.0;
		FILE *f = fopen(fileName, "r");
		int num, numBids;
		double budget;

		/*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);

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

  		fscanf(f, "%d\n", &num);
		
		for(i = 0; i < num; i++)
		{
				maxBidTest = 0.0;
				maxBidderID = 0;
				maxBid = 0.0;

				fscanf(f, "%d", &numBids);

				for(j = 0; j < numBids; j++)
				{
						fscanf(f, "%d %lg", &bidderID, &bid);
						
						bidTest = bid * (1 - exp(bidders[bidderID].T - 1));

						if(bidTest > maxBidTest)
						{
								maxBidTest = bidTest;
								maxBidderID = bidderID;
								maxBid = bid;
						}
  				}
				
				bidders[maxBidderID].T +=  maxBid/bidders[maxBidderID].budget;
				revenue += maxBid;
		}
		fclose(f);
		
		return revenue;
}


/**
 * 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. 
 */
double runFromRandomQueries(int numQueries, double min, double max)
{
		int num;
		double revenue = 0.0;

		int j;
		for(j = 0; j < numQueries; j++)
		{
				
				//Check that this works
				num = (int) numBidders * (double) rand() / (double) (RAND_MAX + 1.0);
				revenue += getQueryRevenue(num, min, max);
		}
	 	return revenue;
}


int main(int argc, char *argv[])
{
		srand(time(0));
		double revenue;

		/*
		 * The only parameter is a fileName, so we call
		 * runFromFile. 
		 */
		if(argc == 2)
		{
				fflush(stdout);
				revenue = runFromFile(argv[1]);
				free(bidders);
				printf("revenue: %g\n", revenue);
		}
		/*
		 * 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[1], "%d", &numBidders);
				sscanf(argv[2], "%lg", &minBudget);
				sscanf(argv[3], "%lg", &maxBudget);
				sscanf(argv[4], "%d", &numQueries);
				sscanf(argv[5], "%lg", &minBid);
				sscanf(argv[6], "%lg", &maxBid);

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


				generateBidders(numBidders, minBudget, maxBudget);
				revenue = runFromRandomQueries(numQueries, minBid, maxBid);
				
				printf("revenue: %g\n", revenue);

				free(bidders);
				free(subsetArray);
					
		}
		/*
		 * 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;

				sscanf(argv[2], "%d", &numBidders);
				sscanf(argv[3], "%lg", &minBudget);
				sscanf(argv[4], "%lg", &maxBudget);
					 
				bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders);
				subsetArray = (int*) malloc(sizeof(int) * numBidders);
 
				generateBidders(numBidders, minBudget, maxBudget);
				revenue = runFromQueryFile(argv[1]);

				printf("revenue: %g\n", revenue);
				
				free(bidders);
				free(subsetArray);
		}
		else
		{
				printf("Invalid arguments\n");
		}

		return 0;
}
