/*
 * Electric(tm) VLSI Design System
 *
 * File: compact.c
 * One-dimensional compactor aid
 * Written by: Nora Ryan, Schlumberger Palo Alto Research
 * Rewritten by: Steven M. Rubin, Schlumberger Palo Alto Research
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

/*
 * When compacting facet instances, the system only examines polygons
 * within a protection frame of the facet border.  This frame is the largest
 * design rule distance in the technology.  If the facet border is irregular,
 * there may be objects that are not seen, causing the facet to overlap
 * more than it should.
 */

#include "config.h"
#if COMAID

#include "global.h"
#include "compact.h"
#include "efunction.h"
#include "edialogs.h"
#include "conlay.h"

#define DEFAULT_VAL -99999999
#define HORIZONTAL          0
#define VERTICAL            1


#define	NOPOLYLIST ((POLYLIST *)-1)

typedef struct Ipolylist
{
	POLYGON          *poly;
	TECHNOLOGY       *tech;
	INTSML            networknum;
	struct Ipolylist *nextpolylist;
} POLYLIST;


#define	NOOBJECT ((OBJECT *)-1)

typedef struct Iobject
{
	union u_inst
	{
		NODEINST   *ni;
		ARCINST    *ai;
	} inst;
	INTSML          insttype;
	POLYLIST       *firstpolylist;
	INTBIG          lowx, highx, lowy, highy;
	struct Iobject *nextobject;
} OBJECT;


#define	NOLINE ((LINE *)-1)

typedef struct Iline
{
	INTBIG        val;
	INTBIG        low, high;
	INTBIG        top, bottom;
	OBJECT       *firstobject;
	struct Iline *nextline;
	struct Iline *prevline;
} LINE;


#define	NOTECHARRAY ((TECH_ARRAY *)-1)

typedef struct Itecharray
{
	INTSML            *layer;
	INTSML             taindex;
	struct Itecharray *nexttecharray;
} TECH_ARRAY;
TECH_ARRAY *com_conv_layer;

/* Compaction Options */
DIALOGITEM com_optionsdialogitems[] =
{
 /*  1 */ {0, {64,92,88,156}, BUTTON, N_("OK")},
 /*  2 */ {0, {64,8,88,72}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {8,8,24,154}, CHECK, N_("Allow spreading")},
 /*  4 */ {0, {32,8,48,154}, CHECK, N_("Verbose")}
};
DIALOG com_optionsdialog = {{50,75,147,243}, N_("Compaction Options"), 0, 4, com_optionsdialogitems};

/* prototypes for local routines */
void com_buildtechnologies(void);
OBJECT *com_make_ni_object(NODEINST*, OBJECT*, XARRAY, INTSML, INTBIG, INTBIG, INTBIG, INTBIG);
void com_fillnode(NODEINST*);
LINE *com_sort(LINE*, INTSML);
void com_computeline_hi_and_low(LINE*, INTSML);
INTSML com_lineup_firstrow(LINE*, LINE*, INTSML, INTBIG);
INTSML com_findleastlow(LINE*, INTSML);
INTSML com_compact(LINE*, LINE*, INTSML, INTSML, NODEPROTO*);
INTBIG com_checkinst(OBJECT*, LINE*, INTSML, OBJECT**, POLYLIST **, POLYLIST **, NODEPROTO*);
INTBIG com_minseparate(OBJECT*, INTSML, POLYLIST*, LINE*, INTSML, OBJECT**, POLYLIST **, NODEPROTO*);
INTBIG com_check(INTSML, INTSML, OBJECT*, INTBIG, INTBIG, INTBIG, INTBIG, INTSML, INTSML, OBJECT*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTSML);
INTSML com_cropnodeinst(POLYLIST*, INTBIG*, INTBIG*, INTBIG*, INTBIG*, INTSML, INTSML);
INTSML com_in_bound(INTBIG, INTBIG, INTBIG, INTBIG);
void com_fixed_nonfixed(LINE*, LINE*);
void com_undo_fixed_nonfixed(LINE*, LINE*);
void com_noslide(LINE*);
void com_slide(LINE*);
INTSML com_move(LINE*, INTBIG, INTBIG, INTSML);
INTSML com_convertpintonode_layer(INTSML, TECHNOLOGY*);
void com_subsmash(NODEPROTO*);
void com_add_poly_polylist(POLYGON*, OBJECT*, INTSML, TECHNOLOGY*);
OBJECT *com_allocate_object(void);
void com_add_object_to_object(OBJECT**, OBJECT*);
LINE *com_make_object_line(LINE*, OBJECT*);
void com_clearspace(LINE*);
void com_freeobject(OBJECT*);
char *com_describeline(LINE*);
void com_addlinedescription(LINE*);
char *com_describeobject(OBJECT*);
void com_createobjects(NODEINST*, INTSML, OBJECT**, OBJECT**);
OBJECT *com_make_ai_object(ARCINST*, OBJECT*, XARRAY, INTSML, INTBIG, INTBIG, INTBIG, INTBIG);
INTSML com_examineonefacet(NODEPROTO*, INTSML, INTSML);
void comp_optionsdlog(void);

