/* ========================================================================
   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
   ======================================================================== */


/* ------------------------------------------------------------------------
   File: MULTICOMIO.C 
   Purpose: Input/Output routines
   Contains functions called from: MULTICOM3.C
   Calls functions in: MINCOST.C
   ------------------------------------------------------------------------ */


/* ------------------------------------------------------------------------
   Include libraries
   Macro definition, type definition, structure definition
   Declaration of global variables and arrays
   ------------------------------------------------------------------------ */
#include "common.h"
#include "mcf.h"


/* ------------------------------------------------------------------------
   Function declarations
   ------------------------------------------------------------------------ */
void readGraph();	      /* read in graph from input file		    */
void writeResults();	      /* write results to output file		    */
static void ungroup();	      /* extract commodity and its flows from group */
extern void min_cost_flow();  /* in MINCOST.C				    */

/* ------------------------------------------------------------------------
   readGraph:  read in graph from input file
   called by function:  main in MULTICOM3.C
   ------------------------------------------------------------------------ */
void readGraph(filename)
    char filename[];
{
    FILE *fp;
    static int i;
    static int end1, end2;
    static char c;

    fp = fopen(filename, "r");

    /* Find beginning of actual input (skip over comment lines) */
    while (c = getc(fp) != 'p')
        while (c != '\n')
	    c = getc(fp);
    fscanf(fp, "%*s");

    /* Read number of nodes, edges, commodities */
    fscanf(fp, "%d", &n_nodes);
    fscanf(fp, "%d", &n_edges);
    fscanf(fp, "%d", &n_commodities);

    /* Read in data for edges */     /* DIGRAPH CHANGE */
    for (i = 1; i <= n_edges; i++) {
        fscanf(fp, "%*1s %d %d %d", &end1, &end2, &capacities[i - 1]);
	tails[i - 1] = end1;
	heads[i - 1] = end2;
    }

    /* Read in data for commodities */
    for (i = 1; i <= n_commodities; i++)
        fscanf(fp, "%*1s %d %d %d",
	       &sources[i - 1], &sinks[i - 1], &demands[i - 1]);

    /* Read in error parameter (epsilon) */
    fscanf(fp, "%*1s %lf", &epsilon);

    fclose(fp);
    return;
}


/* ------------------------------------------------------------------------
   writeResults:  write final flows, congestions, & lengths to output file
   called by function:  main in MULTICOM3.C
   calls function:  ungroup
   ------------------------------------------------------------------------ */
void writeResults(filename, input_file)
    char filename[], input_file[];
{
    FILE *fp;
    int i, j;

    fp = fopen(filename, "w");
    fprintf(fp, "Input file: %s\n\n", input_file);

    /* Write flows for each commodity group */
    for (i = 1; i <= n_commodityGroups; i++) {
        fprintf(fp, "COMMODITY GROUP %d\n", i);
	for (j = 1; j <= n_edges; j++)
	    fprintf(fp, "  Edge %d\tFlow = %f\n", j, 
		    best_flows[n_edges * (i - 1) + (j - 1)]);
	fprintf(fp, "\n");
    }

    /* Write total flow, congestion, and length for each edge */
    fprintf(fp, "TOTAL FLOWS, CONGESTIONS and LENGTHS\n");
    for (i = 1; i <= n_edges; i++)
        fprintf(fp, "  Edge %d\tFlow=%f\tCongestion=%f\tLength=%d\n",
		i, total_flows[i - 1], congestions[i - 1], lengths[i - 1]);
    fprintf(fp, "\n");

    /* Extract and write flows for each commodity */
    for (i = 1; i <= n_commodities; i++) {
        ungroup(fp, i);
	fprintf(fp, "COMMODITY %d\n", i);
	for (j = 1; j <= n_edges; j++)
	    fprintf(fp, "  Edge %d\tFlow = %f\n", j, desired_flows[j - 1]);
	fprintf(fp, "\n");
    }

    fclose(fp);

    return;
}


/* ------------------------------------------------------------------------
   ungroup:  remove commodity from group
             remove flows for commodity from grouped flows
   called by function:  writeResults	     
   calls functions:  min_cost_flow in MINCOST.C
   ------------------------------------------------------------------------ */
static void ungroup(fp, commodity)
    FILE *fp;
    int commodity;
{
    static int group, scale;
    static int *base, demand;
    static int i;

    /* Figure out which group commodity is in */
    group = 1;
    while (commodityGroups[group - 1].source != sources[commodity - 1])
        group++;

    /* Locate beginning of group, calculate scaling factor */
    base = commodityGroups[group - 1].demands;
    demand = -1 * base[commodityGroups[group - 1].source - 1];
    scale = 200000000 / demand;

    /* Set up demands for min-cost flow problem */
    for (i = 1; i <= n_nodes; i++)
        comDemands[i - 1] = 0;
    comDemands[sources[commodity - 1] - 1] = -1 * demands[commodity - 1] *
        scale;
    comDemands[sinks[commodity - 1] - 1] = demands[commodity - 1] * scale;

    /* Set up capacities and lengths for min-cost flow problem */
    for (i = 1; i <= n_edges; i++) {
	    comCapacities[i - 1] =
	        (int)floor(fabs((double)(best_flows[n_edges * (group - 1) +
						    i - 1] * scale)));
	    comCapacities[n_edges + i - 1] = 0;

	comLengths[i - 1] = 0;
	comLengths[n_edges + i - 1] = 0;
    }

/* ABOVE CHANGED FOR DIGRAPH */

    /* Add edge to catch extra flow */
    comCapacities[2 * n_edges] = scale;
    comLengths[2 * n_edges] = 1;
    heads[2 * n_edges] = sinks[commodity - 1];
    tails[2 * n_edges] = sources[commodity - 1];

    /* Get flow for commodity */
    min_cost_flow(n_nodes, 2 * n_edges + 1, heads, tails, comLengths,
		  comCapacities, comDemands, intFlows);

    for (i = 1; i <= n_edges; i++)
	desired_flows[i - 1] = (Ftype)(intFlows[i - 1] -
	    intFlows[n_edges + i - 1]) / scale;
    if (intFlows[2 * n_edges] > 0)
        fprintf(fp, "extra flow for commmodity %d = %f\n", commodity,
		(Ftype)intFlows[2 * n_edges] / scale);

    /* Subtract flow for commodity from flow for grouped commodity */
    for (i = 1; i <= n_edges; i++)
        best_flows[n_edges * (group - 1) + i - 1] =
	    best_flows[n_edges * (group - 1) + i - 1] - desired_flows[i - 1];

    return;
}
