#include <iostream>
#include <fstream>

using namespace std;

struct Node
{
	int    same_author;
	double dp;
	double euclid;
	Node*  next;
};

class DLL
{
	public:
	DLL();
	Node* head;
	void insert (Node* newbie);
};

DLL::DLL()
{
	head = NULL;
}

void DLL::insert(Node* newbie)
{
	newbie -> next = head;
	head = newbie;
	return;
}

double cmp_euclid(Node *a, Node *b) {
    return a->euclid - b->euclid;
}

Node *listsort_euclid(Node *list) {
    Node *p, *q, *e, *tail;
    int insize, nmerges, psize, qsize, i;

    /*
     * Silly special case: if `list' was passed in as NULL, return
     * NULL immediately.
     */
    if (!list)
	return NULL;

    insize = 1;

    while (1) {
        p = list;

        list = NULL;
        tail = NULL;

        nmerges = 0;  /* count number of merges we do in this pass */

        while (p) {
            nmerges++;  /* there exists a merge to be done */
            /* step `insize' places along from p */
            q = p;
            psize = 0;
            for (i = 0; i < insize; i++) {
                psize++;

		    q = q->next;
                if (!q) break;
            }

            /* if q hasn't fallen off end, we have two lists to merge */
            qsize = insize;

            /* now we have two lists; merge them */
            while (psize > 0 || (qsize > 0 && q)) {

                /* decide whether next element of merge comes from p or q */
                if (psize == 0) {
		    /* p is empty; e must come from q. */
		    e = q; q = q->next; qsize--;
		} else if (qsize == 0 || !q) {
		    /* q is empty; e must come from p. */
		    e = p; p = p->next; psize--;
		} else if (cmp_euclid(p,q) <= 0) {
		    /* First element of p is lower (or same);
		     * e must come from p. */
		    e = p; p = p->next; psize--;
		} else {
		    /* First element of q is lower; e must come from q. */
		    e = q; q = q->next; qsize--;
		}

                /* add the next element to the merged list */
		if (tail) {
		    tail->next = e;
		} else {
		    list = e;
		}
		tail = e;
            }

            /* now p has stepped `insize' places along, and q has too */
            p = q;
        }
	    tail->next = NULL;

        /* If we have done only one merge, we're finished. */
        if (nmerges <= 1)   /* allow for nmerges==0, the empty list case */
            return list;

        /* Otherwise repeat, merging lists twice the size */
        insize *= 2;
    }
}

double cmp_dp(Node *a, Node *b) {
    return a->dp - b->dp;
}

Node *listsort_dp(Node *list) {
    Node *p, *q, *e, *tail;
    int insize, nmerges, psize, qsize, i;

    /*
     * Silly special case: if `list' was passed in as NULL, return
     * NULL immediately.
     */
    if (!list)
	return NULL;

    insize = 1;

    while (1) {
        p = list;

        list = NULL;
        tail = NULL;

        nmerges = 0;  /* count number of merges we do in this pass */

        while (p) {
            nmerges++;  /* there exists a merge to be done */
            /* step `insize' places along from p */
            q = p;
            psize = 0;
            for (i = 0; i < insize; i++) {
                psize++;

		    q = q->next;
                if (!q) break;
            }

            /* if q hasn't fallen off end, we have two lists to merge */
            qsize = insize;

            /* now we have two lists; merge them */
            while (psize > 0 || (qsize > 0 && q)) {

                /* decide whether next element of merge comes from p or q */
                if (psize == 0) {
		    /* p is empty; e must come from q. */
		    e = q; q = q->next; qsize--;
		} else if (qsize == 0 || !q) {
		    /* q is empty; e must come from p. */
		    e = p; p = p->next; psize--;
		} else if (cmp_dp(p,q) >= 0) {
		    /* First element of p is lower (or same);
		     * e must come from p. */
		    e = p; p = p->next; psize--;
		} else {
		    /* First element of q is lower; e must come from q. */
		    e = q; q = q->next; qsize--;
		}

                /* add the next element to the merged list */
		if (tail) {
		    tail->next = e;
		} else {
		    list = e;
		}
		tail = e;
            }

            /* now p has stepped `insize' places along, and q has too */
            p = q;
        }
	    tail->next = NULL;

        /* If we have done only one merge, we're finished. */
        if (nmerges <= 1)   /* allow for nmerges==0, the empty list case */
            return list;

        /* Otherwise repeat, merging lists twice the size */
        insize *= 2;
    }
}

int main(int argc, char *argv[])
{
	Node* insert_me;
	Node* curr;
	Node* next;
	ifstream in;
	char foo[121];
	int bar, same_auth;
	unsigned long pair_count;
	unsigned long total_same_auth;
	unsigned long running_sum_auth;
	double running_sum_fraction, auc;
	double dot, euc;
	DLL pairs;
	
	if (argc < 2)
	{
		cout << "Arguments missing. Aborting.\n";
		return 1;
	}
	
	for (int file_count = 1; file_count < argc; file_count++)
	{
		cout << "\nWorking on: " << argv[file_count] << endl;
		
		in.open(argv[file_count]);
		in.getline(foo, 120);
		
		pair_count = total_same_auth = 0;

		in >> same_auth >> bar >> bar >> bar >> bar >> dot >> euc;	
		
		while (!in.eof())
		{
			pair_count++;
			total_same_auth += same_auth;
			insert_me = new Node;
			insert_me -> same_author = same_auth;
			insert_me -> dp = dot;
			insert_me -> euclid = euc;
			pairs.insert(insert_me);
			
			in >> same_auth >> bar >> bar >> bar >> bar >> dot >> euc;
		}

		in.close();
		in.clear();

		// do euclidean distance AUC math
		cout << "File loaded. Starting first sort..." << endl;
		pairs.head = listsort_euclid(pairs.head);

		running_sum_auth = 0;
		running_sum_fraction = 0;
		curr = pairs.head;

		while(curr != NULL)
		{
			running_sum_auth += curr -> same_author;
			running_sum_fraction += (double)running_sum_auth / (double)total_same_auth;
			curr = curr -> next;
		}
		auc = running_sum_fraction / pair_count;
		cout << "EUCLIDEAN DISTANCE AUC:\t" << auc << endl;
		
		// do dot product AUC math
		pairs.head = listsort_dp(pairs.head);

		running_sum_auth = 0;
		running_sum_fraction = 0;
		curr = pairs.head;
		while(curr != NULL)
		{
			running_sum_auth += curr -> same_author;
			running_sum_fraction += (double)running_sum_auth / (double)total_same_auth;
			curr = curr -> next;
		}
		auc = running_sum_fraction / pair_count;
		cout << "DOT PRODUCT AUC:\t" << auc << endl;

		// clean out the linked list
		curr = pairs.head;
		while (curr != NULL)
		{
			next = curr -> next;
			delete curr;
			curr = next;
		}
		pairs.head = NULL;
	}
}

