/* "NETGEN", a netlist-specification tool for VLSI
   Copyright (C) 1989, 1990   Massimo A. Sivilotti
   Author's address: mass@csvax.cs.caltech.edu;
                     Caltech 256-80, Pasadena CA 91125.

This program 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 (any version).

This program 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 this program; see the file copying.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/* embed.c  -- efficient exhaustive embedding algorithm for PROTOCHIP */

/* define to get reams of extra output */
#undef EXTREE_DEBUG

/* define this to use ex_tree to test Exist() */
#undef EX_TREE_FOR_EXIST

#include "config.h"

#include <stdio.h>
#ifdef IBMPC
#include <stdlib.h>
#endif

#ifdef TCL_NETGEN
#include <tcl.h>
#endif

#include "netgen.h"
#include "hash.h"
#include "objlist.h"
#include "netfile.h"
#include "print.h"
#include "embed.h"

int CountExists; /* count number of times test is performed */

#ifdef EX_TREE_FOR_EXIST
/* safe, but excessive */
/* #define EX_SIZE (MAX_ELEMENTS * (MAX_LEAVES+1)) */
#ifdef VMUNIX
#define EX_SIZE 250000
#else
#define EX_SIZE 60000
#endif
#endif /* EX_TREE_FOR_EXIST */



#ifdef EX_TREE_FOR_EXIST

#if EX_SIZE > 64000
#define EX_TREE_WITH_POINTERS
#else
#undef EX_TREE_WITH_POINTERS
#endif

#ifdef EX_TREE_WITH_POINTERS
struct ex {
  struct ex *zero;
  struct ex *one;
};
typedef struct ex *EX_LIST_PTR;
long free_list; /* index into ex_array */

#else

typedef unsigned short EX_LIST_PTR;
struct ex {
  EX_LIST_PTR zero;
  EX_LIST_PTR one;
};
unsigned short free_list; /* index into ex_array */
#endif /* EX_TREE_WITH_POINTERS */

struct ex ex_array[EX_SIZE];
EX_LIST_PTR tree_root;
#endif /* EX_TREE_FOR_EXIST */



#ifdef EX_TREE_FOR_EXIST

#ifdef EX_TREE_WITH_POINTERS
int InitializeExistTest(void)
{
#ifdef EXTREE_DEBUG
  Printf("called InitializeExistTest\n");
#endif
  tree_root = NULL;
  free_list = 0;
  memzero(ex_array, sizeof(ex_array));
  return (1);
}

EX_LIST_PTR GetExListElement(void)
/* return index of next free element */
{
  if (free_list >= EX_SIZE) {
    Fprintf(stderr,"Too many ExListElements; garbage list exhausted\n");
    FatalError = 1;
    return(NULL);
  }
  return(&(ex_array[free_list++]));
}

void AddToExistSet(int E1, int E2)
{
  char ownedleaves[MAX_LEAVES+1];
  int i;
  EX_LIST_PTR ptr;

#if 1
  for (i = 1; i <= Leaves; i++)
    ownedleaves[i] = TestPackedArrayBit(MSTAR[E1],i) || 
      TestPackedArrayBit(MSTAR[E2],i);
#else
  memzero(ownedleaves, sizeof(ownedleaves));
  for (i = 1; i <= Leaves; i++)
    if (TestPackedArrayBit(MSTAR[E1],i) || 
	TestPackedArrayBit(MSTAR[E2],i)) ownedleaves[i] = 1;
#endif

  if (tree_root == NULL) 
    if ((tree_root = GetExListElement()) == NULL) return;

  ptr = tree_root;
  for (i = 1; i <= Leaves; i++) {
    if (ownedleaves[i] == 1) {
      if (ptr->one == NULL) 
	if ((ptr->one = GetExListElement()) == NULL) return;
      ptr = ptr->one;
    }
    else {
      if (ptr->zero == NULL) 
	if ((ptr->zero = GetExListElement()) == NULL) return;
      ptr = ptr->zero;
    }
  }
}
    
