static char *SccsId = "@(#)sort.c 4.7 (TU-Delft) 04/16/93";
/**********************************************************

Name/Version      : makegln/4.7

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author            : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1988. All rights reserved.
**********************************************************/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#ifndef apollo
#include <sys/vfs.h>
#else
#include <sys/statfs.h>
#endif

#include "dmincl.h"
#include "aux/aux.h"
#include "makegln.h"

#define STATE_INIT 0
#define STATE_SORT 1
#define STATE_FETCH 2
static int state = STATE_INIT;

#ifdef DEBUG
#define SB_INITIAL_BUFS 1
#define BT_INITIAL_SIZE 1
#define EDGEBUF_SIZE    512
#else
#define SB_INITIAL_BUFS 10
#define BT_INITIAL_SIZE 100
#define EDGEBUF_SIZE    512
#endif DEBUG


extern int optMaxMemory;

static _edge_t ** sortBuf = NULL;
static int      sbSize = 0;
static int      sbMaxSize = 0;
static int      sbCnt = 0;

typedef struct {
    long start;
    int  size;
    int cnt;
    _edge_t * p;
    _edge_t * bufp;
    int       bufs;
    coor_t cursor_x;
    coor_t cursor_y;
} block_t;

static block_t * blocktable;
static int btCnt = 0;

static _edge_t ** edgebuftable = NULL;
static int ebtCnt;
static int ebtSize;

static FILE * fp_tmp;
static char * tmpname;

static int infoMaxTempFileSize = 0;
static int infoNumEdges = 0;

int infoNumPartitions;
double infoPartitionBalance;

private void readEdge ();
private void writeEdge ();

# ifdef DRIVER
static bool_t optPrint = FALSE;
# endif /* DRIVER */

void sortEdge (xl, yl, xr, yr, slope, sign)
coor_t xl, yl, xr, yr;
slope_t slope;
int sign;
{
    FILE * cfopen ();
    void writeBlock ();
    char * strsave ();
    _edge_t * e;

    ASSERT (sign == START || sign == STOP);

    infoNumEdges++;

    if (state != STATE_SORT) {
	sbCnt = 0;
	btCnt = 0;
	if (state != STATE_INIT) {
	    rewind (fp_tmp);
	}
	else {
	    ebtCnt        = 0;
	    ebtSize       = optMaxMemory / (EDGEBUF_SIZE * sizeof (_edge_t));
	    if (ebtSize == 0) ebtSize = 1;
	    edgebuftable  = NEW (_edge_t *, ebtSize);

	    sbSize        = SB_INITIAL_BUFS * EDGEBUF_SIZE;
	    sbMaxSize     = ebtSize * EDGEBUF_SIZE;
	    sortBuf       = NEW (_edge_t *, sbSize);

	    tmpname       = tempnam (tempdir (), "mkgln");
	    fp_tmp        = cfopen (tmpname, "w+");

	    unlink (tmpname);
#           ifdef DEBUG
            if (DEBUG) fprintf (stderr, "sbMaxSize %d\n", sbMaxSize);
#           endif DEBUG
	}
	state = STATE_SORT;
    }

    if (sbCnt >= sbSize) {
	unsigned to = Min (2 * sbSize, sbMaxSize);
	sortBuf = GROW (_edge_t *, sortBuf, sbSize, to);
	sbSize = to;
    }

    if (sbCnt >= ebtCnt * EDGEBUF_SIZE) {
	int i;
	_edge_t * eb;
	eb = NEW (_edge_t, EDGEBUF_SIZE);
	edgebuftable[ebtCnt++] = eb;
	for (i = 0; i < Min (EDGEBUF_SIZE, sbSize - sbCnt); i++) {
	    ASSERT (sbCnt + i < sbSize);
	    sortBuf[sbCnt + i] = eb + i;
	}
    }

    e = sortBuf [sbCnt++];

    e -> xl = xl, e -> xr = xr;
    e -> yl = yl, e -> yr = yr;
    e -> slope = slope;
    e -> sign = sign;

#   ifdef DRIVER
    if (optPrint) {fprintf (stderr, "sortedge: "); pe (e);}
#   endif /* DRIVER */

    if (sbCnt == sbMaxSize) {
	sortBlock (sortBuf, sbCnt);
	writeBlock (sortBuf, sbCnt);
	sbCnt = 0;
    }
}

