/* ========================================================================
   MULTICOMMODITY (CONCURRENT) FLOW PROGRAM

   Uses the following files:
       MULTICOM3.C	main program
       MULTICOMIO.C	input / output routines
       ROUTE.C		routine to find initial routes
       DECONGEST3.C	iterated procedure
       MINCOST.C	minimum-cost flow routine (Bertsekas & Tseng)
       COMMON.H		include files; macro, type, struct definitions
       MCF.H		global variable and array declarations
       EXPINTEGERS	integral powers of e
       EXPHUNDRETHS	decimal powers of e (to the 2nd decimal place)
       LIBF2C.A		library functions from Fortran to C conversion

   Written:
       August 8, 1991 by Tishya A. Leong

   Usage:
       mcf3 inputfile outputfile

   Notes:
       call to function writeResults is currently commented out
   ======================================================================== */


/* ------------------------------------------------------------------------
   File: MULTICOM3.C
   Purpose: Main program for finding multicommodity flows
   Calls routines in: MULTICOMIO.C, ROUTE.C, DECONGEST3.C
   ------------------------------------------------------------------------ */


/* ------------------------------------------------------------------------
   Include libraries
   Macro definition, type definition, structure definition
   ------------------------------------------------------------------------ */
#include "common.h"


/* ------------------------------------------------------------------------
   Function declarations
   ------------------------------------------------------------------------ */
void main();				/* main program             */
static Ftype *f_allocate();		/* allocate arrays of Ftype */
static int *d_allocate();		/* allocate arrays of int   */
static void groupCommodities();		/* group commodities        */
extern void readGraph();		/* in MULTICOMIO.C          */
extern void writeResults();		/* in MULTICOMIO.C 	    */
extern void findInitialRoute();		/* in ROUTE.C      	    */
extern int decongest();			/* in DECONGEST.C  	    */


/* ------------------------------------------------------------------------
   Definition of global variables and arrays
   These appear as "extern" in MCF.H
   ------------------------------------------------------------------------ */
/*--- Graph parameters ---*/
int n_nodes;		/* number of nodes (n) 			     	*/
int n_edges;		/* number of edges (m)				*/
int n_commodities;	/* number of commodities (k)		      	*/
int *heads;		/* higher numbered node incident to each edge 	*/
int *tails;		/* lower numbered node incident to each edge  	*/
int *capacities;	/* capacity of each edge 		      	*/
int *sources;		/* source node for each commodity 		*/
int *sinks;		/* sink node for each commodity   		*/
int *demands;		/* demand for each commodity 	  	   	*/
Ftype epsilon;		/* desired closeness to lower bound		*/

/*--- Constants set at beginning of program ---*/
Ftype logConstant;	/* constant to prevent overflow of edge lengths */
int n_commodityGroups;	/* number of commodity groups (k')		*/

/*--- Arrays for finding exponents ---*/
Ftype expIntegers[89];	 /* lookup table for integral powers of e      	*/
Ftype expHundreths[100]; /* lookup table for powers of e from 0 to .99	*/

/*--- Arrays used for calculations in various routines ---*/
int *comCapacities;	 /* edge capacities used to find min cost flows	    */
int *comDemands;	 /* node demands used to find min cost flows	    */
int *comLengths;	 /* edge lengths used to find min cost flows	    */
int *intFlows;		 /* integral flows returned by min cost flow routine*/
int *lengths;		 /* edge lengths 				    */
Ftype *desired_flows;	 /* min cost flows 				    */
Ftype *minCostFlowSums;  /* total flows for computing sum of min cost flows */
Ftype *individual_flows; /* flows for each commodity group                  */
Ftype *best_flows;	 /* flows giving lowest max congestion		    */
Ftype *total_flows;	 /* total flow on each edge          		    */
Ftype *congestions;	 /* congestion of each edge 			    */
struct groupType *commodityGroups; /* list of commodity groups	 	    */
struct cost_list_elt *costs;	   /* list for ordering groups by cost      */


/* #include "/usr/bondcliff/cliff/temp/clock.c"   DARTMOUTH HACK */
/* ------------------------------------------------------------------------
   main:  primary control of program
   calls functions:  f_allocate, d_allocate, groupCommodities,
                     readGraph in MULTICOMIO.C, 
		     findInitialRoute in ROUTE.C,
		     decongest in DECONGEST.C
   ------------------------------------------------------------------------ */