int Exists(int E1, int E2)
{
  char ownedleaves[MAX_LEAVES+1];
  int i;
  EX_LIST_PTR ptr;

  CountExists++;
  if (tree_root == NULL) {
#ifdef EXTREE_DEBUG
    Printf("(%d,%d) does not exist (empty list)\n",E1,E2);
#endif
    return(0);
  }

  memzero(ownedleaves, sizeof(ownedleaves));
  for (i = 1; i <= Leaves; i++)
    if (TestPackedArrayBit(MSTAR[E1],i) || 
	TestPackedArrayBit(MSTAR[E2],i)) ownedleaves[i] = 1;

#ifdef EXTREE_DEBUG
  Printf("checking existence of :");
  for (i = 1; i <= Leaves; i++) Printf(" %d",ownedleaves[i]);
  Printf("  ");
#endif

  ptr = tree_root;
  for (i = 1; i <= Leaves; i++) {
    if (ownedleaves[i] == 1) {
      if (ptr->one == NULL) {
#ifdef EXTREE_DEBUG
	Printf("(%d,%d) does not exist (i = %d)\n",E1,E2,i);
#endif
	return(0);
      }
      ptr = ptr->one;
    }
    else {
      if (ptr->zero == NULL) {
#ifdef EXTREE_DEBUG
	Printf("(%d,%d) does not exist (i = %d)\n",E1,E2,i);
#endif
	return(0);
      }
      ptr = ptr->zero;
    }
  }
#ifdef EXTREE_DEBUG
  Printf("(%d,%d) already exists (i = %d)\n",E1,E2,i);
#endif
  return (1);
}

#else

int InitializeExistTest(void)
{
#ifdef EXTREE_DEBUG
  Printf("called InitializeExistTest\n");
#endif
  tree_root = 0;
  free_list = 1;
  memzero(ex_array, sizeof(ex_array));
  return (1);
}

EX_LIST_PTR GetExListElement(void)
/* return index of next free element */
{
  if (free_list >= EX_SIZE) {
    Pprintf(stderr,"Too many ExListElements; garbage list exhausted\n");
    FatalError = 1;
    return(NULL);
  }
  return(free_list++);
}

void AddToExistSet(int E1, int E2)
{
  char ownedleaves[MAX_LEAVES+1];
  int i;
  EX_LIST_PTR ptr;

#if 1
  for (i = 1; i <= Leaves; i++)
    ownedleaves[i] = TestPackedArrayBit(MSTAR[E1],i) || 
      TestPackedArrayBit(MSTAR[E2],i);
#else
  memzero(ownedleaves, sizeof(ownedleaves));
  for (i = 1; i <= Leaves; i++)
    if (TestPackedArrayBit(MSTAR[E1],i) || 
	TestPackedArrayBit(MSTAR[E2],i)) ownedleaves[i] = 1;
#endif

  if (tree_root == 0) 
    if ((tree_root = GetExListElement()) == 0) return;

  ptr = tree_root;
  for (i = 1; i <= Leaves; i++) {
    if (ownedleaves[i] == 1) {
      if (ex_array[ptr].one == 0) {
	/* add it to list */
	if (i == Leaves) ex_array[ptr].one = ptr;
	else if ((ex_array[ptr].one = GetExListElement()) == 0) return;
      }
      ptr = ex_array[ptr].one;
    }
    else {
      if (ex_array[ptr].zero == 0) {
	/* add it to list */
	if (i == Leaves) ex_array[ptr].zero = ptr;
	else if ((ex_array[ptr].zero = GetExListElement()) == 0) return;
      }
      ptr = ex_array[ptr].zero;
    }
  }
}
    