edge_t * fetchEdge ()
{
    edge_t * e;
    void makeBtBufs ();
    edge_t * createEdge ();
    _edge_t * fetch ();
    _edge_t * edge;
    int       block;
    static int sortBufIndex= -1000;

    if (state == STATE_INIT)		/* this can happen if there are */
	goto ready;		        /* empty streams */

    if (state != STATE_FETCH) {
	state = STATE_FETCH;
#       ifdef DRIVER
	if (optPrint) {
	    int i;
	    fprintf (stderr, "===== before fetch =====\n");
	    for (i = 0; i < sbCnt; i++) pe (sortBuf[i]);
	    fprintf (stderr, "===== end before fetch =====\n");
	}
#       endif /* DRIVER */
	if (btCnt > 0) {
	    if (sbCnt > 0) {
		sortBlock (sortBuf, sbCnt);
		writeBlock (sortBuf, sbCnt);
		sbCnt = 0;
	    }
	    fflush (fp_tmp);

	    infoMaxTempFileSize = Max (infoMaxTempFileSize, ftell (fp_tmp));

	    makeBtBufs ();

	    pqInit (btCnt);
	    for (block = 0; block < btCnt; block++) {
		pqInsert (fetch (block), block);
	    }
	}
	else {
	    if (sbCnt > 0) sortBlock (sortBuf, sbCnt);
	    sortBufIndex = 0;
	}
    }

    if (btCnt > 0) {
	pqHead (&edge, &block);
	e = createEdge (edge -> xl, edge -> yl, edge -> xr, edge -> yr,
			edge -> slope, edge -> sign);
	pqReplaceHead (fetch (block), block);
    }
    else {
	static _edge_t z;
	if (sortBufIndex < sbCnt)
	    edge = sortBuf [sortBufIndex++];
	else {
ready:
	    edge = &z;
	    edge -> xl = INF;
	}
	e = createEdge (edge -> xl, edge -> yl, edge -> xr, edge -> yr,
			edge -> slope, edge -> sign);
    }

#   ifdef DEBUG
    if (DEBUG) printEdge ("fetchEdge", e);
#   endif DEBUG

    return (e);
}

private
void writeBlock (base, N)
_edge_t ** base;
int N;
{
    int i;
    static int btSize =  0;
    coor_t cursor_x = 0;
    coor_t cursor_y = 0;

    if (btCnt >= btSize) {
	unsigned to = btSize == 0 ? BT_INITIAL_SIZE : 2 * btSize;
	blocktable = GROW (block_t, blocktable, btSize, to);
	btSize = to;
    }
    
    blocktable[btCnt].start = ftell (fp_tmp);
    blocktable[btCnt].size  = N;

    for (i = 0; i < N; i++) {
	writeEdge (fp_tmp, base[i], &cursor_x, &cursor_y);
    }

    infoMaxTempFileSize = Max (infoMaxTempFileSize, ftell (fp_tmp));

    btCnt++;
}

/* writeEdge - put an edge in tmp file
 *
 * Write differences instead of absolute coordinates,
 * to save space.
 * Differences are taken accros record boundaries,
 * this works very well because the edges are in scanline order.
 */
