/*
 * ExtCouple.c --
 *
 * Circuit extraction.
 * Extraction of coupling capacitance.
 *
 *     *********************************************************************
 *     * 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 rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extract/ExtCouple.c,v 1.2 2010/06/24 12:37:17 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <math.h>		/* For atan() */

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "textio/textio.h"

/* --------------------- Data local to this file ---------------------- */

/* Pointer to hash table currently being updated with coupling capacitance */
HashTable *extCoupleHashPtr;

/* Clipping area for coupling searches */
Rect *extCoupleSearchArea;

/* Def being processed */
CellDef *extOverlapDef;

/* Forward procedure declarations */
int extBasicOverlap(), extBasicCouple();
int extAddOverlap(), extAddCouple();
int extSideLeft(), extSideRight(), extSideBottom(), extSideTop();
int extWalkLeft(), extWalkRight(), extWalkBottom(), extWalkTop();
int extSideOverlap(), extSideOverlapHalo(), extFindOverlap();
int extCornerOverlap(), extSubtractCornerOverlap(), extSubtractCornerOverlap2();
double extCornerFrac();
void extAddCornerCouple();
void extSideCommon();

/* Structure to pass on to the coupling and sidewall capacitance	*/
/* routines to include the current cell definition and the current	*/
/* plane being searched.						*/

typedef struct _ecs {
    CellDef *def;
    int	     plane;
} extCapStruct;

/* Structure to pass on two planes to check for coupling and the tile	*/
/* which is doing the coupling.						*/

typedef struct _ecpls {
    Tile *tile;
    TileType dinfo;
    int  plane_of_tile;
    int  plane_checked;
} extCoupleStruct;

/* Structure to pass on two planes to check for coupling and the	*/
/* boundary which initiated the check.					*/

typedef struct _esws {
    Boundary *bp;
    int       plane_of_boundary;
    int       plane_checked;
    bool      fringe_halo;
    Rect     *area;
    EdgeCap  *extCoupleList;	/* List of sidewall capacitance rules */
    EdgeCap  *extOverlapList;	/* List of overlap capacitance rules */
    CellDef  *def;
} extSidewallStruct;

/* Structure to pass information about a corner of a node region	*/
/* found at the endpoint of a boundary, for computing corner fringe	*/
/* capacitance (see extAddCornerCouple).				*/

typedef struct _ecns {
    extSidewallStruct *ec_esws;	/* Info from the boundary owning the corner */
    Point     ec_corner;	/* Position of the corner */
    int       ec_quadrant;	/* Quadrant of the fringe field relative to
				 * the corner (GEO_NORTHEAST, etc.)
				 */
    int       ec_sign;		/* +1 for a convex corner (fringe cap is
				 * added), -1 for a concave corner (fringe
				 * cap double-counted by the two edges
				 * meeting at the corner is subtracted).
				 */
    Rect      ec_area;		/* Quadrant halo search area */
} extCornerStruct;

/* --------------------- Debugging stuff ---------------------- */
#define CAP_DEBUG	FALSE

void extNregAdjustCap(nr, c, str)
    NodeRegion *nr;
    CapValue c;
    char *str;
{
    char *name;
    name = extNodeName((LabRegion *) nr);
    fprintf(stderr, "CapDebug: %s += %f (%s)\n", name, c, str);
}

void extAdjustCouple(he, c, str)
    HashEntry *he;
    CapValue c;
    char *str;
{
    char *name1;
    char *name2;
    CoupleKey *ck;
    ck = (CoupleKey *) he->h_key.h_words;
    name1 = extNodeName((LabRegion *) ck->ck_1);
    name2 = extNodeName((LabRegion *) ck->ck_2);
    fprintf(stderr, "CapDebug: %s-%s += %f (%s)\n", name1, name2, c, str);
}


/*
 * ----------------------------------------------------------------------------
 *
 * extFindCoupling --
 *
 * Find the coupling capacitances in the cell def.  Such capacitances
 * arise from three causes:
 *
 *	Overlap.  When two tiles on different planes overlap, they
 *		  may have a coupling capacitance proportional to
 *		  their areas.  If this is so, we subtract the substrate
 *		  capacitance of the overlapped type, and add the overlap
 *		  capacitance to the coupling hash table.
 *
 *	Sidewall. When tiles on the same plane are adjacent, they may
 *		  have a coupling capacitance proportional to the
 *		  length of their edges, divided by the distance between
 *		  them.  In this case, we just add the sidewall coupling
 *		  capacitance to the hash table.
 *
 *	Sidewall
 *	overlap.  When the edge of a tile on one plane overlaps a tile
 *		  on a different plane, the two tiles may have a coupling
 *		  capacitance proportional to the length of the overlapping
 *		  edge.  In this case we add the coupling capacitance to the
 *		  hash table.  (We may want to deduct the perimeter capacitance
 *		  to substrate?).
 *
 * Requires that ExtFindRegions has been run on 'def' to label all its
 * tiles with NodeRegions.  Also requires that the HashTable 'table'
 * has been initialized by the caller.
 *
 * If 'clipArea' is non-NULL, search for overlap capacitance only inside
 * the area *clipArea.  Search for sidewall capacitance only from tiles
 * inside *clipArea, although this capacitance may be to tiles outside
 * *clipArea.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When done, the HashTable 'table' will have been filled
 *	in with an entry for each pair of nodes having coupling
 *	capacitance.  Each entry will have a two-word key organized
 *	as an CoupleKey struct, with ck_1 and ck_2 pointing to the
 *	coupled nodes.  The value of the hash entry will be the
 *	coupling capacitance between that pair of nodes.
 *
 * ----------------------------------------------------------------------------
 */

void
extFindCoupling(def, table, clipArea)
    CellDef *def;
    HashTable *table;
    Rect *clipArea;
{
    const Rect *searchArea;
    int pNum;
    extCapStruct ecs;

    ecs.def = def;

    extCoupleHashPtr = table;
    extCoupleSearchArea = clipArea;
    searchArea = clipArea ? clipArea : &TiPlaneRect;
    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
    {
	ecs.plane = pNum;

	if (PlaneMaskHasPlane(ExtCurStyle->exts_overlapPlanes, pNum))
	    (void) DBSrPaintArea((Tile *) NULL, def->cd_planes[pNum],
			searchArea, &ExtCurStyle->exts_overlapTypes[pNum],
			extBasicOverlap, (ClientData) &ecs);
	if (PlaneMaskHasPlane(ExtCurStyle->exts_sidePlanes, pNum))
	    (void) DBSrPaintArea((Tile *) NULL, def->cd_planes[pNum],
			searchArea, &ExtCurStyle->exts_sideTypes[pNum],
			extBasicCouple, (ClientData) &ecs);
    }

}

/*
 * ----------------------------------------------------------------------------
 *
 * extRelocateSubstrateCoupling ---
 *
 * Move coupling capacitance to the substrate node from the coupling
 * cap table onto the source node's cap-to-substrate record.
 *
 * ----------------------------------------------------------------------------
 */

