/****************************************************************************/
/*                                                                          */
/*                      Chaine de CAO & VLSI   Alliance                     */
/*                                                                          */
/*    Produit : synthetiseur logique                                        */
/*    Fichier : moteur.c                                                    */
/*                                                                          */
/*    (c) copyright 1991 Laboratoire MASI equipe CAO & VLSI                 */
/*    Tous droits reserves                                                  */
/*    Support : e-mail cao-vlsi@masi.ibp.fr                                 */
/*                                                                          */
/*    Auteur(s) :     P. Allegre                        le : 05/08/91       */
/*                                                                          */
/*    Modifie par :                                     le : 11/09/91       */
/*    Modifie par :                                     le : ../../....     */
/*    Modifie par :                                     le : ../../....     */
/*                                                                          */
/****************************************************************************/

#include <stdio.h>
#include MUT_H
#include LOG_H
#include BEH_H
#include "../compil/lax_param.h"
#include "../synthe/sl_type.h"
#include "types.h"
#include "util.h"
#include "arbres.h"
#include "compile.h"
#include "cout.h"
#include "decomp.h"
#include "mapper.h"
#include "oper.h"
#include "regles.h"
#include "moteur.h"

/*--------------------------------------------------------------------------
ssExprArite	: reduction d'arite des sous operateurs
----------------------------------------------------------------------------
retour 		: variables de filtrage modifiees
--------------------------------------------------------------------------*/
void ssExprArite(prem,varRegle)
chain_list *prem;
Alist *varRegle;
{
short oper;
Alist *var;

   if (prem && !ATOM(prem)) oper = OPER(prem); 
   while (prem = CDR(prem))
   {
      if (ATOM(CAR(prem)) && (VALUE_ATOM(CAR(prem)) != nameRest))
      {
      chain_list *expr;
      char *varF = VALUE_ATOM(CAR(prem));

         var = searchAlist(varRegle,varF);
         if (var->name[0] == '*')
            if (var->ptChain && CDR(var->ptChain))
            {
	       switch (oper)
	       {
		case NAND : expr = createExpr(AND);break;
		case NOR  : expr = createExpr(OR);break;
		default : expr = createExpr(oper);
	       }
               expr->NEXT = var->ptChain;
	       addArite(expr);
               var->ptChain = decomp(copyExpr(expr));
               expr->NEXT = NULL;freeExpr(expr); /*liberation de la tete*/
            }
            else
               var->ptChain = copyExpr(CAR(var->ptChain));
/* copie car partie de 'aux' */
      }
      else
         ssExprArite(CAR(prem),varRegle);
   }
}

/*--------------------------------------------------------------------------
decArite	: reduction d'arite du declencheur
----------------------------------------------------------------------------
retour 		: expression modifiee
--------------------------------------------------------------------------*/
chain_list *decArite(regle,expr,var)
regle_list *regle;
chain_list *expr;
Alist *var;
{
chain_list *rest = searchAlist(var,nameRest)->ptChain;
chain_list *aux;
ptype_list *sol;

   switch (OPER(regle->premisse))
   {
      case NAND : 
                  for (aux = rest;aux;aux = CDR(aux))
		  {
                     aux->DATA = (void *)invExpr(CAR(aux));
		     if (!ATOM(CAR(aux)) && OPER(CAR(aux)) == NOT)
		     {
		        sol = evalue(CAR(aux));
		        freeExpr(CAR(aux));
		        aux->DATA = sol->DATA;
		        libere(CDR(sol),sol);
		     }
                  }
                  aux = createExpr(OR);
                  break;
      case NOR  : 
                  for (aux = rest;aux;aux = CDR(aux))
		  {
                     aux->DATA = (void *)invExpr(CAR(aux));
		     if (!ATOM(CAR(aux)) && OPER(CAR(aux)) == NOT)
		     {
		        sol = evalue(CAR(aux));
		        freeExpr(CAR(aux));
		        aux->DATA = sol->DATA;
		        libere(CDR(sol),sol);
		     }
                  }
                  aux = createExpr(AND);
                  break;
      default   : aux = createExpr(OPER(regle->premisse));
   }
   addHExpr(aux,expr);
   while (rest)
      {
      addHExpr(aux,copyExpr(CAR(rest))); /* copie car partie de 'aux' */
      rest = CDR(rest);
      }
   addArite(aux);
   /* pas de liberation car non recopie... */
   searchAlist(var,nameRest)->ptChain = NULL;
   return aux;
}