private void writeEdge (fp, e, cursor_x, cursor_y)
FILE * fp;
_edge_t *e;
coor_t *cursor_x, *cursor_y;
{
    e -> xr -= e -> xl;
    e -> yr -= e -> yl;

    /* Encode the sign of the edges in the direction
     * of the edge.
     */
    ASSERT (e -> xr > 0);
    if (e -> sign == STOP) e -> xr = -(e -> xr);

    e -> xl -= *cursor_x;
    *cursor_x += e -> xl;
    if (e -> xl != 0) *cursor_y = 0; /* to prevent range overflow */
    e -> yl -= *cursor_y;
    *cursor_y += e -> yl;

    if (pack4D (fp, e -> xr, e -> yl, e -> xl, e -> yr) != 0) {
	say ("tmp file write error, size %ld bytes", (long) ftell (fp));
	perror (tmpname);
	die ();
    }
}

/* readEdge - get an edge from tmp file.
 * 
 * See the comments under writeEdge for the format.
 * This routine performs an 'inverse' transformation
 * to the coordinates.
 */
private void readEdge (fp, e, cursor_x, cursor_y)
FILE * fp;
_edge_t *e;
coor_t *cursor_x, *cursor_y;
{
    if (unpack4D (fp, &(e -> xr), &(e -> yl), &(e -> xl), &(e -> yr)) == EOF) {
	say ("tmp file read error, size %ld bytes", (long) ftell (fp));
	perror (tmpname);
	die ();
    }

    if (e -> xl != 0) *cursor_y = 0;
    e -> xl += *cursor_x;
    e -> yl += *cursor_y;
    *cursor_x = e -> xl;
    *cursor_y = e -> yl;

    if (e -> xr > 0) {
	e -> sign = START;
    }
    else {
	e -> sign = STOP;
	e -> xr = -(e -> xr);
    }

    e -> xr += e -> xl;
    e -> yr += e -> yl;

    if (e -> yr == e -> yl) e -> slope = 0;
    else if (e -> yr - e -> yl == e -> xr - e -> xl) e -> slope = 1;
    else if (e -> yr - e -> yl == e -> xl - e -> xr) e -> slope = -1;
}

private
void makeBtBufs ()
{
    int b, b1, b2, bs;

    for (b = 0; b < btCnt && b < ebtCnt; b++) {
	blocktable[b].bufp = edgebuftable[b];
	blocktable[b].bufs = EDGEBUF_SIZE;
	blocktable[b].cnt  = 0;
	blocktable[b].cursor_x = 0;
	blocktable[b].cursor_y = 0;
    }

    while (b < btCnt) {

	for (b1 = 0, b2 = b; b1 < b && b2 < btCnt; b1++, b2++) {
	    bs = blocktable[b1].bufs / 2;
	    blocktable[b1].bufs -= bs;
	    blocktable[b2].bufs  = bs;
	    blocktable[b2].bufp  = blocktable[b1].bufp + blocktable[b1].bufs;
	    blocktable[b2].cnt  = 0;
	}
	b = b2;
    }
}

private
_edge_t * fetch (b)
int b;
{
    void fillBuf ();

    if (blocktable[b].cnt <= 0)
	fillBuf (b);

    blocktable[b].cnt--;

    return (blocktable[b].p++);
}

private
void fillBuf (b)
int b;
{
    int nedges = Min (blocktable[b].bufs, blocktable[b].size);
    int i;

    if (nedges <= 0) {
	nedges = 1;
	blocktable[b].bufp -> xl = INF;
    }
    else {
	fseek (fp_tmp, (long) blocktable[b].start, 0);
	for (i = 0; i < nedges; i++)
	    readEdge (fp_tmp, blocktable[b].bufp + i,
		      &blocktable[b].cursor_x, &blocktable[b].cursor_y);

    }

    blocktable[b].cnt    = nedges;
    blocktable[b].p      = blocktable[b].bufp;
    blocktable[b].start  = ftell (fp_tmp);
    blocktable[b].size  -= nedges;

#   ifdef DEBUG
    if (DEBUG) fprintf (stderr, "fillBuf: block %d cnt %d size %d\n",
	b, blocktable[b].cnt, blocktable[b].size);
#   endif DEBUG
}