int Exists(int E1, int E2)
{
  char ownedleaves[MAX_LEAVES+1];
  int i;
  EX_LIST_PTR ptr;

  CountExists++;
  if (tree_root == 0) {
#ifdef EXTREE_DEBUG
    Printf("(%d,%d) does not exist (empty list)\n",E1,E2);
#endif
    return(0);
  }

  memzero(ownedleaves, sizeof(ownedleaves));
  for (i = 1; i <= Leaves; i++)
    if (TestPackedArrayBit(MSTAR[E1],i) || 
	TestPackedArrayBit(MSTAR[E2],i)) ownedleaves[i] = 1;

#ifdef EXTREE_DEBUG
  Printf("checking existence of :");
  for (i = 1; i <= Leaves; i++) Printf(" %d",ownedleaves[i]);
  Printf("  ");
#endif

  ptr = tree_root;
  for (i = 1; i <= Leaves; i++) {
    if (ownedleaves[i] == 1) {
      if (ex_array[ptr].one == 0) {
#ifdef EXTREE_DEBUG
	Printf("(%d,%d) does not exist (i = %d)\n",E1,E2,i);
#endif
	return(0);
      }
      ptr = ex_array[ptr].one;
    }
    else {
      if (ex_array[ptr].zero == 0) {
#ifdef EXTREE_DEBUG
	Printf("(%d,%d) does not exist (i = %d)\n",E1,E2,i);
#endif
	return(0);
      }
      ptr = ex_array[ptr].zero;
    }
  }
#ifdef EXTREE_DEBUG
  Printf("(%d,%d) already exists (i = %d)\n",E1,E2,i);
#endif
  return (1);
}

void PrintExistSetStats(FILE *f)
{
  Fprintf(f,"Free list = %d of %d entries; ",free_list,EX_SIZE);
}


#endif /* EX_TREE_WITH_POINTERS */
#else /* EX_TREE_FOR_EXIST */

struct ex_entry {
  unsigned long mstar[(MAX_LEAVES / BITS_PER_LONG)  + 1];
  struct ex_entry *next;
};

#if 1
struct ex_entry *ex_tab[MAX_ELEMENTS];
#else
#ifdef IBMPC
struct ex_entry *ex_tab[4100];
#else
struct ex_entry *ex_tab[66000];
#endif
#endif


void PRINTPACKED(unsigned long *mstar)
{
  int i;
  for (i = 0; i <= PackedLeaves; i++) Printf("%lX ",mstar[i]);
}


#ifdef IBMPC
static unsigned int lochash(unsigned long *mstar)
#else
static unsigned long lochash(unsigned long *mstar)
#endif
{
  int i;
  unsigned long hashval;
  
  hashval = *mstar;
  for (i = 1; i <= PackedLeaves; i++) hashval ^= mstar[i];
  /* we now have a 32 bit field that we want to collapse to 16 / 12 bits */
#ifdef EXTREE_DEBUG
  Printf("hashval = %ld; ",(long)hashval);
#endif

#if 1
  hashval = hashval % (MAX_ELEMENTS - 1);
#else
  hashval = (hashval >> 16) ^ (hashval & 0x0000FFFFL);
#ifdef IBMPC
  hasval = hashval >> 4;
#endif
#endif

#ifdef EXTREE_DEBUG
  Printf("element hashed to %ld\n",(long)hashval);
#endif
#ifdef IBMPC
  return((int)hashval);
#else
  return(hashval);
#endif
}

struct ex_entry *hashlookup(unsigned long *mstar)
{
  struct ex_entry *np;
  int i;

  for (np = ex_tab[lochash(mstar)]; np != NULL; np = np->next) 
    for (i = 0; i <= PackedLeaves && mstar[i] == (np->mstar)[i]; i++) 
      if (i == PackedLeaves) {
#ifdef EXTREE_DEBUG
	Printf("element found in hash table\n");
#endif
	return(np);
      }
#ifdef EXTREE_DEBUG
  Printf("element not in hash table\n");
#endif
  return(NULL);
}

static struct ex_entry *hashinstall(unsigned long *mstar)
{
  struct ex_entry *np;
#ifdef IBMPC
  unsigned int hashval;
#else
  unsigned long hashval;
#endif
  int i;

