#ifndef lint
static char *SccsId = "@(#)determine.c 4.22 (TU-Delft) 04/20/93";
#endif /* lint */
/**********************************************************

Name/Version      : space/4.22

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

Author(s)         : A.J. van Genderen
		  : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


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

        Phone : 015 - 786234

        COPYRIGHT (C) 1988, 1989, 1990. All rights reserved.
**********************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <dmincl.h>
#include "include/config.h"
#include "include/type.h"
#include "aux/aux.h"
#include "scan/scan.h"
#include "scan/determine.h"


/* local operations */
private struct cell *makeTree ();
private void    addChild ();
private void    addParent ();
private struct cell *newCell ();
private struct cell *lookupCell ();
private void    reportCell ();
private void    invertHierarchy ();
private void    bfs ();
private void    fappend ();
private struct cell *fdelete ();
private void    determineDepths ();
private do_t * makeCandidateArray ();
private void findCandidateParents ();

extern int    optMaxDepth;
extern int    optMinDepth;
extern bool_t alsoPrePass;
extern bool_t optOnlyPrePass;
extern bool_t optResMesh;
extern bool_t optNoPrepro;
extern bool_t optFlat;
extern bool_t optGenNetTerm;
extern bool_t optBackInfo;
extern bool_t optSelectiveRes;
extern bool_t optPrick;

struct cell {
    char name[DM_MAXNAME + 1];
    struct cell * next;         /* all cells seen so far */
    struct adj * children;
    struct adj * parents;
    struct cell * fringe;
    struct cell * candidates;	/* extraction candidates in dfs order */
    int depth;
    unsigned int status;
    int versionnumber;
    DM_CELL * lkey;
};

struct adj {
    struct cell * cell;
    struct adj  * next;
};

static DM_PROJECT *dmproject;

/* The head of a list of all cells that must be extracted (if not
 * restricted by the optMaxDepth option). 
 * The list is in reverse depth-first order, after another reversion
 * it is suitable for extraction.
 */
static struct cell * candidates;

/* The head of a list of all cells seen so far. */
static struct cell * cells;

/* Sole entry point of this module.
 *
 * Given a project and cell name as argument, this function returns
 * an array of do_t's (not pointers to them) describing all cells
 * in the complete hierarchical tree that must be extracted.
 * The depth of the tree can be restricted by optMaxDepth.
 * The do_t also contains info describing whether the cell must
 * be preprocessed first.
 *
 * Primarily meant for hierarchical mode, but for convenience it
 * also works in flat mode.
 *
 * The end of the list is marked by .name == NULL.
 *
 * General strategy:
 * 1. Make a dag with root at the top.
 * 2. Determine depth (distance to the root) of each cell.
 * 3. (Only when R3)
 *    Also handle the parents of each cell to be dealt with.
 * 4. Make a linked list in correct order for extraction,
 *    and return the list.
 */
do_t * findCandidates (project, root)
DM_PROJECT * project;
char * root;
{
    do_t * array;
    struct cell * c;

    dmproject = project;

    (void) makeTree (root, ROOT_DEPTH);

#   if NCF_RELEASE < 400
    findCandidateParents ();
#   endif

    /* Reset the depth of all cells,
     * must be done for the case that
     * this function is called more then one time.
     */
    for (c = cells; c != NULL; c = c -> next) c -> depth = ROOT_DEPTH-1;

    determineDepths (root);

    array = makeCandidateArray ();

    return (array);
}