/* the COMPACTOR aid table */
static COMCOMP comcp = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, 0, " \t", M_("facet to be rechecked"), M_("default is current facet")};
static KEYWORD comnotopt[] =
{
	{"spread",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP comnotp = {comnotopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, 0, " \t", M_("Compactor negative action"), 0};
static KEYWORD comhvopt[] =
{
	{"horizontal",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"vertical",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP comhvp = {comhvopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, 0, " \t", M_("Compactor direction"), 0};
static KEYWORD comopt[] =
{
	{"check",          2,{&comhvp,&comcp,NOKEY,NOKEY,NOKEY}},
	{"not",            1,{&comnotp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"debug-toggle",   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"spread",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP com_compactp = {comopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", M_("Compactor action"), M_("show defaults")};

AIDENTRY *com_aid;
INTBIG    com_spread;				/* cached key for "COM_spread" */
INTSML    com_debug;
INTBIG    com_maxboundary, com_lowbound;
INTBIG    com_flatindex;			/* counter for unique network numbers */
INTSML    com_do_axis;

/************************ CONTROL ***********************/

void com_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	/* ignore pass 3 initialization */
	if (thisaid == 0) return;

	/* miscellaneous initialization during pass 2 */
	if (thisaid == NOAID)
	{
		com_spread = makekey("COM_spread");
		nextvarchangequiet();
		(void)setvalkey((INTBIG)com_aid, VAID, com_spread, 0, VINTEGER|VDONTSAVE);
		com_debug = 0;
		com_do_axis = VERTICAL;
		com_conv_layer = NOTECHARRAY;
		registertechnologycache(com_buildtechnologies);
		DiaDeclareHook("compopt", &com_compactp, comp_optionsdlog);
		return;
	}

	/* copy aid pointer during pass 1 */
	com_aid = thisaid;
}

/*
 * routine called when the technology configuration changes.  It recaches
 * the layer conversion information in the global linked list "com_conv_layer"
 */
void com_buildtechnologies(void)
{
	REGISTER TECHNOLOGY *tech;
	REGISTER TECH_ARRAY *cur_tech_array, *next_techarray;
	REGISTER INTSML j, k;
	REGISTER INTBIG layer;

	/* first free the former list of layer conversions */
	for(cur_tech_array = com_conv_layer; cur_tech_array != NOTECHARRAY;
		cur_tech_array = next_techarray)
	{
		next_techarray = cur_tech_array->nexttecharray;
		efree((char *)cur_tech_array->layer);
		efree((char *)cur_tech_array);
	}
	com_conv_layer = NOTECHARRAY;

	/* initialize technology layer conversion array */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		cur_tech_array = (TECH_ARRAY *)emalloc((sizeof(TECH_ARRAY)), com_aid->cluster);
		if (cur_tech_array == 0) return;
		cur_tech_array->taindex = tech->techindex;
		cur_tech_array->nexttecharray = com_conv_layer;
		com_conv_layer = cur_tech_array;

		cur_tech_array->layer = (INTSML *)emalloc((sizeof(INTSML) * tech->layercount),
			com_aid->cluster);

		/* define the layer translation for pseudo layers and regular layers */
		for(j = 0; j < tech->layercount; j++)
		{
			cur_tech_array->layer[j] = j;
			layer = layerfunction(tech, j);
			if ((layer & LFPSEUDO) == 0) continue;

			/* layer is a pseudo layer */
			layer &= ~LFPSEUDO;	/* layer to be matched */
			for(k = 0; k < tech->layercount; k++)
				if (layerfunction(tech, k) == layer)
			{
				/* found matching layer */
				cur_tech_array->layer[j] = k;
				break;
			}
		}
	}
}

void com_done(void)
{
	REGISTER TECH_ARRAY *cur_tech_array, *next_techarray;

	/* first free the former list of layer conversions */
	for(cur_tech_array = com_conv_layer; cur_tech_array != NOTECHARRAY;
		cur_tech_array = next_techarray)
	{
		next_techarray = cur_tech_array->nexttecharray;
		efree((char *)cur_tech_array->layer);
		efree((char *)cur_tech_array);
	}
	com_conv_layer = NOTECHARRAY;
}

void com_slice(void)
{
	REGISTER INTSML change;
	REGISTER NODEPROTO *np;

	np = getcurfacet();
	if (np == NONODEPROTO) return;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on"));
		aidturnon(net_aid, 0);
		return;
	}

	/* on alternate slices, do vertical then horizontal compaction */
	if (com_do_axis == VERTICAL) com_do_axis = HORIZONTAL; else
		com_do_axis = VERTICAL;

	change = com_examineonefacet(np, com_do_axis, 1);

	if (!change)
	{
		 ttyputmsg(_("No further change.  Compactor turned off"));
		 aidturnoff(com_aid, 0);
	} else setactivity(_("Compaction"));
}

INTSML com_set(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER INTSML l, i;
	REGISTER char *pp;
	REGISTER VARIABLE *var;

	if (count == 0)
	{
		var = getvalkey((INTBIG)com_aid, VAID, VINTEGER, com_spread);
		if (var != NOVARIABLE)
		{
			if (var->addr != 0) ttyputmsg(_("Compactor will spread if necessary")); else
				ttyputmsg(_("Compactor will not spread designs"));
		}
		return(0);
	}

	l = strlen(pp = par[0]);
	if (namesamen(pp, "not", l) == 0)
	{
		if (count <= 1)
		{
			count = ttygetparam(_("COMPACTOR negate option:"), &comnotp, MAXPARS-1,
				&par[1]) + 1;
			if (count <= 1)
			{
				ttyputerr(_("Aborted"));
				return(1);
			}
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "spread", l) == 0)
		{
			(void)setvalkey((INTBIG)com_aid, VAID, com_spread, 0, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Compactor will not spread designs"));
			return(0);
		}
		ttyputbadusage("tellaid compaction not");
		return(1);
	}

	if (namesamen(pp, "spread", l) == 0)
	{
		(void)setvalkey((INTBIG)com_aid, VAID, com_spread, 1, VINTEGER|VDONTSAVE);
		ttyputverbose(_("Compactor will spread if necessary"));
		return(0);
	}

	if (namesamen(pp, "check", l) == 0)
	{
		/* make sure network tool is on */
		if ((net_aid->aidstate&AIDON) == 0)
		{
			ttyputerr(_("Network tool must be running...turning it on"));
			aidturnon(net_aid, 0);
			ttyputerr(_("...now reissue the compaction command"));
			return(1);
		}

		if (count <= 1)
		{
			ttyputmsg(_("Do you want horizontal or vertical compaction? "));
			i = (INTSML)ttygetchar();
			if (i == (INTSML)'v' || i == (INTSML)'V') com_do_axis = VERTICAL; else
				if (i == (INTSML)'h' || i == (INTSML)'H') com_do_axis = HORIZONTAL; else
			{
				ttyputerr(_("Unknown compaction axis: '%c', expected 'v' or 'h'"), i);
				return(1);
			}
		} else
		{
			l = strlen(pp = par[1]);
			if (namesamen(pp, "horizontal", l) == 0) com_do_axis = HORIZONTAL; else
			if (namesamen(pp, "vertical", l) == 0) com_do_axis = VERTICAL; else
			{
				ttyputbadusage("tellaid compaction check");
				return(1);
			}
		}

		if (count >= 3)
		{
			np = getnodeproto(par[2]);
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No facet named %s"), par[2]);
				return(1);
			}
			if (np->primindex != 0)
			{
				ttyputerr(_("Can only compact facets, not primitives"));
				return(1);
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
		}

		(void)com_examineonefacet(np, com_do_axis, 0);
		ttyputmsg(_("Facet %s compacted"), describenodeproto(np));
		return(0);
	}

	if (namesamen(pp, "debug-toggle", l) == 0)
	{
		if (com_debug == 0)
		{
			com_debug = 1;
			ttyputmsg(_("Compactor will print debugging information"));
		} else
		{
			com_debug = 0;
			ttyputmsg(_("Compactor will not print debugging information"));
		}
		return(0);
	}
	ttyputbadusage("tellaid compaction");
	return(1);
}

/************************ COMPACTION ***********************/

/*
 * routine to do vertical compaction (if "axis" is VERTICAL) or horizontal
 * compaction (if "axis" is HORIZONTAL) to facet "np".  Displays state if
 * "verbose" is nonzero.  Returns nonzero if a change was made.
 */
INTSML com_examineonefacet(NODEPROTO *np, INTSML axis, INTSML verbose)
{
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER OBJECT *cur_object, **thisobject, **otherobject;
	OBJECT *object1, *object2;
	REGISTER LINE *linecomp, *linestretch, *cur_line;
	REGISTER INTSML i, change;
	REGISTER INTBIG max;

	if (stopping(STOPREASONCOMPACT)) return(0);

	if (np == NONODEPROTO)
	{
		ttyputmsg(_("No current facet to compact"));
		return(0);
	}

	/* determine maximum drc surround for entire technology */
	com_maxboundary = 0;
	for(i=0; i<el_curtech->layercount; i++)
	{
		max = maxdrcsurround(el_curtech, np->cell->lib, i);
		if (max > com_maxboundary) com_maxboundary = max;
	}
	if (com_maxboundary < 0) com_maxboundary = 0;

	if (verbose != 0)
	{
		if (axis == HORIZONTAL) ttyputmsg(_("Doing a horizontal compaction")); else
			ttyputmsg(_("Doing a vertical compaction"));
	}

	/* number ports of cell "np" in the "temp1" field */
	com_flatindex = 1;
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		/* see if this port is on the same net as previously examined one */
		for(opp = np->firstportproto; opp != pp; opp=opp->nextportproto)
			if (opp->network == pp->network) break;
		if (opp != pp) pp->temp1 = opp->temp1; else
			pp->temp1 = com_flatindex++;
	}

	/* copy port numbering onto arcs */
	com_subsmash(np);

	/* clear "seen" information on every node */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		ni->temp1 = 0;

	/* clear object information */
	linecomp = NOLINE;
	otherobject = &object2;
	*otherobject = NOOBJECT;

	/* now check every object */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* clear "thisobject" before calling com_createobject */
		thisobject = &object1;
		*thisobject = NOOBJECT;
		com_createobjects(ni, axis, thisobject, otherobject);

		/* create object of layout */
		if (*thisobject != NOOBJECT)
			linecomp = com_make_object_line(linecomp, *thisobject);
	}

	/* create list of perpendicular line which need to be set stretchable */
	if (*otherobject == NOOBJECT) linestretch = NOLINE; else
		linestretch = com_make_object_line(NOLINE, *otherobject);

	/* print the stretch object */
	if (com_debug != 0)
	{
		(void)initinfstr();
		if (linestretch != NOLINE)
			for(cur_object = linestretch->firstobject; cur_object != NOOBJECT;
				cur_object = cur_object->nextobject)
		{
			(void)addtoinfstr(' ');
			if (cur_object->insttype == OBJARCINST)
				(void)addstringtoinfstr(describearcinst(cur_object->inst.ai)); else
					(void)addstringtoinfstr(describenodeinst(cur_object->inst.ni));
		}
		ttyputmsg(_("Arcs that stretch:%s"), returninfstr());
	}

	/* sort the compacting line of objects */
	linecomp = com_sort(linecomp, axis);

	/* print the merged structure */
	if (com_debug != 0)
	{
		if (axis == VERTICAL) ttyputmsg(_("Bottom-to-top:")); else
			ttyputmsg(_("Left-to-right:"));
		for(cur_line = linecomp; cur_line != NOLINE; cur_line = cur_line->nextline)
			ttyputmsg(_("  Line: %s"), com_describeline(cur_line));
	}

	/* compute bounds for each line */
	for(cur_line = linecomp; cur_line != NOLINE; cur_line = cur_line->nextline)
		com_computeline_hi_and_low(cur_line, axis);

	/* prevent the stretching line from sliding */
	com_noslide(linestretch);

	/* set rigidity properly */
	com_fixed_nonfixed(linecomp, linestretch);

	/* do the compaction */
	com_lowbound = com_findleastlow(linecomp, axis);
	change = com_lineup_firstrow(linecomp, linestretch, axis, com_lowbound);
	change = com_compact(linecomp, linestretch, axis, change, np);

	/* restore rigidity if no changes were made */
	if (change == 0) com_undo_fixed_nonfixed(linecomp, linestretch);

	/* allow the streteching line to slide again */
	com_slide(linestretch);

	/* free the object data */
	com_clearspace(linecomp);
	com_clearspace(linestretch);
	return(change);
}

/******************** SUPPORT ROUTINES ********************/

void com_createobjects(NODEINST *ni, INTSML axis, OBJECT **thisobject,
	OBJECT **otherobject)
{
	REGISTER OBJECT *new_object, *second_object;
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *other_end;
	REGISTER INTBIG bd_low, bd_high, st_low, st_high;

	/* if node has already been examined, quit now */
	if (ni->temp1 != 0) return;
	ni->temp1 = 1;

	/* if this is the first object, add it */
	if (*thisobject == NOOBJECT)
		*thisobject = com_make_ni_object(ni, NOOBJECT, el_matid, axis, 0,0,0,0);
	if (axis == HORIZONTAL)
	{
		st_low = (*thisobject)->lowx;
		st_high = (*thisobject)->highx;
	} else
	{
		st_low = (*thisobject)->lowy;
		st_high = (*thisobject)->highy;
	}

	/* for each arc on node, find node at other end and add to object */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		ai = pi->conarcinst;
		if (ai->end[0].nodeinst != ni) other_end = ai->end[0].nodeinst; else
			other_end = ai->end[1].nodeinst;

		/* stop if other end has already been examined */
		if (other_end->temp1 != 0) continue;

		new_object = com_make_ai_object(ai, NOOBJECT, el_matid, axis, 0,0,0,0);

		second_object = com_make_ni_object(other_end, NOOBJECT, el_matid,
			axis, 0,0,0,0);

		if (axis == HORIZONTAL)
		{
			bd_low = second_object->lowx;
			bd_high = second_object->highx;
		} else
		{
			bd_low = second_object->lowy;
			bd_high = second_object->highy;
		}
		if (bd_high > st_low && bd_low < st_high)
		{
			com_add_object_to_object(thisobject, new_object);
			com_add_object_to_object(thisobject, second_object);
			com_createobjects(other_end, axis, thisobject, otherobject);
		} else
		{
			/* arcs in object to be used later in fixed_non_fixed */
			com_add_object_to_object(otherobject, new_object);
			com_freeobject(second_object);
		}
	}
}

/*
 * routine to build a "object" structure that describes arc "ai".  If "object"
 * is NOOBJECT, this arc is at the top level, and a new OBJECT should be
 * constructed for it.  Otherwise, the arc is in a subfacet and it must be
 * transformed through "newtrans" and clipped to the two protection frames
 * defined by "low1", "high1" and "low2", "high2" before being added to "object".
 */
OBJECT *com_make_ai_object(ARCINST *ai, OBJECT *object, XARRAY newtrans,
	INTSML axis, INTBIG low1, INTBIG high1, INTBIG low2, INTBIG high2)
{
	REGISTER OBJECT *new_object;
	REGISTER INTBIG j, tot;
	INTBIG lx, hx, ly, hy;
	REGISTER POLYGON *poly;

	/* create the object if at the top level */
	if (object == NOOBJECT)
	{
		new_object = com_allocate_object();
		if (new_object == NOOBJECT) return(NOOBJECT);
		new_object->inst.ai = (ARCINST *)ai;
		new_object->insttype = OBJARCINST;
		new_object->nextobject = NOOBJECT;
		new_object->firstpolylist = NOPOLYLIST;
		new_object->lowx = ai->geom->lowx;
		new_object->highx = ai->geom->highx;
		new_object->lowy = ai->geom->lowy;
		new_object->highy = ai->geom->highy;
	} else new_object = object;

	tot = arcpolys(ai);
	for(j=0; j<tot; j++)
	{
		poly = allocpolygon(4, com_aid->cluster);
		shapearcpoly(ai, j, poly);
		if (poly->layer < 0)
		{
			freepolygon(poly);
			continue;
		}

		/* make sure polygon is within protection frame */
		if (object != NOOBJECT)
		{
			xformpoly(poly, newtrans);
			getbbox(poly, &lx, &hx, &ly, &hy);
			if (axis == HORIZONTAL)
			{
				if ((hx < low1 || lx > high1) && (hx < low2 || lx > high2))
				{
					freepolygon(poly);
					continue;
				}
			} else
			{
				if ((hy < low1 || ly > high1) && (hy < low2 || ly > high2))
				{
					freepolygon(poly);
					continue;
				}
			}
		}

		/* add the polygon */
		com_add_poly_polylist(poly, new_object, (INTSML)ai->temp1,
			ai->proto->tech);
	}
	return(new_object);
}

/*
 * routine to build a object describing node "ni" in axis "axis".  If "object"
 * is NOOBJECT, this node is at the top level, and a new OBJECT should be
 * constructed for it.  Otherwise, the node is in a subfacet and it must be
 * transformed through "newtrans" and clipped to the two protection frames
 * defined by "low1" to "high1" and "low2" to "high2" before being added to
 * "object".
 */
OBJECT *com_make_ni_object(NODEINST *ni, OBJECT *object, XARRAY newtrans,
	INTSML axis, INTBIG low1, INTBIG high1, INTBIG low2, INTBIG high2)
{
	REGISTER OBJECT *new_object;
	XARRAY trans, t1, temp;
	INTBIG lx, hx, ly, hy;
	REGISTER INTBIG pindex;
	REGISTER INTBIG j, tot;
	REGISTER NODEINST *subni;
	REGISTER ARCINST *subai;
	REGISTER POLYGON *poly;

	if (object == NOOBJECT)
	{
		new_object = com_allocate_object();
		if (new_object == NOOBJECT) return(NOOBJECT);
		new_object->inst.ni = ni;
		new_object->insttype = OBJNODEINST;
		new_object->nextobject = NOOBJECT;
		new_object->firstpolylist = NOPOLYLIST;
		new_object->lowx = ni->geom->lowx;
		new_object->highx = ni->geom->highx;
		new_object->lowy = ni->geom->lowy;
		new_object->highy = ni->geom->highy;
	} else new_object = object;

	/* propagate global network info to local port prototypes on "ni" */
	com_fillnode(ni);

	/* create pseudo-object for complex ni */
	if (ni->proto->primindex == 0)
	{
		/* compute transformation matrix from subnode to this space */
		makerot(ni, t1);
		transmult(t1, newtrans, temp);
		maketrans(ni, t1);
		transmult(t1, temp, trans);

		/*
		 * create a line for facet "ni->proto" at the current location and
		 * translation.  Put only the instances which are within com_maxboundary
		 * of the perimeter of the facet.
		 */
		com_subsmash(ni->proto);

		/* compute protection frame if at the top level */
		if (object == NOOBJECT)
		{
			if (axis == HORIZONTAL)
			{
				low1 = ni->geom->lowx;
				high1 = ni->geom->lowx + com_maxboundary;
				low2 = ni->geom->highx - com_maxboundary;
				high2 = ni->geom->highx;
			} else
			{
				low1 = ni->geom->lowy;
				high1 = ni->geom->lowy + com_maxboundary;
				low2 = ni->geom->highy - com_maxboundary;
				high2 = ni->geom->highy;
			}
		}

		/* include polygons from those nodes and arcs in the protection frame */
		for(subni = ni->proto->firstnodeinst; subni != NONODEINST;
			subni = subni->nextnodeinst)
				(void)com_make_ni_object(subni, new_object, trans, axis,
					low1, high1, low2, high2);
		for(subai = ni->proto->firstarcinst; subai != NOARCINST;
			subai = subai->nextarcinst)
				(void)com_make_ai_object(subai, new_object, trans, axis,
					low1, high1, low2, high2);
	} else
	{
		makerot(ni, temp);
		transmult(temp, newtrans, trans);
		tot = nodeEpolys(ni, 0);
		for(j=0; j<tot; j++)
		{
			poly = allocpolygon(4, com_aid->cluster);
			shapeEnodepoly(ni, j, poly);
			xformpoly(poly, trans);

			/* make sure polygon is within protection frame */
			if (object != NOOBJECT)
			{
				getbbox(poly, &lx, &hx, &ly, &hy);
				if (axis == HORIZONTAL)
				{
					if ((hx < low1 || lx > high1) && (hx < low2 || lx > high2))
					{
						freepolygon(poly);
						continue;
					}
				} else
				{
					if ((hy < low1 || ly > high1) && (hy < low2 || ly > high2))
					{
						freepolygon(poly);
						continue;
					}
				}
			}

			if (poly->portproto == NOPORTPROTO) pindex = -1; else
				pindex = poly->portproto->temp1;
			com_add_poly_polylist(poly, new_object, (INTSML)pindex, ni->proto->tech);
		}
	}
	return(new_object);
}

void com_fillnode(NODEINST *ni)
{
	REGISTER PORTPROTO *pp, *opp;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;

	/* initialize network information for this node instance */
	for(pp = ni->proto->firstportproto; pp!=NOPORTPROTO; pp = pp->nextportproto)
		pp->temp1 = 0;

	/* set network numbers from arcs */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		pp = pi->proto;
		if (pp->temp1 != 0) continue;
		pp->temp1 = pi->conarcinst->temp1;
	}

	/* set network numbers from exports */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
	{
		pp = pe->proto;
		if (pp->temp1 != 0) continue;
		pp->temp1 = pe->exportproto->temp1;
	}

	/* look for unconnected ports and assign new network numbers */
	for(pp = ni->proto->firstportproto; pp!=NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 != 0) continue;

		/* look for similar connected port */
		for(opp = ni->proto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
			if (opp->network == pp->network && opp->temp1 != 0)
		{
			pp->temp1 = opp->temp1;
			break;
		}
		if (pp->temp1 == 0) pp->temp1 = com_flatindex++;
	}
}

/*
 * routine to sort line by center val from least to greatest
 */
LINE *com_sort(LINE *line, INTSML axis)
{
	REGISTER LINE *new_line, *cur_line, *bestline;
	REGISTER OBJECT *cur_object;
	REGISTER INTBIG bestval;
	float ave, ctr, len, totallen;
	REGISTER INTSML first;

	if (line == NOLINE)
	{
		ttyputerr(_("Error: com_sort called with null argument"));
		return(NOLINE);
	}

	/* first figure out the weighting factor that will be sorted */
	for(cur_line = line; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		ave = totallen = 0.0;
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
		{
			if (axis == HORIZONTAL)
			{
				len = (float)(cur_object->highy - cur_object->lowy);
				ctr = (cur_object->lowx+cur_object->highx) / 2.0f;
			} else
			{
				len = (float)(cur_object->highx - cur_object->lowx);
				ctr = (cur_object->lowy+cur_object->highy) / 2.0f;
			}

			ctr *= len;
			totallen += len;
			ave += ctr;
		}
		if (totallen != 0) ave /= totallen;
		cur_line->val = (INTBIG)ave;
	}

	/* now sort on the "val" field */
	new_line = NOLINE;
	for(;;)
	{
		if (line == NOLINE) break;
		first = 1;
		for(cur_line = line; cur_line != NOLINE; cur_line = cur_line->nextline)
		{
			if (first != 0)
			{
				bestval = cur_line->val;
				bestline = cur_line;
				first = 0;
			} else if (cur_line->val > bestval)
			{
				bestval = cur_line->val;
				bestline = cur_line;
			}
		}

		/* remove bestline from the list */
		if (bestline->prevline == NOLINE) line = bestline->nextline; else
			bestline->prevline->nextline = bestline->nextline;
		if (bestline->nextline != NOLINE)
			bestline->nextline->prevline = bestline->prevline;

		/* insert at the start of this list */
		if (new_line != NOLINE) new_line->prevline = bestline;
		bestline->nextline = new_line;
		bestline->prevline = NOLINE;
		new_line = bestline;
	}
	return(new_line);
}

void com_computeline_hi_and_low(LINE *line, INTSML axis)
{
	REGISTER INTSML first_time;
	REGISTER INTBIG lx, hx, ly, hy;
	REGISTER OBJECT *cur_object;

	/* find smallest and highest vals for the each object */
	first_time = 1;
	for(cur_object = line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		if (cur_object->insttype == OBJARCINST) continue;
		if (first_time)
		{
			lx = cur_object->lowx;
			hx = cur_object->highx;
			ly = cur_object->lowy;
			hy = cur_object->highy;
			first_time = 0;
		} else
		{
			if (cur_object->lowx < lx) lx = cur_object->lowx;
			if (cur_object->highx > hx) hx = cur_object->highx;
			if (cur_object->lowy < ly) ly = cur_object->lowy;
			if (cur_object->highy > hy) hy = cur_object->highy;
		}
	}
	if (axis == HORIZONTAL)
	{
		line->low = lx;
		line->high = hx;
		line->top = hy;
		line->bottom = ly;
	} else
	{
		line->low = ly;
		line->high = hy;
		line->top = hx;
		line->bottom = lx;
	}
}

INTSML com_lineup_firstrow(LINE *line, LINE *other_line, INTSML axis, INTBIG lowbound)
{
	REGISTER INTBIG i;
	REGISTER INTSML change;

	change = 0;
	i = line->low - lowbound;
	if (i > 0)
	{
		if (com_debug != 0)
			ttyputmsg(_("Lining up the first row to low edge %s"), latoa(lowbound));
		if (axis == HORIZONTAL) change = com_move(line, i, 0, change); else
			change = com_move(line, 0, i, change);
		com_fixed_nonfixed(line, other_line);
	}
	return(change);
}

/*
 * find least low of the line. re-set first line low in the list
 * finds the smallest low value (lowx for VERTICAL, lowy for HORIZ case)
 * stores it in line->low.
 */
INTSML com_findleastlow(LINE *line, INTSML axis)
{
	REGISTER INTSML first_time;
	REGISTER INTBIG low, thislow;
	REGISTER OBJECT *cur_object;

	if (line == NOLINE) return(0);

	/* find smallest low for the each object */
	first_time = 1;
	for(cur_object = line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		if (cur_object->insttype == OBJARCINST) continue;
		if (axis == HORIZONTAL) thislow = cur_object->lowx; else
			thislow = cur_object->lowy;
		if (first_time == 0) low = mini(low, thislow); else
		{
			low = thislow;
			first_time = 0;
		}
	}
	line->low = low;

	return((INTSML)low);
}

INTSML com_compact(LINE *line, LINE *other_line, INTSML axis, INTSML change,
	NODEPROTO *facet)
{
	REGISTER LINE *cur_line, *prev_line;
	REGISTER OBJECT *cur_object, *thisoreason, *otheroreason;
	REGISTER POLYLIST *thispreason, *otherpreason;
	OBJECT *oreason;
	POLYLIST *preason, *opreason;
	REGISTER INTBIG this_motion, best_motion, spread;
	REGISTER VARIABLE *var;

	spread = 0;
	var = getvalkey((INTBIG)com_aid, VAID, VINTEGER, com_spread);
	if (var != NOVARIABLE && var->addr != 0) spread++;

	/* loop through all lines that may compact */
	for(cur_line = line->nextline; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		/* look at every object in the line that may compact */
		best_motion = DEFAULT_VAL;
		thisoreason = otheroreason = NOOBJECT;
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
		{
			/* look at all previous lines */
			for(prev_line = cur_line->prevline; prev_line != NOLINE;
				prev_line = prev_line->prevline)
			{
				/* no need to test this line if it is farther than best motion */
				if (best_motion != DEFAULT_VAL &&
					cur_line->low - prev_line->high > best_motion) continue;

				/* simple object compaction */
				this_motion = com_checkinst(cur_object, prev_line, axis,
					&oreason, &opreason, &preason, facet);
				if (this_motion == DEFAULT_VAL) continue;
				if (best_motion == DEFAULT_VAL || this_motion < best_motion)
				{
					best_motion = this_motion;
					if (oreason != NOOBJECT)
					{
						thisoreason = oreason;
						otheroreason = cur_object;
						thispreason = opreason;
						otherpreason = preason;
					}
				}
			}
		}

		if (best_motion == DEFAULT_VAL)
		{
			/* no constraints: allow overlap */
			best_motion = cur_line->low - com_lowbound;
#if 0
			if (com_debug != 0) ttyputmsg(_("Default motion %s"), latoa(best_motion));
#endif
		}
		if (com_debug != 0)
		{
			ttyputmsg(_("Moving object '%s' by %s"), com_describeline(cur_line),
				latoa(best_motion));
			if (thisoreason != NOOBJECT && otheroreason != NOOBJECT)
			{
				(void)initinfstr();
				if (thispreason->poly->tech != otherpreason->poly->tech ||
					thispreason->poly->layer != otherpreason->poly->layer)
				{
					(void)formatinfstr(_("  Limit is %s, layer %s to %s, layer %s"),
						com_describeobject(thisoreason),
							layername(thispreason->poly->tech, thispreason->poly->layer),
								com_describeobject(otheroreason),
									layername(otherpreason->poly->tech,
										otherpreason->poly->layer));
				} else
				{
					(void)formatinfstr(_("  Limit is %s to %s, layer %s"),
						com_describeobject(thisoreason), com_describeobject(otheroreason),
							layername(thispreason->poly->tech, thispreason->poly->layer));
				}
				ttyputmsg("%s", returninfstr());
			}
		}
		if (best_motion > 0 || (spread != 0 && best_motion < 0))
		{
			if (axis == HORIZONTAL)
				change = com_move(cur_line, best_motion, 0, change); else
					change = com_move(cur_line, 0, best_motion, change);
			com_fixed_nonfixed(line, other_line);
		}
	}
	return(change);
}

INTBIG com_checkinst(OBJECT *object, LINE *line, INTSML axis, OBJECT **oreason,
	POLYLIST **opreason, POLYLIST **preason, NODEPROTO *facet)
{
	REGISTER INTSML pindex, layer;
	REGISTER INTBIG this_motion, best_motion;
	REGISTER POLYGON *poly;
	REGISTER POLYLIST *polys;
	POLYLIST *subpreason;
	OBJECT *suboreason;

	best_motion = DEFAULT_VAL;
	*oreason = NOOBJECT;
	for(polys = object->firstpolylist; polys != NOPOLYLIST;
		polys = polys->nextpolylist)
	{
		poly = polys->poly;
		pindex = polys->networknum;

		/* translate any pseudo layers for this node */
		layer = com_convertpintonode_layer(poly->layer, polys->tech);

		/* find distance line can move toward this poly */
		this_motion = com_minseparate(object, layer, polys, line, axis,
			&suboreason, &subpreason, facet);
		if (this_motion == DEFAULT_VAL) continue;
		if (best_motion == DEFAULT_VAL || this_motion < best_motion)
		{
			best_motion = this_motion;
			*oreason = suboreason;
			*opreason = subpreason;
			*preason = polys;
		}
	}
	return(best_motion);
}

/*
 * this routine finds the minimum distance which is necessary between polygon
 * "obj" (from object "object" on layer "nlayer" with network connectivity
 * "nindex") and the previous line in "line".  It returns the amount
 * to move this object to get it closest to the line (DEFAULT_VAL if they can
 * overlap).  The object "reason" is set to the object that is causing the
 * constraint.
 */
INTBIG com_minseparate(OBJECT *object, INTSML nlayer, POLYLIST *npolys,
	LINE *line, INTSML axis, OBJECT **oreason, POLYLIST **preason,
	NODEPROTO *facet)
{
	REGISTER INTBIG bound, dist, con, geom_lo, ai_hi, ni_hi,
		best_motion, this_motion, fun;
	REGISTER INTSML pindex, nindex, layer;
	REGISTER POLYGON *poly, *npoly;
	INTBIG lx, hx, ly, hy, xl, xh, yl, yh;
	REGISTER OBJECT *cur_object;
	REGISTER POLYLIST *polys;
	REGISTER TECHNOLOGY *tech;

	*oreason = NOOBJECT;
	npoly = npolys->poly;
	tech = npolys->tech;
	nindex = npolys->networknum;

	/* see how far around the box it is necessary to search */
	bound = maxdrcsurround(tech, facet->cell->lib, nlayer);

	/* if there is no separation, allow them to sit on top of each other */
	if (bound < 0) return(DEFAULT_VAL);

	/* can only handle orthogonal rectangles for now */
	if (isbox(npoly, &lx, &hx, &ly, &hy) == 0) return(bound);

	best_motion = DEFAULT_VAL;
	if (axis == HORIZONTAL) geom_lo = object->lowx; else
		geom_lo = object->lowy;

	/* search the line */
	for(cur_object = line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		if (cur_object->insttype == OBJNODEINST)
		{
			if (axis == HORIZONTAL) ni_hi = cur_object->highx; else
				ni_hi = cur_object->highy;

			if ((axis == HORIZONTAL && com_in_bound(ly-bound, hy+bound,
				cur_object->lowy, cur_object->highy)) ||
					(axis == VERTICAL && com_in_bound(lx-bound, hx+bound,
						cur_object->lowx, cur_object->highx)))
			{
				/* examine every layer in this object */
				for(polys = cur_object->firstpolylist; polys != NOPOLYLIST;
					polys = polys->nextpolylist)
				{
					/* don't check between technologies */
					if (polys->tech != tech) continue;

					poly = polys->poly;
					layer = com_convertpintonode_layer(poly->layer, tech);
					pindex = polys->networknum;

					/* see whether the two objects are electrically connected */
					if (pindex == nindex && pindex != -1) con = 1;
						else con = 0;
					if (isbox(poly, &xl, &xh, &yl, &yh) == 0)
					{
						this_motion = geom_lo - ni_hi - bound;
						if (this_motion == DEFAULT_VAL) continue;
						if (this_motion < best_motion || best_motion == DEFAULT_VAL)
						{
							best_motion = this_motion;
							*oreason = cur_object;
							*preason = polys;
						}
						continue;
					}

					/* see how close they can get */
					dist = drcmindistance(tech, facet->cell->lib, nlayer,
						layer, (INTSML)con);
					if (dist < 0) continue;

					/*
					 * special rule for ignoring distance:
					 *   the layers are the same and either:
					 *     they connect and are *NOT* contact layers
					 *   or:
					 *     they don't connect and are implant layers (substrate/well)
					 */
					if (nlayer == layer)
					{
						fun = layerfunction(tech, nlayer) & LFTYPE;
						if (con != 0)
						{
							if (layeriscontact(fun) == 0) continue;
						} else
						{
							if (fun == LFSUBSTRATE || fun == LFWELL ||
								fun == LFIMPLANT) continue;
						}
					}

					/*
					 * if the two layers are located on the y-axis so
					 * that there is no necessary contraint between them
					 */
					if ((axis == HORIZONTAL && !com_in_bound(ly-dist, hy+dist, yl, yh)) ||
						(axis == VERTICAL && !com_in_bound(lx-dist, hx+dist, xl, xh)))
							continue;

					/* check the distance */
					this_motion = com_check(nlayer, nindex, object, xl, xh, yl,
						yh, layer, pindex, cur_object, lx, hx, ly, hy, dist, axis);
					if (this_motion == DEFAULT_VAL) continue;
					if (this_motion < best_motion || best_motion == DEFAULT_VAL)
					{
						best_motion = this_motion;
						*oreason = cur_object;
						*preason = polys;
					}
				}
			}
		} else
		{
			if (axis == HORIZONTAL) ai_hi = cur_object->highx; else
				ai_hi = cur_object->highy;

			if ((axis == HORIZONTAL && com_in_bound(ly-bound, hy+bound,
				cur_object->lowy, cur_object->highy)) ||
					(axis == VERTICAL && com_in_bound(lx-bound, hx+bound,
						cur_object->lowx, cur_object->highx)))
			{
				/* prepare to examine every layer in this arcinst */
				for(polys = cur_object->firstpolylist; polys != NOPOLYLIST;
					polys = polys->nextpolylist)
				{
					/* don't check between technologies */
					if (polys->tech != tech) continue;

					poly = polys->poly;

					/* see whether the two objects are electrically connected */
					pindex = polys->networknum;
					if (nindex == -1 || pindex == nindex) con = 1; else con = 0;

					/* warning: non-manhattan arcs are ignored here */
					if (isbox(poly, &xl, &xh, &yl, &yh) == 0)
					{
						this_motion = geom_lo - ai_hi - bound;
						if (this_motion == DEFAULT_VAL) continue;
						if (this_motion < best_motion || best_motion == DEFAULT_VAL)
						{
							best_motion = this_motion;
							*oreason = cur_object;
							*preason = polys;
						}
						continue;
					}

					/* see how close they can get */
					dist = drcmindistance(tech, facet->cell->lib, nlayer,
						poly->layer, (INTSML)con);
					if (dist < 0) continue;

					/*
					 * if the two layers are so located on the y-axis so
					 * that there is no necessary contraint between them
					 */
					if ((axis == HORIZONTAL && !com_in_bound(ly-dist, hy+dist, yl, yh)) ||
						(axis == VERTICAL && !com_in_bound(lx-dist, hx+dist, xl, xh)))
							continue;

					/* check the distance */
					this_motion = com_check(nlayer, nindex, object, xl, xh, yl,
						yh, poly->layer, pindex, cur_object, lx, hx, ly, hy, dist, axis);
					if (this_motion == DEFAULT_VAL) continue;
					if (this_motion < best_motion || best_motion == DEFAULT_VAL)
					{
						best_motion = this_motion;
						*oreason = cur_object;
						*preason = polys;
					}
				}
			}
		}
	}
	return(best_motion);
}

/*
 * routine to see if the object in "object1" on layer "layer1" with electrical
 * index "index1" comes within "dist" from the object in "object2" on layer
 * "layer2" with electrical index "index2" in the perpendicular axis to "axis".
 * The bounds of object "object1" are (lx1-hx1,ly1-hy1), and the bounds of object
 * "object2" are (lx2-hx2,ly2-hy2).  If the objects are in bounds, the spacing
 * between them is returned.  Otherwise, DEFAULT_VAL is returned.
 */
INTBIG com_check(INTSML layer1, INTSML index1, OBJECT *object1, INTBIG lx1, INTBIG hx1,
	INTBIG ly1, INTBIG hy1, INTSML layer2, INTSML index2, OBJECT *object2, INTBIG lx2,
	INTBIG hx2, INTBIG ly2, INTBIG hy2, INTBIG dist, INTSML axis)
{
	/* crop out parts of a box covered by a similar layer on the other node */
	if (object1->insttype == OBJNODEINST)
	{
		if (com_cropnodeinst(object1->firstpolylist, &lx2, &hx2, &ly2, &hy2,
			layer2, index2))
				return(DEFAULT_VAL);
	}
	if (object2->insttype == OBJNODEINST)
	{
		if (com_cropnodeinst(object2->firstpolylist, &lx1, &hx1, &ly1, &hy1,
			layer1, index1))
				return(DEFAULT_VAL);
	}

	/* now compare the box extents */
	if (axis == HORIZONTAL)
	{
		if (hy1+dist > ly2 && ly1-dist < hy2) return(lx2 - hx1 - dist);
	} else if (hx1+dist > lx2 && lx1-dist < hx2) return(ly2 - hy1 - dist);
	return(DEFAULT_VAL);
}

/*
 * routine to crop the box on layer "nlayer", electrical index "nindex"
 * and bounds (lx-hx, ly-hy) against the nodeinst "ni".  Only those layers
 * in the nodeinst that are the same layer and the same electrical index
 * are checked.  The routine returns non-zero if the bounds are reduced
 * to nothing.
 */
INTSML com_cropnodeinst(POLYLIST *polys, INTBIG *bx, INTBIG *by,
	INTBIG *ux, INTBIG *uy, INTSML nlayer, INTSML nindex)
{
	INTBIG xl, xh, yl, yh;
	REGISTER INTBIG temp;
	REGISTER POLYLIST *cur_polys;

	for(cur_polys = polys; cur_polys != NOPOLYLIST;
		cur_polys = cur_polys->nextpolylist)
	{
		if (cur_polys->networknum != nindex) continue;
		if (cur_polys->poly->layer != nlayer) continue;
		if (isbox(cur_polys->poly, &xl, &xh, &yl, &yh) == 0) continue;
		if (xl > xh) { temp = xl; xl = xh; xh = temp; }
		if (yl > yh) { temp = yl; yl = yh; yh = temp; }
		temp = cropbox(bx, ux, by, uy, xl, xh, yl, yh);
		if (temp > 0) return(1);
	}
	return(0);
}

INTSML com_in_bound(INTBIG ll, INTBIG lh, INTBIG rl, INTBIG rh)
{
	if (rh > ll && rl < lh) return(1);
	return(0);
}

/*
 * this routine temporarily makes all arcs in fixline rigid and those
 * in nfixline nonrigid in order to move fixline over
 */
void com_fixed_nonfixed(LINE *fixline, LINE *nfixline)
{
	REGISTER LINE *cur_line;
	REGISTER OBJECT *cur_object;

	for(cur_line = fixline; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
				if (cur_object->insttype == OBJARCINST)    /* arc rigid */
					(void)(*el_curconstraint->setobject)((INTBIG)cur_object->inst.ai,
						VARCINST, CHANGETYPETEMPRIGID, 0);
	}
	for(cur_line = nfixline; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
				if (cur_object->insttype == OBJARCINST)   /* arc unrigid */
					(void)(*el_curconstraint->setobject)((INTBIG)cur_object->inst.ai,
						VARCINST, CHANGETYPETEMPUNRIGID, 0);
	}
}

/*
 * this routine resets temporary changes to arcs in fixline and nfixline
 * so that they are back to their default values.
 */
void com_undo_fixed_nonfixed(LINE *fixline, LINE *nfixline)
{
	REGISTER LINE *cur_line;
	REGISTER OBJECT *cur_object;

	for(cur_line = fixline; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
				if (cur_object->insttype == OBJARCINST)
					(void)(*el_curconstraint->setobject)
						((INTBIG)cur_object->inst.ai, VARCINST,
							CHANGETYPEREMOVETEMP, 0);
	}
	for(cur_line = nfixline; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
				if (cur_object->insttype == OBJARCINST)
					(void)(*el_curconstraint->setobject)
						((INTBIG)cur_object->inst.ai, VARCINST,
							CHANGETYPEREMOVETEMP, 0);
	}
}

/*
 * set the CANTSLIDE bit of userbits for each object in line so that this
 * line will not slide.
 */
void com_noslide(LINE *line)
{
	REGISTER OBJECT *cur_object;
	REGISTER ARCINST *ai;
	REGISTER LINE *cur_line;

	for(cur_line = line; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
		{
			if (cur_object->insttype == OBJARCINST)
			{
				ai = cur_object->inst.ai;
				ai->temp2 = ai->userbits;
				ai->userbits |= CANTSLIDE;
			}
		}
	}
}

/*
 * restore the CANTSLIDE bit of userbits for each object in line
 */
void com_slide(LINE *line)
{
	REGISTER OBJECT *cur_object;
	REGISTER ARCINST *ai;
	REGISTER LINE *cur_line;

	for(cur_line = line; cur_line != NOLINE; cur_line = cur_line->nextline)
	{
		for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
			cur_object = cur_object->nextobject)
		{
			if (cur_object->insttype == OBJARCINST)
			{
				ai = cur_object->inst.ai;
				ai->userbits = (ai->userbits & ~CANTSLIDE) |
					(ai->temp2&CANTSLIDE);
			}
		}
	}
}

