/* ========================================================================
   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: DECONGEST3.C
   Purpose: Iterated procedure for multicommodity flow program
   Contains functions called from: MULTICOM3.C
   Calls functions in: MINCOST.C

   This version scales alpha by comparing improvement in phi to 
   precision/(k'*alpha_scale) and also by checking if the ratio goes up
   ------------------------------------------------------------------------ */


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


/* ------------------------------------------------------------------------
   Function declarations
   ------------------------------------------------------------------------ */
int decongest();		/* iterated procedure for rerouting flow    */
static void initialize();	/* initialize variables before 1st iteration*/
static void getMinCostFlow();	/* find minimum-cost flow 		    */
static Ftype getPercent();	/* choose percentage of flow to reroute     */
static Ftype getMaxCongestion();/* find maximum edge congestion for flow    */
static Ftype getAlpha();	/* compute alpha for flow 		    */
static int getLength();		/* compute edge lengths for flow	    */
static Ftype getPhi();		/* compute value of potential function      */
static Ftype adjustFlows();	/* reroute flow				    */
static Ftype EXP();		/* faster exponentiation function	    */
static Ftype getRatio();	/* compute ratio of solution to lower bound */
static void processBestFlow();	/* retrieve info from best flow achieved    */
extern void min_cost_flow();	/* in MINCOST.C				    */
extern double drand48();        /* I ADDED THIS -- CS */


/* ------------------------------------------------------------------------
   decongest:  iterated procedure for rerouting flow
   called by function:  main in MULTICOM3.C
   calls functions:  initialize, getMinCostFlow, getPercent, 
                     getMaxCongestion, getAlpha, getLength, 
		     adjustFlows, getRatio, processBestFlow
   return value:
       1 = keep iterating
       0 = stop iterating
   ------------------------------------------------------------------------ */
int decongest(iteration)
    int iteration;
{
    /* Variables for communication between iterations */
    static Ftype alpha_scale;	/* scale factor for alpha	 	    */
    static Ftype precision;	/* minimum percent to reroute		    */
    static Ftype lowestMaxCongestion;  /* lowest max congestion achieved    */
    static Ftype lowestRatio;	/* lowest ratio achieved	    	    */
    static int stagnantCount;	/* number of iterations without improvement */
    static int checkCount;	/* counter for when to check ratio	    */
    static Ftype maxDecrease; 	/* max % decrease of phi in k' iterations   */
    static Ftype prev_ratio;	/* ratio from previous iteration	    */

    /* Variables local to one iteration */
    static int commodity;	/* commodity we reroute 		    */
    static int firstCommodity;	/* first commodity we try to reroute 	    */
    static Ftype max_congestion;/* highest edge congestion 		    */
    static Ftype alpha;		/* constant used in calculating lengths     */
    static Ftype percent;	/* percentage to reroute 		    */
    static int i;		/* counter for loops 			    */
    static Ftype phiChange;	/* amount phi has decreased this iteration  */
    static Ftype oldPhi;	/* phi from previous iteration 		    */
    static int scale_flag;	/* indicates whether to increase alpha_scale*/

    /* Initialize variables before first iteration */
    if (iteration == 1)
        initialize(&alpha_scale, &precision, &stagnantCount, &checkCount,
		   &maxDecrease, &lowestMaxCongestion, &lowestRatio,
		   &prev_ratio);

    /* Find max congestion and alpha */
    max_congestion = getMaxCongestion();
    alpha = getAlpha(max_congestion, alpha_scale);

    /* Calcultate length of each edge */
    for (i = 1; i <= n_edges; i++) {
        lengths[i - 1] = getLength(congestions[i - 1], alpha, max_congestion);
    }

    /* Check if done */
    /* --check if stuck */
    if (max_congestion < lowestMaxCongestion) {
        lowestMaxCongestion = max_congestion;
	for (i = 1; i <= n_commodityGroups * n_edges; i++)
	    best_flows[i - 1] = individual_flows[i - 1];
	stagnantCount = 0;
    }
    else
        stagnantCount += 1;
    if (stagnantCount > 2000) {
        printf("No improvement in last 2000 iterations.\n");
	printf("Lowest ratio achieved = %f\n", lowestRatio);
	printf("Lowest congestion achieved = %f\n", lowestMaxCongestion);
	processBestFlow(&max_congestion, &alpha, alpha_scale);

	return(0);
    }

    /* --check if alpha_scale too high
         increase alpha_scale if appropriate */
    if (checkCount % n_commodityGroups == 1) {
	if (maxDecrease < MIN(precision / n_commodityGroups / alpha_scale,
			      epsilon / n_commodityGroups)) {
	    alpha_scale = alpha_scale + .25;
	    precision = .001 / (alpha_scale * alpha_scale);
	    prev_ratio = (Ftype)exp(88.0);
	}
	else if (scale_flag == 1) {
	    alpha_scale = alpha_scale + .25;
	    precision = .001 / (alpha_scale * alpha_scale);
	    prev_ratio = (Ftype)exp(88.0);
	}
	if (alpha_scale > 40) {
	    printf("Stopping because alpha too high.\n");
	    return(0);
	}
	maxDecrease = 0;
    }

    /* --check if flow is close enough to optimal */
    if (checkCount % n_commodityGroups == 0) {
        static Ftype ratio;

        ratio = getRatio(max_congestion);
	if (ratio > prev_ratio)
	    scale_flag = 1;
	else
	    scale_flag = 0;
	prev_ratio = ratio;
	if (ratio < lowestRatio)
	    lowestRatio = ratio;
	printf("ratio=%f (%f)\tcong=%f\ta-scale=%.2f\ti=%d\n",
	       ratio, lowestRatio, max_congestion, alpha_scale, iteration);
	fflush(stdout);
	if (ratio <= 1 + epsilon) {
	    processBestFlow(&max_congestion, &alpha, alpha_scale);
	    return(0);
	}
	checkCount = 1;
    }
    else
        checkCount++;

    /* Find commodity, min cost flow, and percentage to reroute */
    if (checkCount <= n_commodityGroups * .5) {
        firstCommodity = costs[checkCount - 1].commodity;
    }
    else
        firstCommodity = (int)(drand48() * n_commodityGroups) + 1;
    commodity = firstCommodity;
    do {
	/* Find min cost flow for commodity */
	getMinCostFlow(commodity, max_congestion);
	/* Find percent to reroute */
	percent = getPercent(commodity, max_congestion, alpha_scale,
			     precision, &phiChange, &oldPhi);
	if (phiChange / oldPhi > maxDecrease)
	    maxDecrease = phiChange / oldPhi;
	commodity++;
	if (commodity > n_commodityGroups)
	    commodity = 1;
    } while ((percent < precision) &&
	     (commodity != firstCommodity));
    commodity--;
    if (commodity < 1)
        commodity = n_commodityGroups;

    /* Reroute flow */
    if (percent >= precision)
        adjustFlows(percent, commodity);
    else {
	alpha_scale = alpha_scale + .25;
	precision = .001 / (alpha_scale * alpha_scale);
    }

    return(1);
}