void main(argc, argv)
    int argc;
    char *argv[];
{
    /* Variables for interaction with function decongest */
    static int iteration;

    /* Variables to keep track of run time */
    static clock_t prev_time, iteration_time_sum, total_time, duration;
    static time_t *tp;

    /* Miscellaneous variables */
    static FILE *fp;
    static int i, j;
    static char c;

    /* Initialize clock */
    printf("Input file: %s\n\n", argv[1]);
    srand48((unsigned short)time(tp));
    clock();
    total_time = 0;

    /* Read arrays for looking up exponentials */
    fp = fopen("expintegers", "r");
    for (i = 0; i <= 88; i++)
        fscanf(fp, "%lf", &expIntegers[i]);
    fclose(fp);
    fp = fopen("exphundreths", "r");
    for (i = 0; i <= 99; i++)
        fscanf(fp, "%lf", &expHundreths[i]);
    fclose(fp);

    /* Read number of nodes, edges, and commodities */
    fp = fopen(argv[1], "r");
    while (c = getc(fp) != 'p')
        while (c != '\n')
	    c = getc(fp);
    fscanf(fp, "%*1s %*s");
    fscanf(fp, "%d", &n_nodes);
    fscanf(fp, "%d", &n_edges);
    fscanf(fp, "%d", &n_commodities);
    fclose(fp);

    /* Read graph from input file */
    heads = d_allocate(2 * n_edges + 1);
    tails = d_allocate(2 * n_edges + 1);
    capacities = d_allocate(n_edges);
    sources = d_allocate(n_commodities);
    sinks= d_allocate(n_commodities);
    demands = d_allocate(n_commodities);
    readGraph(argv[1]);

    /* Record time */
    duration = clock() * .001;
    total_time += duration;
    printf("Time reading input = %ld msec\n\n", duration);
    prev_time = clock();

    /* Group commodities */
    groupCommodities();

    /* Print graph parameters */
    printf("%d nodes\n", n_nodes);
    printf("%d edges\n", n_edges);
    printf("%d commodities\n", n_commodities);
    printf("%d commodity groups\n", n_commodityGroups);
    printf("epsilon=%f\n\n", epsilon);

    /* Allocate space for arrays */
    individual_flows = f_allocate(n_commodityGroups * n_edges);
    best_flows = f_allocate(n_commodityGroups * n_edges);
    total_flows	= f_allocate(n_edges);
    congestions = f_allocate(n_edges);
    comCapacities = d_allocate(2 * n_edges + 1);
    comLengths = d_allocate(2 * n_edges + 1);
    comDemands = d_allocate(n_nodes);
    intFlows = d_allocate(2 * n_edges + 1);
    lengths = d_allocate(n_edges);
    desired_flows = f_allocate(n_edges);
    minCostFlowSums = f_allocate(n_edges);
    costs = (struct cost_list_elt *) calloc(n_commodityGroups,
					    sizeof(struct cost_list_elt));
    if (costs == NULL) {
        printf("Cannot allocate space.\n");
	exit();
    }

    /* Compute constants */
    logConstant = 19.1 - (Ftype)log((double)n_edges);

    /* FOR DIGRAPH -- second m are reversals */
    /* Set up arrays of heads and tails for computing min cost flows
       For first m edges, tail is lower and head is higher numbered node
       For second m edges, head is lower and tail is higher numbered node */
    for (i = 1; i <= n_edges; i++) {
	tails[n_edges + i - 1] = heads[i - 1];
	heads[n_edges + i - 1] = tails[i - 1];
    }

    /* Find an initial route for each commodity group */
    for (i = 1; i <= n_commodityGroups; i++) {
        findInitialRoute(i);
    }

    /* Compute total flow for each edge */
    for (i = 1; i <= n_edges; i++)
        total_flows[i - 1] = 0;
    for (i = 1; i <= n_commodityGroups; i++)
        for (j = 1; j <= n_edges; j++)
	    total_flows[j - 1] +=
	        (Ftype)fabs((double)individual_flows[n_edges * (i - 1) +
						     (j - 1)]);

    /* Compute congestion for each edge */
    for (i = 1; i <= n_edges; i++)
        congestions[i - 1] = total_flows[i - 1] / capacities[i - 1];

    /* Initialize variables */
    iteration = 1;

    /* Record time */
    if ((prev_time >= 0) && (clock() < 0))
        duration = ((2147483647 - prev_time) + (clock() + 2147483648)) * .001;
    else
        duration = (clock() - prev_time) * .001;
    total_time += duration;
    printf("Time preprocessing = %ld msec\n\n", duration);
    iteration_time_sum = 0;
    prev_time = clock();

    /* Reroute flows until result is satisfactory */
    while (decongest(iteration) == 1) {
        iteration++;
	if ((prev_time >= 0) && (clock() < 0))
	    duration = ((2147483647 - prev_time) + (clock() + 2147483648)) *
	        .001;
	else
	    duration = (clock() - prev_time) * .001;
	total_time += duration;
	iteration_time_sum += duration;
        prev_time = clock();
    }
    if ((prev_time >= 0) && (clock() < 0))
        duration = ((2147483647 - prev_time) + (clock() + 2147483648)) * .001;
    else
        duration = (clock() - prev_time) * .001;
    total_time += duration;
    iteration_time_sum += duration;
    prev_time = clock();

    /* Record time */
    printf("\nTime iterating = %ld msec\n", iteration_time_sum);
    printf("Average time per iteration = %f msec\n",
	   (float)iteration_time_sum / (float)iteration);

    /* Write final flows and congestions to output file */
    writeResults(argv[2], argv[1]);


    /* Record time */
    printf("%d iterations\n", iteration);
    if ((prev_time >= 0) && (clock() < 0))
        duration = ((2147483647 - prev_time) + (clock() + 2147483648)) * .001;
    else
        duration = (clock() - prev_time) * .001;
    total_time += duration;
    printf("Total time = %ld msec\n", total_time);

    return;
}