/*
 * moves a object of instances distance (movex, movey), and returns a 1 if
 * there is actually a move
 */
INTSML com_move(LINE *line, INTBIG movex, INTBIG movey, INTSML change)
{
	REGISTER OBJECT *cur_object;
	REGISTER POLYLIST *polys;
	REGISTER INTBIG move;
	REGISTER INTSML i;

	if (movex == 0) move = movey; else move = movex;
	if (line == NOLINE) return(0);
	if (!change && move != 0)
	{
		(void)askaid(us_aid, "clear");
		change = 1;
	}

	for(cur_object = line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		if (cur_object->insttype == OBJNODEINST)
		{
			startobjectchange((INTBIG)cur_object->inst.ni, VNODEINST);
			modifynodeinst(cur_object->inst.ni, -movex, -movey, -movex, -movey, 0, 0);
			endobjectchange((INTBIG)cur_object->inst.ni, VNODEINST);
			break;
		}
	}

	for(cur_object = line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		cur_object->lowx -= movex;
		cur_object->highx -= movex;
		cur_object->lowy -= movey;
		cur_object->highy -= movey;
		for(polys = cur_object->firstpolylist; polys != NOPOLYLIST;
			polys = polys->nextpolylist)
		{
			for(i=0; i<polys->poly->count; i++)
			{
				polys->poly->xv[i] -= movex;
				polys->poly->yv[i] -= movey;
			}
		}
	}
	line->high -= move;
	line->low -= move;

	return(change);
}