void
extRelocateSubstrateCoupling(table, subsnode)
    HashTable *table;		/* Coupling capacitance hash table */
    NodeRegion *subsnode;	/* Node record for substrate */
{
    HashEntry *he;
    CoupleKey *ck;
    HashSearch hs;
    CapValue cap;
    NodeRegion *rtp;
    NodeRegion *rbp;

    HashStartSearch(&hs);
    while ((he = HashNext(table, &hs)))
    {
	cap = extGetCapValue(he);
	if (cap == 0) continue;

	ck = (CoupleKey *) he->h_key.h_words;
	rtp = (NodeRegion *) ck->ck_1;
	rbp = (NodeRegion *) ck->ck_2;

	if (rtp == subsnode)
	{
	    rbp->nreg_cap += cap;
	    extSetCapValue(he, (CapValue)0);
	}
	else if (rbp == subsnode)
	{
	    rtp->nreg_cap += cap;
	    extSetCapValue(he, (CapValue)0);
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * extOutputCoupling --
 *
 * Output the coupling capacitance table built up by extFindCoupling().
 * Each entry in the hash table is a capacitance between the pair of
 * nodes identified by he->h_key, an CoupleKey struct.
 *
 * ExtFindRegions and ExtLabelRegions should have been called prior
 * to this procedure.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See the comments above.
 *
 * ----------------------------------------------------------------------------
 */

void
extOutputCoupling(table, outFile)
    HashTable *table;	/* Coupling capacitance hash table */
    FILE *outFile;	/* Output file */
{
    HashEntry *he;
    CoupleKey *ck;
    HashSearch hs;
    char *text;
    CapValue cap;  /* value of capacitance. */

    HashStartSearch(&hs);
    while ((he = HashNext(table, &hs)))
    {
	cap = extGetCapValue(he) / ExtCurStyle->exts_capScale;
	if (cap == 0)
	    continue;

	ck = (CoupleKey *) he->h_key.h_words;
	text = extNodeName((LabRegion *) ck->ck_1);
	fprintf(outFile, "cap \"%s\" ", text);
	text = extNodeName((LabRegion *) ck->ck_2);
	fprintf(outFile, "\"%s\" %lg\n", text, cap);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * extBasicOverlap --
 *
 * Filter function for overlap capacitance.
 * Called for each tile that might have coupling capacitance
 * to another node because it overlaps a tile or tiles in that
 * node.  Causes an area search over the area of 'tile' in all
 * planes to which 'tile' has overlap capacitance, for any tiles
 * to which 'tile' has overlap capacitance.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	See extAddOverlap().
 *
 * ----------------------------------------------------------------------------
 */

int
extBasicOverlap(tile, dinfo, ecs)
    Tile *tile;
    TileType dinfo;
    extCapStruct *ecs;
{
    int thisType;
    int pNum;
    PlaneMask pMask;
    TileTypeBitMask *tMask;
    Rect r;
    CellDef *def = ecs->def;
    int thisPlane = ecs->plane;
    extCoupleStruct ecpls;

    if (IsSplit(tile))
	thisType = ((dinfo & TT_SIDE)) ? SplitRightType(tile) :
		SplitLeftType(tile);
    else
	thisType = TiGetTypeExact(tile);

    if (DBIsContact(thisType))
	thisType = DBPlaneToResidue(thisType, thisPlane);

    pMask = ExtCurStyle->exts_overlapOtherPlanes[thisType];
    tMask = &ExtCurStyle->exts_overlapOtherTypes[thisType];

    TITORECT(tile, &r);
    extOverlapDef = def;
    if (extCoupleSearchArea)
    {
	GEOCLIP(&r, extCoupleSearchArea);
    }

    ecpls.tile = tile;
    ecpls.dinfo = dinfo;
    ecpls.plane_of_tile = thisPlane;

    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
    {
	/* Skip if nothing interesting on the other plane */
	if (pNum == thisPlane || !PlaneMaskHasPlane(pMask, pNum))
	    continue;

	ecpls.plane_checked = pNum;
	(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[pNum], &r, tMask,
		extAddOverlap, (ClientData) &ecpls);
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extAddOverlap --
 *
 * We are called for each tile that is overlapped by the tile passed to
 * extBasicOverlap() above (our argument 'tabove').  The intent is that
 * 'tbelow' actually shields 'tabove' from the substrate, so we should
 * replace node(tabove)'s capacitance to substrate with a capacitance
 * to node(tbelow) whose size is proportional to the area of the overlap.
 *
 * We check to insure that tabove is not shielded from tbelow by any
 * intervening material; if it is, we deduct the capacitance between
 * node(tabove) and node(tbelow) for the area of the overlap.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Updates the HashEntry with key node(tbelow), node(tabove)
 *	by adding the capacitance of the overlap if node(tbelow)
 *	and node(tabove) are different, and if they are not totally
 *	shielded by intervening material.  Also subtracts the capacitance
 *	to substrate from node(tabove) for the area of the overlap.
 *	If node(tbelow) and node(tabove) are the same, we do nothing.
 *
 * ----------------------------------------------------------------------------
 */

struct overlap
{
    Rect		 o_clip;
    int			 o_area;
    PlaneMask		 o_pmask;
    TileTypeBitMask	 o_tmask;
};

struct sideoverlap
{
    Rect		  so_clip;
    double		  so_coupfrac;
    double		  so_subfrac;
    int		  	  so_length;
    extSidewallStruct    *so_esws;
    PlaneMask		  so_pmask;
    TileTypeBitMask	  so_tmask;
    TileType		  so_ctype;
};

struct corneroverlap
{
    Rect		  co_clip;
    double		  co_coupfrac;
    double		  co_subfrac;
    extCornerStruct	 *co_ecos;
    PlaneMask		  co_pmask;
    TileTypeBitMask	  co_tmask;
    TileType		  co_ctype;
};

int
extAddOverlap(tbelow, dinfo, ecpls)
    Tile *tbelow;
    TileType dinfo;		/* unused, but needs to be handled */
    extCoupleStruct *ecpls;
{
    int extSubtractOverlap(), extSubtractOverlap2();
    NodeRegion *rabove, *rbelow;
    HashEntry *he;
    struct overlap ov;
    TileType ta, tb;
    CoupleKey ck;
    int pNum;
    CapValue c;
    Tile *tabove = ecpls->tile;

    /* Check if both tiles are connected.  If they are, we don't need   */
    /* to check for shielding material, and we don't want to add any    */
    /* coupling capacitance between them.  However, we *do* want to     */
    /* subtract off any substrate (area) capacitance previously added   */
    /* (Correction made 4/29/04 by Tim from a tip by Jeff Sondeen).     */

    rabove = (NodeRegion *) ExtGetRegion(tabove, ecpls->dinfo);
    rbelow = (NodeRegion *) ExtGetRegion(tbelow, dinfo);

    /* Quick check on validity of tile's ti_client record */
    if (rbelow == (NodeRegion *)CLIENTDEFAULT) return 0;
    if (rabove == (NodeRegion *)CLIENTDEFAULT) return 0;

    /* Compute the area of overlap */
    ov.o_clip.r_xbot = MAX(LEFT(tbelow), LEFT(tabove));
    ov.o_clip.r_xtop = MIN(RIGHT(tbelow), RIGHT(tabove));
    ov.o_clip.r_ybot = MAX(BOTTOM(tbelow), BOTTOM(tabove));
    ov.o_clip.r_ytop = MIN(TOP(tbelow), TOP(tabove));
    if (extCoupleSearchArea)
    {
	GEOCLIP(&ov.o_clip, extCoupleSearchArea);
    }
    ov.o_area = (ov.o_clip.r_ytop - ov.o_clip.r_ybot)
	      * (ov.o_clip.r_xtop - ov.o_clip.r_xbot);

    if (IsSplit(tabove))
	ta = (ecpls->dinfo & TT_SIDE) ? TiGetRightType(tabove) : TiGetLeftType(tabove);
    else
	ta = TiGetTypeExact(tabove);

    if (IsSplit(tbelow))
	tb = (dinfo & TT_SIDE) ? TiGetRightType(tbelow) : TiGetLeftType(tbelow);
    else
	tb = TiGetTypeExact(tbelow);

    /* Revert any contacts to their residues */

    if (DBIsContact(ta))
	ta = DBPlaneToResidue(ta, ecpls->plane_of_tile);

    if (DBIsContact(tb))
	tb = DBPlaneToResidue(tb, ecpls->plane_checked);

    /*
     * Find whether rabove and rbelow are shielded by intervening material.
     * Deduct the area shielded from the area of the overlap, so we adjust
     * the overlap capacitance correspondingly.
     */
    if ((ov.o_pmask = ExtCurStyle->exts_overlapShieldPlanes[ta][tb]))
    {
	ov.o_tmask = ExtCurStyle->exts_overlapShieldTypes[ta][tb];
	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	{
	    if (!PlaneMaskHasPlane(ov.o_pmask, pNum)) continue;
	    ov.o_pmask &= ~(PlaneNumToMaskBit(pNum));
	    if (ov.o_pmask == 0)
	    {
		(void) DBSrPaintArea((Tile *) NULL,
		    extOverlapDef->cd_planes[pNum], &ov.o_clip, &ov.o_tmask,
		    extSubtractOverlap, (ClientData) &ov);
	    }
	    else
	    {
		(void) DBSrPaintArea((Tile *) NULL,
		    extOverlapDef->cd_planes[pNum], &ov.o_clip, &DBAllTypeBits,
		    extSubtractOverlap2, (ClientData) &ov);
	    }
	    break;
	}
    }

    /* If any capacitance remains, add this record to the table */
    if (ov.o_area > 0)
    {
	int oa = ExtCurStyle->exts_planeOrder[ecpls->plane_of_tile];
	int ob = ExtCurStyle->exts_planeOrder[ecpls->plane_checked];
	if (oa > ob)
	{
	    Tile *tp;
	    TileType t, tres;
	    TileTypeBitMask *mask;
	    int len;
	    CapValue cp;
	    /*
	     * Subtract the substrate capacitance from tabove's region due to
	     * the area of the overlap, minus any shielded area.  The shielded
	     * areas get handled later, when processing coupling between tabove
	     * and the shielding tile.  (Tabove was the overlapping tile, so it
	     * is shielded from the substrate by tbelow if the Tabove plane is
	     * above the Tbelow plane).
	     */
	    rabove->nreg_cap -= ExtCurStyle->exts_areaCap[ta] * ov.o_area;
	    if (CAP_DEBUG)
		extNregAdjustCap(rabove,
		    -(ExtCurStyle->exts_areaCap[ta] * ov.o_area),
		    "obsolete_overlap");

	} else if (CAP_DEBUG)
	    extNregAdjustCap(rabove, 0.0,
		"obsolete_overlap (skipped, wrong direction)");

        /* If the regions are the same, skip this part */
        if (rabove == rbelow) return (0);

	/* Find the coupling hash record */
	if (rabove < rbelow) ck.ck_1 = rabove, ck.ck_2 = rbelow;
	else ck.ck_1 = rbelow, ck.ck_2 = rabove;
	he = HashFind(extCoupleHashPtr, (char *) &ck);

	/* Add the overlap capacitance to the table */
	c = extGetCapValue(he);
	c += ExtCurStyle->exts_overlapCap[ta][tb] * ov.o_area;
	if (CAP_DEBUG)
	    extAdjustCouple(he, ExtCurStyle->exts_overlapCap[ta][tb] *
		ov.o_area, "overlap");
	extSetCapValue(he, c);
    }
    return (0);
}

/* Simple overlap.  The area of overlap is subtracted from ov->o_area */

int
extSubtractOverlap(tile, dinfo, ov)
    Tile *tile;
    TileType dinfo;		/* (unused) */
    struct overlap *ov;
{
    Rect r;
    int area;

    TITORECT(tile, &r);
    GEOCLIP(&r, &ov->o_clip);
    area = (r.r_xtop - r.r_xbot) * (r.r_ytop - r.r_ybot);
    if (IsSplit(tile)) area /= 2;
    if (area > 0)
	ov->o_area -= area;

    return (0);
}

/* Recursive shielding overlap check.  If the tile shields,	*/
/* then the area of overlap is subtracted from ov->o_area.  If	*/
/* not, then this routine is called recursively on the next	*/
/* shielding plane.						*/

int
extSubtractOverlap2(tile, dinfo, ov)
    Tile *tile;
    TileType dinfo;		/* (unused) */
    struct overlap *ov;
{
    TileType ttype;
    struct overlap ovnew;
    int area, pNum;
    Rect r;

    TITORECT(tile, &r);
    GEOCLIP(&r, &ov->o_clip);
    area = (r.r_xtop - r.r_xbot) * (r.r_ytop - r.r_ybot);
    if (area <= 0)
	return (0);
    if (IsSplit(tile))
    {
	area /= 2;
	ttype = (dinfo & TT_SIDE) ? TiGetRightType(tile) : TiGetLeftType(tile);
    }
    else
	ttype = TiGetTypeExact(tile);

    /* This tile shields everything below */
    if (TTMaskHasType(&ov->o_tmask, ttype))
    {
	ov->o_area -= area;
	return (0);
    }

    /* Tile doesn't shield, so search next plane */
    ovnew = *ov;
    ovnew.o_clip = r;
    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
    {
	if (!PlaneMaskHasPlane(ovnew.o_pmask, pNum)) continue;
	ovnew.o_pmask &= ~(PlaneNumToMaskBit(pNum));
	if (ovnew.o_pmask == 0)
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &ovnew.o_clip, &ovnew.o_tmask,
		extSubtractOverlap, (ClientData) &ovnew);
	}
	else
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &ovnew.o_clip, &DBAllTypeBits,
		extSubtractOverlap2, (ClientData) &ovnew);
	}
	break;
    }
    ov->o_area = ovnew.o_area;

    return (0);
}

/* Side overlap shielding.  The fraction of the side overlap (fringe)
 * capacitance over this area is added to ov->o_frac, so that it can
 * be subtracted from the total.
 */

int
extSubtractSideOverlap(tile, dinfo, sov)
    Tile *tile;
    TileType dinfo;			/* (unused) */
    struct sideoverlap *sov;
{
    Rect r;
    int area, dnear, dfar, length;
    double mult, snear, sfar;
    TileType ta, tb;
    Boundary *bp = sov->so_esws->bp;

    TITORECT(tile, &r);
    GEOCLIP(&r, &sov->so_clip);
    area = (r.r_xtop - r.r_xbot) * (r.r_ytop - r.r_ybot);
    if (area <= 0) return 0;

    ta = TiGetType(bp->b_inside);
    tb = sov->so_ctype;

    if (bp->b_segment.r_xtop == bp->b_segment.r_xbot)
	length = r.r_ytop - r.r_ybot;
    else
	length = r.r_xtop - r.r_xbot;

    switch (bp->b_direction)
    {
	case BD_LEFT:	/* Tile tp is to the left of the boundary */
	    dnear = bp->b_segment.r_xbot - r.r_xtop;
	    dfar = bp->b_segment.r_xbot - r.r_xbot;
	    break;
	case BD_RIGHT:	/* Tile tp is to the right of the boundary */
	    dnear = r.r_xbot - bp->b_segment.r_xtop;
	    dfar = r.r_xtop - bp->b_segment.r_xtop;
	    break;
	case BD_BOTTOM:	/* Tile tp is below the boundary */
	    dnear = bp->b_segment.r_ybot - r.r_ytop;
	    dfar = bp->b_segment.r_ybot - r.r_ybot;
	    break;
	case BD_TOP:	/* Tile tp is above the boundary */
	    dnear = r.r_ybot - bp->b_segment.r_ytop;
	    dfar = r.r_ytop - bp->b_segment.r_ytop;
	    break;
    }

    if (dnear < 0) dnear = 0;	/* Don't count underlap */
    mult = ExtCurStyle->exts_overlapMult[ta][0];
    snear = 0.6366 * atan(mult * dnear);
    sfar = 0.6366 * atan(mult * dfar);

    /* "sfar - snear" is the fractional portion of the fringe cap	*/
    /* seen by the substrate in the direction perpendicular to the edge	*/
    /* generating the fringe cap.  This is multiplied by the fraction	*/
    /* of the total edge length to get the total fraction of the entire	*/
    /* fringe capacitance being shielded.				*/

    sov->so_subfrac += (sfar - snear) * ((double)length / (double)sov->so_length);

    /* Do the same calculation but the the overlap multiplier for the	*/
    /* coupling layer, since the fringe capacitance has a different	*/
    /* halo than for the substrate.					*/

    if (ExtCurStyle->exts_overlapMult[ta][tb] != mult)
    {
	mult = ExtCurStyle->exts_overlapMult[ta][tb];
	snear = 0.6366 * atan(mult * dnear);
	sfar = 0.6366 * atan(mult * dfar);
    }
    sov->so_coupfrac += (sfar - snear) * ((double)length / (double)sov->so_length);

    return (0);
}

/* Recursive shielding side overlap check.  If the tile shields	*/
/* then the area of overlap is subtracted from ov->o_area.  If	*/
/* not, then this routine is called recursively on the next	*/
/* shielding plane.						*/

int
extSubtractSideOverlap2(tile, dinfo, sov)
    Tile *tile;
    TileType dinfo;
    struct sideoverlap *sov;
{
    TileType ttype;
    struct sideoverlap sovnew;
    int area, pNum;
    Rect r;

    if (IsSplit(tile))
	ttype = (dinfo & TT_SIDE) ? TiGetRightType(tile) : TiGetLeftType(tile);
    else
	ttype = TiGetTypeExact(tile);

    TITORECT(tile, &r);
    GEOCLIP(&r, &sov->so_clip);
    area = (r.r_xtop - r.r_xbot) * (r.r_ytop - r.r_ybot);
    if (area <= 0) return (0);
    if (IsSplit(tile)) area /= 2;

    /* This tile shields everything below */
    if (TTMaskHasType(&sov->so_tmask, ttype))
    {
	extSubtractSideOverlap(tile, dinfo, sov);
	return (0);
    }

    /* Tile doesn't shield, so search next plane */
    sovnew = *sov;
    sovnew.so_clip = r;
    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
    {
	if (!PlaneMaskHasPlane(sovnew.so_pmask, pNum)) continue;
	sovnew.so_pmask &= ~(PlaneNumToMaskBit(pNum));
	if (sovnew.so_pmask == 0)
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &sovnew.so_clip, &sovnew.so_tmask,
		extSubtractSideOverlap, (ClientData) &sovnew);
	}
	else
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &sovnew.so_clip, &DBAllTypeBits,
		extSubtractSideOverlap2, (ClientData) &sovnew);
	}
	break;
    }
    sov->so_subfrac = sovnew.so_subfrac;
    sov->so_coupfrac = sovnew.so_coupfrac;

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extBasicCouple --
 *
 * Filter function for sidewall coupling capacitance.
 * Called for each tile that might have coupling capacitance
 * to another node because it is near tiles on the same plane,
 * or because its edge overlaps tiles on a different plane.
 *
 * Causes an area search over a halo surrounding each edge of
 * 'tile' for edges to which each edge has coupling capacitance
 * on this plane, and a search for tiles on different planes that
 * this edge overlaps.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	See extAddCouple().
 *
 * ----------------------------------------------------------------------------
 */