private struct cell * makeTree (name, depth)
char *name;
int depth;
{
    struct cell * c;
    DM_STREAM * dsp;
    DM_CELL   * lkey;
    DM_CELL   * ckey = NULL;
    char * versionstatus;
    int checkoutmode;

#   if NCF_RELEASE >= 400
    char **equiv4;
    char * modname;
#   endif /* NCF_RELEASE >= 400 */

    int flag;

    ASSERT (depth == ROOT_DEPTH || optFlat == FALSE);

    /* Check if cell has already been done.
     * If so, return immediately
     */
    if (c = lookupCell (name)) return (c);

    Debug (fprintf (stderr, "pre-order visit '%s', depth %d\n",
	name, depth));

    c = newCell (name);

    versionstatus = (depth == ROOT_DEPTH ? WORKING : ACTUAL);

    /* Determine checkout mode.
     * Do read-only if possible to increase potential concurency
     * when running under release 4.
     */
    checkoutmode = (((alsoPrePass || optOnlyPrePass)
		      && (optResMesh || optSelectiveRes || optPrick)) 
		    || optGenNetTerm) ? ATTACH : READONLY;

    if ((lkey = c -> lkey = dmCheckOut (dmproject, name, 
			    versionstatus, DONTCARE, LAYOUT, 
			    checkoutmode)) == NULL) {
	return (NULL);
    }

    ASSERT (c -> status == 0);

#   if NCF_RELEASE >= 400

    c -> versionnumber = lkey -> versionnumber;

    /* Question: what if it is an imported cell? */

    if (existDmStream (lkey, "is_macro")) {
	dsp = dmOpenStream (lkey, "is_macro", "r");
	if (fscanf (dsp -> dmfp, "%d", &flag) > 0 && flag == 1)
	    c -> status |= ISMACRO;
	dmCloseStream (dsp, COMPLETE);
    }
    
    /* Cannot use an argument name of form 'name#3' */
    if (!strchr (name, '#'))
	modname = name;
    else {
	char * c;
	modname = strsave (name);
	for (c = modname; *c != '#'; c++);
	*c = '\0';
    }

    /* Extract only if we can not find an equivalent circuit.
     * The equivalences should be obtained in any case,
     * independent of optMinDepth, since we must know whether
     * an equivalent circuit is a device model or not. A device model
     * must not be extracted.
     */

    equiv4 = (char **) dmGetMetaDesignData
	(CELLEQUIVALENCE, dmproject, modname, LAYOUT, CIRCUIT);

    if (equiv4 && equiv4[0]) {

	/* There exists (at least one) equivalent circuit.
	 * Take the first one.
	 * To do: working-actual-most recent derived order of selection.
	 * Could also search for appropriate equivalent circuit, based
	 * on e.g. extraction options.
	 *
	 */
	Debug (fprintf (stderr, "equivalent circuit '%s'\n", equiv4[0]));

	ckey = dmCheckOut (dmproject, equiv4[0], WORKING,
		    DONTCARE, CIRCUIT, READONLY);

#       if 0
	/* In R4, we can stop climbing down when the current cell
	 * has an equivalence and when it as at a depth > mindepth,
	 * because the framework guarantees the existence and validity of
	 * all deeper equivalent circuits.
	 * However, climbing down completely would give nicer messages
	 * about cells wich are already extracted, when using
	 * the verbose option.
	 */

	/* Pieter: what if we change from hierarchical to flat
	 * extraction mode, or vice-versa.
	 * Now, we re-use flat cells.
	 * By moving this return from makeTree down,
	 * we can implement the other option.
	 */

        /* This must be done after checkout above, because of the
	 * binding mechanmism.
         */

	if (depth > optMinDepth) {
	    dmCheckIn (ckey, COMPLETE);
	    return (c);
	}
#       endif

	if (!optFlat && existDmStream (ckey, "flat")) {
	    Debug (fprintf (stderr, "previous extraction was linear\n"));
	    c -> status |= DO_SPACE;
	}

	if (optFlat && !existDmStream (ckey, "flat")) {
	    Debug (fprintf (stderr, "previous extraction was hierarchical\n"));
	    c -> status |= DO_SPACE;
	}

	if (existDmStream (ckey, "devmod")) {
	    Debug (fprintf (stderr, "device model present\n"));
	    c -> status |= DEVMOD;	 /* device model present */
	}

	dmCheckIn (ckey, COMPLETE);
    }
    else {
	/* no circuit present */
	Debug (fprintf (stderr, "no equivalent circuit\n"));
	c -> status |= DO_SPACE;
    }

#   else /* NCF_RELEASE */

    c -> versionnumber = -999;
    ASSERT (c -> status == 0);

    /* To do: what if it is also a macro, or a device mod, or imported */

    if (existDmStream (lkey, "is_macro")) {
	dsp = dmOpenStream (lkey, "is_macro", "r");
	if (fscanf (dsp -> dmfp, "%d", &flag) > 0 && flag == 1)
	    c -> status |= ISMACRO;
	dmCloseStream (dsp, COMPLETE);
    }

    if ((int) dmGetMetaDesignData
	(EXISTCELL, dmproject, cktName(name), CIRCUIT)) {

	/* A circuit with same name (or as modified by cktName)
	 * exists, this one is assumed to be equivalent.
	 */
	Debug (fprintf (stderr, "equivalent circuit '%s'\n",
	    cktName (name)));

	if ((ckey = dmCheckOut (dmproject, cktName (name), ACTUAL,
			DONTCARE, CIRCUIT, READONLY)) == NULL) {
	    return (NULL);
	}

	if (compareDmStreamDate (lkey, "mc", ckey, "mc") < 0) {
	 /* circuit present but layout is newer */
	    Debug (fprintf (stderr, "Layout is newer\n"));
	    c -> status |= DO_SPACE;
	}

	if (!optFlat && existDmStream (ckey, "flat")) {
	    Debug (fprintf (stderr, "previous extraction was linear\n"));
	    c -> status |= DO_SPACE;
	}

	if (optFlat && !existDmStream (lkey, "flat")) {
	    Debug (fprintf (stderr, "previous extraction was hierarchical\n"));
	    c -> status |= DO_SPACE;
	}

	if (existDmStream (ckey, "devmod")) {
	    Debug (fprintf (stderr, "device model present\n"));
	    c -> status |= DEVMOD;	 /* device model present */
	}

	/* why QUIT? Above we do COMPLETE. */
	dmCheckIn (ckey, QUIT);
    }
    else {
	/* no circuit present */
	Debug (fprintf (stderr, "no equivalent circuit\n"));
	c -> status |= DO_SPACE;
    }
#   endif /* NCF_RELEASE */

    /* See if the cell must be preprocessed.
     * However, these checks are disabled, and preprocessing not done,
     * when optNoPrepro is TRUE.
     * This is for the design flow management of R4, where these
     * checks are modeled in the flow. Moreover, the stats performed
     * for checking the existence of a cell would confuse the flow module.
     *
     * To do: How about imported cells? Can we extract them locally?
     */

    /* We can not restrict the test for running the preprocessors
     * to the cells that have, at this moment, DO_SPACE true, because
     * the actions for optMinDepth can also make DO_SPACE true.
     */
    if (optNoPrepro == FALSE && !(c -> status & DEVMOD)) {
	if (optFlat == TRUE && !existDmStream (lkey, "spec")) {
	    Debug (fprintf (stderr, "no previous exp or hierarchical\n"));
	    c -> status |= DO_EXP;
	}
	else if (optFlat == FALSE && existDmStream (lkey, "spec")) {
	    Debug (fprintf (stderr, "previous exp was linear\n"));
	    c -> status |= DO_EXP;
	}

#       if NCF_RELEASE < 400
	/* Under R3, another reason for preprocessing is that the
	 * box file is newer then the gln file.
	 */
	if (!(c -> status & DO_EXP)) {
	    if (compareGlnDate (lkey, "box", lkey) <= 0) {
		Debug (fprintf (stderr, "no gln file or box file is newer\n"));
		c -> status |= DO_EXP;
	    }
	}
#       else /* NCF_RELEASE */
	/* Such a situation can not exist under R4.
	 * Nevertheless, perform the following test since we then find
	 * wheter the gln file exists or not.
	 */
	if (!(c -> status & DO_EXP)) {
	    extern maskinfo_t * masktable;
	    if (!existDmStream (lkey,
		    mprintf ("%s_gln", masktable[0].name))) {
		Debug (fprintf (stderr, "no gln file\n"));
		c -> status |= DO_EXP;
	    }
	}
#       endif /* NCF_RELEASE */
    }

    /* In hierarchical mode, recursively visit the children
     * to see whether they must be extracted.
     */
    if (!optFlat) {
	if ((dsp = dmOpenStream (lkey, "mc", "r")) == NULL) return (NULL);

	while (dmGetDesignData (dsp, GEO_MC) > 0) {
	    /* !!! Skip imported cells. In R4, this is an error.
	     * In R3, we hope that there exist a circuit with the same
	     * name.
	     * To do: appropriate handling of imported cells, under R3 and R4.
	     */
	    if (gmc.imported == IMPORTED) {
#               if NCF_RELEASE >= 400
                say ("Cannot deal with imported cell '%s'", gmc.cell_name);
#               endif /* NCF_RELEASE */
		continue;
	    }

	    /* This one will perform recursive call. */
	    addChild (c, gmc.cell_name, depth);
	}
	dmCloseStream (dsp, COMPLETE);
    }

    /* Add cell to depth-first list. */
    reportCell (c);

    /* Note: formal parameter "name" possibly changed
     * due to recursive calls.
     * So, use c -> name instead.
     */
    Debug (fprintf (stderr, "post-order visit '%s', depth %d\n",
	c -> name, depth));

    return (c);
}