/*
 * convert a PIN layer to its corresponding real layer.  Need this since the
 * design rules require "real" layers.
 */
INTSML com_convertpintonode_layer(INTSML layer, TECHNOLOGY *tech)
{
	REGISTER TECH_ARRAY *cur_technology;
	REGISTER INTSML newlay;

	/* null layers do not convert */
	if (layer < 0) return(layer);

	/* make sure range of layers is valid */
	if (layer >= tech->layercount)
	{
		ttyputmsg(_("Compactor cannot find layer %d of technology %s"),
			layer, tech->techname);
		return(layer);
	}

	/* find this technology */
	for(cur_technology = com_conv_layer; cur_technology != NOTECHARRAY;
		cur_technology = cur_technology->nexttecharray)
			if (cur_technology->taindex == tech->techindex) break;

	/* no technology: warn and return */
	if (cur_technology == NOTECHARRAY)
	{
		ttyputmsg(_("Compactor cannot find technology %s"), tech->techname);
		return(layer);
	}

	/* convert the layer */
	newlay = cur_technology->layer[layer];

	/* make sure conversion is valid */
	if (newlay >= tech->layercount)
	{
		ttyputmsg(_("Converted layer %d to bogus layer %d in technology %s"),
			layer, newlay, tech->techname);
		return(layer);
	}
	return(newlay);
}