/* ------------------------------------------------------------------------
   f_allocate:  allocate space for array of Ftype
   called by function:  main
   ------------------------------------------------------------------------ */
static Ftype *f_allocate(size)
    int size;
{
    static Ftype *array;

    array = (Ftype *) calloc(size, sizeof(Ftype));
    if (array == NULL) {
        printf("Cannot allocate space.\n");
	exit();
    }

    return(array);
}


/* ------------------------------------------------------------------------
   d_allocate:  allocate space for array of int
   called by function:  main
   ------------------------------------------------------------------------ */
static int *d_allocate(size)
    int size;
{
    static int *array;

    array = (int *) calloc(size, sizeof(int));
    if (array == NULL) {
        printf("Cannot allocate space.\n");
	exit();
    }

    return(array);
}


/* ------------------------------------------------------------------------
   groupCommodities:  create commodity groups (a commodity group consists
                      of all commodities with the same source and is
		      specified by the common source and an array of 
		      demands (one demand per node))
   called by function:  main
   ------------------------------------------------------------------------ */
static void groupCommodities()
{
    static int i;

    /* Allocate space */
    commodityGroups = (struct groupType *) calloc(n_nodes,
						  sizeof(struct groupType));
    if (commodityGroups == NULL) {
        printf("Cannot allocate space.\n");
	exit();
    }

    /* Initialize */
    n_commodityGroups = 0;
    for (i = 1; i <= n_nodes; i++)
        commodityGroups[i - 1].source = 0;

    /* Create groups */
    for (i = 1; i <= n_commodities; i++) {
        static int *base, group;

	/* Find correct group for commodity */
	group = 1;
	while ((group <= n_commodityGroups) &&
	       (sources[i - 1] != commodityGroups[group - 1].source))
	    group++;
	if (group > n_commodityGroups) {
	    commodityGroups[group - 1].source = sources[i - 1];
	    commodityGroups[group - 1].demands = d_allocate(n_nodes);
	    n_commodityGroups++;
	}
	/* Add commodity to group */
	base = commodityGroups[group - 1].demands;
	base[sources[i - 1] - 1] += -1 * demands[i - 1];
	base[sinks[i - 1] - 1] += demands[i - 1];
    }

    return;
}