/* Add a new child with name 'name' to the list of children
 * of the cell identified by 'cell'.
 * Do nothing if this child is already in the list.
 * Otherwise, process the new child recursively.
 * Depth is the depth of the parent of this child along the
 * depth-first search tree.
 */
private void addChild (cell, name, depth)
struct cell * cell;
char * name;
int depth;
{
    struct adj * a;

    ASSERT (depth >= ROOT_DEPTH);
    ASSERT (optFlat == FALSE);

    for (a = cell -> children; a != NULL; a = a -> next) {
	if (strcmp (a -> cell -> name, name) == 0)
	    return;	/* cell found */
    }

    a = NEW (struct adj, 1);
    a -> next = cell -> children;
    cell -> children = a;
    a -> cell = makeTree (name, depth + 1); /* recursive call */
}

/* Create a new cell structure for the cell named 'name'.
 * All cells are entered in a linked list.
 * The new cell is returned.
 */
private struct cell * newCell (name)
char * name;
{
    struct cell * c = NEW (struct cell, 1);

    strcpy (c -> name, name);
    c -> children = NULL;
    c -> parents = NULL;
    c -> status = 0;		/* assume circuit present */
    c -> fringe = NULL;
    c -> depth = ROOT_DEPTH - 1;
    c -> candidates = NULL;
    c -> lkey = NULL;

    c -> next = cells;
    cells = c;
    return (c);
}