  hashval = lochash(mstar);
  for (np = ex_tab[hashval]; np != NULL; np = np->next) 
    for (i = 0; i <= PackedLeaves && mstar[i] == (np->mstar)[i]; i++) 
      if (i == PackedLeaves) {
#ifdef EXTREE_DEBUG
	Printf("element found in hash table\n");
#endif
	return(np);
      }

  /* not found in hash table, so install it */
  if ((np = (struct ex_entry *)CALLOC(1, sizeof(struct ex_entry))) == NULL)
    return(NULL);
  /*    memcpy(np->mstar, mstar, (PackedLeaves + 1)*sizeof(long)); */
  memcpy(np->mstar, mstar, sizeof(np->mstar));
  np->next = ex_tab[hashval];
#ifdef EXTREE_DEBUG
  Printf("Element installed in hash table\n");
#endif
  return(ex_tab[hashval] = np);
}

int Exists(int E1, int E2)
{
  int i;
  unsigned long mstar[(MAX_LEAVES / BITS_PER_LONG)  + 1];

  CountExists++;

  for (i = 0; i <= PackedLeaves; i++)
    mstar[i] = MSTAR[E1][i] | MSTAR[E2][i];

#ifdef EXTREE_DEBUG
  Printf("TESTING Existence of (%d,%d)",E1,E2);
  PRINTPACKED(mstar);
  Printf("\n");
#endif
  return (hashlookup(mstar) != NULL);
}

int InitializeExistTest(void) 
{
#ifdef IBMPC
  int i;
#else
  long i;
#endif
  struct ex_entry *np;
  struct ex_entry *npnext;

  for (i = 0; i < sizeof(ex_tab) / sizeof(ex_tab[0]); i++) 
    for (np = ex_tab[i]; np != NULL; np = npnext) {
      npnext = np->next;
      FREE(np);
    }
  memzero(ex_tab, sizeof(ex_tab));
  return(1);
}

void AddToExistSet(int E1, int E2) 
{
  int i;
  unsigned long mstar[(MAX_LEAVES / BITS_PER_LONG)  + 1];

  for (i = 0; i <= PackedLeaves; i++)
    mstar[i] = MSTAR[E1][i] | MSTAR[E2][i];

#ifdef EXTREE_DEBUG
  Printf("Requesting installation of (%d,%d) in hash table\n",E1,E2);
#endif
  hashinstall(mstar);
}

void PrintExistSetStats(FILE *f)
{
  struct ex_entry *np;
#ifdef IBMPC
  int i;
#else
  long i;
#endif
  long bins;
  long nodes;

  bins = 0;
  nodes = 0;
  for (i = 0; i < sizeof(ex_tab) / sizeof(ex_tab[0]); i ++) 
    if ((np = ex_tab[i]) != NULL) {
      bins++;
      while (np != NULL) {
	nodes++;
	np = np->next;
      }
    }

  Fprintf(f,"Exist hash table stats: %ld of %ld bins used",
	 (long)bins, (long)(sizeof(ex_tab) / sizeof(ex_tab[0])));
  if (bins != 0) Fprintf(f,", %ld nodes (%.2f nodes/bin)",
			 nodes, (float)nodes / (float)bins);
  Fprintf(f,"\n");
  Fprintf(f,"Exist hash table memory usage: %ld bytes\n",
	  (long)(sizeof(ex_tab) + nodes * sizeof(struct ex_entry)));
}


#endif /* EX_TREE_FOR_EXIST */

int linelength = 80;


void FreeEmbeddingTree(struct embed *E)
{
  if (E == NULL) return;
  if (E->left != NULL) FreeEmbeddingTree(E->left);
  if (E->right != NULL) FreeEmbeddingTree(E->right);
  FREE(E);
}