/* tempdir - Return a suitable tempdir name.
 * The one with largest free space from the candidates
 * in dirs [] below, is returned.
 * Also used in output.c.
 */
char * tempdir ()
{
    int i, imax;
    double avail = 0;

    struct statfs statbuf;

    static char * dirs [] = {
	"/tmp",
	"/usr/tmp"
    };

    for (i = 0; i < (sizeof (dirs) / sizeof (*dirs)); i++) {
	if (statfs (dirs[i], &statbuf)) {
	    continue;
	}
#ifndef apollo
	if (avail < statbuf.f_bsize * statbuf.f_bavail) {
	   avail  = statbuf.f_bsize * statbuf.f_bavail;
#else
	if (avail < statbuf.f_bsize * statbuf.f_bfree) {
	   avail  = statbuf.f_bsize * statbuf.f_bfree;
#endif
	   imax = i;
	}
    }

    return (dirs[imax]);
}

sortPrintInfo (fp)
FILE *fp;
{
    fprintf (fp, "\n");
    fprintf (fp, "\t# edges sorted               : %d\n", infoNumEdges);
    fprintf (fp, "\tsize of tempfile for sorting : %d\n", infoMaxTempFileSize);
    fprintf (fp, "\t# quicksort partitions       : %d\n", infoNumPartitions);
    fprintf (fp, "\tpartition balance            : %g\n",
	infoPartitionBalance/infoNumPartitions);
}


#ifdef DRIVER

DM_CELL          * cellKey;
DM_PROJECT       * dmproject;
bool_t optNoDelete = TRUE;

/* test the sort module.
 * Usage: test cellname maskname [cnt]
 */

int optMaxMemory = 1024 * 1024;

int scale = SCALE;

main (argc, argv)
int     argc;
char  **argv;
{
    extern int    optind;
    int errflg = 0;
    extern char * optarg;
    bool_t  optVerify = TRUE;
    char  * argv0 = "test";
    edge_t * e1, * e2;
    int i, c;

    while ((c = getopt (argc, argv, "n:pv")) != EOF) {
	switch (c) {
	    case '?': errflg++;              break;
	    case 'p': optPrint = TRUE; break;
	    case 'v': optVerify = TRUE; break;
	}
    }

    if (errflg)
        printf ("Usage: %s [-n#] [-ps] cell mask [...]\n", argv0), die ();

    dmInit ("test");

    dmproject = dmOpenProject (DEFAULT_PROJECT, DEFAULT_MODE);

    cellKey = dmCheckOut 
	(dmproject, argv[optind], ACTUAL, DONTCARE, LAYOUT, READONLY);

    dmGetDesignData (dmOpenStream (cellKey, "info3", "r"), GEO_INFO3);

    for (optind = optind + 1; argv[optind]; optind++) {

	fprintf (stderr, "Mask %s\n", argv[optind]);

	readEdges (cellKey, argv[optind], DM_OTHER_MASK, 4);

	i = 1;
	e1 = fetchEdge ();
	e2 = fetchEdge ();
	if (optPrint) pe (e1);

	while (e2 -> xl < INF) {
	    if (optPrint) pe (e2);
	    if (optVerify && smaller (e2, e1)) {
		fprintf (stderr, "Edges %d and %d out of order\n", i-1, i);
		if (!optPrint) break;
	    }
	    i++;
	    disposeEdge (e1);
	    e1 = e2, e2 = fetchEdge ();
	}

	if (e1 -> xl == INF)
	    fprintf (stderr, "Total %d edges\n", 0);
	else
	    fprintf (stderr, "Total %d edges\n", i);
    }

    sortPrintInfo (stdout);

    dmQuit ();
}

static pe (e)
_edge_t * e;
{
    fprintf (stderr, "xl=%d yl=%d xr=%d yr=%d\n",
	e -> xl, e -> yl, e -> xr, e -> yr);
}
#endif DRIVER