/* Lookup the named cell in the linked list
 * of all known cells.
 * Return NULL if not found.
 */
private struct cell * lookupCell (name)
char * name;
{
    struct cell * c;

    for (c = cells; (c != NULL) && (strcmp (c -> name, name) != 0);
	 c = c -> next);

    return (c);
}

/* Cell must be extracted.
 * Make it part of the depth-first linked list of candidates.
 */
private void reportCell (cell)
struct cell * cell;
{
    Debug (fprintf (stderr, "report '%s', depth %d, status %d\n",
	cell -> name, cell -> depth, (int) cell -> status));

    cell -> candidates = candidates;
    candidates = cell;
}

# if NCF_RELEASE < 400
/*
 * If a cell must be extracted, also extract its immediate parents.
 * If a cell must be expanded, also expand its immediate parents.
 * If a cell has recently changed its macro status, expand and extract 
 * its relevant parents.
 */
private void findCandidateParents ()
{
    struct cell * c;
    struct adj * a;

    /* Add pointers from children to their parents.
     */
    invertHierarchy ();

    for (c = candidates; c; c = c -> candidates) {

	/* must continue, because layout already checked in. */
	if (c -> status & DONE_SPACE)
	    continue;

	if (c -> status & DO_SPACE) {
	    for (a = c -> parents; a; a = a -> next)
		a -> cell -> status |= DO_SPACE;
	}

	if (c -> status & DO_EXP) {
	    for (a = c -> parents; a; a = a -> next)
		a -> cell -> status |= DO_EXP;
	}

        if (c -> status & ISMACRO
            || (!(c -> status & DO_EXP) 
                && existDmStream (c -> lkey, "is_macro"))) {
	    for (a = c -> parents; a; a = a -> next) {
		if (compareGlnDate (c -> lkey, "is_macro",
				     a -> cell -> lkey) <= 0) {
		    a -> cell -> status |= DO_SPACE;
		    a -> cell -> status |= DO_EXP;
		}
	    }
        }
    }
}