/*
 * copy network information from ports to arcs in facet "topfacet"
 */
void com_subsmash(NODEPROTO *topfacet)
{
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai, *oai;

	/* first erase the arc node information */
	for(ai = topfacet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;

	/* copy network information from ports to arcs */
	for(ai = topfacet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		/* ignore arcs that have already been numbered */
		if (ai->temp1 != 0) continue;

		/* see if this arc connects to a port */
		for(pp = topfacet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (pp->network == ai->network)
		{
			/* propagate port numbers into all connecting arcs */
			for(oai = topfacet->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
				if (oai->network == ai->network) oai->temp1 = pp->temp1;
			break;
		}

		/* if not connected to a port, this is an internal network */
		if (pp == NOPORTPROTO)
		{
			/* copy new net number to all of these connected arcs */
			for(oai = topfacet->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
				if (oai->network == ai->network) oai->temp1 = com_flatindex;
			com_flatindex++;
		}
	}
}

/**************************** ALLOCATION ****************************/

/*
 * routine to link polygon "poly" into object "object" with network number
 * "networknum"
 */
void com_add_poly_polylist(POLYGON *poly, OBJECT *object, INTSML networknum,
	TECHNOLOGY *tech)
{
	REGISTER POLYLIST *new_polys;

	new_polys = (POLYLIST *)emalloc((sizeof(POLYLIST)), com_aid->cluster);
	if (new_polys == 0) return;
	new_polys->poly = poly;
	new_polys->tech = tech;
	new_polys->networknum = networknum;
	new_polys->nextpolylist = object->firstpolylist;
	object->firstpolylist = new_polys;
}

OBJECT *com_allocate_object(void)
{
	REGISTER OBJECT *cur_object;

	cur_object = (OBJECT *)emalloc((sizeof(OBJECT)), com_aid->cluster);
	if (cur_object == 0) return(NOOBJECT);
	cur_object->nextobject = NOOBJECT;
	return(cur_object);
}

/*
 * add object add_object to the beginning of list "*object"
 */
void com_add_object_to_object(OBJECT **object, OBJECT *add_object)
{
	add_object->nextobject = *object;
	*object = add_object;
}

/*
 * create a new line with the element object and add it to the beginning of
 * the given line
 */
LINE *com_make_object_line(LINE *line, OBJECT *object)
{
	REGISTER LINE *new_line;

	new_line = (LINE *)emalloc((sizeof(LINE)), com_aid->cluster);
	if (new_line == 0) return(NOLINE);
	new_line->nextline = line;
	new_line->prevline = NOLINE;
	new_line->firstobject = object;
	if (line != NOLINE) line->prevline = new_line;
	return(new_line);
}

/* free up allocated space for all objects and lines */
void com_clearspace(LINE *line)
{
	REGISTER LINE *cur_line, *next_line;
	REGISTER OBJECT *this_object;

	for(cur_line = line; cur_line != NOLINE; cur_line = next_line)
	{
		next_line = cur_line->nextline;

		/* erase all objects in the line */
		while (cur_line->firstobject != NOOBJECT)
		{
			this_object = cur_line->firstobject;
			cur_line->firstobject = this_object->nextobject;
			com_freeobject(this_object);
		}

		/* erase the line */
		efree((char *)cur_line);
	}
}

void com_freeobject(OBJECT *cur_object)
{
	REGISTER POLYLIST *polys;

	/* erase all polygons in the object */
	while (cur_object->firstpolylist != NOPOLYLIST)
	{
		polys = cur_object->firstpolylist;
		cur_object->firstpolylist = polys->nextpolylist;
		freepolygon(polys->poly);
		efree((char *)polys);
	}

	/* erase the object */
	efree((char *)cur_object);
}

/******************** DEBUGGING ********************/

/* debugging routine to return a description of line "cur_line" */
char *com_describeline(LINE *cur_line)
{
	(void)initinfstr();
	com_addlinedescription(cur_line);
	return(returninfstr());
}

void com_addlinedescription(LINE *cur_line)
{
	REGISTER OBJECT *cur_object;

	for(cur_object = cur_line->firstobject; cur_object != NOOBJECT;
		cur_object = cur_object->nextobject)
	{
		if (cur_object != cur_line->firstobject) (void)addtoinfstr(' ');
		if (cur_object->insttype == OBJARCINST)
			(void)addstringtoinfstr(describearcinst(cur_object->inst.ai)); else
				(void)addstringtoinfstr(describenodeinst(cur_object->inst.ni));
	}
}

/* debugging routine to return a description of object "cur_object" */
char *com_describeobject(OBJECT *cur_object)
{
	if (cur_object->insttype == OBJARCINST)
		return(describearcinst(cur_object->inst.ai));
	return(describenodeinst(cur_object->inst.ni));
}

/****************************** DIALOG ******************************/

/*
 * special case for the "Compaction Options" dialog
 * Allow spreading = 3 (check)
 * Verbose         = 4 (check)
 */
void comp_optionsdlog(void)
{
	REGISTER INTBIG itemHit, spread, origspread;
	REGISTER VARIABLE *var;

	if (DiaInitDialog(&com_optionsdialog) != 0) return;
	var = getval((INTBIG)com_aid, VAID, VINTEGER, "COM_spread");
	if (var == NOVARIABLE) origspread = 0; else origspread = var->addr;
	if (origspread != 0) DiaSetControl(3, 1);
	if (com_debug != 0) DiaSetControl(4, 1);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 3 || itemHit == 4)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		if (DiaGetControl(3) != 0) spread = 1; else
			spread = 0;
		if (spread != origspread)
			(void)setval((INTBIG)com_aid, VAID, "COM_spread", spread, VINTEGER);
		if (DiaGetControl(4) != 0) com_debug = 1; else
			com_debug = 0;
	}
	DiaDoneDialog();
}

#endif  /* COMAID - at top */
