/* This software was developed by Bruce Hendrickson and Robert Leland   *
 * at Sandia National Laboratories under US Department of Energy        *
 * contract DE-AC04-76DP00789 and is copyrighted by Sandia Corporation. */

#include <stdio.h>
#include "Gmsh_printf.h"
#include "defs.h"
#include "structs.h"

/* Print metrics of partition quality. */

void      countup_cube(graph, nvtxs, assignment, ndims, ndims_tot, print_lev,
		       outfile, using_ewgts)
struct vtx_data **graph;	/* graph data structure */
int       nvtxs;		/* number of vtxs in graph */
short    *assignment;		/* set number of each vtx (length nvtxs+1) */
int       ndims;		/* number of cuts at each level */
int       ndims_tot;		/* total number of divisions of graph */
int       print_lev;		/* level of output */
FILE     *outfile;		/* output file if not NULL */
int       using_ewgts;		/* are edge weights being used? */
{
    double   *hopsize;		/* number of hops for each set */
    double   *cutsize;		/* number of cuts for each set */
    int      *setsize;		/* number of vtxs in each set */
    int      *setseen;		/* flags for sets adjacent to a particular set */
    int      *inorder;		/* list of vtxs in each set */
    int      *startptr;		/* indices into inorder array */
    double    ncuts;		/* total number of edges connecting sets */
    double    nhops;		/* total cuts weighted by hypercube hops */
    double    ewgt;		/* edge weight */
    int       nsets;		/* number of sets after a level */
    int       vtx;		/* vertex in graph */
    int       set, set2, set3;	/* sets neighboring vtxs are assigned to */
    int       onbdy;		/* counts number of neighboring set for a vtx */
    int       bdyvtxs;		/* sum of onbdy values for a set */
    int       internal;		/* number of internal nodes in a set */
    int       min_internal;	/* smallest number of internal vertices */
    int       max_internal;	/* largest number of internal vertices */
    int       total_internal;	/* total number of internal vertices */
    int       min_size, max_size; /* smallest and largest set sizes */
    int       tot_size;		 /* total of all set sizes */
    double    bdyvtx_hops;	/* bdyvtxs weighted by wire lengths */
    double    bdyvtx_hops_tot;	/* total bdyvtx_hops */
    double    bdyvtx_hops_max;	/* largest value of bdyvtx_hops among all sets */
    double    bdyvtx_hops_min;	/* smallest value of bdyvtx_hops among all sets */
    int       neighbor_sets;	/* number of neighboring sets for a set */
    double    total_bdyvtxs;	/* sum of all onbdy values in whole graph  */
    int       total_neighbors;	/* number of neighboring sets in graph */
    double    maxcuts;		/* largest cuts among all processors */
    double    mincuts;		/* smallest cuts among all processors */
    double    maxhops;		/* largest hops among all processors */
    double    minhops;		/* smallest hops among all processors */
    double    maxbdy;		/* largest bdy_vtxs among all processors */
    double    minbdy;		/* smallest bdy_vtxs among all processors */
    int       maxneighbors;	/* largest neighbor_sets among all processors */
    int       minneighbors;	/* smallest neighbor_sets among all processors */
    int       neighbor;		/* neighbor of a vertex */
    int       mask;		/* mask for active bits */
    int       bits;		/* bit pattern for counting hops */
    int       start_dims;	/* starting dimension for output loop */
    int       level;		/* recursion level of partition */
    int       print2file;	/* should I print to a file? */
    int       i, j, k, l, ll;	/* loop counters */
    double   *smalloc();
    int       abs(), sfree();

    print2file = (outfile != NULL);
    ewgt = 1;

    nsets = (1 << ndims_tot);
    cutsize = (double *) smalloc((unsigned) nsets * sizeof(double));
    hopsize = (double *) smalloc((unsigned) nsets * sizeof(double));
    setsize = (int *) smalloc((unsigned) nsets * sizeof(int));

    setseen = (int *) smalloc((unsigned) nsets * sizeof(int));
    startptr = (int *) smalloc((unsigned) (nsets + 1) * sizeof(int));
    inorder = (int *) smalloc((unsigned) nvtxs * sizeof(int));
    for (j = 0; j < nsets; j++)
	setsize[j] = 0;
    for (i = 1; i <= nvtxs; i++)
	++setsize[assignment[i]];

    /* Modify setsize to become index into vertex list. */
    for (j = 1; j < nsets; j++)
	setsize[j] += setsize[j - 1];
    for (j = nsets - 1; j > 0; j--)
	startptr[j] = setsize[j] = setsize[j - 1];
    startptr[0] = setsize[0] = 0;
    startptr[nsets] = nvtxs;
    for (i = 1; i <= nvtxs; i++) {
	set = assignment[i];
	inorder[setsize[set]] = i;
	setsize[set]++;
    }

    if (abs(print_lev) > 1) {	/* Print data from all levels of recursion. */
	start_dims = ndims;
	level = 0;
    }
    else {			/* Only print data from final level. */
	start_dims = ndims_tot;
	level = (ndims_tot + ndims - 1) / ndims - 1;
    }
    k = start_dims;
    while (k <= ndims_tot) {
	level++;
	nsets = (1 << k);
	for (j = 0; j < nsets; j++) {
	    cutsize[j] = 0;
	    hopsize[j] = 0;
	    setsize[j] = 0;
	}
	mask = 0;
	for (j = 0; j < k; j++)
	    mask = (mask << 1) + 1;

	for (i = 1; i <= nvtxs; i++) {
	    set = assignment[i] & mask;
	    setsize[set] += graph[i]->vwgt;
	    for (j = 1; j < graph[i]->nedges; j++) {
		neighbor = graph[i]->edges[j];
		set2 = assignment[neighbor] & mask;
		if (set != set2) {
		    if (using_ewgts)
			ewgt = graph[i]->ewgts[j];
		    cutsize[set] += ewgt;
		    bits = set ^ set2;
		    for (l = bits; l; l >>= 1)
			if (l & 1)
			    hopsize[set] += ewgt;
		}
	    }
	}

	tot_size = 0;
	max_size = 0;
	for (set = 0; set < nsets; set++) {
	    tot_size += setsize[set];
	    if (setsize[set] > max_size)
		max_size = setsize[set];
	}

	min_size = max_size;
	for (set = 0; set < nsets; set++) {
	    if (setsize[set] < min_size)
		min_size = setsize[set];
	}

	ncuts = nhops = 0;
	total_bdyvtxs = total_neighbors = 0;
	bdyvtx_hops_tot = bdyvtx_hops_max = bdyvtx_hops_min = 0;
	maxcuts = mincuts = 0;
	maxhops = minhops = 0;
	total_internal = 0;
	min_internal = max_size;
	max_internal = 0;
	maxbdy = minbdy = 0;
	maxneighbors = minneighbors = 0;

	printf("\nAfter level %d  (nsets = %d):\n", level, nsets);
	if (print2file)
	    fprintf(outfile, "\nAfter level %d  (nsets = %d):\n", level, nsets);
	if (print_lev < 0) {
	    printf("    set    size      cuts       hops   bndy_vtxs    adj_sets\n");
	    if (print2file)
		fprintf(outfile, "    set    size      cuts       hops   bndy_vtxs    adj_sets\n");
	}
	for (set = 0; set < nsets; set++) {
	    internal = setsize[set];
	    for (i = 0; i < nsets; i++)
		setseen[i] = 0;
	    /* Compute number of set neighbors, and number of vtxs on boundary. */
	    /* Loop through multiple assignments defining current set. */
	    bdyvtxs = 0;
	    bdyvtx_hops = 0;
	    for (l = 0; l < (1 << (ndims_tot - k)); l++) {
		set2 = (l << k) + set;
		for (i = startptr[set2]; i < startptr[set2 + 1]; i++) {
		    onbdy = 0;
		    vtx = inorder[i];
		    for (j = 1; j < graph[vtx]->nedges; j++) {
			neighbor = graph[vtx]->edges[j];
			set3 = assignment[neighbor] & mask;
			if (set3 != set) {	/* Is vtx on boundary? */
			    /* Has this neighboring set been seen already? */
			    if (setseen[set3] >= 0) {
				bits = set ^ set3;
				for (ll = bits; ll; ll >>= 1)
				    if (ll & 1)
					++bdyvtx_hops;
				++onbdy;
				setseen[set3] = -setseen[set3] - 1;
			    }
			}
		    }
		    /* Now reset all the setseen values to be positive. */
		    if (onbdy != 0) {
			for (j = 1; j < graph[vtx]->nedges; j++) {
			    neighbor = graph[vtx]->edges[j];
			    set3 = assignment[neighbor] & mask;
			    if (setseen[set3] < 0)
				setseen[set3] = -setseen[set3];
			}
			internal -= graph[vtx]->vwgt;
		    }
		    bdyvtxs += onbdy;
		}
	    }

	    total_internal += internal;
	    bdyvtx_hops_tot += bdyvtx_hops;
	    if (bdyvtx_hops > bdyvtx_hops_max)
		bdyvtx_hops_max = bdyvtx_hops;
	    if (set == 0 || bdyvtx_hops < bdyvtx_hops_min)
		bdyvtx_hops_min = bdyvtx_hops;
	    if (internal > max_internal)
		max_internal = internal;
	    if (set == 0 || internal < min_internal)
		min_internal = internal;

	    /* Now count up the number of neighboring sets. */
	    neighbor_sets = 0;
	    for (i = 0; i < nsets; i++) {
		if (setseen[i] != 0)
		    ++neighbor_sets;
	    }

	    if (print_lev < 0) {
		printf(" %5d    %5d    %6g     %6g   %6d      %6d\n",
		       set, setsize[set], cutsize[set], hopsize[set],
		       bdyvtxs, neighbor_sets);
		if (print2file)
		    fprintf(outfile, " %5d    %5d    %6g     %6g   %6d      %6d\n",
			    set, setsize[set], cutsize[set], hopsize[set],
			    bdyvtxs, neighbor_sets);
	    }
	    if (cutsize[set] > maxcuts)
		maxcuts = cutsize[set];
	    if (set == 0 || cutsize[set] < mincuts) {
		mincuts = cutsize[set];
	    }
	    if (hopsize[set] > maxhops)
		maxhops = hopsize[set];
	    if (set == 0 || hopsize[set] < minhops) {
		minhops = hopsize[set];
	    }
	    if (bdyvtxs > maxbdy)
		maxbdy = bdyvtxs;
	    if (set == 0 || bdyvtxs < minbdy) {
		minbdy = bdyvtxs;
	    }
	    if (neighbor_sets > maxneighbors)
		maxneighbors = neighbor_sets;
	    if (set == 0 || neighbor_sets < minneighbors)
	        minneighbors = neighbor_sets;
	    ncuts += cutsize[set];
	    nhops += hopsize[set];
	    total_bdyvtxs += bdyvtxs;
	    total_neighbors += neighbor_sets;
	}
	ncuts /= 2;
	nhops /= 2;

	printf("\n");
	printf("                            Total      Max/Set      Min/Set\n");
	printf("                            -----      -------      -------\n");
	printf("Set Size:             %11d  %11d  %11d\n",
		tot_size, max_size, min_size);
	printf("Edge Cuts:            %11g  %11g  %11g\n",
		ncuts, maxcuts, mincuts);
	printf("Hypercube Hops:       %11g  %11g  %11g\n",
		nhops, maxhops, minhops);
	printf("Boundary Vertices:    %11g  %11g  %11g\n",
		total_bdyvtxs, maxbdy, minbdy);
	printf("Boundary Vertex Hops: %11g  %11g  %11g\n",
		bdyvtx_hops_tot, bdyvtx_hops_max, bdyvtx_hops_min);
	printf("Adjacent Sets:        %11d  %11d  %11d\n",
		total_neighbors, maxneighbors, minneighbors);
	printf("Internal Vertices:    %11d  %11d  %11d\n\n",
		total_internal, max_internal, min_internal);

	if (print2file) {
	    fprintf(outfile, "\n");
	    fprintf(outfile, "                            Total      Max/Set      Min/Set\n");
	    fprintf(outfile, "                            -----      -------      -------\n");
	    fprintf(outfile, "Set Size:             %11d  %11d  %11d\n",
		tot_size, max_size, min_size);
	    fprintf(outfile, "Edge Cuts:            %11g  %11g  %11g\n",
		ncuts, maxcuts, mincuts);
	    fprintf(outfile, "Hypercube Hops:       %11g  %11g  %11g\n",
		nhops, maxhops, minhops);
	    fprintf(outfile, "Boundary Vertices:    %11g  %11g  %11g\n",
		total_bdyvtxs, maxbdy, minbdy);
	    fprintf(outfile, "Boundary Vertex Hops: %11g  %11g  %11g\n",
		bdyvtx_hops_tot, bdyvtx_hops_max, bdyvtx_hops_min);
	    fprintf(outfile, "Adjacent Sets:        %11d  %11d  %11d\n",
		total_neighbors, maxneighbors, minneighbors);
	    fprintf(outfile, "Internal Vertices:    %11d  %11d  %11d\n\n",
		total_internal, max_internal, min_internal);
	}

	if (k == ndims_tot)
	    k++;
	else {
	    k += ndims;
	    if (k > ndims_tot)
		k = ndims_tot;
	}
    }

    sfree((char *) cutsize);
    sfree((char *) hopsize);
    sfree((char *) setsize);
    sfree((char *) setseen);
    sfree((char *) startptr);
    sfree((char *) inorder);
}