private void invertHierarchy ()
{
    struct cell * c;
    struct adj * a;

    for (c = cells; c != NULL; c = c -> next) {
	for (a = c -> children; a != NULL; a = a -> next) {
	    Debug (fprintf (stderr, "'%s' parent of '%s'\n",
		c -> name, a -> cell -> name));
	    addParent (a -> cell, c);
	}
    }
}

/* Add a new parent to the list of parents of 'child'.
 * Do nothing if the new parent is already in the list.
 */
private void addParent (child, parent)
struct cell * child, * parent;
{
    struct adj * a;

    for (a = child -> parents; a != NULL; a = a -> next) {
	if (strcmp (a -> cell -> name, parent -> name) == 0)
	    return;	/* parent link already exists */
    }

    /* make new adjacency
     */
    a = NEW (struct adj, 1);
    a -> next = child -> parents;
    child -> parents = a;
    a -> cell = parent;
}

# endif /* NCF_RELEASE */


private void determineDepths (name)
char * name;
{
    struct cell * c = lookupCell (name);
    c -> depth = ROOT_DEPTH;
    bfs (c);
}

/* Perform breadth-first search of the tree, to determine
 * the level of each node.
 */
private void bfs (fringe)
struct cell * fringe;
{
    struct cell * c;
    struct adj  * a;

    int depth;

    while (fringe) {
	c = fringe;
	depth = c -> depth;
	Debug (fprintf (stderr, "bfs '%s', depth %d\n", c -> name, depth));

	for (a = c -> children; a != NULL; a = a -> next) {
	    if (a -> cell -> depth < ROOT_DEPTH) {
		a -> cell -> depth = depth + 1;
		fappend (fringe, a -> cell);
	    }
	}
	fringe = fdelete (fringe);
    }
}

private void fappend (fringe, cell)
struct cell * fringe;
struct cell * cell;
{
    struct cell * c;

    for (c = fringe; c; c = c -> fringe) {
	if (c -> fringe == NULL) {
	    c -> fringe = cell;
	    break;
	}
    }
    cell -> fringe = NULL;
    return;
}

private struct cell * fdelete (fringe)
struct cell * fringe;
{
    return (fringe -> fringe);
}

/* Make an array of do_t's that must be extracted:
 * OptMaxDepth is for debugging only, it can deliver a non-consistent tree.
 */
private do_t * makeCandidateArray ()
{
    struct cell * c;
    static do_t * list = NULL;
    int num = 0;
    static int size = 0;

    /* count the number of cells to be extracted */
    num = 0;
    for (c = candidates; c; c = c -> candidates) {
	if ((c -> depth <= optMaxDepth) && (c -> depth >= ROOT_DEPTH))
	    num++;
    }


    /* make sure the list is long enough */
    if (num + 1 > size) {
	if (size > 0) {
	    ASSERT (list);
	    DISPOSE (list);
	}
	size = num + 1;
	list = NEW (do_t, size);
    }

    /* put marker in last position */
    list [num].name = NULL;

    /* prepare vector to be returned, in reverse order. */
    for (c = candidates; c; c = c -> candidates) {
	if ((c -> depth <= optMaxDepth) && (c -> depth >= ROOT_DEPTH)) {
	    do_t * l = list + (--num);

	    l -> status = c -> status;
	    l -> name = c -> name;
	    l -> lkey = c -> lkey;
	    l -> versionnumber = c -> versionnumber;

	    /* Because the macro status has a meaning only
	     * in the context of a father cell, we allow
	     * extraction of a cell with macro status when it is
	     * invoked at the command line.
	     */
	    if (c -> depth == ROOT_DEPTH)
		l -> status &= ~ISMACRO;

	    /* In principle, do not re-extract cells in one run
	     */
	    if (c -> status & DONE_SPACE)
		l -> status &= ~DO_SPACE;

	    /* But (re-)extract cells based on min-depth option
	     */
	    if (c -> depth <= optMinDepth) {
		l -> status |= DO_SPACE;

		/* If they were done before, check 'm out again.
		 */
		if (c -> status & DONE_SPACE) {
		    int checkoutmode =
			(((alsoPrePass || optOnlyPrePass)
			   && (optResMesh || optSelectiveRes || optPrick))
			 || optGenNetTerm) ? ATTACH : READONLY;
		    char * versionstatus =
			c -> depth == ROOT_DEPTH ? WORKING : ACTUAL;

		    l -> lkey = dmCheckOut (dmproject, c -> name,
			versionstatus, DONTCARE, LAYOUT, checkoutmode);

		    /* but do not preprocess */
		    l -> status &= ~DO_EXP;
		}
	    }

	    /* Remember they were done already.
	     */
	    c -> status |= DONE_SPACE;
	}
    }

    ASSERT (num == 0);

    return (list);
}