/* ------------------------------------------------------------------------
   initialize:  initialize variables that carry over between iterations;
   called by function:  decongest
   ------------------------------------------------------------------------ */
static void initialize(palpha_scale, pprecision, pstagnantCount,
		       pcheckCount, pmaxDecrease, plowestMaxCongestion,
		       plowestRatio, pprev_ratio)
    Ftype *palpha_scale, *pprecision;
    int *pstagnantCount, *pcheckCount;
    Ftype *pmaxDecrease, *plowestMaxCongestion, *plowestRatio, *pprev_ratio;
{
    static Ftype infinity;	/* a large real representing infinity */

    infinity = (Ftype)exp(88.0);
    *palpha_scale = .25;
    *pprecision = .001 / (*palpha_scale * *palpha_scale);
    *pstagnantCount = 0;
    *pcheckCount = n_commodityGroups;
    *pmaxDecrease = infinity;
    *plowestMaxCongestion = infinity;
    *plowestRatio = infinity;
    *pprev_ratio = infinity;

    return;
}


/* ------------------------------------------------------------------------
   getMinCostFlow:  set up min-cost flow problem and find solution
   called by functions:  decongest, getRatio
   calls function:  min_cost_flow in MINCOST.C
   ------------------------------------------------------------------------ */
static void getMinCostFlow(commodity, max_congestion)
    int commodity;
    Ftype max_congestion;
{
    static int i;
    static int *base, demand;
    static int scale;

    base = commodityGroups[commodity - 1].demands;
    demand = -1 * base[commodityGroups[commodity - 1].source - 1];
    scale = 200000000 / (n_edges * demand);

    /* Set up capacities for auxiliary graph */
    for (i = 1; i <= n_edges; i++) {
        static Ftype capacity;

	capacity = capacities[i - 1] * max_congestion;
	comCapacities[i - 1] = (int)ceil((double)(MIN(demand, capacity) *
						  scale));
        comCapacities[n_edges + i - 1] = 
	  individual_flows[n_edges * (commodity - 1) + i - 1];
/* ABOVE CHANGED FOR DIGRAPH */
    }

    /* Set up costs for auxiliary graph */
    for (i = 1; i <= n_edges; i++) {
	comLengths[i - 1] = lengths[i - 1];
	comLengths[n_edges + i - 1] = lengths[i - 1];
/* THIS IS FLOW ON REVERSE EDGE, MAY WANT TO TRY HEURISTICS FOR 
LENGTH ON DIGRAPH -- cs */
    }
      
    /* Set up demands for auxiliary graph */
    for (i = 1; i <= n_nodes; i++)
	comDemands[i - 1] = base[i - 1] * scale;

    /* Calculate min-cost flow of auxiliary graph */
    min_cost_flow(n_nodes, 2 * n_edges, 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;

    return;
}


/* ------------------------------------------------------------------------
   getPercent:  find best percentage to reroute
   called by function:  decongest
   calls functions:  getAlpha, getPhi
   ------------------------------------------------------------------------ */
static Ftype getPercent(commodity, fixed_max_congestion, alpha_scale,
			precision, pphiChange, poldPhi)
    int commodity;
    Ftype fixed_max_congestion;
    Ftype alpha_scale, precision;
    Ftype *pphiChange, *poldPhi;
{
    static Ftype phis[6], percents[6], interval;
    static Ftype fixed_alpha, min_product, ceilConstant;
    static int index, i, index2;

    /* Set up constants */
    fixed_alpha = getAlpha(fixed_max_congestion, alpha_scale);
    ceilConstant = 88 - (Ftype)log((double)(n_edges * fixed_max_congestion));

    /* Sample to minimize phi */
    /* --calculate first 5 phi-congestion products */
    if (alpha_scale <= .25)
        interval = .25;
    else
        interval = 5 * precision;
    for (i = 1; i <= 5; i++) {
        percents[i] = (i - 1) * interval;
	phis[i] = getPhi(percents[i], commodity,
			     fixed_max_congestion, fixed_alpha, ceilConstant);
    }
    /* --locate min product */
    min_product = phis[1];
    index = 1;
    for (i = 2; i <= 5; i++)
	if ((phis[i] < min_product) &&
	    (phis[i] >= 0)) {
	    min_product = phis[i];
	    index = i;
	}
    /* --sample sets of 5 phis */
    while (interval > precision) {
	/* setup new sample set */
	switch(index) {
	    static int j;
	case 1:
	    interval = (percents[2] - percents[1]) * .25;
	    percents[5] = percents[2];
	    phis[5] = phis[2];
	    index2 = 1;
	    for (j = 2; j <= 4; j++) {
	        percents[j] = percents[1] + (j - 1) * interval;
	        phis[j] = getPhi(percents[j], commodity, fixed_max_congestion,
				 fixed_alpha, ceilConstant);
		if (phis[j] >= phis[index2])
		    break;
		else
		    index2 = j;
	    }
	    break;
	case 2:
	    interval = (percents[3] - percents[1]) * .25;
	    percents[5] = percents[3];
	    phis[5] = phis[3];
	    percents[3] = percents[2];
	    phis[3] = phis[2];
	    index2 = 3;
	    percents[2] = percents[1] + interval;
	    phis[2] = getPhi(percents[2], commodity, fixed_max_congestion,
			     fixed_alpha, ceilConstant);
	    if (phis[2] <= phis[index2])
	        index2 = 2;
	    else {
		percents[4] = percents[1] + 3 * interval;
		phis[4] = getPhi(percents[4], commodity, fixed_max_congestion,
				 fixed_alpha, ceilConstant);
		if (phis[4] < phis[index2])
		    index2 = 4;
	    }
	    break;
	case 3:
	    interval = (percents[4] - percents[2]) * .25;
	    percents[1] = percents[2];
	    phis[1] = phis[2];
	    percents[5] = percents[4];
	    phis[5] = phis[4];
	    index2 = 3;
	    percents[2] = percents[1] + interval;
	    phis[2] = getPhi(percents[2], commodity, fixed_max_congestion,
			     fixed_alpha, ceilConstant);
	    if (phis[2] <= phis[index2])
	        index2 = 2;
	    else {
		percents[4] = percents[1] + 3 * interval;
		phis[4] = getPhi(percents[4], commodity, fixed_max_congestion,
				 fixed_alpha, ceilConstant);
		if (phis[4] < phis[index2])
		    index2 = 4;
	    }
	    break;
	case 4:
	    interval = (percents[5] - percents[3]) * .25;
	    percents[1] = percents[3];
	    phis[1] = phis[3];
	    percents[3] = percents[4];
	    phis[3] = phis[4];
	    index2 = 3;
	    percents[2] = percents[1] + interval;
	    phis[2] = getPhi(percents[2], commodity, fixed_max_congestion,
			     fixed_alpha, ceilConstant);
	    if (phis[2] <= phis[index2])
	        index2 = 2;
	    else {
		percents[4] = percents[1] + 3 * interval;
		phis[4] = getPhi(percents[4], commodity, fixed_max_congestion,
				 fixed_alpha, ceilConstant);
		if (phis[4] < phis[index2])
		    index2 = 4;
	    }
	    break;
	case 5:
	    interval = (percents[5] - percents[4]) * .25;
	    percents[1] = percents[4];
	    phis[1] = phis[4];
	    index2 = 5;
	    for (j = 4; j >= 2; j--) {
	        percents[j] = percents[1] + (j - 1) * interval;
	        phis[j] = getPhi(percents[j], commodity, fixed_max_congestion,
				 fixed_alpha, ceilConstant);
		if (phis[j] > phis[index2])
		    break;
		else
		    index2 = j;
	    }
	    break;
	}
	index = index2;
    }
    *poldPhi = getPhi(0.0, commodity, fixed_max_congestion, fixed_alpha,
		      ceilConstant);
    *pphiChange = *poldPhi - phis[index];

    return(percents[index]);
}


/* ------------------------------------------------------------------------
   getMaxCongestion:  find maximum of edge congestions
   called by functions:  decongest, processBestFlow
   ------------------------------------------------------------------------ */
static Ftype getMaxCongestion()
{
    static Ftype max_congestion;
    static int i;

    max_congestion = congestions[0];
    for (i = 2; i <= n_edges; i++)
        max_congestion = MAX(max_congestion, congestions[i - 1]);

    return(max_congestion);
}


/* ------------------------------------------------------------------------
   getAlpha:  calculate constant alpha used in computing lengths
   called by functions:  decongest, getPercent, processBestFlow
   ------------------------------------------------------------------------ */
static Ftype getAlpha(max_congestion, alpha_scale)
    Ftype max_congestion, alpha_scale;
{
        return(logConstant / max_congestion * alpha_scale);
}


/* ------------------------------------------------------------------------
   getLength:  calculate the length of an edge
   called by functions:  decongest, processBestFlow
   calls function:  EXP
   ------------------------------------------------------------------------ */
static int getLength(congestion, alpha, max_congestion)
    Ftype congestion;
    Ftype alpha, max_congestion;
{
    static Ftype realLength;

    realLength = EXP(MAX(alpha * (congestion - max_congestion) + logConstant,
			 0));

    return((int)realLength);
}


/* ------------------------------------------------------------------------
   getPhi:  compute value of potential function
   called by function:  getPercent
   calls function:  EXP
   ------------------------------------------------------------------------ */
static Ftype getPhi(percent, commodity, fixed_max_congestion, fixed_alpha,
		    ceilConstant)
    Ftype percent;
    int commodity;
    Ftype fixed_max_congestion, fixed_alpha;
    Ftype ceilConstant;
{
    static int i;
    static Ftype sample_total_flow, sample_flow, sample_congestion;
    static Ftype phi, power;

    phi = 0;
    for (i = 1; i <= n_edges; i++) {
	sample_flow = percent * desired_flows[i - 1] +
	    (1 - percent) * individual_flows[n_edges * (commodity - 1) +
					     i - 1];
	sample_total_flow = total_flows[i - 1] -
	    (Ftype)fabs((double)individual_flows[n_edges * (commodity - 1) +
						 i - 1]) + 
	    (Ftype)fabs((double)sample_flow);
	sample_congestion = sample_total_flow / capacities[i - 1];
	power = MAX(fixed_alpha * (sample_congestion - fixed_max_congestion) +
		    logConstant, 0);
	phi += EXP(MIN(power, ceilConstant)) * capacities[i - 1];
    }

    return(phi);
}


/* ------------------------------------------------------------------------
   adjustFlows:  reroute flow and update congestions
   called by function:  decongest
   ------------------------------------------------------------------------ */
static Ftype adjustFlows(percent, commodity)
    Ftype percent;
    int commodity;
{
    static Ftype temp_flow;
    static int i;

    for (i = 1; i <= n_edges; i++) {
        static Ftype *flowPtr;

	flowPtr = &individual_flows[n_edges * (commodity - 1) + i - 1];
        temp_flow = *flowPtr;
	*flowPtr = percent * desired_flows[i - 1] +
	    (1 - percent) * *flowPtr;
	total_flows[i - 1] = total_flows[i - 1] -
	    (Ftype)fabs((double)temp_flow) + 
	    (Ftype)fabs((double)*flowPtr);
	congestions[i - 1] = total_flows[i - 1] / capacities[i - 1];
    }

    return;
}


/* ------------------------------------------------------------------------
   EXP:  calculate a power of e (equivalent to exp())
   called by functions:  getLength, getPhi
   ------------------------------------------------------------------------ */
static Ftype EXP(x)
    Ftype x;
{
    static int a, b;
    static double c;

    a = (int)x;
    b = (int)((x - a) * 100);
    c = x - a - b * .01;

    return(expIntegers[a] * expHundreths[b] * (1 + c + c * c * .5));
}


/* ------------------------------------------------------------------------
   getRatio:  compute the ratio between the current congestion and the
              lower bound on the congestion
   called by function:  decongest
   calls function:  getMinCostFlow
   ------------------------------------------------------------------------ */
static Ftype getRatio(max_congestion)
    Ftype max_congestion;
{
    static Ftype minCostSum, phiSum;
    static int i, j;

    /* Calculate sum of min cost flows and min cost of each commodity */
    for (i = 1; i <= n_edges; i++)
        minCostFlowSums[i - 1] = 0;
    for (i = 1; i <= n_commodityGroups; i++)
        costs[i - 1].minCost = 0;
    for (i = 1; i <= n_commodityGroups; i++) {
        getMinCostFlow(i, max_congestion); 
	for (j = 1; j <= n_edges; j++) {
	    minCostFlowSums[j - 1] +=
	        (Ftype)fabs((double)desired_flows[j - 1]);
	    costs[i - 1].minCost += (Ftype)lengths[j - 1] *
	        (Ftype)fabs((double)desired_flows[j - 1]);
	}
    }
    minCostSum = 0;
    for (i = 1; i <= n_edges; i++)
        minCostSum +=  lengths[i - 1] * minCostFlowSums[i - 1];

    /* Calculate phi */
    phiSum = 0;
    for (i = 1; i <= n_edges; i++)
        phiSum += capacities[i - 1] * lengths[i - 1];

    /* Calculate difference between cost and min cost for each commodity */
    for (i = 1; i <= n_commodityGroups; i++) {
        costs[i - 1].commodity = i;
	costs[i - 1].cost = 0;
	for (j = 1; j <= n_edges; j++)
	    costs[i - 1].cost += (Ftype)lengths[j - 1] *
	        (Ftype)fabs((double)individual_flows[n_edges * (i - 1) + 
						     j - 1]);
	costs[i - 1].cost = costs[i - 1].cost - costs[i - 1].minCost;
    }

    /* Sort from highest cost difference to lowest */
    for (i = 2; i <= n_commodityGroups; i++) {
        static struct cost_list_elt temp;
	static int k;

	j = i - 1;
	while ((j >= 1) && (costs[i - 1].cost > costs[j - 1].cost))
	    j--;
	temp.cost = costs[i - 1].cost;
	temp.commodity = costs[i - 1].commodity;
	for (k = i - 1; k >= j + 1; k--) {
	    costs[k].cost = costs[k - 1].cost;
	    costs[k].commodity = costs[k - 1].commodity;
	}
	costs[j].cost = temp.cost;
	costs[j].commodity = temp.commodity;
    }

    /* Return ratio */
    return(max_congestion * phiSum / minCostSum);
}


/* ------------------------------------------------------------------------
   processBestFlow:  retrieve the best flow achieved and calculate
                     total flows, congestions, lengths, max congestion,
		     and alpha for the best flow
   called by function:  decongest
   calls functions:  getMaxCongestion, getAlpha, getLength
   ------------------------------------------------------------------------ */
static void processBestFlow(pmax_congestion, palpha, alpha_scale)
    Ftype *pmax_congestion, *palpha, alpha_scale;
{
    static int i, j;

    /* Calculate congestions */
    for (i = 1; i <= n_edges; i++) {
        total_flows[i - 1] = 0;
	for (j = 1; j <= n_commodityGroups; j++)
	    total_flows[i - 1] += (Ftype)fabs((double)best_flows[n_edges *
								 (j - 1) +
								 i - 1]);
	congestions[i - 1] = total_flows[i - 1] / capacities[i - 1];
    }

    /* Find max congestion and alpha */
    *pmax_congestion = getMaxCongestion();
    *palpha = getAlpha(*pmax_congestion, alpha_scale);

    /* Calcultate length of each edge */
    for (i = 1; i <= n_edges; i++) {
        lengths[i - 1] = getLength(congestions[i - 1],
				   *palpha, *pmax_congestion);
    }

    return;
}