/*--------------------------------------------------------------------------
back_track	: retour arriere sur une conclusion
----------------------------------------------------------------------------
retour 		: nouvelle conclusion.
--------------------------------------------------------------------------*/

chain_list *backTrack(expr)
chain_list *expr;
{
chain_list *res = expr;
int traceSauv = SL_TRACE;

   SL_TRACE = 0;
   if (OPER(expr) == NOT)
      res = CADR(res);
   while (res = CDR(res))
   {
      if (!ATOM(CAR(res)) && OPER(CAR(res)) == NOT)
      {
      chain_list *aux = remp(NULL,CAR(res),1);
      ptype_list *sol = evalue(aux),*sol2;
      char *name = NULL;

         /* detection d'un signal interne */
         if (!ATOM(CADR(CAR(res))))
	 {
	 chain_list *aux2 = triVar(copyExpr(CADR(CAR(res))));
	     name = cherchePorte(aux2,100);
	     freeExpr(aux2);
	 }
/* en surface le gain est nul car les elements de la 2eme solution
   ne sont pas dans la table de hachage. Peut-etre un gain en delai... */
         if (name)
         {
	 chain_list *expr2 = createExpr(NOT),*aux3;

	   addHExpr(expr2,createAtom(name));
	   addArite(expr2);
	   aux3 = remp(NULL,expr2,2);
	   sol2 = evalue(aux3);
	   freeExpr(expr2);
	   freeExpr(aux3);
         }
	 freeExpr(CAR(res));
	 if (name && (sol2->TYPE < sol->TYPE))
	 {
	    res->DATA = sol2->DATA;
	    libere(CDR(sol2),sol2);
	 }
         else res->DATA = sol->DATA;
	 freeExpr(aux);
	 libere(CDR(sol),sol);
      }
   }
   SL_TRACE = traceSauv;
   return expr;
}

/*--------------------------------------------------------------------------
filtre 		: remplit une liste associative de variables de filtrage 
----------------------------------------------------------------------------
retour 		: 0 ou 1 si OK ou pas. 
--------------------------------------------------------------------------*/

short filtre(prem,expr,tabVar)
chain_list *prem;
chain_list *expr;
Alist *tabVar;
{
char *ptChar;

   if (ATOM(prem))
   {
      Alist *tabAux = searchAlist(tabVar,VALUE_ATOM(prem));

      if (tabAux->ptChain!= NULL)	/* variable de filtrage multiple */
      {
         if (equalExpr(remp(NULL,tabAux->ptChain,1),CAR(expr)) == 1 ||
             equalExpr(tabAux->ptChain,remp(NULL,CAR(expr),1)) == 1)
            return 1;
         else
            return 0;
      }
      else
      {
         ptChar = VALUE_ATOM(prem);
         if (ptChar[0] != '*' )
            tabAux->ptChain = copyExpr(CAR(expr));
         else
            tabAux->ptChain = expr; 
         return 1;
      }
   }
   else
      if (ATOM(expr)) return(1);
      else
      {
         if (OPER(prem) != OPER(expr)) return(0);
         else
         {
            while (prem = CDR(prem))
            {
               expr = CDR(expr);
               if (expr == NULL)
               {
                  if (ATOM(CAR(prem)))
                  {
                     ptChar = VALUE_ATOM(CAR(prem));
                     if (ptChar[0] != '*')
                        return(0);
                     else
                        return(1);
                  }
                  else
                     return(0);
               }
               if (ATOM(CAR(prem)))
               {
                  if (CDR(prem))
		  {
                     if (filtre(CAR(prem),expr,tabVar) == 0)
			return 0;
		  }
                  else
                     return filtre(CAR(prem),expr,tabVar);
               }
               else
                  if (filtre(CAR(prem),CAR(expr),tabVar) == 0) return(0);
            }
            if (CDR(expr)) return(0);
            else return(1);
         }
      }
}