/* Returns TRUE if stream exists, FALSE otherwise.
 */
bool_t existDmStream (key, streamName)
DM_CELL * key;
char * streamName;
{
    struct stat buf;
    return (dmStat (key, streamName, &buf) == 0 ? TRUE : FALSE);
}

/* returns < 0 if stream2 older then stream 1, and stream1 exists
 *
 * If stream 2 does not exist: -2
 * If stream 2 exists:         -1
 */
int compareDmStreamDate (key1, stream1, key2, stream2)
DM_CELL * key1, * key2;
char * stream1, * stream2;
{
    struct stat buf1, buf2;

    if (dmStat (key1, stream1, &buf1) != 0) return ( 2);
    if (dmStat (key2, stream2, &buf2) != 0) return (-2);

    if (buf1.st_mtime < buf2.st_mtime) return ( 1);
    if (buf1.st_mtime > buf2.st_mtime) return (-1);

    return (0);
}

int compareGlnDate (key1, stream1, key2)
DM_CELL * key1, * key2;
char * stream1;
{
    extern maskinfo_t * masktable;
    extern char * strsave ();
    static char * stream2 = NULL;

#   ifndef DRIVER
    if (stream2 == NULL)
	stream2 = strsave (mprintf ("%s_gln", masktable[0].name));

    return (compareDmStreamDate (key1, stream1, key2, stream2));
#   else /* DRIVER */
    extern int glncompare;
    return (glncompare);
#   endif /* DRIVER */
}



char * cktName (name)
char * name;
{
    static char buf[100];
    extern bool_t paramCapitalize;

    if (paramCapitalize) {
	strcpy (buf, name);
	*buf = toupper (*buf);
	name = buf;
    }

    return (name);
}

#ifdef DRIVER

int optMaxDepth = 100;
int optMinDepth = 0;
bool_t optNoPrepro = FALSE;
bool_t optResMesh = FALSE;
bool_t optFlat = FALSE;
bool_t alsoPrePass = FALSE;
bool_t optOnlyPrePass = FALSE;
int glncompare = 1;

main (argc, argv)
int argc;
char **argv;
{
    do_t *l;
    int c;
    bool_t vFlag = FALSE, sFlag = FALSE;
    char * cellname;
    extern int optind;
    extern char * optarg;

    while ((c = getopt (argc, argv, "g:iL:d:v")) != EOF) {
	switch (c) {
	    case 'd': optMinDepth = atoi (optarg); break;
	    case 'L': optMaxDepth = atoi (optarg); break;
	    case 'g': glncompare  = atoi (optarg); break;
	    case 'i': optMinDepth = INF; break;
	    case 'v': vFlag = TRUE; break;
	    case '?': break;
	}
    }

    dmInit ("test");
    dmproject = dmOpenProject (DEFAULT_PROJECT, DEFAULT_MODE);

    while (optind < argc) {
	cellname = argv[optind++];
	l = findCandidates (dmproject, cellname);

	if (vFlag == TRUE) {
	    fprintf (stdout, "Candidates from %s:\n", cellname);
	    while (l -> name) {
		fprintf (stdout, "%s, status %d, version %d\n",
		    l -> name, (int) l -> status,
		    (int) l -> versionnumber);
		l++;
	    }    
	}
	else {
	    while (l -> name) {
		if (l -> status) fprintf (stdout, "%s\n", l -> name);
		l++;
	    }
	}
    }
    dmCloseProject (dmproject, QUIT);
}

#endif /* DRIVER */