struct embed *EmbeddingTree(struct nlist *tp, int E)
/* return element E embedded in a tree of 'struct embed' */
{
  struct embed *node;

  if (E == 0) return(NULL);
  if ((node = (struct embed *)CALLOC(1, sizeof(struct embed))) == NULL)
    return(NULL);

  node->cell = tp;
#if 0
  if (LEVEL(E) == 0) {
#else
  if (L(E) == 0 && R(E) == 0) {
#endif
    /* root element */
    node->instancenumber = E;
    node->level = LEVEL(E);
    return(node);
  }

  /* not a root element */
  node->right = EmbeddingTree(tp, R(E));
  node->left = EmbeddingTree(tp, L(E));
  if (R(E) == 0) node->level = node->left->level + 1;
  else if (L(E) == 0) node->level = node->right->level + 1;
  else node->level = MAX(node->left->level, node->right->level) + 1;
  return(node);
}

struct embed *FlattenEmbeddingTree(struct embed *E)
/* flattens a forest of trees, returning a copy */
{
  struct embed *node;
  int index;

  if (E == NULL) return(NULL);
  if ((node = (struct embed *)CALLOC(1, sizeof(struct embed))) == NULL)
    return(NULL);

  node->cell = E->cell;
  node->level = E->level;

  if (E->left == NULL && E->right == NULL) {
    /* root element */
    struct embed *tmp;
    struct nlist *tp;
    struct objlist *ob;

    ob = InstanceNumber(E->cell,E->instancenumber);
    tp = LookupCell(ob->model.class);

    if (tp->embedding != NULL) {
      tmp = FlattenEmbeddingTree((struct embed *)(tp->embedding));
      node->left = tmp->left;
      node->right = tmp->right;
      node->level = E->level;
      node->instancenumber = 0;
    }
    else memcpy(node, E, sizeof(struct embed));
    return(node);
  }

  /* not a root element */
  node->right = FlattenEmbeddingTree(E->right);
  node->left = FlattenEmbeddingTree(E->left);
  node->level = E->level;

  /* make it a balanced tree */
  for  (index = E->right->level + 1; index < node->level; index++) {
    struct embed *tmp;
    
    if ((tmp = (struct embed *)CALLOC(1, sizeof(struct embed))) == NULL)
      return(NULL);
    tmp->level = index;
    tmp->left = NULL;
    tmp->right = node->right;
    node->right = tmp;
  }
  for  (index = E->right->level + 1; index < node->level; index++) {
    struct embed *tmp;
    
    if ((tmp = (struct embed *)CALLOC(1, sizeof(struct embed))) == NULL)
      return(NULL);
    tmp->level = index;
    tmp->left = NULL;
    tmp->right = node->right;
    node->right = tmp;
  }
    
  return(node);
}

#define PRINT_INDENT 2

int LenEmbed(char *prefix, struct nlist *np, struct embed *E, int flatten)
/* return the number of characters required to print element E */
{
  char longstr[200];

  if (E == NULL) return(0);
  if (E->left == NULL && E->right == NULL) {
    /* this is a root in our cell's embedding heirarchy */
    struct objlist *ob;
    char *instancename;
    char *model;
    struct nlist *np2;

    ob = InstanceNumber(np,E->instancenumber);
    instancename = ob->instance.name;
    model = ob->model.class;
    np2 = LookupCell(model);
    if (np2 == NULL) return(0);
    sprintf(longstr, "%s%s", prefix, instancename);
    if ((np2->class != CLASS_SUBCKT) || np2->embedding == NULL || !flatten) 
      return(strlen(longstr));
    /* else, prepend model */
    strcat(longstr,SEPARATOR);
    return(LenEmbed(longstr, np2, (struct embed *)np2->embedding, flatten));
  }

  /* else it is a compound element (with 2 parentheses) */
  return(PRINT_INDENT + 2 + LenEmbed(prefix, np, E->left, flatten) + 
	 LenEmbed(prefix, np, E->right, flatten));
}

void PrintEmb(FILE *outfile, char *prefix, struct nlist *np, 
	      struct embed *E, int indent, int flatten)
/* just print out the element on a single line */
/* assumes that we have been indented enough to print it directly */
{
  if (E == NULL) return;
  if (E->left == NULL && E->right == NULL) {
    /* this is a root in our cell's embedding heirarchy */
    struct objlist *ob;
    char *instancename;
    struct nlist *np2;
    char name[200];

    ob = InstanceNumber(np,E->instancenumber);
    instancename = ob->instance.name;
    np2 = LookupCell(ob->model.class);
    if (np2 == NULL) return;
     sprintf(name,"%s%s", prefix, instancename);
    if ((np2->class != CLASS_SUBCKT) || np2->embedding == NULL || !flatten) 
      Fprintf(outfile, "%s", name);
    else {    
      strcat(name,SEPARATOR);
      PrintEmb(outfile, name, np2, (struct embed *)np2->embedding, 
	       indent + 2*PRINT_INDENT, flatten);
    }
    return;
  }

  /* it is a compound element */
  Fprintf(outfile,"(");
  PrintEmb(outfile, prefix, np, E->left, indent, flatten);
  Fprintf(outfile," ");
  PrintEmb(outfile, prefix, np, E->right, indent, flatten);
  Fprintf(outfile,")");
}

void PrintEmbed(FILE *outfile, char *prefix, struct nlist *np,
	       struct embed *E, int indent, int flatten)
/* assumes that cursor is on col. 1 of line at entry */
/* upon return, cursor is on col. 1 of next free line */
{
  int i;

  if (E == NULL) return;
  if (E->right == NULL && E->left == NULL) {
    /* it is a root element */
    struct objlist *ob;
    char *instancename;
    struct nlist *np2;
    char name[200];

    ob = InstanceNumber(np,E->instancenumber);
    instancename = ob->instance.name;
    np2 = LookupCell(ob->model.class);
    if (np2 == NULL) return;

    if (np2->embedding != NULL && flatten) {
      sprintf(name,"%s%s%s", prefix, instancename, SEPARATOR);
      PrintEmbed(outfile, name, np2, (struct embed *)np2->embedding, 
		 indent + PRINT_INDENT, flatten);
      return;
    }
    else {
      for (i = 0; i < indent; i++) Fprintf(outfile," ");
      PrintEmb(outfile, prefix, np, E, indent, flatten);
      Fprintf(outfile,"\n");
      return;
    }
  }
    
  if (indent + LenEmbed(prefix, np, E, flatten) >= linelength) {
    for (i = 0; i < indent; i++) Fprintf(outfile," ");
    Fprintf(outfile,"(\n");
    PrintEmbed(outfile, prefix, np, E->left, indent + PRINT_INDENT, flatten);
    PrintEmbed(outfile, prefix, np, E->right,indent + PRINT_INDENT, flatten);
    for (i = 0; i < indent; i++) Fprintf(outfile," ");
    Fprintf(outfile,")\n");
  }
  else {
    for (i = 0; i < indent; i++) Fprintf(outfile," ");
    Fprintf(outfile,"(");
    PrintEmb(outfile, prefix, np, E->left, indent, flatten);
    Fprintf(outfile," ");
    PrintEmb(outfile, prefix, np, E->right, indent, flatten);
    Fprintf(outfile,")\n");
  }
}

void PrintEmbeddingTree(FILE *outfile, char *cellname, int flatten)
{
  struct nlist *np;

  if (outfile == NULL) return;
  np = LookupCell(cellname);
  if (np == NULL) return;
  if (np->embedding == NULL) 
    Fprintf(outfile, "No embedding for '%s' has been determined.\n",cellname);
  else {
    Fprintf(outfile, "Embedding for %s (level %d):\n",cellname,
	    ((struct embed *)(np->embedding))->level);
    PrintEmbed(outfile, "", np, (struct embed *)(np->embedding), 0, flatten);
    Fprintf(outfile, "\n");
  }
}




