/*
 * grouteNet.c -
 *
 * Global signal router.  Code to route one segment of a
 * net, from a set of possible starting points to a single
 * destination point.  Uses a Lee-like wavefront maze router
 * approach, with several performance heuristics to focus
 * the search strongly toward the destination rather than
 * propagating isotropically from the starting points.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char sccsid[] = "@(#)grouteNet.c	4.3 MAGIC (Berkeley) 12/6/85";
#endif  /* not lint */

#include <stdio.h>
#include <sys/types.h>
#include <sys/times.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "utils/malloc.h"
#include "debug/debug.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "gcr/gcr.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "dbwind/dbwind.h"
#include "utils/signals.h"
#include "router/router.h"
#include "grouter/grouter.h"
#include "utils/netlist.h"
#include "textio/textio.h"
#include "utils/styles.h"

Point glDestPoint;	/* Point we're routing to */

/* The following penalties get scaled by RtrGridSpacing */
bool glPenaltiesScaled = FALSE;
int glJogPenalty = 5;
int glObsPenalty1 = 5, glObsPenalty2 = 3;
int glNbrPenalty1 = 2, glNbrPenalty2 = 5;
int glOrphanPenalty = 3;
int glChanPenalty = 1;

/* Used in glNormalPropagate() */
typedef struct
{
    int		 pr_tmin, pr_tmax;	/* Range of top pin indices */
    int		 pr_bmin, pr_bmax;	/* Range of bottom pin indices */
    int		 pr_lmin, pr_lmax;	/* Range of left pin indices */
    int		 pr_rmin, pr_rmax;	/* Range of right pin indices */
} PinRanges;

PinRanges glInitRange =
{
	INFINITY, MINFINITY,
	INFINITY, MINFINITY,
	INFINITY, MINFINITY,
	INFINITY, MINFINITY
};

/*
 * Auxiliary information used by the new maze routing algorithm.
 *
 * Whenever the maze router is ready to select the next frontier
 * point to expand, it first moves some points to these two heaps:
 * glProximityHeap, which is sorted by distance to the destination
 * glDestPoint, and glBestHeap, which is sorted by the same cost
 * as glHeap, namely total estimated cost: gl_length plus distance
 * to glDestPoint.
 *
 * In addition, glBestCost is used to remember the total estimated
 * cost of the best total estimated cost point in any of the heaps.
 */
Heap glProximityHeap;
Heap glBestHeap;
int glBestCost;
GlPoint *glBestPt;

/*
 * Points are transferred from glHeap to the two heaps above
 * only when their cost is less than (fudgeNumer/fudgeDenom)
 * times the best total cost of any point still in any of the
 * heaps, i.e, (fudgeNumer * glBestCost) / fudgeDenom.
 */
int fudgeNumer = 13;
int fudgeDenom = 10;

/* Marker to indicate an already-processed GlPoint */
#define	PROCESSED_MARK	((GlPoint *) 1)

/*
 * ----------------------------------------------------------------------------
 *
 * glRouteToPoint --
 *
 * Perform the global routing for the given pin.  The heap starts off
 * with one or more starting points.  This routine finds the least
 * cost path from any starting point to the destination point,
 * where cost includes both distance and crossing penalties.
 *
 * Results:
 *	Pointer to the ending point in the global routing.  Chasing parent
 *	pointers to the root yields the global routing result.  Returns
 *	NULL if no path was found.
 *
 * Side effects:
 *	The search point heap (glHeap) changes.  If a successful global
 *	routing was found, all points along the path are removed from
 *	the heap, leaving other partial path points still on the heap.
 *	If no routing was found, the heap is empty.
 *
 * ----------------------------------------------------------------------------
 */