/*--------------------------------------------------------------------------
moteur  	: applique une regle sur une expression
----------------------------------------------------------------------------
retour 		: nouvelle expression
--------------------------------------------------------------------------*/
chain_list *moteur(regle,expr)
regle_list *regle;
chain_list *expr;

{
chain_list *res,*resBis,*aux = copyExpr(expr);
Alist *var;

   if (ARITE(expr) < ARITE(regle->premisse) ||
       profExpr(aux) < profExpr(regle->premisse))
   {
      freeExpr(aux); return NULL;
   }
   if ((searchCharOper(OPER(aux)) != nameLatch) &&
       (searchCharOper(OPER(aux)) != nameBD) &&
       (searchCharOper(OPER(aux)) != nameBus))
      {
       	if (permutOp(regle->premisse,aux,1,regle->prof_premi,1) == 0)
   	{
           freeExpr(aux); return NULL;
   	}
      }
      else
      {
	if (verifExpr(regle->premisse,aux) == 0 ||
            ARITE(aux) < ARITE(regle->premisse))
   	{
           freeExpr(aux); return NULL;
   	}
        if (profExpr(regle->premisse) > 1 && 
	    ARITE(CADR(regle->premisse)) < ARITE(CADR(aux)) &&
            permutOp(regle->premisse,aux,0,regle->prof_premi,1) == 0)
   	{
           freeExpr(aux); return NULL;
   	}
      }

/* verification condition d'application */
   if ((regle->condition == 1) &&
       (ARITE(regle->premisse) != ARITE(aux)))
   {	
      freeExpr(aux); return NULL;
   }

   var = createAlist(NULL,regle->premisse);
   if (filtre(regle->premisse,aux,var) == 0)
   {
      resetAlist(var,2);
      freeAlist(var);
      freeExpr(aux);return NULL;}

if (SL_TRACE == 2 )
{
printf("regle : ");displayArite(regle->premisse);printf("->");displayArite(regle->conclusion);printf("\n");
displayArite(aux);printf("\n");
}

/* traitement de l'arite des sous operateurs */

   if (regle->prof_premi > 1) 
      ssExprArite(regle->premisse,var);


/* substitution des variable de conclusion par leur valeur
   et retour arriere					   */
/* optimisation du tri des variables d'entree		   */

   res = remplace(regle->conclusion,var);
   if (OPER(regle->premisse) == NOT)
      res = triVar(res);
   else
      res = triVar(backTrack(res));

/* traitement arite du declencheur */

   if (ARITE(regle->premisse) < ARITE(aux))
   {
   ptype_list *sol;

      resBis = decArite(regle,res,var);
      sol = evalue(resBis);
      freeExpr(resBis);		 
      res = decomp(CAR(sol));
      libere(CDR(sol),sol);
   }

   regle->util++;
   freeExpr(aux);
   freeAlist(var);
   return res;
}

/*--------------------------------------------------------------------------
permutOp	: permute l'expression expr2 par rapport a l'expression  
		  expr1. Les termes en trop sont mis a la fin de expr2
----------------------------------------------------------------------------
retour 		: 1 si ok 0 sinon (modification physique de expr2)
--------------------------------------------------------------------------*/

int permutOp(expr1,expr2,tri,profPrem,prof)
chain_list *expr1,*expr2;
short tri,profPrem,prof;