int
extBasicCouple(tile, dinfo, ecs)
    Tile *tile;
    TileType dinfo;
    extCapStruct *ecs;
{
    TileType ttype;

    if (IsSplit(tile))
	ttype = (dinfo & TT_SIDE) ? TiGetRightType(tile) : TiGetLeftType(tile);
    else
	ttype = TiGetTypeExact(tile);

    (void) extEnumTilePerim(tile, dinfo, &ExtCurStyle->exts_sideEdges[ttype],
			ecs->plane, extAddCouple, (ClientData) ecs);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extGetBoundaryTypes ---
 *
 * 	Return the tile types of the boundary inside and outside type in the
 *	argument pointers.  The routine takes care of deciding if either side
 *	of the boundary is a split tile, and choosing the correct type based
 *	on the direction of the boundary.
 *
 * Result:
 *	None.
 *
 * Side effects:
 *	The tile types are passed by pointer reference in the arguments.
 *
 * ----------------------------------------------------------------------------
 */

void
extGetBoundaryTypes(Boundary *bp,
    TileType *tin,
    TileType *tout)
{
    TileType loctin, loctout;

    if (IsSplit(bp->b_inside))
    {
	switch (bp->b_direction)
	{
	    case BD_LEFT:
		loctin = TiGetLeftType(bp->b_inside);
		break;
	    case BD_RIGHT:
		loctin = TiGetRightType(bp->b_inside);
		break;
	    case BD_TOP:
		loctin = TiGetTopType(bp->b_inside);
		break;
	    case BD_BOTTOM:
		loctin = TiGetBottomType(bp->b_inside);
		break;
	}
    }
    else
	loctin = TiGetTypeExact(bp->b_inside);

    if (IsSplit(bp->b_outside))
    {
	switch (bp->b_direction)
	{
	    case BD_LEFT:
		loctout = TiGetRightType(bp->b_outside);
		break;
	    case BD_RIGHT:
		loctout = TiGetLeftType(bp->b_outside);
		break;
	    case BD_TOP:
		loctout = TiGetBottomType(bp->b_outside);
		break;
	    case BD_BOTTOM:
		loctout = TiGetTopType(bp->b_outside);
		break;
	}
    }
    else
	loctout = TiGetTypeExact(bp->b_outside);

    *tin = loctin;
    *tout = loctout;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extGetBoundaryTypes2 ---
 *
 *	Similar to extGetBoundaryTypes(), but where the tiles for which we
 *	want to get the tile type are not necessarily the ones in the boundary
 *	record;  the boundary just provides the direction that determines which
 *	side the tile type is on, in the case of a split tile.
 *
 * Result:
 *	None.
 *
 * Side effects:
 *	The tile types are passed by pointer reference in the arguments.
 *
 * ----------------------------------------------------------------------------
 */

void
extGetBoundaryTypes2(int bdir,
    Tile *tbin,
    Tile *tbout,
    TileType *tin,
    TileType *tout)
{
    TileType loctin, loctout;

    if (IsSplit(tbin))
    {
	switch (bdir)
	{
	    case BD_LEFT:
		loctin = TiGetLeftType(tbin);
		break;
	    case BD_RIGHT:
		loctin = TiGetRightType(tbin);
		break;
	    case BD_TOP:
		loctin = TiGetTopType(tbin);
		break;
	    case BD_BOTTOM:
		loctin = TiGetBottomType(tbin);
		break;
	}
    }
    else
	loctin = TiGetTypeExact(tbin);

    if (IsSplit(tbout))
    {
	switch (bdir)
	{
	    case BD_LEFT:
		loctout = TiGetRightType(tbout);
		break;
	    case BD_RIGHT:
		loctout = TiGetLeftType(tbout);
		break;
	    case BD_TOP:
		loctout = TiGetBottomType(tbout);
		break;
	    case BD_BOTTOM:
		loctout = TiGetTopType(tbout);
		break;
	}
    }
    else
	loctout = TiGetTypeExact(tbout);

    *tin = loctin;
    *tout = loctout;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extGetBoundaryRegions ---
 *
 *	Given a boundary direction and two tiles, one on the boundary inside
 *	and one on the boundary outside, find the two node regions associated
 *	with the facing sides of the two tiles.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The tile types are passed by pointer reference in the arguments.
 *
 * ----------------------------------------------------------------------------
 */

void
extGetBoundaryRegions(int bdir,
    Tile *tbin,
    Tile *tbout,
    NodeRegion **rinptr,
    NodeRegion **routptr)
{
    NodeRegion *locrin, *locrout;

    if (IsSplit(tbin))
    {
	switch (bdir)
	{
	    case BD_LEFT:
		locrin = (NodeRegion *)ExtGetRegion(tbin, (TileType)0);
		break;
	    case BD_RIGHT:
		locrin = (NodeRegion *)ExtGetRegion(tbin, (TileType)TT_SIDE);
		break;
	    case BD_TOP:
		locrin = (NodeRegion *)ExtGetRegion(tbin,
				TiGetTypeExact(tbin) & TT_DIRECTION ?
				(TileType)TT_SIDE : (TileType)0);
		break;
	    case BD_BOTTOM:
		locrin = (NodeRegion *)ExtGetRegion(tbin,
				TiGetTypeExact(tbin) & TT_DIRECTION ?
				(TileType)0 : (TileType)TT_SIDE);
		break;
	}
    }
    else
	locrin = (NodeRegion *)ExtGetRegion(tbin, (TileType)0);

    if (IsSplit(tbout))
    {
	switch (bdir)
	{
	    case BD_LEFT:
		locrout = (NodeRegion *)ExtGetRegion(tbout, (TileType)TT_SIDE);
		break;
	    case BD_RIGHT:
		locrout = (NodeRegion *)ExtGetRegion(tbout, (TileType)0);
		break;
	    case BD_TOP:
		locrout = (NodeRegion *)ExtGetRegion(tbout,
				TiGetTypeExact(tbin) & TT_DIRECTION ?
				(TileType)0 : (TileType)TT_SIDE);
		break;
	    case BD_BOTTOM:
		locrout = (NodeRegion *)ExtGetRegion(tbout,
				TiGetTypeExact(tbin) & TT_DIRECTION ?
				(TileType)TT_SIDE : (TileType)0);
		break;
	}
    }
    else
	locrout = (NodeRegion *)ExtGetRegion(tbout, (TileType)0);

    *rinptr = locrin;
    *routptr = locrout;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extAddCouple --
 *
 * Called for each segment along the boundary of the tile bp->b_inside
 * that might have coupling capacitance with its neighbors.
 * Causes an area search over a halo surrounding the boundary bp->b_segment
 * on the side outside bp->b_inside for edges to which this one has coupling
 * capacitance on this plane, and for tiles overlapping this edge on different
 * planes.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	For each edge (tnear, tfar) we find that has coupling capacitance
 *	to us, update the HashEntry with key node(bp->b_inside), node(tfar)
 *	by adding the sidewall capacitance if node(bp->b_inside) and node(tfar)
 *	are different.  If node(bp->b_inside) and node(tfar) are the same, we
 *	do nothing.
 *
 *	For each tile tp we find on a different plane that overlaps this
 *	edge, update the HashEntry with key node(bp->b_inside), node(tp)
 *	by adding the sidewall overlap capacitance.  If node(bp->b_inside)
 *	and node(tp) are the same, do nothing.
 *
 * ----------------------------------------------------------------------------
 */

int
extAddCouple(bp, ecs)
    Boundary *bp;	/* Boundary being considered */
    extCapStruct *ecs;
{
    TileType tin, tout;
    int pNum;
    PlaneMask pMask;
    Boundary bpCopy;
    Rect r, ovr;
    extSidewallStruct esws;
    int distFringe;

    /* Non-Manhattan boundaries are not handled.  This is
     * future work to be done.
     */
    switch (bp->b_direction)
    {
	case BD_NW:
	case BD_NE:
	case BD_SW:
	case BD_SE:
	    return 0;
    }

    /* Get the types on the inside and outside of the boundary */
    extGetBoundaryTypes(bp, &tin, &tout);

    /* Check here for a zero exts_sideCoupleOtherEdges mask.
     * that handles cases such as FET types not being declared in
     * defaultperimeter, as the edge between poly and FET will be
     * seen as a boundary.  The lack of any area coupling should
     * then prevent it from being checked for fringe cap.
     */

    if (TTMaskIsZero(&ExtCurStyle->exts_sideCoupleOtherEdges[tin][tout]))
	return 0;

    /* Revert any edge contacts to their residues */
    if (DBIsContact(tin))
	tin = DBPlaneToResidue(tin, ecs->plane);
    if (DBIsContact(tout))
	tout = DBPlaneToResidue(tout, ecs->plane);

    esws.extCoupleList = ExtCurStyle->exts_sideCoupleCap[tin][tout];
    esws.extOverlapList = ExtCurStyle->exts_sideOverlapCap[tin][tout];
    if ((esws.extCoupleList == NULL) && (esws.extOverlapList == NULL))
	return (0);

    esws.def = ecs->def;

    /*
     * Clip the edge of interest to the area where we're searching
     * for coupling capacitance, if such an area has been specified.
     */
    if (extCoupleSearchArea)
    {
	bpCopy = *bp;
	bp = &bpCopy;

	if (!GEO_OVERLAP(&bp->b_segment, extCoupleSearchArea))
	    return 0;

	GEOCLIP(&bp->b_segment, extCoupleSearchArea);
    }
    r = ovr = bp->b_segment;

    /* If considering fringe capacitance to be distributed over	*/
    /* a halo surrounding the edge of a shape, then set the	*/
    /* fringe distance to the halo value.  Otherwise, the	*/
    /* fringe cap is (unrealistically) assumed to couple only 	*/
    /* to shapes that are directly below the edge.		*/

    esws.fringe_halo = (ExtOptions & EXT_DOFRINGEHALO) ? 
		((ExtCurStyle->exts_sideCoupleHalo == 0) ? FALSE : TRUE)
		: FALSE;

    distFringe = (ExtOptions & EXT_DOFRINGEHALO) ?
		ExtCurStyle->exts_sideCoupleHalo : 1;

    if (distFringe == 0) distFringe = 1;

    esws.bp = bp;
    esws.plane_of_boundary = ecs->plane;
    esws.area = &ovr;

    switch (bp->b_direction)
    {
	case BD_LEFT:	/* Along left */
	    r.r_xbot -= ExtCurStyle->exts_sideCoupleHalo;
	    ovr.r_xbot -= distFringe;
	    extWalkLeft(&r,
			&ExtCurStyle->exts_sideCoupleOtherEdges[tin][tout],
			extSideLeft, bp, &esws);
	    break;
	case BD_RIGHT:	/* Along right */
	    r.r_xtop += ExtCurStyle->exts_sideCoupleHalo;
	    ovr.r_xtop += distFringe;
	    extWalkRight(&r,
			&ExtCurStyle->exts_sideCoupleOtherEdges[tin][tout],
			extSideRight, bp, &esws);
	    break;
	case BD_TOP:	/* Along top */
	    r.r_ytop += ExtCurStyle->exts_sideCoupleHalo;
	    ovr.r_ytop += distFringe;
	    extWalkTop(&r,
			&ExtCurStyle->exts_sideCoupleOtherEdges[tin][tout],
			extSideTop, bp, &esws);
	    break;
	case BD_BOTTOM:	/* Along bottom */
	    r.r_ybot -= ExtCurStyle->exts_sideCoupleHalo;
	    ovr.r_ybot -= distFringe;
	    extWalkBottom(&r,
			&ExtCurStyle->exts_sideCoupleOtherEdges[tin][tout],
			extSideBottom, bp, &esws);
	    break;
    }

    /* If corner extraction is selected ("extract do corners"), check	*/
    /* the endpoints of horizontal boundaries for convex and concave	*/
    /* corners of the node region, and add (convex) or subtract	*/
    /* (concave) the corner fringe capacitance.  Only horizontal	*/
    /* boundaries are checked;  since every corner joins exactly one	*/
    /* horizontal and one vertical edge, this considers each corner	*/
    /* exactly once.  The corner model only applies to the distributed	*/
    /* (halo) fringe model.						*/

    if ((ExtOptions & EXT_DOCORNERS) && esws.fringe_halo
		&& (esws.extOverlapList != NULL))
	if ((bp->b_direction == BD_TOP) || (bp->b_direction == BD_BOTTOM))
	    extAddCornerCouple(&esws, tin, tout);

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extRemoveSubcap --
 *
 * A nearby shape blocks parasitic fringe capacitance from a layer boundary
 * to substrate, and so all parasitic fringe capacitance from the layer's
 * region to substrate that is blocked must be removed from the total
 * substrate capacitance for that region.
 *
 * ----------------------------------------------------------------------------
 */

void
extRemoveSubcap(bp, clip, esws)
    Boundary *bp;		/* Boundary with fringe capacitance */
    Rect *clip;			/* Area not being blocked */
    extSidewallStruct *esws;	/* Overlapping edge and plane information */
{
    int dnear, length;
    double snear, cfrac;
    NodeRegion *rbp;
    TileType ta, tb;
    float mult;
    CapValue subcap;

    if (!esws->fringe_halo) return;

    /* Get the types on the inside and outside of the boundary */
    extGetBoundaryTypes(bp, &ta, &tb);

    rbp = (NodeRegion *)ExtGetRegion(bp->b_inside, (TileType)0);

    if (bp->b_segment.r_xtop == bp->b_segment.r_xbot)
	length = bp->b_segment.r_ytop - bp->b_segment.r_ybot;
    else
	length = bp->b_segment.r_xtop - bp->b_segment.r_xbot;

    switch (bp->b_direction)
    {
	case BD_LEFT:	/* Tile tp is to the left of the boundary */
	    dnear = bp->b_segment.r_xbot - clip->r_xbot;
	    break;
	case BD_RIGHT:	/* Tile tp is to the right of the boundary */
	    dnear = clip->r_xtop - bp->b_segment.r_xtop;
	    break;
	case BD_BOTTOM:	/* Tile tp is below the boundary */
	    dnear = bp->b_segment.r_ybot - clip->r_ybot;
	    break;
	case BD_TOP:	/* Tile tp is above the boundary */
	    dnear = clip->r_ytop - bp->b_segment.r_ytop;
	    break;
    }

    if (dnear < 0) dnear = 0;	/* Don't count underlap */
    mult = ExtCurStyle->exts_overlapMult[ta][0];
    snear = 0.6366 * atan((double)mult * dnear);

    /* "snear" is the fractional portion of the fringe cap seen by	*/
    /* the substrate, so (1.0 - snear) is the part that is blocked.	*/

    subcap = ExtCurStyle->exts_perimCap[ta][tb] * (1.0 - snear) * length;
    rbp->nreg_cap -= subcap;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extFindOverlap --
 *
 * Callback function for extWalkTop/Bottom/Right/Left to find the
 * side overlap (fringe) capacitance from a material edge (held in
 * esws->bp) and all layers in planes below to which its fringe
 * capacitance may couple.  The area to search will have been reduced
 * by any nearby layers on the same plane that shield the fringe
 * capacitance.
 *
 * Results:
 *	Returns 0 to keep extWalk*() going.
 *
 * Side effects:
 *	See side effects of the called function extSideOverlap()
 *
 * ----------------------------------------------------------------------------
 */


int
extFindOverlap(tp, area, esws)
    Tile *tp;			/* Overlapped tile */
    Rect *area;			/* Area to check for coupling */
    extSidewallStruct *esws;	/* Overlapping edge and plane information */
{
    PlaneMask pMask;
    int pNum;
    Rect *rsave;
    Boundary *bp = esws->bp;
    TileType tin = TiGetType(bp->b_inside);
    TileType tout = TiGetType(bp->b_outside);

    /* Get residues
     * (Note:  Isn't it better to include contacts in the tables?)
     */
    if (DBIsContact(tin))
	tin = DBPlaneToResidue(tin, esws->plane_of_boundary);
    if (DBIsContact(tout))
	tout = DBPlaneToResidue(tout, esws->plane_of_boundary);

    pMask = ExtCurStyle->exts_sideOverlapOtherPlanes[tin][tout];
    extOverlapDef = esws->def;

    /* Replace esws->area with area */
    rsave = esws->area;
    esws->area = area;

    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	if (PlaneMaskHasPlane(pMask, pNum))
	{
	    esws->plane_checked = pNum;
	    (void) DBSrPaintArea((Tile *) NULL, esws->def->cd_planes[pNum],
			area, &ExtCurStyle->exts_sideOverlapOtherTypes[tin][tout],
			(esws->fringe_halo) ? extSideOverlapHalo : extSideOverlap,
			(ClientData)esws);
	}
    esws->area = rsave;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideOverlapHalo --
 *
 * The boundary 'bp' has been found to overlap the tile 'tp', which it
 * has coupling capacitance to.
 *
 * Every tile that couples to an edge is also shielding the substrate
 * from that edge.  To maintain the proper accounting of the amount of
 * substrate shielded, calculate only for the area of the substrate that
 * couples to the *unshielded* portion of the tile.  The part of the
 * substrate that is under a shielded portion of the tile will be handled
 * later when calculating coupling to that shielding tile.
 * 
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Update the coupling capacitance between node(bp->t_inside) and
 *	node(tp) if the two nodes are different.  Does so by updating
 *	the value stored in the HashEntry keyed by the two nodes.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideOverlapHalo(tp, dinfo, esws)
    Tile *tp;			/* Overlapped tile */
    TileType dinfo;		/* Split tile information */
    extSidewallStruct *esws;	/* Overlapping edge and plane information */
{
    Boundary *bp = esws->bp;	/* Overlapping edge */
    NodeRegion *rtp = (NodeRegion *) ExtGetRegion(tp, dinfo);
    NodeRegion *rbp = (NodeRegion *) ExtGetRegion(bp->b_inside, (TileType)0);
    TileType ta, tb;
    Rect tpr;
    struct sideoverlap sov;
    HashEntry *he;
    EdgeCap *e;
    int length;
    double cfrac, sfrac, afrac, mult, efflength;
    CapValue cap, subcap;
    CoupleKey ck;
    int dfar, dnear;
    double sfar, snear, subfrac;

    /* Nothing to do for space tiles, so just return. */
    /* (TO DO:  Make sure TT_SPACE is removed from all exts_sideOverlapOtherTypes */
    if (IsSplit(tp))
	tb = (dinfo & TT_SIDE) ? TiGetRightType(tp) : TiGetLeftType(tp);
    else
	tb = TiGetTypeExact(tp);
    if (tb == TT_SPACE) return (0);

    /* Get the area of the coupling tile, and clip to the fringe area	*/
    /* of the tile edge generating the fringe capacitance.		*/
    TITORECT(tp, &sov.so_clip);
    GEOCLIP(&sov.so_clip, esws->area);
 
    /* Calculate the length of the clipped area */

    if (bp->b_segment.r_xtop == bp->b_segment.r_xbot)
	length = sov.so_clip.r_ytop - sov.so_clip.r_ybot;
    else
	length = sov.so_clip.r_xtop - sov.so_clip.r_xbot;

    /* ta is the tile type of the edge generating the fringe cap. */
    ta = TiGetType(bp->b_inside);

    /* Revert any contacts to their residues */
    if (DBIsContact(ta))
	ta = DBPlaneToResidue(ta, esws->plane_of_boundary);
    if (DBIsContact(tb))
	tb = DBPlaneToResidue(tb, esws->plane_checked);

    /* Find the fraction of the fringe cap seen by tile tp (depends	*/
    /* on the tile width and distance from the boundary)		*/
    switch (bp->b_direction)
    {
	case BD_LEFT:	/* Tile tp is to the left of the boundary */
	    dfar = bp->b_segment.r_ll.p_x - sov.so_clip.r_xbot;
	    dnear = bp->b_segment.r_ll.p_x - sov.so_clip.r_xtop;
	    break;
	case BD_RIGHT:	/* Tile tp is to the right of the boundary */
	    dfar = sov.so_clip.r_xtop - bp->b_segment.r_ur.p_x;
	    dnear = sov.so_clip.r_xbot - bp->b_segment.r_ur.p_x;
	    break;
	case BD_BOTTOM:	/* Tile tp is below the boundary */
	    dfar = bp->b_segment.r_ll.p_y - sov.so_clip.r_ybot;
	    dnear = bp->b_segment.r_ll.p_y - sov.so_clip.r_ytop;
	    break;
	case BD_TOP:	/* Tile tp is above the boundary */
	    dfar = sov.so_clip.r_ytop - bp->b_segment.r_ur.p_y;
	    dnear = sov.so_clip.r_ybot - bp->b_segment.r_ur.p_y;
	    break;
    }
    if (dnear < 0) dnear = 0;	/* Don't count underlap */
    mult = ExtCurStyle->exts_overlapMult[ta][tb];
    sfar = 0.6366 * atan(mult * dfar);
    snear = 0.6366 * atan(mult * dnear);

    /* "cfrac" is the fractional portion of the fringe cap seen	*/
    /* by tile tp along its length.  This is independent of the	*/
    /* portion of the boundary length that tile tp occupies.	*/
	
    cfrac = sfar - snear;

    /* The fringe portion extracted from the substrate will be	*/
    /* different than the portion added to the coupling layer.	*/

    if (ExtCurStyle->exts_overlapMult[ta][0] != mult)
    {
	mult = ExtCurStyle->exts_overlapMult[ta][0];
	sfar = 0.6366 * atan(mult * dfar);
	snear = 0.6366 * atan(mult * dnear);
    }
    sfrac = sfar - snear;

    /* Apply each rule, incorporating shielding into the edge length. */
    cap = subcap = (CapValue) 0;
    subfrac = 0.0;
    for (e = esws->extOverlapList; e; e = e->ec_next)
    {
	/* Only apply rules for the plane in which they are declared */
	if (!PlaneMaskHasPlane(e->ec_pmask, esws->plane_checked)) continue;

	/* Does this rule "e" include the tile we found? */
	if (TTMaskHasType(&e->ec_near, tb))
	{
	    /* We have a possible capacitor, but are the tiles shielded from
	     * each other part of the way?
	     */
	    int pNum;
	    sov.so_pmask = ExtCurStyle->exts_sideOverlapShieldPlanes[ta][tb];
	    sov.so_esws = esws;
	    sov.so_coupfrac = (double)0.0;
	    sov.so_subfrac = (double)0.0;
	    sov.so_length = length;
	    sov.so_ctype = tb;

	    if (sov.so_pmask)
	    {
		sov.so_tmask = e->ec_far;  /* Actually shieldtypes. */
		for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
		{
		    /* Each call to DBSrPaintArea has an opportunity to
		     * subtract a partial capacitance from the total.
		     */
		    if (!PlaneMaskHasPlane(sov.so_pmask, pNum)) continue;
		    sov.so_pmask &= ~(PlaneNumToMaskBit(pNum));
		    if (sov.so_pmask == 0)
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &sov.so_clip,
			    &sov.so_tmask, extSubtractSideOverlap, (ClientData) &sov);
		    }
		    else
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &sov.so_clip,
			    &DBAllTypeBits,
			    extSubtractSideOverlap2, (ClientData) &sov);
		    }
		    break;
		}
	    }
	    if (rtp != rbp)
	    {
		efflength = (cfrac - sov.so_coupfrac) * (double)length;
		cap += e->ec_cap * efflength;

		subfrac += sov.so_subfrac;	/* Just add the shielded fraction */
	    }
	}
    }

    /* Add in the new capacitance. */

    if (tb != TT_SPACE)
    {
	int oa = ExtCurStyle->exts_planeOrder[esws->plane_of_boundary];
	int ob = ExtCurStyle->exts_planeOrder[esws->plane_checked];
	if (oa > ob)
	{
	    /* If the overlapped tile is between the substrate and the boundary
	     * tile, then we subtract the fringe substrate capacitance
	     * from rbp's region due to the area of the sideoverlap, since
	     * we now know it is shielded from the substrate.
	     */
	    TileType outtype = TiGetType(bp->b_outside);

	    /* Decompose contacts into their residues */
	    if (DBIsContact(ta))
		ta = DBPlaneToResidue(ta, esws->plane_of_boundary);
	    if (DBIsContact(outtype))
		outtype = DBPlaneToResidue(outtype, esws->plane_of_boundary);

	    efflength = (sfrac - subfrac) * (double)length;
	    subcap = ExtCurStyle->exts_perimCap[ta][0] * efflength;
	    rbp->nreg_cap -= subcap;
	    /* Ignore residual error at ~zero zeptoFarads.  Probably	*/
	    /* there should be better handling of round-off here.	*/
	    if ((rbp->nreg_cap > -0.001) && (rbp->nreg_cap < 0.001))
		rbp->nreg_cap = 0;
	    if (CAP_DEBUG)
	    	extNregAdjustCap(rbp, -subcap, "obsolete_perimcap");
    	}
	else if (CAP_DEBUG)
	    extNregAdjustCap(rbp, 0.0, "obsolete_perimcap (skipped, wrong direction)");

	/* If the nodes are electrically connected, then we don't add	*/
	/* any side overlap capacitance to the node.			*/
	if (rtp == rbp) return 0;
    	if (rtp == (NodeRegion *)CLIENTDEFAULT) return 0;
    	if (rbp == (NodeRegion *)CLIENTDEFAULT) return 0;

	if (rtp < rbp)
	{
	    ck.ck_1 = rtp;
	    ck.ck_2 = rbp;
	}
	else
	{
	    ck.ck_1 = rbp;
	    ck.ck_2 = rtp;
	}
	he = HashFind(extCoupleHashPtr, (char *) &ck);
	if (CAP_DEBUG) extAdjustCouple(he, cap, "sideoverlap");
	extSetCapValue(he, cap + extGetCapValue(he));
    }
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideOverlap --
 *
 * The boundary 'bp' has been found to overlap the tile 'tp', which it
 * has coupling capacitance to.  This is legacy behavior when no fringe
 * halo is considered, but the fringe is modeled as being directed
 * downward from an edge onto any layer directly below the edge, and
 * occupying no area.  This approximation is generally only reasonable for
 * processes down to 0.5um or so.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Update the coupling capacitance between node(bp->t_inside) and
 *	node(tp) if the two nodes are different.  Does so by updating
 *	the value stored in the HashEntry keyed by the two nodes.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideOverlap(tp, dinfo, esws)
    Tile *tp;			/* Overlapped tile */
    TileType dinfo;		/* Split tile information */
    extSidewallStruct *esws;	/* Overlapping edge and plane information */
{
    Boundary *bp = esws->bp;	/* Overlapping edge */
    NodeRegion *rtp = (NodeRegion *) ExtGetRegion(tp, dinfo);
    NodeRegion *rbp = (NodeRegion *) ExtGetRegion(bp->b_inside, (TileType)0);
    TileType ta, tb;
    Rect tpr;
    struct overlap ov;
    HashEntry *he;
    EdgeCap *e;
    int length, areaAccountedFor, areaTotal;
    double afrac;
    CapValue cap;
    CoupleKey ck;

    /* Nothing to do for space tiles, so just return. */
    /* (TO DO:  Make sure TT_SPACE is removed from all exts_sideOverlapOtherTypes */
    if (IsSplit(tp))
	tb = (dinfo & TT_SIDE) ? TiGetRightType(tp) : TiGetLeftType(tp);
    else
	tb = TiGetTypeExact(tp);
    if (tb == TT_SPACE) return (0);

    if (bp->b_segment.r_xtop == bp->b_segment.r_xbot)
    {
	length = MIN(bp->b_segment.r_ytop, TOP(tp))
	       - MAX(bp->b_segment.r_ybot, BOTTOM(tp));
    }
    else
    {
	length = MIN(bp->b_segment.r_xtop, RIGHT(tp))
	       - MAX(bp->b_segment.r_xbot, LEFT(tp));
    }

    TITORECT(tp, &ov.o_clip);
    GEOCLIP(&ov.o_clip, esws->area);
    areaTotal = GEO_WIDTH(&ov.o_clip) * GEO_HEIGHT(&ov.o_clip);
    areaAccountedFor = 0;
 
    ta = TiGetType(bp->b_inside);

    /* Revert any contacts to their residues */
    if (DBIsContact(ta))
	ta = DBPlaneToResidue(ta, esws->plane_of_boundary);
    if (DBIsContact(tb))
	tb = DBPlaneToResidue(tb, esws->plane_checked);

    /* Apply each rule, incorporating shielding into the edge length. */
    cap = (CapValue) 0;
    for (e = esws->extOverlapList; e; e = e->ec_next)
    {
	/* Only apply rules for the plane in which they are declared */
	if (!PlaneMaskHasPlane(e->ec_pmask, esws->plane_checked)) continue;

	/* Does this rule "e" include the tile we found? */
	if (TTMaskHasType(&e->ec_near, tb))
	{
	    /* We have a possible capacitor, but are the tiles shielded from
	     * each other part of the way?
	     */
	    int pNum;
	    ov.o_area = areaTotal;
	    ov.o_pmask = ExtCurStyle->exts_sideOverlapShieldPlanes[ta][tb];
	    if (ov.o_pmask)
	    {
		ov.o_tmask = e->ec_far;  /* Actually shieldtypes. */
		for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
		{
		    /* Each call to DBSrPaintArea has an opportunity to
		     * subtract from the area (really length 'cause width=1).
		     */
		    if (!PlaneMaskHasPlane(ov.o_pmask, pNum)) continue;
		    ov.o_pmask &= ~(PlaneNumToMaskBit(pNum));
		    if (ov.o_pmask == 0)
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &ov.o_clip,
			    &ov.o_tmask, extSubtractOverlap, (ClientData) &ov);
		    }
		    else
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &ov.o_clip,
			    &DBAllTypeBits,
			    extSubtractOverlap2, (ClientData) &ov);
		    }
		    break;
		}
	    }
	    if (rtp != rbp)
		cap += e->ec_cap * ov.o_area;

	    areaAccountedFor += ov.o_area;
	}
    }

    /* Add in the new capacitance. */

    if (tb != TT_SPACE)
    {
	int oa = ExtCurStyle->exts_planeOrder[esws->plane_of_boundary];
	int ob = ExtCurStyle->exts_planeOrder[esws->plane_checked];
	if (oa > ob)
	{
	    /* If the overlapped tile is between the substrate and the boundary
	     * tile, then we subtract the fringe substrate capacitance
	     * from rbp's region due to the area of the sideoverlap, since
	     * we now know it is shielded from the substrate.
	     */
	    CapValue subcap;
	    TileType outtype = TiGetType(bp->b_outside);

	    /* Decompose contacts into their residues */
	    if (DBIsContact(ta))
		ta = DBPlaneToResidue(ta, esws->plane_of_boundary);
	    if (DBIsContact(outtype))
		outtype = DBPlaneToResidue(outtype, esws->plane_of_boundary);

	    afrac = (double)areaAccountedFor / (double)areaTotal;
	    subcap = (ExtCurStyle->exts_perimCap[ta][outtype] *
			MIN(areaAccountedFor, length));
	    rbp->nreg_cap -= subcap;
	    /* Ignore residual error at ~zero zeptoFarads.  Probably	*/
	    /* there should be better handling of round-off here.	*/
	    if ((rbp->nreg_cap > -0.001) && (rbp->nreg_cap < 0.001))
		rbp->nreg_cap = 0;
	    if (CAP_DEBUG)
	    	extNregAdjustCap(rbp, -subcap, "obsolete_perimcap");
    	}
	else if (CAP_DEBUG)
	    extNregAdjustCap(rbp, 0.0, "obsolete_perimcap (skipped, wrong direction)");

	/* If the nodes are electrically connected, then we don't add	*/
	/* any side overlap capacitance to the node.			*/
	if (rtp == rbp) return 0;
    	if (rtp == (NodeRegion *)CLIENTDEFAULT) return 0;
    	if (rbp == (NodeRegion *)CLIENTDEFAULT) return 0;

	if (rtp < rbp)
	{
	    ck.ck_1 = rtp;
	    ck.ck_2 = rbp;
	}
	else
	{
	    ck.ck_1 = rbp;
	    ck.ck_2 = rtp;
	}
	he = HashFind(extCoupleHashPtr, (char *) &ck);
	if (CAP_DEBUG) extAdjustCouple(he, cap, "sideoverlap");
	extSetCapValue(he, cap + extGetCapValue(he));
    }
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extCornerFrac --
 *
 * Compute the fraction of the fringe capacitance field from a corner of
 * a node region that is intercepted by the rectangle 'r', which must lie
 * within the quadrant 'quadrant' (GEO_NORTHEAST, etc.) diagonal to the
 * corner point 'corner'.  Following the superposition model, the corner
 * field is the product of the fields of the two edges meeting at the
 * corner, each modeled as an arctangent with halo multiplier 'mult':
 *
 *	Fx * Fy, where Fx = 0.6366 * (atan(mult * dxfar) - atan(mult * dxnear))
 *
 * and dxnear, dxfar are the distances of the near and far sides of 'r'
 * from the vertical edge at the corner (likewise Fy for the horizontal
 * edge).  The product approaches 1 for a rectangle filling the entire
 * quadrant.
 *
 * Results:
 *	The fraction (0.0 to 1.0) of the corner fringe cap seen by 'r'.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

double
extCornerFrac(
    Rect *r,		/* Area intercepting the corner fringe field */
    Point *corner,	/* Position of the corner */
    int quadrant,	/* Quadrant of the field relative to the corner */
    double mult)	/* Fringe halo multiplier */
{
    int dxnear, dxfar, dynear, dyfar;
    double fx, fy;

    switch (quadrant)
    {
	case GEO_NORTHEAST:
	case GEO_SOUTHEAST:	/* r is to the right of the corner */
	    dxnear = r->r_xbot - corner->p_x;
	    dxfar = r->r_xtop - corner->p_x;
	    break;
	default:		/* r is to the left of the corner */
	    dxnear = corner->p_x - r->r_xtop;
	    dxfar = corner->p_x - r->r_xbot;
	    break;
    }
    switch (quadrant)
    {
	case GEO_NORTHEAST:
	case GEO_NORTHWEST:	/* r is above the corner */
	    dynear = r->r_ybot - corner->p_y;
	    dyfar = r->r_ytop - corner->p_y;
	    break;
	default:		/* r is below the corner */
	    dynear = corner->p_y - r->r_ytop;
	    dyfar = corner->p_y - r->r_ybot;
	    break;
    }
    if (dxnear < 0) dxnear = 0;		/* Don't count underlap */
    if (dynear < 0) dynear = 0;

    fx = 0.6366 * (atan(mult * dxfar) - atan(mult * dxnear));
    fy = 0.6366 * (atan(mult * dyfar) - atan(mult * dynear));
    return fx * fy;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSubtractCornerOverlap --
 *
 * Corner version of extSubtractSideOverlap.  The fraction of the corner
 * fringe capacitance shielded by this tile's area is added to the running
 * totals cov->co_coupfrac (using the halo multiplier of the coupling
 * layer) and cov->co_subfrac (using the halo multiplier of the substrate),
 * so that it can be subtracted from the unshielded corner fraction.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Updates cov->co_coupfrac and cov->co_subfrac.
 *
 * ----------------------------------------------------------------------------
 */

int
extSubtractCornerOverlap(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    struct corneroverlap *cov)
{
    extCornerStruct *ecos = cov->co_ecos;
    Boundary *bp = ecos->ec_esws->bp;
    Rect r;
    TileType ta, tb;
    double mult;

    TITORECT(tile, &r);
    GEOCLIP(&r, &cov->co_clip);
    if ((r.r_xtop <= r.r_xbot) || (r.r_ytop <= r.r_ybot)) return 0;

    ta = TiGetType(bp->b_inside);
    tb = cov->co_ctype;

    mult = (double)ExtCurStyle->exts_overlapMult[ta][0];
    if (mult > 0)
	cov->co_subfrac += extCornerFrac(&r, &ecos->ec_corner,
			ecos->ec_quadrant, mult);

    /* Do the same calculation with the overlap multiplier for the	*/
    /* coupling layer, since the fringe capacitance has a different	*/
    /* halo than for the substrate.					*/

    mult = (double)ExtCurStyle->exts_overlapMult[ta][tb];
    if (mult > 0)
	cov->co_coupfrac += extCornerFrac(&r, &ecos->ec_corner,
			ecos->ec_quadrant, mult);

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSubtractCornerOverlap2 --
 *
 * Recursive shielding corner overlap check;  the corner version of
 * extSubtractSideOverlap2.  If the tile shields, then the shielded
 * fraction is accumulated.  If not, then this routine is called
 * recursively on the next shielding plane.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Updates cov->co_coupfrac and cov->co_subfrac.
 *
 * ----------------------------------------------------------------------------
 */

int
extSubtractCornerOverlap2(
    Tile *tile,
    TileType dinfo,
    struct corneroverlap *cov)
{
    TileType ttype;
    struct corneroverlap covnew;
    int pNum;
    Rect r;

    if (IsSplit(tile))
	ttype = (dinfo & TT_SIDE) ? TiGetRightType(tile) : TiGetLeftType(tile);
    else
	ttype = TiGetTypeExact(tile);

    TITORECT(tile, &r);
    GEOCLIP(&r, &cov->co_clip);
    if ((r.r_xtop <= r.r_xbot) || (r.r_ytop <= r.r_ybot)) return 0;

    /* This tile shields everything below */
    if (TTMaskHasType(&cov->co_tmask, ttype))
    {
	extSubtractCornerOverlap(tile, dinfo, cov);
	return 0;
    }

    /* Tile doesn't shield, so search next plane */
    covnew = *cov;
    covnew.co_clip = r;
    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
    {
	if (!PlaneMaskHasPlane(covnew.co_pmask, pNum)) continue;
	covnew.co_pmask &= ~(PlaneNumToMaskBit(pNum));
	if (covnew.co_pmask == 0)
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &covnew.co_clip, &covnew.co_tmask,
		extSubtractCornerOverlap, (ClientData) &covnew);
	}
	else
	{
	    (void) DBSrPaintArea((Tile *) NULL,
		extOverlapDef->cd_planes[pNum], &covnew.co_clip, &DBAllTypeBits,
		extSubtractCornerOverlap2, (ClientData) &covnew);
	}
	break;
    }
    cov->co_coupfrac = covnew.co_coupfrac;
    cov->co_subfrac = covnew.co_subfrac;

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extCornerOverlap --
 *
 * A corner of the node region of tile esws->bp->b_inside (described by
 * 'ecos') has fringe capacitance into the quadrant ecos->ec_area, where
 * the tile 'tp' has been found on another plane.  Compute the fraction
 * of the corner fringe capacitance coupling to 'tp' using the
 * superposition product model (see extCornerFrac), reduced by any
 * shielding layers, and update the coupling capacitance hash table.
 * The total capacitance of an unobstructed corner is modeled as that of
 * a straight edge of length (ExtCornerFactor / mult), where "mult" is
 * the fringe halo multiplier of the coupling layers.
 *
 * For a concave corner (ecos->ec_sign < 0), the fringe capacitance in
 * the quadrant has been counted twice, once by each of the two edges
 * meeting at the corner, so the corner term is subtracted instead of
 * added.
 *
 * The corner fringe capacitance to the substrate, which was added
 * (convex) or subtracted (concave) along with the perimeter capacitance
 * in extNodeCornerCap() (see ExtBasic.c), is likewise adjusted here for
 * the portion blocked by the coupling tile when the tile lies between
 * the corner and the substrate.
 *
 * Results:
 *	Returns 0 to keep DBSrPaintArea() going.
 *
 * Side effects:
 *	Updates the coupling capacitance between node(bp->b_inside) and
 *	node(tp) if the two nodes are different.  May update the
 *	substrate capacitance of node(bp->b_inside).
 *
 * ----------------------------------------------------------------------------
 */

int
extCornerOverlap(
    Tile *tp,			/* Coupling tile in the corner quadrant */
    TileType dinfo,		/* Split tile information */
    extCornerStruct *ecos)	/* Corner and boundary information */
{
    extSidewallStruct *esws = ecos->ec_esws;
    Boundary *bp = esws->bp;	/* Boundary owning the corner */
    NodeRegion *rtp = (NodeRegion *) ExtGetRegion(tp, dinfo);
    NodeRegion *rbp = (NodeRegion *) ExtGetRegion(bp->b_inside, (TileType)0);
    TileType ta, tb;
    struct corneroverlap cov;
    HashEntry *he;
    EdgeCap *e;
    int pNum;
    double mult, cfrac, subfrac;
    CapValue cap;
    CoupleKey ck;

    /* Nothing to do for space tiles, so just return. */
    if (IsSplit(tp))
	tb = (dinfo & TT_SIDE) ? TiGetRightType(tp) : TiGetLeftType(tp);
    else
	tb = TiGetTypeExact(tp);
    if (tb == TT_SPACE) return 0;

    /* Get the area of the coupling tile, clipped to the corner quadrant */
    TITORECT(tp, &cov.co_clip);
    GEOCLIP(&cov.co_clip, &ecos->ec_area);
    if ((cov.co_clip.r_xtop <= cov.co_clip.r_xbot) ||
		(cov.co_clip.r_ytop <= cov.co_clip.r_ybot))
	return 0;

    /* ta is the tile type of the region corner generating the fringe cap. */
    ta = TiGetType(bp->b_inside);

    /* Revert any contacts to their residues */
    if (DBIsContact(ta))
	ta = DBPlaneToResidue(ta, esws->plane_of_boundary);
    if (DBIsContact(tb))
	tb = DBPlaneToResidue(tb, esws->plane_checked);

    /* Apply each rule, incorporating shielding into the fraction. */
    cap = (CapValue) 0;
    subfrac = (double)0.0;
    for (e = esws->extOverlapList; e; e = e->ec_next)
    {
	/* Only apply rules for the plane in which they are declared */
	if (!PlaneMaskHasPlane(e->ec_pmask, esws->plane_checked)) continue;

	/* Does this rule "e" include the tile we found? */
	if (TTMaskHasType(&e->ec_near, tb))
	{
	    mult = (double)ExtCurStyle->exts_overlapMult[ta][tb];
	    if (mult <= 0) continue;

	    cfrac = extCornerFrac(&cov.co_clip, &ecos->ec_corner,
			ecos->ec_quadrant, mult);

	    /* We have a possible capacitor, but are the tiles shielded
	     * from each other part of the way?
	     */
	    cov.co_ecos = ecos;
	    cov.co_ctype = tb;
	    cov.co_coupfrac = (double)0.0;
	    cov.co_subfrac = (double)0.0;
	    cov.co_pmask = ExtCurStyle->exts_sideOverlapShieldPlanes[ta][tb];
	    if (cov.co_pmask)
	    {
		cov.co_tmask = e->ec_far;  /* Actually shieldtypes. */
		for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
		{
		    /* Each call to DBSrPaintArea has an opportunity to
		     * subtract a partial capacitance from the total.
		     */
		    if (!PlaneMaskHasPlane(cov.co_pmask, pNum)) continue;
		    cov.co_pmask &= ~(PlaneNumToMaskBit(pNum));
		    if (cov.co_pmask == 0)
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &cov.co_clip,
			    &cov.co_tmask, extSubtractCornerOverlap,
			    (ClientData) &cov);
		    }
		    else
		    {
			(void) DBSrPaintArea((Tile *) NULL,
			    extOverlapDef->cd_planes[pNum], &cov.co_clip,
			    &DBAllTypeBits,
			    extSubtractCornerOverlap2, (ClientData) &cov);
		    }
		    break;
		}
	    }
	    cfrac -= cov.co_coupfrac;
	    if (cfrac < 0) cfrac = 0;

	    if (rtp != rbp)
	    {
		cap += e->ec_cap * (ExtCornerFactor / mult) * cfrac;
		subfrac += cov.co_subfrac;  /* Just add the shielded fraction */
	    }
	}
    }

    if (rbp != (NodeRegion *)CLIENTDEFAULT)
    {
	int oa = ExtCurStyle->exts_planeOrder[esws->plane_of_boundary];
	int ob = ExtCurStyle->exts_planeOrder[esws->plane_checked];
	if (oa > ob)
	{
	    /* The overlapped tile is between the substrate and the
	     * corner, and blocks the corner fringe capacitance to the
	     * substrate, which was added (convex) or subtracted
	     * (concave) with the perimeter capacitance in
	     * extNodeCornerCap() (see ExtBasic.c).  For a convex
	     * corner, subtract the blocked portion.  For a concave
	     * corner, each of the two edges meeting at the corner will
	     * have subtracted the blocked substrate fringe capacitance
	     * over this (shared) area, so add one portion back.
	     */
	    mult = (double)ExtCurStyle->exts_overlapMult[ta][0];
	    if (mult > 0)
	    {
		CapValue subcap;
		double sfrac;

		sfrac = extCornerFrac(&cov.co_clip, &ecos->ec_corner,
				ecos->ec_quadrant, mult);
		sfrac -= subfrac;
		if (sfrac < 0) sfrac = 0;

		subcap = (CapValue)ecos->ec_sign *
				ExtCurStyle->exts_perimCap[ta][0] *
				(ExtCornerFactor / mult) * sfrac;
		rbp->nreg_cap -= subcap;
		/* Ignore residual error at ~zero zeptoFarads */
		if ((rbp->nreg_cap > -0.001) && (rbp->nreg_cap < 0.001))
		    rbp->nreg_cap = 0;
		if (CAP_DEBUG)
		    extNregAdjustCap(rbp, -subcap, "corner_subcap");
	    }
	}
    }

    /* Add (convex) or subtract (concave) the corner capacitance. */

    if (cap == (CapValue) 0) return 0;
    if (rtp == rbp) return 0;
    if (rtp == (NodeRegion *)CLIENTDEFAULT) return 0;
    if (rbp == (NodeRegion *)CLIENTDEFAULT) return 0;

    if (rtp < rbp)
    {
	ck.ck_1 = rtp;
	ck.ck_2 = rbp;
    }
    else
    {
	ck.ck_1 = rbp;
	ck.ck_2 = rtp;
    }
    cap *= (CapValue)ecos->ec_sign;
    he = HashFind(extCoupleHashPtr, (char *) &ck);
    if (CAP_DEBUG) extAdjustCouple(he, cap, "corneroverlap");
    extSetCapValue(he, cap + extGetCapValue(he));

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extAddCornerCouple --
 *
 * Check the two endpoints of the horizontal boundary esws->bp for convex
 * and concave corners of the node region of bp->b_inside, and for each
 * corner found, search the quadrant of the fringe halo diagonal to the
 * corner for tiles on other planes that couple to the corner fringe
 * capacitance (see extCornerOverlap).  Called only for boundaries with
 * direction BD_TOP or BD_BOTTOM;  since every corner joins exactly one
 * horizontal and one vertical edge, checking only horizontal boundaries
 * considers each corner exactly once.
 *
 * A convex corner is one where the tile beyond the endpoint on the
 * inside of the boundary and the tile diagonally beyond the endpoint
 * are both space;  the corner fringe occupies the quadrant diagonally
 * outward from the corner and is added to the coupling capacitance.
 * A concave corner is one where both of those tiles belong to the same
 * node region as bp->b_inside;  the fringe capacitance in the free
 * quadrant between the two edges meeting at the corner has been counted
 * once by each edge, so the corner term is subtracted.  Anything else
 * (a boundary fractured by tile splits, or shapes of different nodes
 * meeting at a point) is left uncorrected.
 *
 * In-plane material within the quadrant does not clip the corner fringe
 * (unlike the edge fringe, which is clipped to the nearest coupling
 * neighbor by extWalk*());  this is a second-order effect of a second-
 * order correction.  Shielding by layers on planes between the corner
 * and the coupling tile is handled.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See extCornerOverlap().
 *
 * ----------------------------------------------------------------------------
 */

void
extAddCornerCouple(
    extSidewallStruct *esws,	/* Boundary and plane information */
    TileType tin,		/* Type inside boundary (residue if contact) */
    TileType tout)		/* Type outside boundary (residue if contact) */
{
    Boundary *bp = esws->bp;
    NodeRegion *rbp;
    PlaneMask pMask;
    int halo = ExtCurStyle->exts_sideCoupleHalo;
    int end, pNum, cx, cy;
    extCornerStruct ecos;

    pMask = ExtCurStyle->exts_sideOverlapOtherPlanes[tin][tout];
    if (pMask == 0) return;

    /* Corners adjacent to non-Manhattan geometry are not handled. */
    if (IsSplit(bp->b_inside) || IsSplit(bp->b_outside)) return;

    rbp = (NodeRegion *) ExtGetRegion(bp->b_inside, (TileType)0);

    cy = (bp->b_direction == BD_TOP) ?
		bp->b_segment.r_ur.p_y : bp->b_segment.r_ll.p_y;

    for (end = 0; end < 2; end++)	/* 0 = left endpoint, 1 = right */
    {
	Tile *tp, *tcont, *tdiag;
	bool contsame, diagsame;

	cx = (end == 1) ? bp->b_segment.r_xtop : bp->b_segment.r_xbot;

	/* Find the tile continuing beyond the endpoint on the inside	*/
	/* side of the boundary (tcont), and the tile diagonally	*/
	/* beyond the endpoint on the outside side (tdiag), using the	*/
	/* corner stitches of the boundary tiles.			*/

	if (end == 1)		/* Right endpoint */
	{
	    if (RIGHT(bp->b_inside) > cx)
		tcont = bp->b_inside;
	    else if (bp->b_direction == BD_TOP)
		tcont = TR(bp->b_inside);
	    else
	    {
		for (tp = TR(bp->b_inside); BOTTOM(tp) > cy; tp = LB(tp));
		tcont = tp;
	    }

	    if (RIGHT(bp->b_outside) > cx)
		tdiag = bp->b_outside;
	    else if (bp->b_direction == BD_TOP)
	    {
		for (tp = TR(bp->b_outside); BOTTOM(tp) > cy; tp = LB(tp));
		tdiag = tp;
	    }
	    else
		tdiag = TR(bp->b_outside);
	}
	else			/* Left endpoint */
	{
	    if (LEFT(bp->b_inside) < cx)
		tcont = bp->b_inside;
	    else if (bp->b_direction == BD_TOP)
	    {
		for (tp = BL(bp->b_inside); TOP(tp) < cy; tp = RT(tp));
		tcont = tp;
	    }
	    else
		tcont = BL(bp->b_inside);

	    if (LEFT(bp->b_outside) < cx)
		tdiag = bp->b_outside;
	    else if (bp->b_direction == BD_TOP)
		tdiag = BL(bp->b_outside);
	    else
	    {
		for (tp = BL(bp->b_outside); TOP(tp) < cy; tp = RT(tp));
		tdiag = tp;
	    }
	}

	if (IsSplit(tcont) || IsSplit(tdiag)) continue;

	contsame = (ExtGetRegion(tcont, (TileType)0) == (ExtRegion *)rbp)
			? TRUE : FALSE;
	diagsame = (ExtGetRegion(tdiag, (TileType)0) == (ExtRegion *)rbp)
			? TRUE : FALSE;

	if (!contsame && !diagsame)
	{
	    /* Convex corner, but only if the corner faces open space */
	    if ((TiGetTypeExact(tcont) != TT_SPACE) ||
			(TiGetTypeExact(tdiag) != TT_SPACE))
		continue;

	    /* Fringe quadrant is diagonally outward from the corner */
	    ecos.ec_sign = 1;
	    if (bp->b_direction == BD_TOP)
		ecos.ec_quadrant = (end == 1) ? GEO_NORTHEAST : GEO_NORTHWEST;
	    else
		ecos.ec_quadrant = (end == 1) ? GEO_SOUTHEAST : GEO_SOUTHWEST;
	}
	else if (contsame && diagsame)
	{
	    /* Concave corner, but only if the free quadrant between	*/
	    /* the two edges is open space (which is the tile on the	*/
	    /* outside of the boundary).				*/
	    if (TiGetTypeExact(bp->b_outside) != TT_SPACE)
		continue;

	    /* Free quadrant is on the outside of the boundary, on the	*/
	    /* segment side of the endpoint.				*/
	    ecos.ec_sign = -1;
	    if (bp->b_direction == BD_TOP)
		ecos.ec_quadrant = (end == 1) ? GEO_NORTHWEST : GEO_NORTHEAST;
	    else
		ecos.ec_quadrant = (end == 1) ? GEO_SOUTHWEST : GEO_SOUTHEAST;
	}
	else
	{
	    /* Straight continuation of the edge (boundary fractured	*/
	    /* by a tile split on the other side), or shapes meeting	*/
	    /* only at a point;  no corner processing.			*/
	    continue;
	}

	ecos.ec_esws = esws;
	ecos.ec_corner.p_x = cx;
	ecos.ec_corner.p_y = cy;

	switch (ecos.ec_quadrant)
	{
	    case GEO_NORTHEAST:
		ecos.ec_area.r_xbot = cx;
		ecos.ec_area.r_ybot = cy;
		ecos.ec_area.r_xtop = cx + halo;
		ecos.ec_area.r_ytop = cy + halo;
		break;
	    case GEO_NORTHWEST:
		ecos.ec_area.r_xbot = cx - halo;
		ecos.ec_area.r_ybot = cy;
		ecos.ec_area.r_xtop = cx;
		ecos.ec_area.r_ytop = cy + halo;
		break;
	    case GEO_SOUTHEAST:
		ecos.ec_area.r_xbot = cx;
		ecos.ec_area.r_ybot = cy - halo;
		ecos.ec_area.r_xtop = cx + halo;
		ecos.ec_area.r_ytop = cy;
		break;
	    case GEO_SOUTHWEST:
		ecos.ec_area.r_xbot = cx - halo;
		ecos.ec_area.r_ybot = cy - halo;
		ecos.ec_area.r_xtop = cx;
		ecos.ec_area.r_ytop = cy;
		break;
	}

	extOverlapDef = esws->def;
	for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	    if (PlaneMaskHasPlane(pMask, pNum))
	    {
		esws->plane_checked = pNum;
		(void) DBSrPaintArea((Tile *) NULL, esws->def->cd_planes[pNum],
			&ecos.ec_area,
			&ExtCurStyle->exts_sideOverlapOtherTypes[tin][tout],
			extCornerOverlap, (ClientData) &ecos);
	    }
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * extWalkTop ---
 *
 * Search in the area 'area' above the boundary 'bp' for coupling tiles of
 * types in 'mask', starting from the right corner above the boundary and
 * sweeping left.  If a coupling tile is found, process it, clipping the
 * boundary to the width of the tile if needed.  Then recursively call
 * extWalkTop on the areas to the left and right sides of the tile with
 * boundaries reduced to the width of those areas.  This way the edge
 * boundary is subdivided into lengths occupied by the nearest neighbor.
 *
 * Return value:
 *	Return 1 if func() returned 1, otherwise return 0.
 *
 * Side effects:
 *	None.
 * 
 * ----------------------------------------------------------------------------
 */

int
extWalkTop(area, mask, func, bp, esws)
    Rect *area;
    TileTypeBitMask *mask;
    int (*func)();
    Boundary *bp;
    extSidewallStruct *esws;
{
    Tile *tile, *tp;
    TileType ttype;
    Boundary bloc;
    Rect aloc;

    tile = RT(bp->b_outside);	/* Tile above tile on top of the boundary */
    while (BOTTOM(tile) < area->r_ytop)
    {
	while (LEFT(tile) >= area->r_xtop) tile = BL(tile);  /* Walk back to area */
	tp = tile;
	while (RIGHT(tp) > area->r_xbot)
	{
	    ttype = TiGetBottomType(tp);
	    if (TTMaskHasType(mask, ttype))
	    {
		bool lookLeft, lookRight;

		bloc = *bp;	/* Copy boundary to bc, then adjust boundary */

		lookLeft = (LEFT(tp) > bp->b_segment.r_xbot) ? TRUE : FALSE;
		lookRight = (RIGHT(tp) < bp->b_segment.r_xtop) ? TRUE : FALSE;

		if (lookLeft)
		    bloc.b_segment.r_xbot = LEFT(tp);
		if (lookRight)
		    bloc.b_segment.r_xtop = RIGHT(tp);

		/* Call sidewall coupling calculation function */
		if (func(tp, &bloc, esws) != 0) return 1;

		/* Clip coupling area and call fringe coupling calculation function */
		aloc = *area;
		aloc.r_ytop = BOTTOM(tp);
		aloc.r_xbot = bloc.b_segment.r_xbot;
		aloc.r_xtop = bloc.b_segment.r_xtop;
		if (extFindOverlap(bp->b_outside, &aloc, esws) != 0) return 1;
		extRemoveSubcap(&bloc, &aloc, esws);

		/* Recurse on tile left side */
		if (lookLeft)
		{
		    aloc = *area;
		    aloc.r_xtop = bloc.b_segment.r_xbot;
		    bloc.b_segment.r_xbot = bp->b_segment.r_xbot;
		    bloc.b_segment.r_xtop = aloc.r_xtop;
		    if (extWalkTop(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Recurse on tile right side */
		if (lookRight)
		{
		    aloc = *area;
		    aloc.r_xbot = RIGHT(tp);
		    bloc.b_segment.r_xtop = bp->b_segment.r_xtop;
		    bloc.b_segment.r_xbot = aloc.r_xbot;
		    if (extWalkTop(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Once a coupling tile is found, it blocks any	*/
		/* coupling to tiles behind it, so return.	*/
		return 0;
	    }

	    /* Continue to walk left until out of bounds */
	    tp = BL(tp);
	}
	/* Continue to walk up from right edge */
	tile = RT(tile);
    }

    /* Any length which does not couple to anything in the	*/
    /* same plane is still checked for coupling to anything	*/
    /* below it.						*/
    return extFindOverlap(bp->b_outside, area, esws);
}
    
/*
 * ----------------------------------------------------------------------------
 *
 * extWalkBottom ---
 *
 * Search in the area 'area' below the boundary 'bp' for coupling tiles of
 * types in 'mask', starting from the left corner below the boundary and
 * sweeping right.  If a coupling tile is found, process it, clipping the
 * boundary to the width of the tile if needed.  Then recursively call
 * extWalkBottom on the areas to the left and right sides of the tile with
 * boundaries reduced to the width of those areas.  This way the edge
 * boundary is subdivided into lengths occupied by the nearest neighbor.
 *
 * Return value:
 *	Return 1 if func() returned 1, otherwise return 0.
 *
 * Side effects:
 *	None.
 * 
 * ----------------------------------------------------------------------------
 */

int
extWalkBottom(area, mask, func, bp, esws)
    Rect *area;
    TileTypeBitMask *mask;
    int (*func)();
    Boundary *bp;
    extSidewallStruct *esws;
{
    Tile *tile, *tp;
    TileType ttype;
    Boundary bloc;
    Rect aloc;

    tile = LB(bp->b_outside);	/* Tile below tile on bottom of the boundary */
    while (TOP(tile) > area->r_ybot)
    {
	while (RIGHT(tile) <= area->r_xbot) tile = TR(tile);  /* Walk back to area */
	tp = tile;
	while (LEFT(tp) < area->r_xtop)
	{
	    ttype = TiGetTopType(tp);
	    if (TTMaskHasType(mask, ttype))
	    {
		bool lookLeft, lookRight;

		bloc = *bp;	/* Copy boundary to bc, then adjust boundary */

		lookLeft = (LEFT(tp) > bp->b_segment.r_xbot) ? TRUE : FALSE;
		lookRight = (RIGHT(tp) < bp->b_segment.r_xtop) ? TRUE : FALSE;

		if (lookLeft)
		    bloc.b_segment.r_xbot = LEFT(tp);
		if (lookRight)
		    bloc.b_segment.r_xtop = RIGHT(tp);

		/* Call sidewall coupling calculation function */
		if (func(tp, &bloc, esws) != 0) return 1;

		/* Clip coupling area and call fringe coupling calculation function */
		aloc = *area;
		aloc.r_ybot = TOP(tp);
		aloc.r_xbot = bloc.b_segment.r_xbot;
		aloc.r_xtop = bloc.b_segment.r_xtop;
		if (extFindOverlap(bp->b_outside, &aloc, esws) != 0) return 1;
		extRemoveSubcap(&bloc, &aloc, esws);

		/* Recurse on tile left side */
		if (lookLeft)
		{
		    aloc = *area;
		    aloc.r_xtop = bloc.b_segment.r_xbot;
		    bloc.b_segment.r_xbot = bp->b_segment.r_xbot;
		    bloc.b_segment.r_xtop = aloc.r_xtop;
		    if (extWalkBottom(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Recurse on tile right side */
		if (lookRight)
		{
		    aloc = *area;
		    aloc.r_xbot = RIGHT(tp);
		    bloc.b_segment.r_xtop = bp->b_segment.r_xtop;
		    bloc.b_segment.r_xbot = aloc.r_xbot;
		    if (extWalkBottom(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Once a coupling tile is found, it blocks any	*/
		/* coupling to tiles behind it, so return.	*/
		return 0;
	    }

	    /* Continue to walk right until out of bounds */
	    tp = TR(tp);
	}
	/* Continue to walk down from left edge */
	tile = LB(tile);
    }

    /* Any length which does not couple to anything in the	*/
    /* same plane is still checked for coupling to anything	*/
    /* below it.						*/
    return extFindOverlap(bp->b_outside, area, esws);
}
    
/*
 * ----------------------------------------------------------------------------
 *
 * extWalkRight ---
 *
 * Search in the area 'area' to the right of the boundary 'bp' for coupling
 * tiles of types in 'mask', starting from the top corner to the right of the
 * boundary and sweeping downward.  If a coupling tile is found, process it,
 * clipping the boundary to the height of the tile if needed.  Then recursively
 * call extWalkRight on the areas above and below the tile with boundaries
 * reduced to the height of those areas.  This way the edge boundary is
 * subdivided into lengths occupied by the nearest neighbor.
 *
 * Return value:
 *	Return 1 if func() returned 1, otherwise return 0.
 *
 * Side effects:
 *	None.
 * 
 * ----------------------------------------------------------------------------
 */

int
extWalkRight(area, mask, func, bp, esws)
    Rect *area;
    TileTypeBitMask *mask;
    int (*func)();
    Boundary *bp;
    extSidewallStruct *esws;
{
    Tile *tile, *tp;
    TileType ttype;
    Boundary bloc;
    Rect aloc;

    tile = TR(bp->b_outside);	/* Tile to the right of tile to right of the boundary */
    while (LEFT(tile) < area->r_xtop)
    {
	while (BOTTOM(tile) >= area->r_ytop) tile = LB(tile);  /* Walk back to area */
	tp = tile;
	while (TOP(tp) > area->r_ybot)
	{
	    ttype = TiGetLeftType(tp);
	    if (TTMaskHasType(mask, ttype))
	    {
		bool lookDown, lookUp;

		bloc = *bp;	/* Copy boundary to bc, then adjust boundary */

		lookDown = (BOTTOM(tp) > bp->b_segment.r_ybot) ? TRUE : FALSE;
		lookUp = (TOP(tp) < bp->b_segment.r_ytop) ? TRUE : FALSE;

		if (lookDown)
		    bloc.b_segment.r_ybot = BOTTOM(tp);
		if (lookUp)
		    bloc.b_segment.r_ytop = TOP(tp);

		/* Call sidewall coupling calculation function */
		if (func(tp, &bloc, esws) != 0) return 1;

		/* Clip coupling area and call fringe coupling calculation function */
		aloc = *area;
		aloc.r_xtop = LEFT(tp);
		aloc.r_ybot = bloc.b_segment.r_ybot;
		aloc.r_ytop = bloc.b_segment.r_ytop;
		if (extFindOverlap(bp->b_outside, &aloc, esws) != 0) return 1;
		extRemoveSubcap(&bloc, &aloc, esws);

		/* Recurse on tile bottom side */
		if (lookDown)
		{
		    aloc = *area;
		    aloc.r_ytop = bloc.b_segment.r_ybot;
		    bloc.b_segment.r_ybot = bp->b_segment.r_ybot;
		    bloc.b_segment.r_ytop = aloc.r_ytop;
		    if (extWalkRight(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Recurse on tile top side */
		if (lookUp)
		{
		    aloc = *area;
		    aloc.r_ybot = TOP(tp);
		    bloc.b_segment.r_ytop = bp->b_segment.r_ytop;
		    bloc.b_segment.r_ybot = aloc.r_ybot;
		    if (extWalkRight(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Once a coupling tile is found, it blocks any	*/
		/* coupling to tiles behind it, so return.	*/
		return 0;
	    }

	    /* Continue to walk down until out of bounds */
	    tp = LB(tp);
	}
	/* Continue to walk right from top edge */
	tile = TR(tile);
    }

    /* Any length which does not couple to anything in the	*/
    /* same plane is still checked for coupling to anything	*/
    /* below it.						*/
    return extFindOverlap(bp->b_outside, area, esws);
}
    
/*
 * ----------------------------------------------------------------------------
 *
 * extWalkLeft ---
 *
 * Search in the area 'area' to the left of the boundary 'bp' for coupling
 * tiles of types in 'mask', starting from the bottom corner to the left of the
 * boundary and sweeping upward.  If a coupling tile is found, process it,
 * clipping the boundary to the height of the tile if needed.  Then recursively
 * call extWalkLeft on the areas above and below the tile with boundaries
 * reduced to the height of those areas.  This way the edge boundary is
 * subdivided into lengths occupied by the nearest neighbor.
 *
 * Return value:
 *	Return 1 if func() returned 1, otherwise return 0.
 *
 * Side effects:
 *	None.
 * 
 * ----------------------------------------------------------------------------
 */

int
extWalkLeft(area, mask, func, bp, esws)
    Rect *area;
    TileTypeBitMask *mask;
    int (*func)();
    Boundary *bp;
    extSidewallStruct *esws;
{
    Tile *tile, *tp;
    TileType ttype;
    Boundary bloc;
    Rect aloc;

    tile = BL(bp->b_outside);	/* Tile to the left of tile to left of the boundary */
    while (RIGHT(tile) > area->r_xbot)
    {
	while (TOP(tile) <= area->r_ybot) tile = RT(tile);  /* Walk back to area */
	tp = tile;
	while (BOTTOM(tp) < area->r_ytop)
	{
	    ttype = TiGetRightType(tp);
	    if (TTMaskHasType(mask, ttype))
	    {
		bool lookDown, lookUp;

		bloc = *bp;	/* Copy boundary to bc, then adjust boundary */

		lookDown = (BOTTOM(tp) > bp->b_segment.r_ybot) ? TRUE : FALSE;
		lookUp = (TOP(tp) < bp->b_segment.r_ytop) ? TRUE : FALSE;

		if (lookDown)
		    bloc.b_segment.r_ybot = BOTTOM(tp);
		if (lookUp)
		    bloc.b_segment.r_ytop = TOP(tp);

		/* Call sidewall coupling calculation function */
		if (func(tp, &bloc, esws) != 0) return 1;

		/* Clip coupling area and call fringe coupling calculation function */
		aloc = *area;
		aloc.r_xbot = RIGHT(tp);
		aloc.r_ybot = bloc.b_segment.r_ybot;
		aloc.r_ytop = bloc.b_segment.r_ytop;
		if (extFindOverlap(bp->b_outside, &aloc, esws) != 0) return 1;
		extRemoveSubcap(&bloc, &aloc, esws);

		/* Recurse on tile bottom side */
		if (lookDown)
		{
		    aloc = *area;
		    aloc.r_ytop = bloc.b_segment.r_ybot;
		    bloc.b_segment.r_ybot = bp->b_segment.r_ybot;
		    bloc.b_segment.r_ytop = aloc.r_ytop;
		    if (extWalkLeft(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Recurse on tile top side */
		if (lookUp)
		{
		    aloc = *area;
		    aloc.r_ybot = TOP(tp);
		    bloc.b_segment.r_ytop = bp->b_segment.r_ytop;
		    bloc.b_segment.r_ybot = aloc.r_ybot;
		    if (extWalkLeft(&aloc, mask, func, &bloc, esws) != 0)
			return 1;
		}

		/* Once a coupling tile is found, it blocks any	*/
		/* coupling to tiles behind it, so return.	*/
		return 0;
	    }

	    /* Continue to walk up until out of bounds */
	    tp = RT(tp);
	}
	/* Continue to walk left from top edge */
	tile = BL(tile);
    }

    /* Any length which does not couple to anything in the	*/
    /* same plane is still checked for coupling to anything	*/
    /* below it.						*/
    return extFindOverlap(bp->b_outside, area, esws);
}
    
/*
 * ----------------------------------------------------------------------------
 *
 * extSideLeft --
 *
 * Searching to the left of the boundary 'bp', we found the tile
 * 'tpfar' which may lie on the far side of an edge to which the
 * edge bp->b_inside | bp->b_outside has sidewall coupling capacitance.
 *
 * Walk along the right-hand side of 'tpfar' searching for such
 * edges, and recording their capacitance in the hash table
 * *extCoupleHashPtr.
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	If node(tpfar) exists, and node(bp->b_inside) != node(tpfar),
 *	search along the inside edge of tpfar (the one closest to
 *	the boundary bp) for edges having capacitance with bp.  For
 *	each such edge found, update the entry in *extCoupleHashPtr
 *	identified by node(bp->b_inside) and node(tpfar) by adding
 *	the capacitance due to the adjacency of the pair of edges.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideLeft(tpfar, bp, esws)
    Tile *tpfar;
    Boundary *bp;
    extSidewallStruct *esws;
{
    NodeRegion *rinside, *rfar;
    Tile *tpnear;

    /* Get the regions associated with bp->b_inside and tpfar */
    extGetBoundaryRegions(bp->b_direction, bp->b_inside, tpfar, &rinside, &rfar);

    if (rfar != (NodeRegion *)CLIENTDEFAULT && rfar != rinside)
    {
	int sep = bp->b_segment.r_xbot - RIGHT(tpfar);
	int limit = MAX(bp->b_segment.r_ybot, BOTTOM(tpfar));
	int start = MIN(bp->b_segment.r_ytop, TOP(tpfar));

	for (tpnear = TR(tpfar); TOP(tpnear) > limit; tpnear = LB(tpnear))
	{
	    int overlap = MIN(TOP(tpnear), start) - MAX(BOTTOM(tpnear), limit);
	    if (overlap > 0)
		extSideCommon(rinside, rfar, tpnear, tpfar, bp->b_direction,
				overlap, sep, esws->extCoupleList);
	}
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideRight --
 *
 * Searching to the right of the boundary 'bp', we found the tile
 * 'tpfar' which may lie on the far side of an edge to which the
 * edge bp->b_inside | bp->b_outside has sidewall coupling capacitance.
 *
 * Walk along the left-hand side of 'tpfar' searching for such
 * edges, and recording their capacitance in the hash table
 * *extCoupleHashPtr.
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	See extSideLeft.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideRight(tpfar, bp, esws)
    Tile *tpfar;
    Boundary *bp;
    extSidewallStruct *esws;
{
    NodeRegion *rinside, *rfar;
    Tile *tpnear;

    /* Get the regions associated with bp->b_inside and tpfar */
    extGetBoundaryRegions(bp->b_direction, bp->b_inside, tpfar, &rinside, &rfar);

    if (rfar != (NodeRegion *) CLIENTDEFAULT && rfar != rinside)
    {
	int sep = LEFT(tpfar) - bp->b_segment.r_xtop;
	int limit = MIN(bp->b_segment.r_ytop, TOP(tpfar));
	int start = MAX(bp->b_segment.r_ybot, BOTTOM(tpfar));

	for (tpnear = BL(tpfar); BOTTOM(tpnear) < limit; tpnear = RT(tpnear))
	{
	    int overlap = MIN(TOP(tpnear), limit) - MAX(BOTTOM(tpnear), start);
	    if (overlap > 0)
		extSideCommon(rinside, rfar, tpnear, tpfar, bp->b_direction,
				overlap, sep, esws->extCoupleList);
	}
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideTop --
 *
 * Searching to the top of the boundary 'bp', we found the tile
 * 'tpfar' which may lie on the far side of an edge to which the
 * edge bp->b_inside | bp->b_outside has sidewall coupling capacitance.
 *
 * Walk along the bottom side of 'tpfar' searching for such
 * edges, and recording their capacitance in the hash table
 * *extCoupleHashPtr.
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	See extSideLeft.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideTop(tpfar, bp, esws)
    Tile *tpfar;
    Boundary *bp;
    extSidewallStruct *esws;
{
    NodeRegion *rinside, *rfar;
    Tile *tpnear;

    /* Get the regions associated with bp->b_inside and tpfar */
    extGetBoundaryRegions(bp->b_direction, bp->b_inside, tpfar, &rinside, &rfar);

    if (rfar != (NodeRegion *) CLIENTDEFAULT && rfar != rinside)
    {
	int sep = BOTTOM(tpfar) - bp->b_segment.r_ytop;
	int limit = MIN(bp->b_segment.r_xtop, RIGHT(tpfar));
	int start = MAX(bp->b_segment.r_xbot, LEFT(tpfar));

	for (tpnear = LB(tpfar); LEFT(tpnear) < limit; tpnear = TR(tpnear))
	{
	    int overlap = MIN(RIGHT(tpnear), limit) - MAX(LEFT(tpnear), start);
	    if (overlap > 0)
		extSideCommon(rinside, rfar, tpnear, tpfar, bp->b_direction,
				overlap, sep, esws->extCoupleList);
	}
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideBottom --
 *
 * Searching to the bottom of the boundary 'bp', we found the tile
 * 'tpfar' which may lie on the far side of an edge to which the
 * edge bp->b_inside | bp->b_outside has sidewall coupling capacitance.
 *
 * Walk along the top side of 'tpfar' searching for such
 * edges, and recording their capacitance in the hash table
 * *extCoupleHashPtr.
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	See extSideLeft.
 *
 * ----------------------------------------------------------------------------
 */

int
extSideBottom(tpfar, bp, esws)
    Tile *tpfar;
    Boundary *bp;
    extSidewallStruct *esws;
{
    NodeRegion *rinside, *rfar;
    Tile *tpnear;

    /* Get the regions associated with bp->b_inside and tpfar */
    extGetBoundaryRegions(bp->b_direction, bp->b_inside, tpfar, &rinside, &rfar);

    if (rfar != (NodeRegion *) CLIENTDEFAULT && rfar != rinside)
    {
	int sep = bp->b_segment.r_ybot - TOP(tpfar);
	int limit = MAX(bp->b_segment.r_xbot, LEFT(tpfar));
	int start = MIN(bp->b_segment.r_xtop, RIGHT(tpfar));

	for (tpnear = RT(tpfar); RIGHT(tpnear) > limit; tpnear = BL(tpnear))
	{
	    int overlap = MIN(RIGHT(tpnear), start) - MAX(LEFT(tpnear), limit);
	    if (overlap > 0)
		extSideCommon(rinside, rfar, tpnear, tpfar, bp->b_direction,
				overlap, sep, esws->extCoupleList);
	}
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extSideCommon --
 *
 * Perform the actual update to the hash table entry for
 * the regions 'rinside' and 'rfar'.  We assume that neither
 * 'rinside' nor 'rfar' are CLIENTDEFAULT, and further that they
 * are not equal.
 *
 * Walk along the rules in extCoupleList, applying the appropriate
 * amount of capacitance for an edge with tpnear on the close side
 * and tpfar on the remote side.
 *
 * Important!  The sidewall coeffient is correct for the coupling between
 * edges, but both edges will be checked, causing a double-count, so each
 * edge should contribute half of the total.  For a long time the sidewall
 * coefficient was claimed to need to be half the value, but the offset
 * makes it incorrect to halve the coefficient, and it is necessary to
 * halve the total sidewall capacitance when doing the calculations.
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	See extSideLeft.
 *
 * ----------------------------------------------------------------------------
 */

void
extSideCommon(rinside, rfar, tpnear, tpfar, bdir, overlap, sep, extCoupleList)
    NodeRegion *rinside, *rfar;	/* Both must be valid */
    Tile *tpnear, *tpfar;	/* Tiles on near and far side of edge */
    int bdir;			/* Boundary direction */
    int overlap, sep;		/* Overlap of this edge with original one,
				 * and distance between the two.
				 */
    EdgeCap  *extCoupleList;	/* List of sidewall capacitance rules */
{
    TileType near, far;
    HashEntry *he;
    EdgeCap *e;
    CoupleKey ck;
    CapValue cap, swcap;

    /* Get the tile types of tpnear and tpfar */
    extGetBoundaryTypes2(bdir, tpnear, tpfar, &near, &far);

    if (rinside < rfar) ck.ck_1 = rinside, ck.ck_2 = rfar;
    else ck.ck_1 = rfar, ck.ck_2 = rinside;
    he = HashFind(extCoupleHashPtr, (char *) &ck);

    cap = extGetCapValue(he);
    for (e = extCoupleList; e; e = e->ec_next)
	if (TTMaskHasType(&e->ec_near, near) && TTMaskHasType(&e->ec_far, far)) {
	    swcap = (e->ec_cap * overlap) / (sep + e->ec_offset);
	    cap += swcap;
	    if (CAP_DEBUG)
		extAdjustCouple(he, swcap, "sidewall");
	}
    extSetCapValue(he, cap);
}

