1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: conlin.c
6  * Linear inequality constraint system
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2001 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 #include "global.h"
32 #include "database.h"
33 #include "conlin.h"
34 
35 /* command completion table for this constraint solver */
36 static COMCOMP cli_linconsp = {NOKEYWORD, topofcells, nextcells, NOPARAMS,
37 	INPUTOPT, x_(" \t"), M_("cell to solve for linear inequalities"), 0};
38 static KEYWORD linconiopt[] =
39 {
40 	{x_("on"),                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
41 	{x_("off"),                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
42 	TERMKEY
43 };
44 static COMCOMP cli_linconip = {linconiopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
45 	INPUTOPT, x_(" \t"), M_("mode that assumes manhattan wires"), 0};
46 static KEYWORD lincontopt[] =
47 {
48 	{x_("go"),                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
49 	{x_("name-nodes"),           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
50 	{x_("highlight-equivalent"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
51 	TERMKEY
52 };
53 static COMCOMP cli_lincontp = {lincontopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
54 	INPUTOPT, x_(" \t"), M_("text-equivalent mode"), 0};
55 static KEYWORD linconopt[] =
56 {
57 	{x_("solve"),                1,{&cli_linconsp,NOKEY,NOKEY,NOKEY,NOKEY}},
58 	{x_("debug-toggle"),         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
59 	{x_("implicit-manhattan"),   1,{&cli_linconip,NOKEY,NOKEY,NOKEY,NOKEY}},
60 	{x_("text-equivalence"),     1,{&cli_lincontp,NOKEY,NOKEY,NOKEY,NOKEY}},
61 	TERMKEY
62 };
63 COMCOMP cli_linconp = {linconopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
64 	0, x_(" \t"), M_("linear constraint system option"), 0};
65 
66 /* global data in linear constraints */
67        CONSTRAINT *cli_constraint;	/* the constraint object for this solver */
68        BOOLEAN     cli_manhattan;	/* true to assume Manhattan constraints */
69        INTBIG      cli_properties_key;	/* variable key for "CONLIN_properties" */
70        NODEPROTO  *cli_curcell;		/* the current cell being equated to text */
71        BOOLEAN     cli_ownchanges;	/* true if changes are internally generated */
72        INTBIG      cli_textlines;	/* number of declaration/connection lines */
73        BOOLEAN     cli_texton;		/* true if text/graphics system is on */
74 static CHAR       *cli_linconops[] = {x_(">="), x_("=="), x_("<=")};
75 
76 static BOOLEAN cli_conlindebug;
77 static BOOLEAN cli_didsolve;
78 
79 #define NOCONNODE ((CONNODE *)-1)
80 typedef struct Iconnode
81 {
82 	NODEINST  *realn;			/* actual node that is constrained */
83 	INTBIG     value;			/* value set on this node */
84 	BOOLEAN    solved;			/* flag for marking this node as solved */
85 	BOOLEAN    loopflag;		/* flag for solving loops */
86 	struct Iconarc *firstconarc;/* head of list of constraints on this node */
87 	struct Iconnode *nextconnode;/* next in list of constraint nodes */
88 } CONNODE;
89 
90 #define NOCONARC ((CONARC *)-1)
91 typedef struct Iconarc
92 {
93 	ARCINST *reala;				/* actual arc that is constrained */
94 	CONNODE *other;				/* other node being constrained */
95 	INTBIG oper;				/* operator of the constraint */
96 	INTBIG value;				/* value of the constraint */
97 	struct Iconarc *nextconarc;	/* next constraint in linked list */
98 } CONARC;
99 
100 static CONARC *cli_conarcfree;	/* list of free constraint arc modules */
101 static CONNODE *cli_connodefree;/* list of free constraint node modules */
102 
103 /* references to routines in "conlay.c"*/
104 extern void cla_adjustmatrix(NODEINST*, PORTPROTO*, XARRAY);
105 extern void cla_oldportposition(NODEINST*, PORTPROTO*, INTBIG*, INTBIG*);
106 
107 /* prototypes for local routines */
108 static CONARC *cli_allocconarc(void);
109 static void cli_freeconarc(CONARC*);
110 static CONNODE *cli_allocconnode(void);
111 static void cli_freeconnode(CONNODE*);
112 static void cli_dosolve(NODEPROTO*);
113 static BOOLEAN cli_buildconstraints(NODEPROTO*, CONNODE**, BOOLEAN, BOOLEAN);
114 static void cli_addconstraint(ARCINST*, CONNODE**, INTBIG, INTBIG, INTBIG, INTBIG, BOOLEAN);
115 static void cli_adjustconstraints(CONNODE*, BOOLEAN);
116 static BOOLEAN cli_solveconstraints(CONNODE*, BOOLEAN);
117 static BOOLEAN cli_satisfy(CONNODE*, CONARC*);
118 static void cli_applyconstraints(NODEPROTO*, CONNODE*, BOOLEAN);
119 static void cli_deleteconstraints(CONNODE*);
120 static void cli_printconstraints(CONNODE*, CHAR*, CHAR*);
121 static void cli_nameallnodes(NODEPROTO*);
122 
123 /****************************** ALLOCATION ******************************/
124 
125 /*
126  * routine to allocate a constraint arc module from the pool (if any) or memory
127  */
cli_allocconarc(void)128 CONARC *cli_allocconarc(void)
129 {
130 	REGISTER CONARC *c;
131 
132 	if (cli_conarcfree == NOCONARC)
133 	{
134 		c = (CONARC *)emalloc(sizeof (CONARC), cli_constraint->cluster);
135 		if (c == 0) return(NOCONARC);
136 	} else
137 	{
138 		c = cli_conarcfree;
139 		cli_conarcfree = (CONARC *)c->nextconarc;
140 	}
141 	return(c);
142 }
143 
144 /*
145  * routine to return constraint arc module "c" to the pool of free modules
146  */
cli_freeconarc(CONARC * c)147 void cli_freeconarc(CONARC *c)
148 {
149 	c->nextconarc = cli_conarcfree;
150 	cli_conarcfree = c;
151 }
152 
153 /*
154  * routine to allocate a constraint node module from the pool (if any) or memory
155  */
cli_allocconnode(void)156 CONNODE *cli_allocconnode(void)
157 {
158 	REGISTER CONNODE *c;
159 
160 	if (cli_connodefree == NOCONNODE)
161 	{
162 		c = (CONNODE *)emalloc(sizeof (CONNODE), cli_constraint->cluster);
163 		if (c == 0) return(NOCONNODE);
164 	} else
165 	{
166 		c = cli_connodefree;
167 		cli_connodefree = (CONNODE *)c->nextconnode;
168 	}
169 	return(c);
170 }
171 
172 /*
173  * routine to return constraint node module "c" to the pool of free modules
174  */
cli_freeconnode(CONNODE * c)175 void cli_freeconnode(CONNODE *c)
176 {
177 	c->nextconnode = cli_connodefree;
178 	cli_connodefree = c;
179 }
180 
181 /****************************** DATABASE HOOKS ******************************/
182 
cli_linconinit(CONSTRAINT * con)183 void cli_linconinit(CONSTRAINT *con)
184 {
185 	if (con != NOCONSTRAINT) cli_constraint = con; else
186 	{
187 		/* only function during pass 2 of initialization */
188 		cli_conarcfree = NOCONARC;
189 		cli_connodefree = NOCONNODE;
190 		cli_conlindebug = FALSE;
191 		cli_didsolve = FALSE;
192 		cli_curcell = NONODEPROTO;
193 		cli_manhattan = TRUE;
194 
195 		/* get the primary key for the properties variable */
196 		cli_properties_key = makekey(x_("CONLIN_properties"));
197 
198 		/* initialize the text/graphics equivalence system */
199 		cli_ownchanges = FALSE;
200 		cli_texton = FALSE;
201 	}
202 }
203 
cli_linconterm(void)204 void cli_linconterm(void) {}
205 
cli_linconsetmode(INTBIG count,CHAR * par[])206 void cli_linconsetmode(INTBIG count, CHAR *par[])
207 {
208 	REGISTER INTBIG l;
209 	REGISTER CHAR *pp;
210 	REGISTER NODEPROTO *np;
211 	REGISTER GEOM *geom;
212 
213 	if (count == 0)
214 	{
215 		ttyputusage(x_("constraint tell linear OPTION"));
216 		return;
217 	}
218 
219 	if (el_curconstraint != cli_constraint)
220 	{
221 		ttyputerr(M_("Must first switch to this solver with 'constraint use'"));
222 		return;
223 	}
224 
225 	l = estrlen(pp = par[0]);
226 
227 	if (namesamen(pp, x_("debug-toggle"), l) == 0 && l >= 1)
228 	{
229 		cli_conlindebug = !cli_conlindebug;
230 		if (cli_conlindebug) ttyputverbose(M_("Linear constraint debugging on")); else
231 			ttyputverbose(M_("Linear constraint debugging off"));
232 		return;
233 	}
234 
235 	if (namesamen(pp, x_("text-equivalence"), l) == 0 && l >= 1)
236 	{
237 		if (count < 2)
238 		{
239 			ttyputusage(x_("constraint tell linear text-equivalence OPTION"));
240 			return;
241 		}
242 		l = estrlen(pp = par[1]);
243 		if (namesamen(pp, x_("name-nodes"), l) == 0 && l >= 1)
244 		{
245 			np = getcurcell();
246 			if (np == NONODEPROTO)
247 			{
248 				ttyputerr(M_("Must have a current cell to generate names"));
249 				return;
250 			}
251 			cli_nameallnodes(np);
252 			return;
253 		}
254 		if (namesamen(pp, x_("go"), l) == 0 && l >= 1)
255 		{
256 			np = getcurcell();
257 			if (np == NONODEPROTO) ttyputverbose(M_("Enabling text/graphics association")); else
258 			{
259 				if (cli_texton)
260 				{
261 					if (np == cli_curcell)
262 						ttyputverbose(M_("Reevaluating textual description")); else
263 							ttyputverbose(M_("Switching association to cell %s"), describenodeproto(np));
264 				} else ttyputverbose(M_("Text window will mirror cell %s"), describenodeproto(np));
265 			}
266 			cli_maketextcell(np);
267 			return;
268 		}
269 		if (namesamen(pp, x_("highlight-equivalent"), l) == 0 && l >= 1)
270 		{
271 			geom = (GEOM *)asktool(us_tool, x_("get-object"));
272 			if (geom == NOGEOM)
273 			{
274 				ttyputerr(M_("Find a single object first"));
275 				return;
276 			}
277 			cli_highlightequivalent(geom);
278 			return;
279 		}
280 		ttyputbadusage(x_("constraint tell linear text-equivalence"));
281 		return;
282 	}
283 
284 	if (namesamen(pp, x_("implicit-manhattan"), l) == 0 && l >= 1)
285 	{
286 		if (count < 2) l = estrlen(pp = x_("X")); else l = estrlen(pp = par[1]);
287 		if (namesame(pp, x_("on")) == 0)
288 		{
289 			cli_manhattan = TRUE;
290 			ttyputverbose(M_("Manhattan arcs will be kept that way"));
291 			return;
292 		}
293 		if (namesamen(pp, x_("off"), l) == 0 && l >= 2)
294 		{
295 			cli_manhattan = FALSE;
296 			ttyputverbose(M_("No assumptions will be made about Manhattan arcs"));
297 			return;
298 		}
299 		ttyputusage(x_("constraint tell linear implicit-manhattan (on|off)"));
300 		return;
301 	}
302 
303 	if (namesamen(pp, x_("solve"), l) == 0 && l >= 1)
304 	{
305 		if (count < 2)
306 		{
307 			np = getcurcell();
308 			if (np == NONODEPROTO)
309 			{
310 				ttyputerr(M_("No cell specified and no current cell"));
311 				return;
312 			}
313 		} else
314 		{
315 			np = getnodeproto(par[1]);
316 			if (np == NONODEPROTO)
317 			{
318 				ttyputerr(M_("No cell named %s"), par[1]);
319 				return;
320 			}
321 			if (np->primindex != 0)
322 			{
323 				ttyputerr(M_("Must solve cells, not primitives"));
324 				return;
325 			}
326 		}
327 
328 		/* do constraint work */
329 		cli_solvecell(np, TRUE, TRUE);
330 		cli_didsolve = TRUE;
331 		return;
332 	}
333 	ttyputbadusage(x_("constraint tell linear"));
334 }
335 
336 /*
337  * the valid "command"s are:
338  *    "describearc"  return a string that describes constraints on arc in "arg1".
339  */
cli_linconrequest(CHAR * command,INTBIG arg1)340 INTBIG cli_linconrequest(CHAR *command, INTBIG arg1)
341 {
342 	REGISTER INTBIG i, len, l;
343 	REGISTER VARIABLE *var;
344 	REGISTER LINCON *conptr;
345 	REGISTER void *infstr;
346 
347 	l = estrlen(command);
348 
349 	if (namesamen(command, x_("describearc"), l) == 0)
350 	{
351 		infstr = initinfstr();
352 		var = getvalkey(arg1, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
353 		if (var != NOVARIABLE)
354 		{
355 			len = getlength(var) / LINCONSIZE;
356 			for(i=0; i<len; i++)
357 			{
358 				conptr = &((LINCON *)var->addr)[i];
359 				if (i != 0) addstringtoinfstr(infstr, x_(", "));
360 				addtoinfstr(infstr, (CHAR)(conptr->variable<=1 ? 'X' : 'Y'));
361 				addstringtoinfstr(infstr, cli_linconops[conptr->oper]);
362 				addstringtoinfstr(infstr, frtoa(conptr->value));
363 			}
364 		}
365 		return((INTBIG)returninfstr(infstr));
366 	}
367 	return(0);
368 }
369 
cli_linconsolve(NODEPROTO * np)370 void cli_linconsolve(NODEPROTO *np)
371 {
372 	REGISTER CHANGECELL *cc;
373 	REGISTER CHANGEBATCH *curbatch;
374 
375 	if (np != NONODEPROTO)
376 	{
377 		cli_dosolve(np);
378 		return;
379 	}
380 
381 	cli_eq_solve();
382 
383 	curbatch = db_getcurrentbatch();
384 	if (curbatch != NOCHANGEBATCH)
385 		for(cc = curbatch->firstchangecell; cc != NOCHANGECELL; cc = cc->nextchangecell)
386 			cli_dosolve(cc->changecell);
387 	cli_didsolve = FALSE;
388 }
389 
cli_dosolve(NODEPROTO * cell)390 void cli_dosolve(NODEPROTO *cell)
391 {
392 	INTBIG nlx, nhx, nly, nhy;
393 	REGISTER NODEINST *ni;
394 	REGISTER INTBIG dlx, dhx, dly, dhy;
395 
396 	if (cli_conlindebug)
397 		ttyputmsg(M_("Re-checking linear constraints for cell %s"), describenodeproto(cell));
398 	if (!cli_didsolve) cli_solvecell(cell, FALSE, FALSE);
399 
400 	/* get current boundary of cell */
401 	db_boundcell(cell, &nlx,&nhx, &nly,&nhy);
402 	dlx = nlx - cell->lowx;  dhx = nhx - cell->highx;
403 	dly = nly - cell->lowy;  dhy = nhy - cell->highy;
404 
405 	/* quit if it has not changed */
406 	if (dlx == 0 && dhx == 0 && dly == 0 && dhy == 0) return;
407 
408 	/* update the cell size */
409 	cell->changeaddr = (CHAR *)db_change((INTBIG)cell, NODEPROTOMOD,
410 		cell->lowx, cell->lowy, cell->highx, cell->highy, 0, 0);
411 	cell->lowx = nlx;  cell->highx = nhx;
412 	cell->lowy = nly;  cell->highy = nhy;
413 
414 	/* now update instances of the cell */
415 	for(ni = cell->firstinst; ni != NONODEINST; ni = ni->nextinst)
416 	{
417 		(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
418 		cli_linconmodifynodeinst(ni, dlx, dly, dhx, dhy, 0, 0);
419 		(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
420 	}
421 }
422 
cli_linconnewobject(INTBIG addr,INTBIG type)423 void cli_linconnewobject(INTBIG addr, INTBIG type)
424 {
425 	REGISTER ARCINST *ai;
426 
427 	switch (type&VTYPE)
428 	{
429 		case VNODEINST: cli_eq_newnode((NODEINST *)addr);   break;
430 		case VARCINST:
431 			ai = (ARCINST *)addr;
432 			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
433 			{
434 				db_setchangecell(ai->parent);
435 				db_forcehierarchicalanalysis(ai->parent);
436 			}
437 			cli_eq_newarc(ai);
438 			break;
439 		case VPORTPROTO: cli_eq_newport((PORTPROTO *)addr);   break;
440 	}
441 }
442 
cli_linconkillobject(INTBIG addr,INTBIG type)443 void cli_linconkillobject(INTBIG addr, INTBIG type)
444 {
445 	REGISTER ARCINST *ai;
446 
447 	switch (type&VTYPE)
448 	{
449 		case VNODEINST: cli_eq_killnode((NODEINST *)addr);   break;
450 		case VARCINST:
451 			ai = (ARCINST *)addr;
452 			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
453 			{
454 				db_setchangecell(ai->parent);
455 				db_forcehierarchicalanalysis(ai->parent);
456 			}
457 			cli_eq_killarc(ai);
458 			break;
459 		case VPORTPROTO: cli_eq_killport((PORTPROTO *)addr);   break;
460 	}
461 }
462 
cli_linconmodifynodeinst(NODEINST * ni,INTBIG dlx,INTBIG dly,INTBIG dhx,INTBIG dhy,INTBIG drot,INTBIG dtrans)463 void cli_linconmodifynodeinst(NODEINST *ni, INTBIG dlx, INTBIG dly, INTBIG dhx, INTBIG dhy,
464 	INTBIG drot, INTBIG dtrans)
465 {
466 	REGISTER INTSML oldang, oldtrans;
467 	REGISTER INTBIG oldlx, oldhx, oldly, oldhy, ox, oy, oldxA, oldyA, oldxB, oldyB, oldlen,
468 		thisend, thatend;
469 	INTBIG ax[2], ay[2], onox, onoy, nox, noy;
470 	XARRAY trans;
471 	REGISTER PORTARCINST *pi;
472 	REGISTER ARCINST *ai;
473 	REGISTER CHANGE *cha;
474 
475 	/* if simple rotation on transposed nodeinst, reverse rotation */
476 	if (ni->transpose != 0 && dtrans == 0) drot = (3600 - drot) % 3600;
477 
478 	/* make changes to the nodeinst */
479 	oldang = ni->rotation;     ni->rotation = (INTSML)((ni->rotation + drot) % 3600);
480 	oldtrans = ni->transpose;  ni->transpose = (INTSML)((ni->transpose + dtrans) & 1);
481 	oldlx = ni->lowx;          ni->lowx += dlx;
482 	oldhx = ni->highx;         ni->highx += dhx;
483 	oldly = ni->lowy;          ni->lowy += dly;
484 	oldhy = ni->highy;         ni->highy += dhy;
485 	updategeom(ni->geom, ni->parent);
486 
487 	/* mark that this nodeinst has changed */
488 	ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTMOD, oldlx, oldly,
489 		oldhx, oldhy, oldang, oldtrans);
490 
491 	/* look at all of the arcs on this nodeinst */
492 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
493 	{
494 		ai = pi->conarcinst;
495 
496 		/* figure where each end of the arcinst is, ignore internal arcs */
497 		thisend = thatend = 0;
498 		if (ai->end[0].nodeinst == ni) thatend++;
499 		if (ai->end[1].nodeinst == ni) thisend++;
500 		if (thisend == thatend)
501 		{
502 			/* arc is internal: compute old center of nodeinst */
503 			cha = (CHANGE *)ni->changeaddr;
504 			ox = (cha->p1 + cha->p3) / 2;
505 			oy = (cha->p2 + cha->p4) / 2;
506 
507 			/* prepare transformation matrix */
508 			makeangle(drot, dtrans, trans);
509 
510 			/* determine the new ends of the arcinst */
511 			cla_adjustmatrix(ni, ai->end[0].portarcinst->proto, trans);
512 			xform(ai->end[0].xpos-ox, ai->end[0].ypos-oy, &ax[0], &ay[0], trans);
513 			cla_adjustmatrix(ni, ai->end[1].portarcinst->proto, trans);
514 			xform(ai->end[1].xpos-ox, ai->end[1].ypos-oy, &ax[1], &ay[1], trans);
515 		} else
516 		{
517 			/* external arc: compute its position on modified nodeinst */
518 			cla_oldportposition(ni,ai->end[thisend].portarcinst->proto,&onox,&onoy);
519 			portposition(ni, ai->end[thisend].portarcinst->proto, &nox, &noy);
520 			ax[thisend] = ai->end[thisend].xpos + nox - onox;
521 			ay[thisend] = ai->end[thisend].ypos + noy - onoy;
522 
523 			/* set position of other end of arcinst */
524 			ax[thatend] = ai->end[thatend].xpos;
525 			ay[thatend] = ai->end[thatend].ypos;
526 		}
527 
528 		/* check for null arcinst motion */
529 		if (ax[0] == ai->end[0].xpos && ay[0] == ai->end[0].ypos &&
530 			ax[1] == ai->end[1].xpos && ay[1] == ai->end[1].ypos) continue;
531 
532 		/* demand constraint satisfaction if arc with properties moved */
533 		if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
534 		{
535 			db_setchangecell(ai->parent);
536 			db_forcehierarchicalanalysis(ai->parent);
537 		}
538 
539 		(void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
540 
541 		/* save old state */
542 		oldxA = ai->end[0].xpos;   oldyA = ai->end[0].ypos;
543 		oldxB = ai->end[1].xpos;   oldyB = ai->end[1].ypos;
544 		oldlen = ai->length;
545 
546 		/* set the proper arcinst position */
547 		ai->end[0].xpos = ax[0];
548 		ai->end[0].ypos = ay[0];
549 		ai->end[1].xpos = ax[1];
550 		ai->end[1].ypos = ay[1];
551 		ai->length = computedistance(ax[0],ay[0], ax[1],ay[1]);
552 		determineangle(ai);
553 		updategeom(ai->geom, ai->parent);
554 
555 		/* announce new arcinst change */
556 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldxA,
557 			oldyA, oldxB, oldyB, ai->width, oldlen);
558 		(void)db_change((INTBIG)ai, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
559 		cli_eq_modarc(ai);
560 	}
561 	cli_eq_modnode(ni);
562 }
563 
cli_linconmodifynodeinsts(INTBIG count,NODEINST ** ni,INTBIG * dlx,INTBIG * dly,INTBIG * dhx,INTBIG * dhy,INTBIG * drot,INTBIG * dtrans)564 void cli_linconmodifynodeinsts(INTBIG count, NODEINST **ni, INTBIG *dlx, INTBIG *dly,
565 	INTBIG *dhx, INTBIG *dhy, INTBIG *drot, INTBIG *dtrans)
566 {
567 	REGISTER INTBIG i;
568 
569 	for(i=0; i<count; i++)
570 		cli_linconmodifynodeinst(ni[i], dlx[i], dly[i], dhx[i], dhy[i], drot[i], dtrans[i]);
571 }
572 
cli_linconmodifyarcinst(ARCINST * ai,INTBIG oldx0,INTBIG oldy0,INTBIG oldx1,INTBIG oldy1,INTBIG oldwid,INTBIG oldlen)573 void cli_linconmodifyarcinst(ARCINST *ai, INTBIG oldx0, INTBIG oldy0, INTBIG oldx1, INTBIG oldy1, INTBIG oldwid, INTBIG oldlen)
574 {
575 	cli_eq_modarc(ai);
576 }
577 
578 /*
579  * routine to accept a constraint property in "changedata" for arcinst "ai".
580  * The value of "changetype" determines what to do: 0 means set the new property,
581  * 1 means add the new property, 2 means remove all properties, 3 means print all
582  * properties.  The constraint is of the form "VARIABLE OPERATOR VALUE" where:
583  *   VARIABLE = "x" | "y"
584  *   OPERATOR = "==" | ">=" | "<="
585  *   VALUE    = numeric constant in WHOLE units
586  * the routine returns nonzero if the change is ineffective or impossible
587  */
cli_linconsetobject(INTBIG addr,INTBIG type,INTBIG changetype,INTBIG changedata)588 BOOLEAN cli_linconsetobject(INTBIG addr, INTBIG type, INTBIG changetype, INTBIG changedata)
589 {
590 	REGISTER ARCINST *ai;
591 	REGISTER CHAR *str, *pack, *pt;
592 	REGISTER BOOLEAN error;
593 	REGISTER INTBIG variable, value, len, i, oper;
594 	REGISTER LINCON *conptr;
595 	REGISTER VARIABLE *var;
596 
597 	if ((type&VTYPE) != VARCINST) return(TRUE);
598 	ai = (ARCINST *)addr;
599 
600 	/* handle printing of constraints */
601 	if (changetype == 3)
602 	{
603 		var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
604 		if (var == NOVARIABLE) ttyputmsg(M_("No linear constraint properties")); else
605 		{
606 			len = getlength(var) / LINCONSIZE;
607 			ttyputmsg(M_("%ld %s on this arc:"), len, makeplural(M_("constraint"), len));
608 			for(i=0; i<len; i++)
609 			{
610 				conptr = &((LINCON *)var->addr)[i];
611 				ttyputmsg(M_("Constraint %ld: %s %s %s"), i+1, (conptr->variable<=1 ? x_("x") : x_("y")),
612 					cli_linconops[conptr->oper], frtoa(conptr->value));
613 			}
614 		}
615 		return(FALSE);
616 	}
617 
618 	/* handle deletion of constraints */
619 	if (changetype == 2)
620 	{
621 		var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
622 		if (var == NOVARIABLE)
623 		{
624 			ttyputerr(M_("No constraints on this arc"));
625 			return(TRUE);
626 		}
627 		len = getlength(var) / LINCONSIZE;
628 		(void)delvalkey((INTBIG)ai, VARCINST, cli_properties_key);
629 		cli_eq_modarc(ai);
630 		db_setchangecell(ai->parent);
631 		db_forcehierarchicalanalysis(ai->parent);
632 		return(FALSE);
633 	}
634 
635 	/* adding a new constraint: parse it */
636 	str = (CHAR *)changedata;
637 
638 	/* eliminate blanks in the string */
639 	for(pack = pt = str; *pt != 0; pt++) if (*pt != ' ') *pack++ = *pt;
640 	*pack = 0;
641 
642 	/* make sure the constraint string is valid */
643 	error = FALSE;
644 	pt = str;
645 
646 	/* get the variable */
647 	if (namesamen(pt, x_("x"), 1) == 0)
648 	{
649 		if (ai->end[0].xpos < ai->end[1].xpos) variable = 1; else
650 			variable = 0;
651 	} else if (namesamen(pt, x_("y"), 1) == 0)
652 	{
653 		if (ai->end[0].ypos < ai->end[1].ypos) variable = 3; else
654 			variable = 2;
655 	} else
656 	{
657 		ttyputerr(M_("Cannot parse variable (should be 'X' or 'Y')"));
658 		error = TRUE;
659 	}
660 	pt++;
661 
662 	/* get the operator */
663 	for(i=0; i<3; i++)
664 		if (namesamen(pt, cli_linconops[i], estrlen(cli_linconops[i])) == 0)
665 			break;
666 	if (i < 3)
667 	{
668 		oper = i;
669 		pt += estrlen(cli_linconops[i]);
670 	} else
671 	{
672 		ttyputerr(M_("Cannot parse operator (must be '>=', '==', or '<=')"));
673 		error = TRUE;
674 	}
675 
676 	/* get the value */
677 	if (!error && isanumber(pt))
678 	{
679 		value = atofr(pt);
680 		if (value < 0)
681 		{
682 			ttyputerr(M_("Values must be positive"));
683 			error = TRUE;
684 		}
685 	} else
686 	{
687 		ttyputerr(M_("Invalid number on right (%s)"), pt);
688 		error = TRUE;
689 	}
690 
691 	if (error)
692 	{
693 		ttyputmsg(M_("Cannot accept linear constraint '%s'"), str);
694 		return(TRUE);
695 	}
696 
697 	db_setchangecell(ai->parent);
698 	db_forcehierarchicalanalysis(ai->parent);
699 	if (cli_addarcconstraint(ai, variable, oper, value, changetype)) return(TRUE);
700 	if (changetype == 0) ttyputverbose(M_("Constraint set")); else
701 		ttyputverbose(M_("Constraint added"));
702 	return(FALSE);
703 }
704 
705 /*
706  * routine to add a constraint to arc "ai".  The constraint is on variable
707  * "variable" (0=left, 1=right, 2=down, 3=up), operation "oper" (0 for >=,
708  * 1 for ==, 2 for <=) and has a value of "value".  If "add" is nonzero, add
709  * this constraint, otherwise replace existing constraints.  Returns true
710  * if there is an error.
711  */
cli_addarcconstraint(ARCINST * ai,INTBIG variable,INTBIG oper,INTBIG value,INTBIG add)712 BOOLEAN cli_addarcconstraint(ARCINST *ai, INTBIG variable, INTBIG oper, INTBIG value,
713 	INTBIG add)
714 {
715 	REGISTER VARIABLE *var;
716 	LINCON thiscon;
717 	REGISTER LINCON *conptr, *conlist;
718 	REGISTER INTBIG len, i;
719 
720 	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
721 	if (var == NOVARIABLE || add == 0)
722 	{
723 		thiscon.variable = variable;
724 		thiscon.oper = oper;
725 		thiscon.value = value;
726 		(void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)&thiscon,
727 			VINTEGER|VISARRAY|(LINCONSIZE<<VLENGTHSH));
728 		cli_eq_modarc(ai);
729 		return(FALSE);
730 	}
731 
732 	len = getlength(var) / LINCONSIZE;
733 	conlist = (LINCON *)emalloc(((len+1) * (sizeof (LINCON))), el_tempcluster);
734 	if (conlist == 0)
735 	{
736 		ttyputnomemory();
737 		return(TRUE);
738 	}
739 	for(i=0; i<len; i++)
740 	{
741 		conptr = &((LINCON *)var->addr)[i];
742 		conlist[i].variable = conptr->variable;
743 		conlist[i].oper = conptr->oper;
744 		conlist[i].value    = conptr->value;
745 	}
746 	conlist[len].variable = variable;
747 	conlist[len].oper = oper;
748 	conlist[len].value    = value;
749 	(void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)conlist,
750 		VINTEGER|VISARRAY|((LINCONSIZE*(len+1))<<VLENGTHSH));
751 	cli_eq_modarc(ai);
752 	efree((CHAR *)conlist);
753 	return(FALSE);
754 }
755 
756 /*
757  * routine to delete a constraint from arc "ai".  The constraint is on variable
758  * "variable" (0=left, 1=right, 2=down, 3=up), operation "oper" (0 for >=,
759  * 1 for ==, 2 for <=) and has a value of "value".
760  */
cli_deletearcconstraint(ARCINST * ai,INTBIG variable,INTBIG oper,INTBIG value)761 void cli_deletearcconstraint(ARCINST *ai, INTBIG variable, INTBIG oper, INTBIG value)
762 {
763 	REGISTER VARIABLE *var;
764 	REGISTER LINCON *conptr, *conlist;
765 	REGISTER INTBIG len, i, j, k;
766 
767 	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
768 	if (var == NOVARIABLE) return;
769 
770 	/* search for the constraint */
771 	len = getlength(var) / LINCONSIZE;
772 	for(i=0; i<len; i++)
773 	{
774 		conptr = &((LINCON *)var->addr)[i];
775 		if (conptr->variable != variable) continue;
776 		if (conptr->oper != oper) continue;
777 		if (conptr->value != value) continue;
778 		break;
779 	}
780 	if (i >= len) return;
781 
782 	/* if this is the only constraint, delete the variable */
783 	if (len == 1)
784 	{
785 		(void)delvalkey((INTBIG)ai, VARCINST, cli_properties_key);
786 		cli_eq_modarc(ai);
787 		return;
788 	}
789 
790 	conlist = (LINCON *)emalloc(((len-1) * (sizeof (LINCON))), el_tempcluster);
791 	if (conlist == 0)
792 	{
793 		ttyputnomemory();
794 		return;
795 	}
796 	k = 0;
797 	for(j=0; j<len; j++)
798 	{
799 		if (j == i) continue;
800 		conptr = &((LINCON *)var->addr)[j];
801 		conlist[k].variable = conptr->variable;
802 		conlist[k].oper = conptr->oper;
803 		conlist[k].value    = conptr->value;
804 		k++;
805 	}
806 	(void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)conlist,
807 		VINTEGER|VISARRAY|((LINCONSIZE*(len-1))<<VLENGTHSH));
808 	efree((CHAR *)conlist);
809 	cli_eq_modarc(ai);
810 }
811 
cli_linconmodifyportproto(PORTPROTO * pp,NODEINST * oni,PORTPROTO * opp)812 void cli_linconmodifyportproto(PORTPROTO *pp, NODEINST *oni, PORTPROTO *opp)
813 {
814 	cli_eq_modport(pp);
815 }
816 
cli_linconmodifynodeproto(NODEPROTO * np)817 void cli_linconmodifynodeproto(NODEPROTO *np) {}
cli_linconmodifydescript(INTBIG addr,INTBIG type,INTBIG key,UINTBIG * descript)818 void cli_linconmodifydescript(INTBIG addr, INTBIG type, INTBIG key, UINTBIG *descript) {}
cli_linconnewlib(LIBRARY * lib)819 void cli_linconnewlib(LIBRARY *lib) {}
cli_linconkilllib(LIBRARY * lib)820 void cli_linconkilllib(LIBRARY *lib) {}
821 
cli_linconnewvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG newtype)822 void cli_linconnewvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG newtype)
823 {
824 	if ((newtype&VCREF) != 0) return;
825 	if (type == VARCINST)
826 	{
827 		if (key == cli_properties_key)
828 		{
829 			/* mark the parent of this arc as changed */
830 			db_setchangecell(((ARCINST *)addr)->parent);
831 			db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
832 		}
833 	}
834 	cli_eq_newvar(addr, type, key);
835 }
836 
cli_linconkillvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG oldaddr,INTBIG oldtype,UINTBIG * olddescript)837 void cli_linconkillvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG oldaddr,
838 	INTBIG oldtype, UINTBIG *olddescript)
839 {
840 	if ((oldtype&VCREF) != 0) return;
841 	if ((type&VTYPE) == VARCINST)
842 	{
843 		if (key == cli_properties_key)
844 		{
845 			/* mark the parent of this arc as changed */
846 			db_setchangecell(((ARCINST *)addr)->parent);
847 			db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
848 		}
849 	}
850 	cli_eq_killvar(addr, type, key);
851 }
852 
cli_linconmodifyvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG stype,INTBIG aindex,INTBIG value)853 void cli_linconmodifyvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG stype, INTBIG aindex,
854 	INTBIG value)
855 {
856 	if (type == VARCINST)
857 	{
858 		if (key == cli_properties_key)
859 		{
860 			/* mark the parent of this arc as changed */
861 			db_setchangecell(((ARCINST *)addr)->parent);
862 			db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
863 		}
864 	}
865 }
866 
cli_linconinsertvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex)867 void cli_linconinsertvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex) {}
868 
cli_lincondeletevariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex,INTBIG oldval)869 void cli_lincondeletevariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex,
870 	INTBIG oldval) {}
871 
cli_linconsetvariable(void)872 void cli_linconsetvariable(void) {}
873 
874 /***************************** CONSTRAINT SOLVING *****************************/
875 
876 /*
877  * routine to solve the linear constraints in cell "np".  If "minimum" is
878  * true, re-solve for minimum spacing even if the network is satisfied.
879  * If "noanchors" is true, do not presume anchoring of the components that
880  * just changed
881  */
cli_solvecell(NODEPROTO * np,BOOLEAN minimum,BOOLEAN noanchors)882 void cli_solvecell(NODEPROTO *np, BOOLEAN minimum, BOOLEAN noanchors)
883 {
884 	CONNODE *firstx, *firsty;
885 	REGISTER BOOLEAN failure, xnotsolved, ynotsolved;
886 
887 	/* build constraint network in both axes */
888 	xnotsolved = cli_buildconstraints(np, &firstx, FALSE, noanchors);
889 	ynotsolved = cli_buildconstraints(np, &firsty, TRUE, noanchors);
890 
891 	/* quit now if all constraints are solved */
892 	if (!xnotsolved && !ynotsolved && !minimum)
893 	{
894 		if (cli_conlindebug) ttyputmsg(M_("All constraints are met"));
895 		cli_deleteconstraints(firstx);
896 		cli_deleteconstraints(firsty);
897 		return;
898 	}
899 
900 	/* adjust constraint network for node center offsets */
901 	cli_adjustconstraints(firstx, FALSE);
902 	cli_adjustconstraints(firsty, TRUE);
903 
904 	if (cli_conlindebug)
905 	{
906 		cli_printconstraints(firstx, M_("Horizontal constraints:"), x_("X"));
907 		if (!xnotsolved) ttyputmsg(M_("HORIZONTAL CONSTRAINTS ARE ALL MET"));
908 		cli_printconstraints(firsty, M_("Vertical constraints:"), x_("Y"));
909 		if (!ynotsolved) ttyputmsg(M_("VERTICAL CONSTRAINTS ARE ALL MET"));
910 	}
911 
912 	/* solve the networks and determine the success */
913 	failure = FALSE;
914 	if (xnotsolved || minimum)
915 	{
916 		if (cli_conlindebug) ttyputmsg(M_("Solving X constraints:"));
917 		if (cli_solveconstraints(firstx, minimum))
918 		{
919 			ttyputmsg(M_("X constraint network cannot be solved!"));
920 			failure = TRUE;
921 		}
922 	}
923 	if (ynotsolved || minimum)
924 	{
925 		if (cli_conlindebug) ttyputmsg(M_("Solving Y constraints:"));
926 		if (cli_solveconstraints(firsty, minimum))
927 		{
928 			ttyputmsg(M_("Y constraint network cannot be solved!"));
929 			failure = TRUE;
930 		}
931 	}
932 
933 	/* if networks are solved properly, apply them to the circuit */
934 	if (!failure)
935 	{
936 		/* make the changes */
937 		if (xnotsolved || minimum) cli_applyconstraints(np, firstx, FALSE);
938 		if (ynotsolved || minimum) cli_applyconstraints(np, firsty, TRUE);
939 	}
940 
941 	/* erase the constraint networks */
942 	cli_deleteconstraints(firstx);
943 	cli_deleteconstraints(firsty);
944 }
945 
946 /*
947  * routine to convert the constraints found in cell "np" into a network of
948  * internal constraint nodes headed by "first".  If "axis" is false, build
949  * the X constraints, otherwise build the Y constraints.  If "noanchors" is
950  * true, do not presume presolution of changed nodes.  The routine returns
951  * false if the constraints are already met.
952  */
cli_buildconstraints(NODEPROTO * np,CONNODE ** first,BOOLEAN axis,BOOLEAN noanchors)953 BOOLEAN cli_buildconstraints(NODEPROTO *np, CONNODE **first, BOOLEAN axis, BOOLEAN noanchors)
954 {
955 	REGISTER VARIABLE *var;
956 	REGISTER ARCINST *ai;
957 	REGISTER INTBIG i, rev, len, opf, opt, gothor, gotver;
958 	BOOLEAN notsolved;
959 	REGISTER INTBIG vaf, vat, lambda;
960 	REGISTER LINCON *conptr;
961 	REGISTER NODEINST *ni;
962 	REGISTER CONNODE *cn;
963 
964 	/* get the value of lambda */
965 	lambda = lambdaofcell(np);
966 
967 	/* mark all real nodes as un-converted to constraint nodes */
968 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
969 		ni->temp1 = -1;
970 
971 	/* initialize the list of constraint nodes */
972 	*first = NOCONNODE;
973 
974 	/* look at all constraints in the cell */
975 	notsolved = FALSE;
976 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
977 	{
978 		var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
979 		if (var == NOVARIABLE) len = 0; else len = getlength(var) / LINCONSIZE;
980 		gothor = gotver = 0;
981 		for(i=0; i<len; i++)
982 		{
983 			conptr = &((LINCON *)var->addr)[i];
984 
985 			/* place "from" and "to" nodes in the correct orientation */
986 			switch (conptr->variable)
987 			{
988 				case CLLEFT:
989 					gothor++;
990 					if (axis) continue;
991 					rev = 1;
992 					break;
993 				case CLRIGHT:
994 					gothor++;
995 					if (axis) continue;
996 					rev = 0;
997 					break;
998 				case CLDOWN:
999 					gotver++;
1000 					if (!axis) continue;
1001 					rev = 1;
1002 					break;
1003 				case CLUP:
1004 					gotver++;
1005 					if (!axis) continue;
1006 					rev = 0;
1007 					break;
1008 			}
1009 
1010 			if (rev != 0)
1011 			{
1012 				opt = conptr->oper;       vat = conptr->value*lambda/WHOLE;
1013 				opf = 2 - conptr->oper;   vaf = -vat;
1014 			} else
1015 			{
1016 				opf = conptr->oper;       vaf = conptr->value*lambda/WHOLE;
1017 				opt = 2 - conptr->oper;   vat = -vaf;
1018 			}
1019 
1020 			/* place the constraint in the list */
1021 			cli_addconstraint(ai, first, opf, vaf, opt, vat, noanchors);
1022 
1023 			/* see if this constraint is already met */
1024 			switch (conptr->variable)
1025 			{
1026 				case CLLEFT:
1027 				switch (conptr->oper)
1028 				{
1029 					case CLEQUALS:
1030 						if (ai->end[0].xpos - ai->end[1].xpos != vat) notsolved = TRUE;
1031 						break;
1032 					case CLGEQUALS:
1033 						if (ai->end[0].xpos - ai->end[1].xpos < vat) notsolved = TRUE;
1034 						break;
1035 					case CLLEQUALS:
1036 						if (ai->end[0].xpos - ai->end[1].xpos > vat) notsolved = TRUE;
1037 						break;
1038 				}
1039 				break;
1040 
1041 				case CLRIGHT:
1042 				switch (conptr->oper)
1043 				{
1044 					case CLEQUALS:
1045 						if (ai->end[1].xpos - ai->end[0].xpos != vaf) notsolved = TRUE;
1046 						break;
1047 					case CLGEQUALS:
1048 						if (ai->end[1].xpos - ai->end[0].xpos < vaf) notsolved = TRUE;
1049 						break;
1050 					case CLLEQUALS:
1051 						if (ai->end[1].xpos - ai->end[0].xpos > vaf) notsolved = TRUE;
1052 						break;
1053 				}
1054 				break;
1055 
1056 				case CLDOWN:
1057 				switch (conptr->oper)
1058 				{
1059 					case CLEQUALS:
1060 						if (ai->end[0].ypos - ai->end[1].ypos != vat) notsolved = TRUE;
1061 						break;
1062 					case CLGEQUALS:
1063 						if (ai->end[0].ypos - ai->end[1].ypos < vat) notsolved = TRUE;
1064 						break;
1065 					case CLLEQUALS:
1066 						if (ai->end[0].ypos - ai->end[1].ypos > vat) notsolved = TRUE;
1067 						break;
1068 				}
1069 				break;
1070 
1071 				case CLUP:
1072 				switch (conptr->oper)
1073 				{
1074 					case CLEQUALS:
1075 						if (ai->end[1].ypos - ai->end[0].ypos != vaf) notsolved = TRUE;
1076 						break;
1077 					case CLGEQUALS:
1078 						if (ai->end[1].ypos - ai->end[0].ypos < vaf) notsolved = TRUE;
1079 						break;
1080 					case CLLEQUALS:
1081 						if (ai->end[1].ypos - ai->end[0].ypos > vaf) notsolved = TRUE;
1082 						break;
1083 				}
1084 				break;
1085 			}
1086 		}
1087 
1088 		/* if Manhattan mode set, add implicit constraints */
1089 		if (cli_manhattan)
1090 		{
1091 			if (!axis)
1092 			{
1093 				/* handle vertical arcs */
1094 				if (gothor == 0 && gotver != 0)
1095 				{
1096 					cli_addconstraint(ai, first, CLEQUALS, 0, CLEQUALS, 0, noanchors);
1097 					if (ai->end[0].xpos != ai->end[1].xpos) notsolved = TRUE;
1098 				}
1099 			} else
1100 			{
1101 				/* handle horizontal arcs */
1102 				if (gotver == 0 && gothor != 0)
1103 				{
1104 					cli_addconstraint(ai, first, CLEQUALS, 0, CLEQUALS, 0, noanchors);
1105 					if (ai->end[0].ypos != ai->end[1].ypos) notsolved = TRUE;
1106 				}
1107 			}
1108 		}
1109 	}
1110 
1111 	/* now assign proper values to each node */
1112 	for(cn = *first; cn != NOCONNODE; cn = cn->nextconnode)
1113 		if (!axis) cn->value = (cn->realn->highx + cn->realn->lowx) / 2; else
1114 			cn->value = (cn->realn->highy + cn->realn->lowy) / 2;
1115 
1116 	return(notsolved);
1117 }
1118 
1119 /*
1120  * routine to add a constraint between the nodes connected by arc "ai".  The
1121  * constraint is added to the list "first" and has operator "opf", value "vaf"
1122  * on the from end (0) and operator "opt", value "vat" on the to end (1).
1123  * If "noanchors" is true, do not presume anchoring and presolution of
1124  * nodes.
1125  */
cli_addconstraint(ARCINST * ai,CONNODE ** first,INTBIG opf,INTBIG vaf,INTBIG opt,INTBIG vat,BOOLEAN noanchors)1126 void cli_addconstraint(ARCINST *ai, CONNODE **first, INTBIG opf, INTBIG vaf, INTBIG opt,
1127 	INTBIG vat, BOOLEAN noanchors)
1128 {
1129 	REGISTER CONARC *caf, *cat;
1130 	REGISTER CONNODE *cnf, *cnt;
1131 	REGISTER CHANGE *ch;
1132 	REGISTER CHANGEBATCH *curbatch;
1133 
1134 	/* store this constraint as two constraint arcs */
1135 	caf = cli_allocconarc();
1136 	cat = cli_allocconarc();
1137 	if (caf == NOCONARC || cat == NOCONARC)
1138 	{
1139 		ttyputnomemory();
1140 		return;
1141 	}
1142 
1143 	/* get current change batch */
1144 	curbatch = db_getcurrentbatch();
1145 
1146 	/* get the "from" constraint node */
1147 	cnf = (CONNODE *)ai->end[0].nodeinst->temp1;
1148 	if (cnf == NOCONNODE)
1149 	{
1150 		cnf = cli_allocconnode();
1151 		if (cnf == NOCONNODE)
1152 		{
1153 			ttyputnomemory();
1154 			return;
1155 		}
1156 		cnf->realn = ai->end[0].nodeinst;
1157 		cnf->solved = FALSE;
1158 		if (!noanchors)
1159 		{
1160 			if (curbatch != NOCHANGEBATCH)
1161 				for(ch = curbatch->changehead; ch != NOCHANGE; ch = ch->nextchange)
1162 					if (ch->changetype == NODEINSTMOD && (NODEINST *)ch->entryaddr == cnf->realn)
1163 						cnf->solved = TRUE;
1164 		}
1165 		cnf->firstconarc = NOCONARC;
1166 		cnf->nextconnode = *first;
1167 		*first = cnf;
1168 		ai->end[0].nodeinst->temp1 = (INTBIG)cnf;
1169 	}
1170 
1171 	/* get the "to" constraint node */
1172 	cnt = (CONNODE *)ai->end[1].nodeinst->temp1;
1173 	if (cnt == NOCONNODE)
1174 	{
1175 		cnt = cli_allocconnode();
1176 		if (cnt == NOCONNODE)
1177 		{
1178 			ttyputnomemory();
1179 			return;
1180 		}
1181 		cnt->realn = ai->end[1].nodeinst;
1182 		cnt->solved = FALSE;
1183 		if (!noanchors)
1184 		{
1185 			if (curbatch != NOCHANGEBATCH)
1186 				for(ch = curbatch->changehead; ch != NOCHANGE; ch = ch->nextchange)
1187 					if (ch->changetype == NODEINSTMOD && (NODEINST *)ch->entryaddr == cnt->realn)
1188 						cnt->solved = TRUE;
1189 		}
1190 		cnt->firstconarc = NOCONARC;
1191 		cnt->nextconnode = *first;
1192 		*first = cnt;
1193 		ai->end[1].nodeinst->temp1 = (INTBIG)cnt;
1194 	}
1195 
1196 	/* append constraint arc "cat" to constraint node "cnt" */
1197 	cat->nextconarc = cnt->firstconarc;
1198 	cnt->firstconarc = cat;
1199 	cat->other = cnf;
1200 
1201 	/* append constraint arc "caf" to constraint node "cnf" */
1202 	caf->nextconarc = cnf->firstconarc;
1203 	cnf->firstconarc = caf;
1204 	caf->other = cnt;
1205 
1206 	caf->oper = opf;
1207 	caf->value = vaf;
1208 	cat->oper = opt;
1209 	cat->value = vat;
1210 	cat->reala = caf->reala = ai;
1211 }
1212 
cli_adjustconstraints(CONNODE * first,BOOLEAN axis)1213 void cli_adjustconstraints(CONNODE *first, BOOLEAN axis)
1214 {
1215 	REGISTER CONNODE *cn;
1216 	REGISTER CONARC *ca;
1217 	REGISTER ARCINST *ai;
1218 	REGISTER NODEINST *nif, *nit;
1219 	REGISTER INTBIG centerf, centert, diff;
1220 	REGISTER INTBIG fromend, toend;
1221 
1222 	for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1223 	{
1224 		for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1225 		{
1226 			ai = ca->reala;
1227 			if (ai->end[0].nodeinst == cn->realn)
1228 			{
1229 				fromend = 0;   toend = 1;
1230 			} else
1231 			{
1232 				fromend = 1;   toend = 0;
1233 			}
1234 			nif = cn->realn;   nit = ca->other->realn;
1235 
1236 			/* adjust constraint to get arc to node center */
1237 			if (!axis)
1238 			{
1239 				/* adjust for X axis constraints */
1240 				centerf = (nif->lowx + nif->highx) / 2;
1241 				centert = (nit->lowx + nit->highx) / 2;
1242 				if (centerf != ai->end[fromend].xpos)
1243 				{
1244 					diff = ai->end[fromend].xpos - centerf;
1245 					ca->value += diff;
1246 				}
1247 				if (centert != ai->end[toend].xpos)
1248 				{
1249 					diff = centert - ai->end[toend].xpos;
1250 					ca->value += diff;
1251 				}
1252 			} else
1253 			{
1254 				/* adjust for Y axis constraints */
1255 				centerf = (nif->lowy + nif->highy) / 2;
1256 				centert = (nit->lowy + nit->highy) / 2;
1257 				if (centerf != ai->end[fromend].ypos)
1258 				{
1259 					diff = ai->end[fromend].ypos - centerf;
1260 					ca->value += diff;
1261 				}
1262 				if (centert != ai->end[toend].ypos)
1263 				{
1264 					diff = centert - ai->end[toend].ypos;
1265 					ca->value += diff;
1266 				}
1267 			}
1268 		}
1269 	}
1270 }
1271 
1272 /*
1273  * routine to solve the constraint network in "first".  If "minimum" is true,
1274  * reduce to minimum distances (rahter than allowing larger values)
1275  */
cli_solveconstraints(CONNODE * first,BOOLEAN minimum)1276 BOOLEAN cli_solveconstraints(CONNODE *first, BOOLEAN minimum)
1277 {
1278 	REGISTER CONNODE *cn, *cno;
1279 	REGISTER CONARC *ca;
1280 	REGISTER BOOLEAN satisfied;
1281 
1282 	/* if there are no constraints, all is well */
1283 	if (first == NOCONNODE) return(FALSE);
1284 
1285 	/* fill in the values */
1286 	for (;;)
1287 	{
1288 		/* choose a node that is solved and constrains to one that is not */
1289 		for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1290 		{
1291 			if (!cn->solved) continue;
1292 			for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1293 				if (!ca->other->solved) break;
1294 			if (ca != NOCONARC) break;
1295 		}
1296 
1297 		/* if no constraints relate solved to unsolved, take any unsolved */
1298 		if (cn == NOCONNODE)
1299 		{
1300 			for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1301 				if (!cn->solved) break;
1302 			if (cn == NOCONNODE) break;
1303 
1304 			/* mark this constraint node as solved */
1305 			cn->solved = TRUE;
1306 		}
1307 
1308 		if (cli_conlindebug) ttyputmsg(M_("  Working from node %s at %ld"),
1309 			describenodeinst(cn->realn), cn->value);
1310 
1311 		/* check all constraint arcs to other nodes */
1312 		for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1313 		{
1314 			/* see if other node meets the constraint */
1315 			satisfied = FALSE;
1316 			switch (ca->oper)
1317 			{
1318 				case CLEQUALS:
1319 					if (ca->other->value == cn->value + ca->value) satisfied = TRUE;
1320 					break;
1321 				case CLGEQUALS:
1322 					if (ca->other->value >= cn->value + ca->value) satisfied = TRUE;
1323 					break;
1324 				case CLLEQUALS:
1325 					if (ca->other->value <= cn->value + ca->value) satisfied = TRUE;
1326 					break;
1327 			}
1328 
1329 			/* if the constraint meets perfectly, ignore it */
1330 			if (ca->other->value == cn->value + ca->value)
1331 			{
1332 				if (cli_conlindebug)
1333 					ttyputmsg(M_("    Constraint to %s is met perfectly"),
1334 						describenodeinst(ca->other->realn));
1335 				ca->other->solved = TRUE;
1336 				continue;
1337 			}
1338 
1339 			/* if the constraint is ok and doesn't have to be exact, ignore it */
1340 			if (satisfied && !minimum)
1341 			{
1342 				if (cli_conlindebug)
1343 					ttyputmsg(M_("    Constraint to %s is met adequately"),
1344 						describenodeinst(ca->other->realn));
1345 				ca->other->solved = TRUE;
1346 				continue;
1347 			}
1348 
1349 			/* if other node is unsolved, set a value on it */
1350 			if (!ca->other->solved)
1351 			{
1352 				ca->other->value = cn->value + ca->value;
1353 				if (cli_conlindebug)
1354 					ttyputmsg(M_("    Constraint to unsolved %s adjusts it to %ld"),
1355 						describenodeinst(ca->other->realn), ca->other->value);
1356 				ca->other->solved = TRUE;
1357 				continue;
1358 			}
1359 
1360 			/* other node solved: solve for this node's value */
1361 			for(cno = first; cno != NOCONNODE; cno = cno->nextconnode)
1362 				cno->loopflag = FALSE;
1363 			cn->loopflag = TRUE;
1364 			if (cli_conlindebug)
1365 				ttyputmsg(M_("    Constraint to solved %s takes some work:"),
1366 					describenodeinst(ca->other->realn));
1367 			if (cli_satisfy(cn, ca)) return(TRUE);
1368 		}
1369 	}
1370 	return(FALSE);
1371 }
1372 
1373 /*
1374  * routine to iteratively force constraint arc "ca" on constrant node "cn"
1375  * to be satisfied
1376  */
cli_satisfy(CONNODE * cn,CONARC * ca)1377 BOOLEAN cli_satisfy(CONNODE *cn, CONARC *ca)
1378 {
1379 	REGISTER CONNODE *cno;
1380 	REGISTER CONARC *cao;
1381 	REGISTER BOOLEAN satisfied;
1382 
1383 	/* get the constraint node on the other end */
1384 	cno = ca->other;
1385 	cno->loopflag = TRUE;
1386 
1387 	/* set new value for that constraint node */
1388 	cno->value = cn->value + ca->value;
1389 	if (cli_conlindebug) ttyputmsg(M_("      Setting %s to %ld"),
1390 		describenodeinst(cno->realn), cno->value);
1391 
1392 	/* see if that constraint node is now satisfied */
1393 	for(cao=cno->firstconarc; cao!=NOCONARC; cao=cao->nextconarc)
1394 	{
1395 		if (!cao->other->solved) continue;
1396 		satisfied = FALSE;
1397 		switch (cao->oper)
1398 		{
1399 			case CLEQUALS:
1400 				if (cao->other->value == cno->value + cao->value) satisfied = TRUE;
1401 				break;
1402 			case CLGEQUALS:
1403 				if (cao->other->value >= cno->value + cao->value) satisfied = TRUE;
1404 				break;
1405 			case CLLEQUALS:
1406 				if (cao->other->value <= cno->value + cao->value) satisfied = TRUE;
1407 				break;
1408 		}
1409 		if (!satisfied)
1410 		{
1411 			/* if this is a loop, quit now */
1412 			if (cao->other->loopflag) return(TRUE);
1413 
1414 			/* attempt to recursively satisfy the constraint */
1415 			if (cli_satisfy(cno, cao)) return(TRUE);
1416 		}
1417 	}
1418 	return(FALSE);
1419 }
1420 
cli_applyconstraints(NODEPROTO * np,CONNODE * first,BOOLEAN axis)1421 void cli_applyconstraints(NODEPROTO *np, CONNODE *first, BOOLEAN axis)
1422 {
1423 	REGISTER CONNODE *cn;
1424 	REGISTER ARCINST *ai;
1425 	REGISTER NODEINST *ni;
1426 	REGISTER PORTARCINST *pi;
1427 	REGISTER INTBIG lowestcon, lowestcir, center, adjust, oldlx, oldhx,
1428 		oldly, oldhy, oldxA, oldyA, oldxB, oldyB, oldlen;
1429 
1430 	/* if there are no constraints, circuit is solved */
1431 	if (first == NOCONNODE) return;
1432 
1433 	/* look for the lowest value in the constraints and the circuit */
1434 	lowestcon = lowestcir = 0;
1435 
1436 	/* mark this cell as changed so its size will be adjusted */
1437 	db_setchangecell(first->realn->parent);
1438 
1439 	/* now adjust the nodes in the circuit */
1440 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1441 		ai->temp1 = 0;
1442 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1443 		ni->temp1 = 0;
1444 	for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1445 	{
1446 		ni = cn->realn;
1447 		if (!axis) center = (ni->lowx + ni->highx) / 2; else
1448 			center = (ni->lowy + ni->highy) / 2;
1449 		adjust = (cn->value - lowestcon) - (center - lowestcir);
1450 		if (adjust == 0) continue;
1451 		ni->temp1 = adjust;
1452 		oldlx = ni->lowx;     oldhx = ni->highx;
1453 		oldly = ni->lowy;     oldhy = ni->highy;
1454 		(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
1455 		if (!axis)
1456 		{
1457 			ni->lowx += adjust;   ni->highx += adjust;
1458 		} else
1459 		{
1460 			ni->lowy += adjust;   ni->highy += adjust;
1461 		}
1462 		updategeom(ni->geom, ni->parent);
1463 		ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTMOD, oldlx,
1464 			oldly, oldhx, oldhy, ni->rotation, ni->transpose);
1465 		(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
1466 		cli_eq_modnode(ni);
1467 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1468 			pi->conarcinst->temp1 = 1;
1469 	}
1470 
1471 	/* finally adjust the arcs in the circuit */
1472 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1473 	{
1474 		if (ai->temp1 == 0) continue;
1475 		oldxA = ai->end[0].xpos;   oldyA = ai->end[0].ypos;
1476 		oldxB = ai->end[1].xpos;   oldyB = ai->end[1].ypos;
1477 		oldlen = ai->length;
1478 		(void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
1479 		if (!axis)
1480 		{
1481 			ai->end[0].xpos += ai->end[0].nodeinst->temp1;
1482 			ai->end[1].xpos += ai->end[1].nodeinst->temp1;
1483 		} else
1484 		{
1485 			ai->end[0].ypos += ai->end[0].nodeinst->temp1;
1486 			ai->end[1].ypos += ai->end[1].nodeinst->temp1;
1487 		}
1488 		determineangle(ai);
1489 		updategeom(ai->geom, ai->parent);
1490 		ai->length = computedistance(ai->end[0].xpos,ai->end[0].ypos,
1491 			ai->end[1].xpos,ai->end[1].ypos);
1492 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldxA,
1493 			oldyA, oldxB, oldyB, ai->width, oldlen);
1494 		(void)db_change((INTBIG)ai, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1495 		cli_eq_modarc(ai);
1496 	}
1497 }
1498 
cli_deleteconstraints(CONNODE * first)1499 void cli_deleteconstraints(CONNODE *first)
1500 {
1501 	REGISTER CONNODE *cn, *nextcn;
1502 	REGISTER CONARC *ca, *nextca;
1503 
1504 	for(cn = first; cn != NOCONNODE; cn = nextcn)
1505 	{
1506 		for(ca = cn->firstconarc; ca != NOCONARC; ca = nextca)
1507 		{
1508 			nextca = ca->nextconarc;
1509 			cli_freeconarc(ca);
1510 		}
1511 		nextcn = cn->nextconnode;
1512 		cli_freeconnode(cn);
1513 	}
1514 }
1515 
cli_printconstraints(CONNODE * first,CHAR * message,CHAR * axis)1516 void cli_printconstraints(CONNODE *first, CHAR *message, CHAR *axis)
1517 {
1518 	REGISTER CONNODE *cn;
1519 	REGISTER CONARC *ca;
1520 
1521 	ttyputmsg(x_("%s"), message);
1522 	for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1523 	{
1524 		ttyputmsg(M_("  Node %s at %ld anchored=%d has these constraints:"),
1525 			describenodeinst(cn->realn), cn->value, cn->solved);
1526 		for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1527 			ttyputmsg(M_("    to node %s (%s %s %ld)"), describenodeinst(ca->other->realn), axis,
1528 				cli_linconops[ca->oper], ca->value);
1529 	}
1530 }
1531 
1532 /*
1533  * routine to name all of the nodes in cell "np"
1534  */
1535 #define NOABBREV ((ABBREV *)-1)
1536 
1537 typedef struct Iabbrev
1538 {
1539 	CHAR   *name;
1540 	INTBIG  aindex;
1541 	struct Iabbrev *nextabbrev;
1542 } ABBREV;
1543 
cli_nameallnodes(NODEPROTO * np)1544 void cli_nameallnodes(NODEPROTO *np)
1545 {
1546 	REGISTER NODEINST *ni;
1547 	REGISTER CHAR *pt;
1548 	REGISTER BOOLEAN upper;
1549 	REGISTER VARIABLE *var;
1550 	REGISTER ABBREV *firstabbrev, *a, *nexta;
1551 	CHAR line[20], *newname;
1552 	REGISTER void *infstr;
1553 
1554 	firstabbrev = NOABBREV;
1555 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1556 	{
1557 		/* ignore this if it has a name */
1558 		if (getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key) != NOVARIABLE) continue;
1559 
1560 		if (ni->proto->primindex == 0) upper = TRUE; else upper = FALSE;
1561 		pt = makeabbrev(ni->proto->protoname, upper);
1562 
1563 		/* if the name is null, assign a name */
1564 		if (estrlen(pt) == 0)
1565 		{
1566 			if (ni->proto->primindex == 0) pt = M_("CELL"); else pt = M_("node");
1567 		}
1568 
1569 		/* find an abbreviation module for this */
1570 		for(a = firstabbrev; a != NOABBREV; a = a->nextabbrev)
1571 			if (namesame(a->name, pt) == 0) break;
1572 		if (a == NOABBREV)
1573 		{
1574 			a = (ABBREV *)emalloc(sizeof (ABBREV), el_tempcluster);
1575 			if (a == 0) break;
1576 			(void)allocstring(&a->name, pt, el_tempcluster);
1577 			a->nextabbrev = firstabbrev;
1578 			firstabbrev = a;
1579 			a->aindex = 1;
1580 		}
1581 
1582 		/* assign a name to the node */
1583 		infstr = initinfstr();
1584 		addstringtoinfstr(infstr, a->name);
1585 		(void)esnprintf(line, 20, x_("%ld"), a->aindex++);
1586 		addstringtoinfstr(infstr, line);
1587 		allocstring(&newname, returninfstr(infstr), el_tempcluster);
1588 		var = setvalkey((INTBIG)ni, VNODEINST, el_node_name_key, (INTBIG)newname, VSTRING|VDISPLAY);
1589 		efree(newname);
1590 		if (var != NOVARIABLE)
1591 			defaulttextsize(3, var->textdescript);
1592 	}
1593 
1594 	/* free the abbreviation keys */
1595 	for(a = firstabbrev; a != NOABBREV; a = nexta)
1596 	{
1597 		nexta = a->nextabbrev;
1598 		efree(a->name);
1599 		efree((CHAR *)a);
1600 	}
1601 }
1602