{
chain_list *exprp2;

   /* 27/07/93 */
   /* tri par delais si autorise et profondeur maximale premisse */
   if (tri && !ATOM(expr2) && OPER(expr2) < 10 && (profPrem == prof))
       sortExpr(expr2,delais,0);
   if (equalVarExpr(expr1,expr2))
      return 1;
   if (profPrem == 1) return 1;
   if (OPER(expr1) != OPER(expr2))
      return 0;
   else
   {
      chain_list *deja = NULL;

      exprp2 = expr2;
      expr1 = CDR(expr1);
      expr2 = CDR(expr2);
      while (expr1 && expr2)
      {
         if (equalVarExpr(CAR(expr1),CAR(expr2)) || ATOM(CAR(expr1)))
         {
            expr1 = CDR(expr1);
            exprp2 = expr2;
            expr2 = CDR(expr2);
         }
         else				/* report terme en trop */
         {
            chain_list *pt = expr2;

               if (CAR(expr2) != deja && !ATOM(CAR(expr2)) &&
                   lengthExpr(CAR(expr1)) <= lengthExpr(CAR(expr2)) &&
                      permutOp(CAR(expr1),CAR(expr2),tri,profPrem,++prof) == 1)
                  {
                     expr1 = CDR(expr1);
                     exprp2 = CDR(expr2);
                     expr2 = CDR(expr2);
                  }
                  else
                  {
                     if (!CDR(expr2)) return 0;
                     if (CAR(expr2) == deja)
                        return 0;
		     if (!ATOM(CAR(expr2)) && 
			 (OPER(CAR(expr1)) < OPER(CAR(expr2))))
			return 0;
                     if (!deja)
                        if (ATOM(expr2))
                           deja = expr2;
                        else
                           deja = CAR(expr2);
                     CDR(exprp2) = CDR(expr2);
                     while (CDR(pt)) pt = CDR(pt);
                     CDR(pt) = expr2;
                     CDR(expr2) = NULL;
                     expr2 = CDR(exprp2);
                  }
         }
      }

      if (expr1)
      {
         char *dernier = VALUE_ATOM(CAR(expr1));
         if (dernier[0] == '*')
            return 1;
         else
            return 0;
      }
      return 1;
   }
}

/*--------------------------------------------------------------------------
remplace 	: reporte sur une conclusion conc les valeurs des variables
		  de filtrage contenues dans tabVar. 
----------------------------------------------------------------------------
retour 		: une expression mise a jour.
--------------------------------------------------------------------------*/

chain_list *remplace(conc,tabVar)
chain_list *conc;
Alist *tabVar;
{
chain_list *expr1;

   if (ATOM(conc))
   {
         return copyExpr(searchAlist(tabVar,VALUE_ATOM(conc))->ptChain);
   }
   expr1 = copyExpr(conc);
   while (tabVar)
   {
      if (tabVar->name != nameRest)
      {
         if (!tabVar->ptChain) 
         {
	    fprintf(stderr,"pas de valeur associee a %s\n",tabVar->name);
	    tabVar->ptChain = createATOM(tabVar->name);
         }
         substPhyExpr(expr1,tabVar->name, tabVar->ptChain);
/* recopie de ptChain par substPhyExpr */
      }
      tabVar = CDR(tabVar);
   }   
   return(expr1);
}

/*--------------------------------------------------------------------------
verifExpr 	: verifie la presence de '0' ou '1' dans la premisse et
		  dans l'expression
----------------------------------------------------------------------------
retour 		: 1 si tout va bien
--------------------------------------------------------------------------*/
short verifExpr(prem,exp)
chain_list *prem,*exp;
{
   while ((prem = CDR(prem)) && (exp = CDR(exp)))
   {
      if ((VALUE_ATOM(CAR(prem)) == nameZero) ||
	  (VALUE_ATOM(CAR(prem)) == nameUn))
	  if (!ATOM(CAR(exp)))
	     return 0;
	  else
	     if (VALUE_ATOM(CAR(prem)) != VALUE_ATOM(CAR(exp)))
		return 0;
   }
   return 1;
}