GlPoint *
glRouteToPoint(loc, bestCost)
    NLTermLoc *loc;	/* Route from points on heap to this point */
    int bestCost;		/* If we haven't found a path with less than
				 * this cost, return NULL.
				 */
{
    int heapPts, frontierPts, startPts, headFree;
    GCRChannel *inCh;
    GlPoint *inPt;
    bool newBest, newPaths;
    HeapEntry hEntry;
    GlPage *headPage;
    GlPoint *lastPt;

    /* Initialize auxiliary heaps */
    HeapInit(&glBestHeap, 64, FALSE, FALSE);
    HeapInit(&glProximityHeap, 64, FALSE, FALSE);

    glNumTries++;
    if (glLogFile)
	fprintf(glLogFile, "---\t%d\t0,0\t0\t0\n", glNumTries);

    /* Initialization */
    if (!glPenaltiesScaled)
	glScalePenalties();

    /* Remember for resetting GCRPins later */
    headPage = glCurPage;
    headFree = glCurPage->glp_free;

    /* Remember for debugging */
    heapPts = glCrossingsExpanded;
    frontierPts = glCrossingsConsidered;
    startPts = glHeap.he_used;

    /*
     * Passed to glPropagateFn() for use in estimating the remaining
     * distance to the destination point.  Invert sense of loc->nloc_dir
     * (the direction of loc->nloc_ch relative to the cell) to give the
     * side of the channel on which loc->nloc_stem lies.
     */
    glDestPoint = loc->nloc_stem;
    ASSERT(GEO_SAMEPOINT(loc->nloc_pin->gcr_point, loc->nloc_stem),
		"glRouteToPoint");

    /*
     * The modified shortest path algorithm extends the partial path for
     * which the sum of the current path cost plus the Manhattan distance
     * to the destination point is the smallest.
     */
    glBestCost = 0;
    glBestPt = (GlPoint *) NULL;
    newPaths = newBest = TRUE;
    lastPt = (GlPoint *) NULL;
    while (!SigInterruptPending && glPopFromHeap(&newBest, newPaths, &hEntry))
    {
	newPaths = FALSE;
	glCrossingsExpanded++;
	inPt = (GlPoint *) hEntry.he_id;

	/* Done if we reach the destination crossing point */
	if (GEO_SAMEPOINT(inPt->gl_point, glDestPoint))
	{
	    lastPt = inPt;
	    break;
	}

	/* Done if the best path is already more expensive than best cost */
	if (inPt->gl_length >= bestCost && !DebugIsSet(glDebugID,glDebNewHeaps))
	    break;

	/* Reject if its pin has already been visited more cheaply */
	if (inPt->gl_length > inPt->gl_pin->gcr_cost)
	{
	    glCrossingsObsolete++;
	    continue;
	}

	if (glLogFile)
	    glLogPath(inPt, hEntry.he_int);

	/*
	 * There are three possibilities:
	 *	- inPt is in the destination channel, or
	 *	- it is in a river-routing channel, or
	 *	- it is in a normal channel.
	 * In the latter case, we use a trick to avoid having to process
	 * all the crossings in the channel at once.
	 */
	inCh = inPt->gl_ch;
	if (inCh != loc->nloc_chan || !glFinalPropagate(inPt, loc))
	{
	    if (inCh->gcr_type != CHAN_NORMAL)
		(void) glRiverPropagate(inPt);
	    else
		glNormalPropagate(inPt, inCh, hEntry.he_int);
	}

	/* Remember that points may have been added to glHeap */
	newPaths = TRUE;
    }

    /* Reset the cost stored with each GCRPin */
    glResetCost(headPage, headFree);

    /* Record number of points processed if debugging */
    if (DebugIsSet(glDebugID, glDebHisto))
	glHistoAdd(heapPts, frontierPts, startPts);

    /* Free the auxiliary heaps */
    HeapKill(&glBestHeap, (cb_heap_kill_t) NULL);
    HeapKill(&glProximityHeap, (cb_heap_kill_t) NULL);

    return (lastPt);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glScalePenalties --
 *
 * Scale the penalties used in the global router cost function so they
 * reflect the actual grid size used during routing.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

void
glScalePenalties()
{
    glJogPenalty *= RtrGridSpacing;
    glObsPenalty1 *= RtrGridSpacing;
    glObsPenalty2 *= RtrGridSpacing;
    glNbrPenalty1 *= RtrGridSpacing;
    glNbrPenalty2 *= RtrGridSpacing;
    glOrphanPenalty *= RtrGridSpacing;
    glChanPenalty *= RtrGridSpacing;
    glPenaltiesScaled = TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * glPopFromHeap --
 *
 * Obtain the next frontier point for consideration from the
 * heap glHeap.  The variable newPaths should be TRUE if
 * points were added to glHeap since the last call to this
 * procedure.
 *
 * Algorithm:
 *	We maintain three heaps.
 *	The first, glHeap, is the one to which points are added,
 *	and is referred to as the Reserve heap.
 *
 *	The two remaining heaps are auxiliary: glProximityHeap is sorted
 *	in order of increasing distance to the goal, and glBestHeap is
 *	sorted using the same key as glHeap (cost so far plus an estimate
 *	of the cost remaining to the destination).
 *
 *	The idea is to remove all points from glHeap that are
 *	within a certain percentage (fudgeNumer/fudgeDenom) of
 *	the best estimated cost so far (glBestCost), and add these
 *	to glProximityHeap.  The actual points we return are then
 *	selected from glProximityHeap, which causes points closest
 *	to the destination to be preferred over points farther away.
 *
 * Results:
 *	Returns TRUE if a new entry was stored in hEntry, or
 *	FALSE if no more points were available.
 *
 * Side effects:
 *	May pop points from glHeap and store points in or pop
 *	points from glBestHeap or glProximityHeap.  May change
 *	*pNewBest (it should be initially TRUE on the very first
 *	call to glPopFromHeap).  Also, may modify glBestCost
 *	(which should be zero on the first call).  Stores the
 *	entry popped from the top of glProximityHeap in the
 *	HeapEntry pointed to by hEntry.
 *
 * ----------------------------------------------------------------------------
 */

bool
glPopFromHeap(pNewBest, newPaths, hEntry)
    bool *pNewBest;	/* Should be TRUE on initial call; afterwards,
			 * we set it to TRUE when glBestCost is updated.
			 */
    bool newPaths;	/* TRUE if points added to glHeap since the last
			 * call to glPopFromHeap().
			 */
    HeapEntry *hEntry;	/* See above */
{
    HeapEntry *bestCostTop, *glHeapTop;
    int minAcceptableCost, newBestCost;
    GlPoint *topPt;

    if (!DebugIsSet(glDebugID, glDebNewHeaps))
	return (HeapRemoveTop(&glHeap, hEntry) != NULL);

    /*
     * If the top element on glBestHeap changed (this occurs
     * when it is popped from glProximityHeap in the previous
     * call), we have to move more points from glHeap to
     * glProximityHeap.
     */
    if (*pNewBest)
    {
	/*
	 * Pop bestcost heap until path that hasn't already been
	 * processed is reached.   (Already processed paths are marked
	 * by setting their gl_next field to PROCESSED_MARK).  This
	 * point will be the one with the best possible cost plus
	 * estimate to the destination, and will be used to compute
	 * the minimum acceptable cost for transfer to glProximityHeap.
	 */
	while ((bestCostTop = HeapLookAtTop(&glBestHeap))
	    && ((GlPoint *) bestCostTop->he_id)->gl_next == PROCESSED_MARK)
	{
	    if (DebugIsSet(glDebugID, glDebHeap))
	    {
		TxPrintf("Discarding point (cost=%d): ", bestCostTop->he_int);
		glPrintPoint((GlPoint *) bestCostTop->he_id);
		TxPrintf("\n");
	    }
	    HeapRemoveTop(&glBestHeap, hEntry);
	}

	/*
	 * The "new" best cost is min of cost of top of the best-cost
	 * heap and the reserve heap.  If both heaps are empty, we've
	 * failed and should return FALSE.
	 */
	if (glHeapTop = HeapLookAtTop(&glHeap))
	{
	    if (bestCostTop == NULL || glHeapTop->he_int < bestCostTop->he_int)
	    {
		if (DebugIsSet(glDebugID, glDebHeap))
		    TxPrintf("Best cost really comes from glHeap\n");
		bestCostTop = glHeapTop;
	    }
	}
	else if (bestCostTop == NULL)
	    return FALSE;

	newBestCost = bestCostTop->he_int;
	glBestPt = (GlPoint *) bestCostTop->he_id;
	if (newBestCost == glBestCost)
	    *pNewBest = FALSE;
	glBestCost = newBestCost;

	if (DebugIsSet(glDebugID, glDebHeap))
	{
	    TxPrintf("New best (cost=%d): ", newBestCost);
	    glPrintPoint(glBestPt);
	    TxPrintf("\nCost %s\n", *pNewBest ? "changed" : "didn't change");
	}
    }

    /*
     * Move acceptably cheap paths from reserve to best heaps.
     * This has to happen if either the cutoff point changed in
     * the code above (*pNewBest == TRUE), or if points had been
     * added to glHeap since the last time we were called
     * (newPaths == TRUE).
     */
    if (*pNewBest || newPaths)
    {
	/*
	 * The minimum acceptable cost for transfer from the
	 * Reserve heap (glHeap) to the proximity and best heaps
	 * will be glBestCost * (fudgeNumer / fudgeDenom).
	 */
	minAcceptableCost = (glBestCost * fudgeNumer) / fudgeDenom;
	if (DebugIsSet(glDebugID, glDebHeap))
	    TxPrintf("Min acceptable cost = %d\n", minAcceptableCost);

	while ((glHeapTop = HeapRemoveTop(&glHeap, hEntry))
		&& glHeapTop->he_int <= minAcceptableCost)
	{
	    Point *p = &(((GlPoint *)(glHeapTop->he_id))->gl_point);
	    int dist = ABSDIFF(p->p_x, glDestPoint.p_x)
		     + ABSDIFF(p->p_y, glDestPoint.p_y);

	    if (DebugIsSet(glDebugID, glDebHeap))
	    {
		TxPrintf("Move to prox (pcost=%d,cost=%d): ",
			dist, glHeapTop->he_int);
		glPrintPoint((GlPoint *) glHeapTop->he_id);
		TxPrintf("\n");
	    }
	    HeapAddInt(&glBestHeap, glHeapTop->he_int, glHeapTop->he_id);
	    HeapAddInt(&glProximityHeap, dist, glHeapTop->he_id);
	}

	if (glHeapTop)
	    HeapAddInt(&glHeap, glHeapTop->he_int, glHeapTop->he_id);
    }

    /*
     * The next point to be processed is the one at the top of
     * the proximity heap, i.e., the one closest to the destination.
     * If we popped the current best, set newBest flag, so we know
     * to compute a new "best cost" the next time we're called.
     * Mark the point as "processed", so it will be discarded if
     * it ever comes to the top of glBestHeap above.
     */
    if (HeapRemoveTop(&glProximityHeap, hEntry) == NULL)
	return FALSE;

    topPt = (GlPoint *) hEntry->he_id;
    *pNewBest = (topPt == glBestPt);
    topPt->gl_next = PROCESSED_MARK;

    /*
     * Fix up the cost field of hEntry (remember, it was popped
     * from the proximity heap, which is keyed only on the estimated
     * distance remaining, while we want the cost plus the estimate
     * to the destination).
     */
    hEntry->he_int = topPt->gl_length
	    + ABSDIFF(topPt->gl_point.p_x, glDestPoint.p_x)
	    + ABSDIFF(topPt->gl_point.p_y, glDestPoint.p_y);

    if (DebugIsSet(glDebugID, glDebHeap))
    {
	TxPrintf("Returning point (%s, cost=%d): ",
			*pNewBest ? "best" : "not best", hEntry->he_int);
	glPrintPoint(topPt);
	TxPrintf("\n");
    }
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * glFinalPropagate --
 *
 * Process a point that lies in the destination channel.
 * These points are treated specially since we don't need
 * to find any more crossings to other channels.
 *
 * Note that we do perform a density computation here, so
 * we should avoid attempts to route through a portion of
 * a channel where its capacity has been exceeded.
 *
 * Results:
 *	FALSE if the path to the destination was blocked because
 *	it was unreachable; TRUE otherwise.
 *
 * Side effects:
 *	May add a point to the heap.
 *
 * ----------------------------------------------------------------------------
 */

bool
glFinalPropagate(inPt, loc)
    GlPoint *inPt;	/* Point being processed */
    NLTermLoc *loc;	/* Destination point */
{
    GCRChannel *destCh = loc->nloc_chan;
    GCRPin *destPin = loc->nloc_pin;
    Point *destPoint = &loc->nloc_stem;
    GlPoint *outPt;
    int cost;

    cost = inPt->gl_length;
    cost += ABSDIFF(inPt->gl_point.p_x, destPoint->p_x);
    cost += ABSDIFF(inPt->gl_point.p_y, destPoint->p_y);

    /* Disallow the path if it exceeds the channel density */
    if (destCh->gcr_dMaxByRow >= destCh->gcr_length
	|| destCh->gcr_dMaxByCol >= destCh->gcr_width)
    {
	if (glDensityExceeded(destCh, inPt->gl_pin, destPin))
	    return (FALSE);
    }

#ifdef	notdef	/* Don't make it too difficult in the final channel */
    if (DebugIsSet(glDebugID, glDebStraight))
    {
	/* If the net runs across the channel, it must not jog */
	if (!glJogsAcrossChannel(inPt->gl_pin, destPin))
	    return (FALSE);
    }
#endif	/* notdef */

    cost = glCrossPenalty(cost, destCh, (GCRChannel *) NULL,
		    inPt->gl_pin, destPin);
    if (cost >= destPin->gcr_cost)
	return (TRUE);

    glCrossingsComplete++;
    outPt = glNewPoint(destPoint, destCh, destPin, cost, inPt);
    HeapAddInt(&glHeap, cost, (char *) outPt);
    if (glLogFile)
    {
	fprintf(glLogFile, "FIN\t%d\t%d,%d\t%d\n",
		glNumTries,
		outPt->gl_point.p_x, outPt->gl_point.p_y,
		outPt->gl_length);
    }

    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glRiverPropagate --
 *
 * Process a point belonging to a river-routing channel.  Since these
 * channels can only be used for routing straight across to their
 * other side, we only need to consider a single crossing point.
 *
 * Results:
 *	Returns the cost with which the point was added to the heap,
 *	or -1 if the point wasn't added.
 *
 * Side effects:
 *	May add a point to the heap.
 *
 * ----------------------------------------------------------------------------
 */

int
glRiverPropagate(inPt)
    GlPoint *inPt;
{
    GCRPin *inPin = inPt->gl_pin, *outPin, *linkedPin;
    GCRChannel *inCh = inPt->gl_ch;
    int cost;
    GlPoint *outPt;

    /* Find the opposing pin */
    switch (inPin->gcr_side)
    {
	case GEO_NORTH: outPin = &inCh->gcr_bPins[inPin->gcr_x]; break;
	case GEO_SOUTH: outPin = &inCh->gcr_tPins[inPin->gcr_x]; break;
	case GEO_EAST:  outPin = &inCh->gcr_lPins[inPin->gcr_y]; break;
	case GEO_WEST:  outPin = &inCh->gcr_rPins[inPin->gcr_y]; break;
    }

    /* Propagate to this pin if it is free */
    if ((linkedPin = outPin->gcr_linked)
	    && linkedPin->gcr_pId == (GCRNet *) NULL)
    {
	/* Only add to heap if this path is cheapest so far */
	cost = inPt->gl_length
	     + ABSDIFF(inPt->gl_point.p_x, linkedPin->gcr_point.p_x)
	     + ABSDIFF(inPt->gl_point.p_y, linkedPin->gcr_point.p_y);
	if (cost < linkedPin->gcr_cost)
	{
	    linkedPin->gcr_cost = outPin->gcr_cost = cost;
	    outPt = glNewPoint(&outPin->gcr_point, linkedPin->gcr_ch, linkedPin,
			cost, inPt);
	    cost += ABSDIFF(glDestPoint.p_x, linkedPin->gcr_point.p_x)
		  + ABSDIFF(glDestPoint.p_y, linkedPin->gcr_point.p_y);
	    HeapAddInt(&glHeap, cost, (char *) outPt);
	    if (glLogFile)
	    {
		fprintf(glLogFile, "RIV\t%d\t%d,%d\t%d\t%d\n",
			glNumTries,
			outPt->gl_point.p_x, outPt->gl_point.p_y,
			outPt->gl_length, cost);
	    }
	    return (cost);
	}
    }

    return (-1);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glNormalPropagate --
 *
 * Process a point (inPt) that lies in a normal routing channel (inCh)
 * that is not the destination channel.  Use a trick to minimize the
 * number of crossing points in inCh that must be processed, speeding
 * up global routing by a factor of 3 - 5, particularly in the case
 * of large channels with lots of crossing points per channel.
 *
 * Motivation:
 *	Most channels contain lots of pins.  Usually, most of them
 *	aren't even close to being on the least-cost path, as in
 *	the case below:
 *
 *				    D
 *
 *	+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *	|							|
 *	+							+
 *	|			    C				|
 *	+							+
 *	|							|
 *	+---+---+---+---+---+---+---S---+---+---+---+---+---+---+
 *
 *	(Here, "S" is a point on the boundary of channel "C", and "D"
 *	is the destination point, which happens to lie in the adjacent
 *	channel.  "D" is a very short distance from "S" compared with
 *	the width of the channel).
 *
 *	It's usually a waste of time to process the points of "C" that
 *	aren't on the direct path to the destination.  However, we
 *	can't just throw them away, because it may turn out that the
 *	direct path is blocked, and we need to use one of the points
 *	to the side, e.g:
 *
 *				    D
 *	BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
 *	+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *	|							|
 *	+							+
 *	|			    C				|
 *	+							+
 *	|							|
 *	+---+---+---+---+---+---+---S---+---+---+---+---+---+---+
 *
 *	(Here "B" is a blockage in the channel containing "D", forcing
 *	points to the side of the channel to be used.)
 *
 * Algorithm:
 *	The trick is to ensure that the points to the side are added
 *	to the heap before they are needed, but not necessarily all
 *	at once.
 *
 *	We compute a rectangle around the starting point "S" that
 *	will contain the crossing points most likely to be of interest.
 *	This rectangle "R" is a bloated version of the bounding rectangle
 *	containing "S" and the destination point, clipped against the
 *	reachable portion of the channel (reachable in the sense of
 *	passing through no regions of maximum density):
 *
 *				................D....
 *				:		    :
 *	+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *	|			:		    :		|
 *	+			:	  R	    :		+
 *	|			:		    :		|
 *	+			:		    :		+
 *	|			:		    :		|
 *	+---+---+---+---+---+---+---S---+---+---+---+---+---+---+
 *
 *	Only crossings inside of "R" are added to the heap.  However,
 *	we put the point "S" BACK on the heap, remembering the area
 *	"R" already processed.  The trick is the cost with which "S"
 *	is added to the heap: sufficiently high so that points on the
 *	direct path to the destination are processed before "S" is
 *	reprocessed, but sufficiently low so that "S" is processed
 *	BEFORE the points outside of "R" would have been removed
 *	from the heap, had they been added at the same time "S"
 *	was originally processed.
 *
 *	When a point is removed from the heap, then, it may either
 *	be "virgin", or it may have been partially expanded.  The
 *	state of its expansion is described in inPt->gl_visited,
 *	which gives grid coordinates of a rectangle (including its
 *	top and right coordinates) covering all pins visited so far.
 *
 *	We determine a new set of pins to visit (based on heapCost,
 *	as described in the comments for glSetPinClip()), visit them,
 *	and then if all feasible points haven't yet been reached, we
 *	add a new point to the heap with a new gl_visited showing the
 *	points we processed on this iteration.  The cost for this new
 *	heap point is chosen to be less than the cost of any of the
 *	remaining points, so we have a chance to process them and put
 *	them on the heap before they are needed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May add many points to the heap.
 *
 * ----------------------------------------------------------------------------
 */

void
glNormalPropagate(inPt, inCh, heapCost)
    GlPoint *inPt;	/* Point on the boundary of inCh */
    GCRChannel *inCh;	/* Channel through which we're passing */
    int heapCost;		/* Cost with which inPt was added to heap */
{
    PinRanges pinRange, prevRange;
    int x, y, baseCost, min, max;
    GCRPin *inPin = inPt->gl_pin;
    Rect pinRect, densRect;
    GCRPin *outPin;
    int i, cost;
    bool noCheckJogs;

    /*
     * Starting at inPt, figure out how high, low, left, and right
     * we can go based on density limits.  If we're completely
     * hemmed in and can't go anywhere, mark the pin as now being
     * blocked and return.
     */
    if (!glSetDensityClip(inPt, inCh, &densRect))
    {
#ifdef	notdef
	inPin->gcr_pId = GCR_BLOCKEDNETID;
	if (inPin->gcr_linked)
	    inPin->gcr_linked->gcr_pId = GCR_BLOCKEDNETID;
#endif	/* notdef */
	return;
    }

    /*
     * Next, figure out the range of pins we're interested in
     * visiting on this pass.  This range will generally cover
     * less than the entire channel, and will depend on the
     * relative positions of inPt and glDestPoint.  The range
     * is clipped against densRect.
     */
    glSetPinClip(inPt, inCh, heapCost, &densRect, &pinRect);
    glRectToRange(inCh, &pinRect, &pinRange);

    /*
     * We've already visited the pins identified in pinRect.
     * Visit all the pins that lie in pinClip but not in pinRect.
     * At the end, if pinRect is not equal to densRect, create
     * a new GlPoint like inPt but with gl_range equal to pinRect,
     * and add this point back to the heap with a carefully-chosen
     * new cost (see below).
     */
    glRectToRange(inCh, &inPt->gl_range, &prevRange);
    baseCost = inPt->gl_length + glChanPenalty;
    x = inPt->gl_point.p_x;
    y = inPt->gl_point.p_y;

#define	OKPIN(p)	\
    ((p)->gcr_pId == NULL \
	&& (p)->gcr_linked && (p)->gcr_linked->gcr_pId == NULL)
#define	XCOST(x, pin)	ABSDIFF((x), (pin)->gcr_point.p_x)
#define	YCOST(y, pin)	ABSDIFF((y), (pin)->gcr_point.p_y)

    /* Top */
    min = pinRange.pr_tmin, max = pinRange.pr_tmax;
    if (inCh->gcr_tPins->gcr_pNext && min <= max)
    {
	cost = baseCost + inCh->gcr_area.r_ytop - y;
	noCheckJogs = TRUE;
	if (inPin->gcr_side != GEO_SOUTH) cost += glJogPenalty;
	else if (DebugIsSet(glDebugID, glDebStraight)) noCheckJogs = FALSE;
	for (i = min, outPin = &inCh->gcr_tPins[i]; i <= max; i++, outPin++)
	{
	    if (i == prevRange.pr_tmin)
	    {
		i = prevRange.pr_tmax;
		outPin += prevRange.pr_tmax - prevRange.pr_tmin;
		continue;
	    }
	    if (OKPIN(outPin) && cost + XCOST(x, outPin) < outPin->gcr_cost)
		if (noCheckJogs || !glJogsAcrossChannel(inPin, outPin))
		    (void) glPropagateFn(outPin->gcr_linked->gcr_ch,
				outPin, inPt);
	}
    }

    /* Bottom */
    min = pinRange.pr_bmin, max = pinRange.pr_bmax;
    if (inCh->gcr_bPins->gcr_pNext && min <= max)
    {
	cost = baseCost + y - inCh->gcr_area.r_ybot;
	noCheckJogs = TRUE;
	if (inPin->gcr_side != GEO_NORTH) cost += glJogPenalty;
	else if (DebugIsSet(glDebugID, glDebStraight)) noCheckJogs = FALSE;
	for (i = min, outPin = &inCh->gcr_bPins[i]; i <= max; i++, outPin++)
	{
	    if (i == prevRange.pr_bmin)
	    {
		i = prevRange.pr_bmax;
		outPin += prevRange.pr_bmax - prevRange.pr_bmin;
		continue;
	    }
	    if (OKPIN(outPin) && cost + XCOST(x, outPin) < outPin->gcr_cost)
		if (noCheckJogs || !glJogsAcrossChannel(inPin, outPin))
		    (void) glPropagateFn(outPin->gcr_linked->gcr_ch,
				outPin, inPt);
	}
    }

    /* Left */
    min = pinRange.pr_lmin, max = pinRange.pr_lmax;
    if (inCh->gcr_lPins->gcr_pNext && min <= max)
    {
	cost = baseCost + x - inCh->gcr_area.r_xbot;
	noCheckJogs = TRUE;
	if (inPin->gcr_side != GEO_EAST) cost += glJogPenalty;
	else if (DebugIsSet(glDebugID, glDebStraight)) noCheckJogs = FALSE;
	for (i = min, outPin = &inCh->gcr_lPins[i]; i <= max; i++, outPin++)
	{
	    if (i == prevRange.pr_lmin)
	    {
		i = prevRange.pr_lmax;
		outPin += prevRange.pr_lmax - prevRange.pr_lmin;
		continue;
	    }
	    if (OKPIN(outPin) && cost + YCOST(y, outPin) < outPin->gcr_cost)
		if (noCheckJogs || !glJogsAcrossChannel(inPin, outPin))
		    (void) glPropagateFn(outPin->gcr_linked->gcr_ch,
				outPin, inPt);
	}
    }

    /* Right */
    min = pinRange.pr_rmin, max = pinRange.pr_rmax;
    if (inCh->gcr_rPins->gcr_pNext && min <= max)
    {
	cost = baseCost + inCh->gcr_area.r_xtop - x;
	noCheckJogs = TRUE;
	if (inPin->gcr_side != GEO_WEST) cost += glJogPenalty;
	else if (DebugIsSet(glDebugID, glDebStraight)) noCheckJogs = FALSE;
	for (i = min, outPin = &inCh->gcr_rPins[i]; i <= max; i++, outPin++)
	{
	    if (i == prevRange.pr_rmin)
	    {
		i = prevRange.pr_rmax;
		outPin += prevRange.pr_rmax - prevRange.pr_rmin;
		continue;
	    }
	    if (OKPIN(outPin) && cost + YCOST(y, outPin) < outPin->gcr_cost)
		if (noCheckJogs || !glJogsAcrossChannel(inPin, outPin))
		    (void) glPropagateFn(outPin->gcr_linked->gcr_ch,
				outPin, inPt);
	}
    }

    /*
     * If we haven't visited all the points in this channel that
     * are reachable because of density, create a copy of inPt and
     * add it to the heap, marking its gl_range as the Rect equivalent
     * of pinRange.  The cost we use in adding this copy to the heap
     * is chosen to ensure that we get a chance to add the least cost
     * of the remaining points to the heap before anything else of that
     * cost gets removed from the heap.
     */
    cost = glMinRemainingCost(inPt, inCh, &pinRect, &densRect);
    if (cost < INFINITY)
    {
	inPt = glNewPoint(&inPt->gl_point, inCh, inPin, inPt->gl_length,
			inPt->gl_parent);
	inPt->gl_range = pinRect;
	HeapAddInt(&glHeap, cost - 1, (char *) inPt);
	glCrossingsPartial++;
    }
    else glCrossingsComplete++;
}

/*
 * ----------------------------------------------------------------------------
 *
 * glMinRemainingCost --
 *
 * Choose the cost with which glNormalPropagate will add a point to the
 * heap.  This cost is INFINITY if all the points inside of dRect have
 * been processed (*pRect == *dRect); otherwise, it is the cost of a
 * route from inPt to the closest pin in the set contained in *dRect
 * but not in *pRect, plus an estimate of the cost from this pin to
 * the destination point, plus the cost of inPt.
 *
 * Results:
 *	Returns the cost above.
 *
 * Side effects:
 *	May add many points to the heap.
 *
 * ----------------------------------------------------------------------------
 */


int
glMinRemainingCost(inPt, inCh, pRect, dRect)
    GlPoint *inPt;
    GCRChannel *inCh;
    Rect *pRect, *dRect;
{
    int cost, n;
    GCRPin *pins;

    /*
     * In the code below, 'cost' represents the sum of the cost
     * from inPt to a point in densRect but not in pinRect, plus
     * the estimated cost from that point to the destination.
     */
    cost = INFINITY;

    /*
     * Pins on top and bottom.
     * Figure out which x-coordinate on the top or bottom will
     * give minimum cost to the destination.
     */
    n = (glDestPoint.p_x - inCh->gcr_origin.p_x) / RtrGridSpacing;
    n = INRANGE(n, pRect->r_xbot - 1, pRect->r_xtop + 1);
    n = INRANGE(n, dRect->r_xbot, dRect->r_xtop);

	/* Top pins */
    if (dRect->r_ytop == inCh->gcr_width + 1)
    {
	pins = inCh->gcr_tPins;
	if (pRect->r_ytop < dRect->r_ytop)
	{
	    /* Haven't reached top yet */
	    cost = glPinCost(inPt, &pins[n], cost);
	}
	else
	{
	    /* Have processed part of the top already */
	    if (pRect->r_xbot > dRect->r_xbot)
		cost = glPinCost(inPt, &pins[pRect->r_xbot - 1], cost);
	    if (pRect->r_xtop < dRect->r_xtop)
		cost = glPinCost(inPt, &pins[pRect->r_xtop + 1], cost);
	}
    }

	/* Bottom pins */
    if (dRect->r_ybot == 0)
    {
	pins = inCh->gcr_bPins;
	if (pRect->r_ybot > dRect->r_ybot)
	{
	    /* Haven't reached bottom yet */
	    cost = glPinCost(inPt, &pins[n], cost);
	}
	else
	{
	    /* Have processed part of the bottom already */
	    if (pRect->r_xbot > dRect->r_xbot)
		cost = glPinCost(inPt, &pins[pRect->r_xbot - 1], cost);
	    if (pRect->r_xtop < dRect->r_xtop)
		cost = glPinCost(inPt, &pins[pRect->r_xtop + 1], cost);
	}
    }

    /* Pins on right and left */
    n = (glDestPoint.p_y - inCh->gcr_origin.p_y) / RtrGridSpacing;
    n = INRANGE(n, pRect->r_ybot - 1, pRect->r_ytop + 1);
    n = INRANGE(n, dRect->r_ybot, dRect->r_ytop);

	/* Right pins */
    if (dRect->r_xtop == inCh->gcr_length + 1)
    {
	pins = inCh->gcr_rPins;
	if (pRect->r_xtop < dRect->r_xtop)
	{
	    /* Haven't reached RHS yet */
	    cost = glPinCost(inPt, &pins[n], cost);
	}
	else
	{
	    /* Have processed part of the RHS already */
	    if (pRect->r_ybot > dRect->r_ybot)
		cost = glPinCost(inPt, &pins[pRect->r_ybot - 1], cost);
	    if (pRect->r_ytop < dRect->r_ytop)
		cost = glPinCost(inPt, &pins[pRect->r_ytop + 1], cost);
	}
    }

	/* Left pins */
    if (dRect->r_xbot == 0)
    {
	pins = inCh->gcr_lPins;
	if (pRect->r_xbot > dRect->r_xbot)
	{
	    /* Haven't reached LHS yet */
	    cost = glPinCost(inPt, &pins[n], cost);
	}
	else
	{
	    /* Have processed part of the LHS already */
	    if (pRect->r_ybot > dRect->r_ybot)
		cost = glPinCost(inPt, &pins[pRect->r_ybot - 1], cost);
	    if (pRect->r_ytop < dRect->r_ytop)
		cost = glPinCost(inPt, &pins[pRect->r_ytop + 1], cost);
	}
    }


    if (cost == INFINITY)
	return (cost);

    return (cost + inPt->gl_length);
}

/*
 * glPinCost --
 *
 * Used by above to give the distance from inPt->gl_point to pin->gcr_point,
 * plus the distance from pin->gcr_point to the destination, plus any
 * penalties that are guaranteed to apply (the channel penalty and
 * possibly a jog penalty are all we consider now).
 *
 * Results:
 *	Returns the minimum of the above cost and 'oldCost'.
 *
 * Side effects:
 *	None.
 */


int
glPinCost(inPt, pin, oldCost)
    GlPoint *inPt;
    GCRPin *pin;
    int oldCost;
{
    int cost;

    /* Length from inPt to pin */
    cost = ABSDIFF(inPt->gl_point.p_x, pin->gcr_point.p_x)
	 + ABSDIFF(inPt->gl_point.p_y, pin->gcr_point.p_y);

    /* Estimate of length from pin to destination */
    cost += ABSDIFF(glDestPoint.p_x, pin->gcr_point.p_x)
	 +  ABSDIFF(glDestPoint.p_y, pin->gcr_point.p_y);

    /* Penalties */
    cost += glChanPenalty;
    if (inPt->gl_point.p_x != pin->gcr_point.p_x
	    && inPt->gl_point.p_y != pin->gcr_point.p_y)
	cost += glJogPenalty;

    return (MIN(cost, oldCost));
}

/*
 * ----------------------------------------------------------------------------
 *
 * glSetDensityClip --
 *
 * Determine which pins in inCh are reachable from inPt->gl_pin,
 * given density restrictions.  Leaves *dRect set to the Rect
 * (in grid coordinates for 'ch') describing visitable pins
 * on each side.  If a whole side of pins is not reachable
 * (e.g, the top), then that coordinate of *dRect (e.g, r_ytop)
 * won't reach all the way to the corresponding extreme value
 * for the pin indices for 'ch' (e.g, ch->gcr_width+1).
 *
 * Results:
 *	Returns the cost above.
 *
 * Side effects:
 *	May add many points to the heap.
 *
 * ----------------------------------------------------------------------------
 */

bool
glSetDensityClip(inPt, ch, dRect)
    GlPoint *inPt;
    GCRChannel *ch;
    Rect *dRect;
{
    GCRPin *inPin = inPt->gl_pin;
    short *den, maxdensity;
    int n;

    /*
     * Default: in the absence of density violations, we can
     * visit all the pins on each side of the channel.
     */
    dRect->r_xbot = dRect->r_ybot = 0;
    dRect->r_xtop = ch->gcr_length + 1;
    dRect->r_ytop = ch->gcr_width + 1;

    if (ch->gcr_dMaxByRow >= ch->gcr_length)
    {
	den = ch->gcr_dColsByRow;
	maxdensity = ch->gcr_length;
	glVDensityChecks++;

	/* Walk up */
	for (n = MAX(inPin->gcr_y, 1); n <= ch->gcr_width; n++)
	{
	    if (den[n] >= maxdensity)
	    {
		/* Can't reach top */
		glVDensityFailures++;
		dRect->r_ytop = n-1;
		break;
	    }
	}

	/* Walk down */
	for (n = MIN(inPin->gcr_y, ch->gcr_width); n >= 1; n--)
	    if (den[n] >= maxdensity)
	    {
		/* Can't reach bottom */
		glVDensityFailures++;
		dRect->r_ybot = n+1;
		break;
	    }
    }

    if (ch->gcr_dMaxByCol >= ch->gcr_width)
    {
	den = ch->gcr_dRowsByCol;
	maxdensity = ch->gcr_width;
	glHDensityChecks++;

	/* Walk right */
	for (n = MAX(inPin->gcr_x, 1); n <= ch->gcr_length; n++)
	    if (den[n] >= maxdensity)
	    {
		/* Can't reach right hand side */
		glHDensityFailures++;
		dRect->r_xtop = n-1;
		break;
	    }

	/* Walk left */
	for (n = MIN(inPin->gcr_x, ch->gcr_length); n >= 1; n--)
	    if (den[n] >= maxdensity)
	    {
		/* Can't reach left hand side */
		glHDensityFailures++;
		dRect->r_xbot = n+1;
		break;
	    }
    }

    if (dRect->r_xtop < dRect->r_xbot) dRect->r_ytop = dRect->r_ybot - 1;
    else if (dRect->r_ytop < dRect->r_ybot) dRect->r_xtop = dRect->r_xbot - 1;

    return (dRect->r_xtop >= dRect->r_xbot && dRect->r_ytop >= dRect->r_ybot);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glSetPinClip --
 *
 * Figure out the range of pins we're interested in visiting on this pass.
 * The Rect for these pins will generally cover less than the entire channel,
 * and will depend on the relative positions of inPt and glDestPoint.  The
 * Rect is clipped against dRect.
 *
 * Algorithm:
 *	Construct an imaginary rectangle ('R') with inPt at one corner and
 *	glDestPoint at the other.  Bloat R on all sides by the amount
 *	described below and then determine all pins that lie both within
 *	this rectangle and dRange.
 *
 *	The amount that R is bloated depends on heapCost, which is the
 *	value currently at the top of the heap.  The simple case is when
 *	heapCost is just inPt->gl_length plus the Manhattan distance from
 *	inPt to glDestPoint (which we'll call estCost).  This case should
 *	correspond to the first time inPt is processed.  If we weren't to
 *	bloat R at all, all pins lying inside it could end up with the same
 *	total cost (gl_length plus estimated cost to glDestPoint) as inPt,
 *	so we at least have to add them.
 *
 *	Things are more complex if heapCost is greater than inPt->gl_length
 *	plus estCost.  Let diff be the difference.  We will bloat the rect
 *	by diff/2.  The reason is that points in the bloated rectangle could
 *	end up with a total cost equal to heapCost, since they involve a
 *	detour of diff/2 units out and then diff/2 units back to align
 *	with the destination point.
 *
 *	The actual amount of the bloat is diff/2 + RtrGridSpacing*4 to
 *	give a little extra slop.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Fills in *pRect.
 *
 * ----------------------------------------------------------------------------
 */

void
glSetPinClip(inPt, inCh, heapCost, dRect, pRect)
    GlPoint *inPt;
    GCRChannel *inCh;
    int heapCost;
    Rect *dRect, *pRect;
{
    int bloat, estCost, t;
    Rect r;

    if (DebugIsSet(glDebugID, glDebAllPoints))
    {
	*pRect = *dRect;
	return;
    }

    /*
     * First construct the rectangle in lambda coordinates.
     * Make it canonical: ll <= ur.
     */
    r.r_ll = inPt->gl_point;
    r.r_ur = glDestPoint;
    if (r.r_xbot > r.r_xtop) t = r.r_xbot, r.r_xbot = r.r_xtop, r.r_xtop = t;
    if (r.r_ybot > r.r_ytop) t = r.r_ybot, r.r_ybot = r.r_ytop, r.r_ytop = t;

    /* Bloat it */
    estCost = ABSDIFF(inPt->gl_point.p_x, glDestPoint.p_x)
	    + ABSDIFF(inPt->gl_point.p_y, glDestPoint.p_y);
    bloat = (heapCost - (estCost + inPt->gl_length)) / 2;
    bloat += RtrGridSpacing * 4;
    GEO_EXPAND(&r, bloat, &r);

    /* Convert to grid coordinates */
    pRect->r_xbot = (r.r_xbot - inCh->gcr_origin.p_x) / RtrGridSpacing;
    pRect->r_ybot = (r.r_ybot - inCh->gcr_origin.p_y) / RtrGridSpacing;
    pRect->r_xtop = (r.r_xtop - inCh->gcr_origin.p_x) / RtrGridSpacing;
    pRect->r_ytop = (r.r_ytop - inCh->gcr_origin.p_y) / RtrGridSpacing;

    /* Clip against dRect */
    GEOCLIP(pRect, dRect);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glResetCost --
 *
 * Reset the costs stored with each GlPin after we've completed
 * the global routing for a single net.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets each pin's cost to INFINITY.
 *
 * ----------------------------------------------------------------------------
 */

void
glResetCost(headPage, headFree)
    GlPage *headPage;
    int headFree;
{
    GlPage *gpage;
    GCRPin *pin;
    int n;

    for (gpage = headPage; gpage; gpage = gpage->glp_next)
    {
	for (n = headFree; n < gpage->glp_free; n++)
	    if (pin = gpage->glp_array[n].gl_pin)
	    {
		pin->gcr_cost = INFINITY;
		if (pin->gcr_linked)
		    pin->gcr_linked->gcr_cost = INFINITY;
	    }
	if (gpage == glCurPage)
	    break;
	headFree = 0;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * glPropagateFn --
 *
 * Search function called by glRouteToPoint for each unused crossing
 * point exiting the channel whose entry point is being processed.
 *
 * Results:
 *	Returns the cost with which the point was added to the heap,
 *	if it was added, or -1 if not.
 *
 * Side effects:
 *	If the crossing point is usable and the path through inPin
 *	is the cheapest, add the new crossing point to the heap.
 *
 * ----------------------------------------------------------------------------
 */

int
glPropagateFn(outCh, outPin, inPt)
    GCRChannel *outCh;		/* Channel entered from outPin */
    GCRPin *outPin;	/* Exit pin, also in inCh */
    GlPoint *inPt;	/* Point being considered */
{
    int cost, n;
    int finalCost;
    GlPoint *outPt;

    glCrossingsConsidered++;

    /* Add the distance to the exit point to the cost */
    cost = inPt->gl_length;
    n = inPt->gl_point.p_x - outPin->gcr_point.p_x;
    if (n < 0) cost -= n; else cost += n;
    n = inPt->gl_point.p_y - outPin->gcr_point.p_y;
    if (n < 0) cost -= n; else cost += n;

    /* Adjust by penalty and see if we can throw out this path */
    cost = glCrossPenalty(cost, inPt->gl_ch, outCh, inPt->gl_pin, outPin);
    if (cost >= outPin->gcr_cost)
	return (-1);

    if (DebugIsSet(glDebugID, glDebMaze))
	glPropagateDebug(inPt, inPt->gl_pin, outCh, outPin,
			outPin->gcr_cost, cost);

    /* Remember the new cheapest cost path and add point to the heap */
#ifdef	notdef
    if (outPin->gcr_point.p_x == iPt.p_x && outPin->gcr_point.p_y == iPt.p_y)
	TxPrintf("Bingo! cost=%d\n", cost);
#endif	/* notdef */
    outPin->gcr_cost = cost;
    if (outPin->gcr_linked)
	outPin->gcr_linked->gcr_cost = cost;
    outPt = glNewPoint(&outPin->gcr_point, outCh,
		outPin->gcr_linked, cost, inPt);

    /*
     * Special handling if outPt is in a river-routing channel.
     * There's no point in adding it to the heap, since we know
     * the only point that is reachable from it.  Just propagate
     * depth-first from here.
     */
    if (outCh->gcr_type != CHAN_NORMAL)
	return (glRiverPropagate(outPt));

    /*
     * Estimate the least possible cost to reach the destination
     * using this path.
     */
    finalCost = cost + ABSDIFF(glDestPoint.p_x, outPin->gcr_point.p_x)
		     + ABSDIFF(glDestPoint.p_y, outPin->gcr_point.p_y);
    HeapAddInt(&glHeap, finalCost, (char *) outPt);
    if (glLogFile)
    {
	fprintf(glLogFile, "ADD\t%d\t%d,%d\t%d\t%d\n",
		glNumTries,
		outPt->gl_point.p_x, outPt->gl_point.p_y,
		outPt->gl_length, finalCost);
    }
    return (finalCost);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glCrossPenalty --
 *
 * Evaluate a set of crossing points through a channel to determine the
 * total cost of bad crossing penalties.  These penalties are added to
 * 'cost' to give the total cost.  (The argument 'cost' should be the
 * sum of the cost to get to 'inPin' plus the Manhattan distance from
 * 'inPin' to 'outPin').
 *
 * Density considerations are ignored here; they are the responsibilty
 * of the caller.
 *
 * Results:
 *	The total cost plus penalty, in lambda units.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
glCrossPenalty(cost, inCh, outCh, inPin, outPin)
    int cost;			/* Distance cost */
    GCRChannel *inCh;		/* Both inPin and outPin are in this channel */
    GCRChannel *outCh;		/* Channel to which outPin exits, or NULL
				 * if outPin is the destination.
				 */
    GCRPin *inPin;	/* Pin used to enter inCh */
    GCRPin *outPin;	/* Pin used to exit inCh into outCh */
{
    GCRPin *otherPin;
    int count;

    /* Penalty for using lots of channels */
    cost += glChanPenalty;

    /* Penalize if the net doesn't run straight across the channel */
    if (inPin->gcr_x != outPin->gcr_x && inPin->gcr_y != outPin->gcr_y)
	cost += glJogPenalty;

    /*
     * If there is an obstacle or hazard over a crossing, or an
     * obstacle somewhere along the track or column of the pin,
     * then assess a penalty.  Look on both sides of the crossing
     * to get this penalty.
     */
#define	BADCROSSFLAGS	(GCROBST|GCRHAZRD|GCRTCC)
    otherPin = outPin->gcr_linked;
    if (outCh && outCh->gcr_type == CHAN_NORMAL)
    {
	if ((otherPin->gcr_pFlags & BADCROSSFLAGS)
		|| otherPin->gcr_pSize != 0)
	{
	    ASSERT(otherPin->gcr_pSize >= 0, "glCrossPenalty");
	    cost += glObsPenalty1;
	    if (otherPin->gcr_pFlags & GCROBST)
		cost += glObsPenalty2 * otherPin->gcr_pSize;
	    else if (otherPin->gcr_pFlags & GCRHAZRD)
		cost += MAX(glObsPenalty2*otherPin->gcr_pSize
				- otherPin->gcr_pDist, 0);
	}
    }

    /*
     * Done if this is not a cheaper way of reaching outPin,
     * or if this channel is used for river-routing (in which
     * case the subsequent penalty computation is not needed).
     */
    if (cost >= outPin->gcr_cost || inCh->gcr_type != CHAN_NORMAL)
	return (cost);

    if ((outPin->gcr_pFlags & BADCROSSFLAGS)
	|| outPin->gcr_pSize != 0)
    {
	ASSERT(outPin->gcr_pSize >= 0, "glCrossPenalty");
	cost += glObsPenalty1;
	if (outPin->gcr_pFlags & GCROBST)
	    cost += glObsPenalty2 * outPin->gcr_pSize;
	else if (outPin->gcr_pFlags & GCRHAZRD)
	    cost += MAX(glObsPenalty2 * outPin->gcr_pSize
				- outPin->gcr_pDist, 0);
    }

    /* Done if this is not a cheaper way of reaching outPin */
    if (cost >= outPin->gcr_cost)
	return (cost);

    /*
     * If both neighboring pins are used, the penalty is 5.
     * If only one of the neighboring pins is used, the penalty is only 2.
     */
    count = 0;
    if ((outPin + 1)->gcr_pId) count++;
    if ((outPin - 1)->gcr_pId) count++;
    if (count == 2) cost += glNbrPenalty2;
    else if (count == 1) cost += glNbrPenalty1;

    /*
     * If the path turns in the channel and the exit crossing
     * isn't a paired orphan, the penalty is 3.
     */
    if (outPin->gcr_side != GeoOppositePos[inPin->gcr_side])
    {
	switch (outPin->gcr_side)
	{
	    case GEO_NORTH: otherPin = &inCh->gcr_bPins[outPin->gcr_x]; break;
	    case GEO_SOUTH: otherPin = &inCh->gcr_tPins[outPin->gcr_x]; break;
	    case GEO_EAST:  otherPin = &inCh->gcr_lPins[outPin->gcr_y]; break;
	    case GEO_WEST:  otherPin = &inCh->gcr_rPins[outPin->gcr_y]; break;
	}
	if (otherPin->gcr_pId == (GCRNet *) NULL)
	    cost += glOrphanPenalty;
    }

    return (cost);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glDensityExceeded --
 *
 * Determine if the path from inPin to outPin through inCh passes
 * through any region of maximum density.
 *
 * Results:
 *	Returns TRUE if the path passes through a maximum-density
 *	region, FALSE if not.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
glDensityExceeded(inCh, inPin, outPin)
    GCRChannel *inCh;
    GCRPin *inPin, *outPin;
{
    int min, max, maxdensity;
    short *den;
    short *dlast;

    if (inCh->gcr_dMaxByRow >= inCh->gcr_length)
    {
	glVDensityChecks++;
	maxdensity = inCh->gcr_length;
	min = MIN(inPin->gcr_y, outPin->gcr_y);
	min = INRANGE(min, 1, inCh->gcr_width);
	max = MAX(inPin->gcr_y, outPin->gcr_y);
	max = INRANGE(max, 1, inCh->gcr_width);
	den = &inCh->gcr_dColsByRow[min];
	dlast = &inCh->gcr_dColsByRow[max];
	while (den <= dlast)
	    if (*den++ >= maxdensity)
	    {
		glVDensityFailures++;
		return (TRUE);
	    }
    }
    if (inCh->gcr_dMaxByCol >= inCh->gcr_width)
    {
	glHDensityChecks++;
	maxdensity = inCh->gcr_width;
	min = MIN(inPin->gcr_x, outPin->gcr_x);
	min = INRANGE(min, 1, inCh->gcr_length);
	max = MAX(inPin->gcr_x, outPin->gcr_x);
	max = INRANGE(max, 1, inCh->gcr_length);
	den = &inCh->gcr_dRowsByCol[min];
	dlast = &inCh->gcr_dRowsByCol[max];
	while (den <= dlast)
	    if (*den++ >= maxdensity)
	    {
		glHDensityFailures++;
		return (TRUE);
	    }
    }

    return (FALSE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glRectToRange --
 *
 * Convert from a Rect to a PinRanges representation of a range of pin
 * values.  Both representations contain the same information, but the
 * latter is more convenient to work with in glNormalPropagate(), while
 * the former requires half the storage.
 *
 * The dimensions of 'ch' are used to determine whether the pins on a
 * given side of a channel are to be excluded or not: if the Rect 'r'
 * doesn't extend all the way to 0 on the left or bottom, or to
 * ch->gcr_width + 1 on the top or ch->gcr_length + 1 on the right,
 * then the pins on that side are excluded.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets *pr to the PinRanges structure implied by 'r'.
 *
 * ----------------------------------------------------------------------------
 */

void
glRectToRange(ch, r, pr)
    GCRChannel *ch;
    Rect *r;
    PinRanges *pr;
{
    Rect clipR;

    /* Initialize to empty */
    *pr = glInitRange;

    /* Actual ranges will be from 1 to height or width of channel */
    clipR.r_xbot = MAX(r->r_xbot, 1);
    clipR.r_ybot = MAX(r->r_ybot, 1);
    clipR.r_xtop = MIN(r->r_xtop, ch->gcr_length);
    clipR.r_ytop = MIN(r->r_ytop, ch->gcr_width);

    /* Top */
    if (r->r_ytop == ch->gcr_width + 1)
	pr->pr_tmin = clipR.r_xbot, pr->pr_tmax = clipR.r_xtop;

    /* Bottom */
    if (r->r_ybot == 0)
	pr->pr_bmin = clipR.r_xbot, pr->pr_bmax = clipR.r_xtop;

    /* Left */
    if (r->r_xbot == 0)
	pr->pr_lmin = clipR.r_ybot, pr->pr_lmax = clipR.r_ytop;

    /* Right */
    if (r->r_xtop == ch->gcr_length + 1)
	pr->pr_rmin = clipR.r_ybot, pr->pr_rmax = clipR.r_ytop;
}

/*
 * ----------------------------------------------------------------------------
 *
 * glJogsAcrossChannel --
 *
 * Determine whether a signal crosses from one side of a channel
 * to the other with a jog.
 *
 * Results:
 *	TRUE if inPin and outPin are on opposite sides of the channel
 *	but are not directly opposite each other.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
glJogsAcrossChannel(inPin, outPin)
    GCRPin *inPin, *outPin;
{
    switch (inPin->gcr_side)
    {
	case GEO_NORTH:
	    if (outPin->gcr_side == GEO_SOUTH
		    && outPin->gcr_point.p_x != inPin->gcr_point.p_x)
		return (TRUE);
	    break;
	case GEO_SOUTH:
	    if (outPin->gcr_side == GEO_NORTH
		    && outPin->gcr_point.p_x != inPin->gcr_point.p_x)
		return (TRUE);
	    break;
	case GEO_EAST:
	    if (outPin->gcr_side == GEO_WEST
		    && outPin->gcr_point.p_y != inPin->gcr_point.p_y)
		return (TRUE);
	    break;
	case GEO_WEST:
	    if (outPin->gcr_side == GEO_EAST
		    && outPin->gcr_point.p_y != inPin->gcr_point.p_y)
		return (TRUE);
	    break;
    }

    return (FALSE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glPropagateDebug --
 *
 * Used for debugging; print lots of information about the pair of
 * crossing points inPin and outPin (propagating through inPt->gl_ch).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Prints stuff on the terminal; also displays the areas of
 *	the two crossing points on the screen.
 *
 * ----------------------------------------------------------------------------
 */

void
glPropagateDebug(inPt, inPin, outCh, outPin, prevCost, distCost)
    GlPoint *inPt;
    GCRPin *inPin, *outPin;
    GCRChannel *outCh;
    int prevCost, distCost;
{
    char mesg[256];
    Point linkedPt;
    Rect r;

    r.r_ll = r.r_ur = inPt->gl_point;
    r.r_xtop++, r.r_ytop++;
    nrShowRect(EditCellUse->cu_def, &r, STYLE_SOLIDHIGHLIGHTS);
    if (inPin->gcr_linked) linkedPt = inPin->gcr_linked->gcr_point;
    else linkedPt = TiPlaneRect.r_ll;
    (void) sprintf(mesg,
	"ENTRY ch=%x (%d,%d) (%d,%d) pin=%x (%d,%d) linked=%x (%d,%d)",
		    outCh,
		    outCh->gcr_area.r_xbot, outCh->gcr_area.r_ybot,
		    outCh->gcr_area.r_xtop, outCh->gcr_area.r_ytop,
		    inPin, inPin->gcr_point.p_x, inPin->gcr_point.p_y,
		    inPin->gcr_linked, linkedPt.p_x, linkedPt.p_y);
    nrMore(mesg);
    nrShowRect(EditCellUse->cu_def, &r, STYLE_ERASEHIGHLIGHTS);

    if (outPin->gcr_linked) linkedPt = outPin->gcr_linked->gcr_point;
    else linkedPt = TiPlaneRect.r_ll;
    (void) sprintf(mesg,
	"EXIT  ch=%x (%d,%d) (%d,%d) %d/%d pin=%x (%d,%d) linked=%x (%d,%d)",
		    outCh,
		    outCh->gcr_area.r_xbot, outCh->gcr_area.r_ybot,
		    outCh->gcr_area.r_xtop, outCh->gcr_area.r_ytop,
		    prevCost, distCost,
		    outPin, outPin->gcr_point.p_x, outPin->gcr_point.p_y,
		    outPin->gcr_linked, linkedPt.p_x, linkedPt.p_y);
    r.r_ll = r.r_ur = outPin->gcr_point;
    r.r_xtop++, r.r_ytop++;
    nrShowRect(EditCellUse->cu_def, &r, STYLE_SOLIDHIGHLIGHTS);
    nrMore(mesg);
    nrShowRect(EditCellUse->cu_def, &r, STYLE_ERASEHIGHLIGHTS);
}

/*
 * ----------------------------------------------------------------------------
 *
 * glLogPath --
 *
 * Print information about the point 'inPt' to the file glLogFile,
 * but only if this is the first time the point was removed from
 * the heap (indicated by inPt->gl_range being the same as glInitRect.)
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May print to glLogFile.
 *
 * ----------------------------------------------------------------------------
 */

void
glLogPath(inPt, cost)
    GlPoint *inPt;
    int cost;
{
    extern Rect glInitRect;

    if (bcmp((char *)&inPt->gl_range, (char *)&glInitRect, sizeof (Rect)) == 0)
    {
	fprintf(glLogFile, "TOP\t%d\t%d,%d\t%d\t%d\n",
		glNumTries,
		inPt->gl_point.p_x, inPt->gl_point.p_y,
		inPt->gl_length, cost);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * glPrintPoint --
 *
 * Print information about the GlPoint 'inPt'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Prints to the terminal.
 *
 * ----------------------------------------------------------------------------
 */

void
glPrintPoint(inPt)
    GlPoint *inPt;
{
    TxPrintf("(%d,%d) l=%d p=0x%x c=0x%x",
	inPt->gl_point.p_x, inPt->gl_point.p_y,
	inPt->gl_length, inPt->gl_pin, inPt->gl_ch);
}

