/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *                     SUBSTITUTION                       * */
/* *                                                        * */
/* *  $Module:      SUBSTITUTION                            * */ 
/* *                                                        * */
/* *  Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001      * */
/* *  MPI fuer Informatik                                   * */
/* *                                                        * */
/* *  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; either version 2 of the License, * */
/* *  or (at your option) any later 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; if not, write * */
/* *  to the Free Software Foundation, Inc., 59 Temple      * */
/* *  Place, Suite 330, Boston, MA  02111-1307  USA         * */
/* *                                                        * */
/* *                                                        * */
/* $Revision$                                        * */
/* $State$                                            * */
/* $Date$                             * */
/* $Author$                                       * */
/* *                                                        * */
/* *             Contact:                                   * */
/* *             Christoph Weidenbach                       * */
/* *             MPI fuer Informatik                        * */
/* *             Stuhlsatzenhausweg 85                      * */
/* *             66123 Saarbruecken                         * */
/* *             Email: weidenb@mpi-sb.mpg.de               * */
/* *             Germany                                    * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


/* $RCSfile$ */

#include "subst.h"

/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  SUBSTITUTION CREATION AND DELETION                    * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


SUBST subst_Add(SYMBOL Symbol, TERM Codomain, SUBST Subst)
{
  SUBST Result;

  Result           = subst_Get();
  Result->next     = Subst;
  Result->dom      = Symbol;
  Result->codomain = Codomain;

  return Result;
}


void subst_Delete(SUBST Subst)
{
  SUBST Next;

  while (subst_Exist(Subst)) {
    Next = subst_Next(Subst);

    if (subst_Cod(Subst))
      term_Delete(subst_Cod(Subst));

    subst_FreeOneNode(Subst);
    Subst = Next;
  }
}

void subst_Free(SUBST Subst)
{
  SUBST Next;

  while (subst_Exist(Subst)) {
    Next = subst_Next(Subst);
    subst_FreeOneNode(Subst);
    Subst = Next;
  }
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  FUNCTIONS ON SUBSTITUTIONS                            * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


TERM subst_Term(SYMBOL Symbol, SUBST Subst)
{
  for (; subst_Exist(Subst); Subst = subst_Next(Subst))
    if (symbol_Equal(Symbol,subst_Dom(Subst)))
      return subst_Cod(Subst);
  return (TERM)NULL;
}


static TERM subst_ApplyIntern(SUBST Subst, TERM Term)
{
  TERM   RplacTerm;
  LIST   Arglist;
  SYMBOL Top;

  Top = term_TopSymbol(Term);

  if (symbol_IsVariable(Top) && (RplacTerm = subst_Term(Top,Subst))) {
    Arglist = term_CopyTermList(term_ArgumentList(RplacTerm));
    term_RplacTop(Term, term_TopSymbol(RplacTerm));
    term_DeleteTermList(term_ArgumentList(Term));
    term_RplacArgumentList(Term, Arglist);
  } else {
    for (Arglist = term_ArgumentList(Term);
	 !list_Empty(Arglist);
	 Arglist = list_Cdr(Arglist))
      subst_ApplyIntern(Subst, list_Car(Arglist));
  }     

  return Term;
}

TERM subst_Apply(SUBST Subst, TERM Term)
{
  if (subst_Empty(Subst))
    return Term;

  return subst_ApplyIntern(Subst, Term);
}


SUBST subst_Merge(SUBST Source, SUBST Drain)
{
  SUBST Scan;
  BOOL  Changed;
  
  for (; subst_Exist(Source); Source = subst_Next(Source)) {
    
    /* Apply current assignment of Source to all  */
    /* assignments of Drain. If the current ass.  */
    /* cannot be applied to any codomain in Drain */
    /* the current assignment is added to Drain.  */
    
    Changed = FALSE;

    for (Scan = Drain;
	 subst_Exist(Scan);
	 Scan = subst_Next(Scan))
      if (term_SubstituteVariable(Source->dom,
				   Source->codomain,
				   &(Scan->codomain)))
	Changed = TRUE;

    if (!Changed)
      Drain = subst_Add(Source->dom, 
			term_Copy(Source->codomain),
			Drain);
  }

  return Drain;
}

SUBST subst_Compose(SUBST Outer, SUBST Inner)
/**************************************************************
  INPUT:   Two substitutions.
  RETURNS: The substitution corresponding to the composition of
           <Outer> and <Inner>.
  EFFECT:  <Outer> is destructively applied to the codomain of <Inner>
           <Inner> is destructively extended
***************************************************************/
{
  SUBST Scan1,Scan2,New;

  New = subst_Nil();
  
  for (Scan1=Outer; subst_Exist(Scan1); Scan1 = subst_Next(Scan1)) {    
    for (Scan2 = Inner;subst_Exist(Scan2);Scan2 = subst_Next(Scan2))
      term_SubstituteVariable(subst_Dom(Scan1),subst_Cod(Scan1),&(Scan2->codomain));
    if (!subst_BindVar(subst_Dom(Scan1),Inner))
      New = subst_Add(subst_Dom(Scan1), term_Copy(subst_Cod(Scan1)),New);
  }
  return subst_NUnion(Inner,New);
}

BOOL subst_BindVar(SYMBOL Var, SUBST Subst)
/**************************************************************
  INPUT:   A variable symbol and a substitution.
  RETURNS: TRUE iff <Var> is contained in the domain of <Subst>
***************************************************************/
{
  SUBST Scan;

  for (Scan=Subst; subst_Exist(Scan); Scan = subst_Next(Scan))
    if (symbol_Equal(subst_Dom(Scan),Var))
      return TRUE;

  return FALSE;
}



SUBST subst_Copy(SUBST Subst)
{
  SUBST Copy, Result;

  for (Result = subst_Nil(),
       Copy   = subst_Nil();
       subst_Exist(Subst);
       Subst = subst_Next(Subst))
    if (subst_Exist(Result)) {
      subst_SetNext(Copy, subst_Add(subst_Dom(Subst),
				    term_Copy(subst_Cod(Subst)),
				    subst_Nil()));
      Copy = subst_Next(Copy);
    } else {
      Result = subst_Add(subst_Dom(Subst),
			 term_Copy(subst_Cod(Subst)),
			 subst_Nil());
      Copy = Result;
    }

  return Result;
}


BOOL subst_MatchTops(const CONTEXT Context, SUBST Subst)
{
  for ( ; subst_Exist(Subst); Subst = subst_Next(Subst))
    if (cont_ContextBindingTerm(Context, subst_Dom(Subst)) &&
	term_EqualTopSymbols(cont_ContextBindingTerm(Context, subst_Dom(Subst)),
			     subst_Cod(Subst)))
      return TRUE;
  return FALSE;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  UNIFICATION TEST                                      * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


BOOL subst_Unify(CONTEXT IndexContext, SUBST Subst)
/*********************************************************
  INPUT:
  RETURNS:
  CAUTION: 'Subst' IS ASSUMED TO BE NON-EMPTY.
**********************************************************/
{
  while (subst_Exist(Subst)) {
    if (!cont_VarIsBound(IndexContext, subst_Dom(Subst))) {
      if (unify_OccurCheck(IndexContext, subst_Dom(Subst), IndexContext, subst_Cod(Subst)))
	return FALSE;
      else
	cont_CreateBinding(IndexContext, subst_Dom(Subst), IndexContext, subst_Cod(Subst));
    } else if (!unify_UnifyAllOC(IndexContext,
				 IndexContext,
				 subst_Cod(Subst),
				 cont_ContextBindingContext(IndexContext,
							       subst_Dom(Subst)),
				 cont_ContextBindingTerm(IndexContext,
							    subst_Dom(Subst))))
      return FALSE;

    Subst = subst_Next(Subst);
  }

  return TRUE;
}

BOOL subst_IsShallow(SUBST Subst) {
/**********************************************************
  INPUT:   A unifier
  RETURNS: TRUE, if the unifier is valid :
           a variable or a ground term  or a function with only
           variables or ground terms as arguments.
***********************************************************/
    SUBST SubstScan;
    for (SubstScan = Subst; SubstScan != subst_Nil();
	 SubstScan = subst_Next(SubstScan)) {
	TERM Codomain = subst_Cod(SubstScan);
	if ((!term_IsVariable(Codomain)) 
	    && (!term_IsGround(Codomain))) {
	  LIST Scan ;
	  for (Scan = term_ArgumentList(Codomain); Scan != list_Nil(); 
	       Scan = list_Cdr(Scan)) {
	    if ((!term_IsVariable(list_Car(Scan))
		 && (!term_IsGround(list_Car(Scan)))))
	      return FALSE;
	  }
	}
    }
    return TRUE;
}

/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  GENERALIZATION TEST                                   * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


BOOL subst_Match(const CONTEXT Context, SUBST Subst)
/*********************************************************
  INPUT:
  RETURNS:
  CAUTION: 'Subst' IS ASSUMED TO BE NON-EMPTY.
**********************************************************/
{
  while (subst_Exist(Subst)) {
    if (!cont_VarIsBound(Context, subst_Dom(Subst)) ||
	!unify_Match(Context,
		     subst_Cod(Subst),
		     cont_ContextBindingTerm(Context, subst_Dom(Subst))))
      return FALSE;
    
    Subst = subst_Next(Subst);
  }

  return TRUE;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  INSTANCE TEST                                         * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


BOOL subst_MatchReverse(const CONTEXT IndexContext, SUBST Subst)
/*********************************************************
  INPUT:
  RETURNS:
  CAUTION: 'Subst' IS ASSUMED TO BE NON-EMPTY.
**********************************************************/
{
  while (subst_Exist(Subst)) {

    if (!cont_VarIsBound(IndexContext, subst_Dom(Subst))) {
      if (symbol_IsIndexVariable(subst_Dom(Subst)))
	cont_CreateBinding(IndexContext,
			      subst_Dom(Subst),
			      cont_InstanceContext(),
			      subst_Cod(Subst));
      else
	return FALSE;
    }
    else if (!unify_MatchReverse(IndexContext,
				 subst_Cod(Subst),
				 cont_ContextBindingContext(IndexContext, subst_Dom(Subst)),
				 cont_ContextBindingTerm(IndexContext, subst_Dom(Subst))))
      return FALSE;

    Subst = subst_Next(Subst);
  }

  return TRUE;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  VARIATION TEST                                        * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


BOOL subst_Variation(const CONTEXT Context, SUBST Subst)
/*********************************************************
  INPUT:
  RETURNS:
  CAUTION: 'Subst' IS ASSUMED TO BE NON-EMPTY.
**********************************************************/
{
  while (subst_Exist(Subst)) {

    if (!cont_VarIsBound(Context, subst_Dom(Subst)) ||
	!unify_Variation(Context,
			 subst_Cod(Subst),
			 cont_ContextBindingTerm(Context, subst_Dom(Subst))))
      return FALSE;
    
    Subst = subst_Next(Subst);
  }

  return TRUE;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  COMMON GENERALIZATIONS                                * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


SUBST subst_ComGen(const CONTEXT Context, SUBST Subst, SUBST* SubstOld,
		   SUBST* SubstNew)
/*********************************************************
  INPUT:
  RETURNS:
  CAUTION: 'Subst' IS ASSUMED TO BE NON-EMPTY.
**********************************************************/
{
  SUBST Result;
  
  Result = *SubstOld = *SubstNew = NULL;
  
  do {
    
    if (!cont_VarIsBound(Context, subst_Dom(Subst)))
      *SubstOld=subst_Add(subst_Dom(Subst), term_Copy(subst_Cod(Subst)), *SubstOld);

    else if (term_Equal(cont_ContextBindingTerm(Context, subst_Dom(Subst)),
			subst_Cod(Subst)))
      Result = subst_Add(subst_Dom(Subst), term_Copy(subst_Cod(Subst)), Result);

    else
      if (!symbol_Equal(term_TopSymbol(cont_ContextBindingTerm(Context,
								  subst_Dom(Subst))),
			term_TopSymbol(subst_Cod(Subst)))) {

      *SubstOld=subst_Add(subst_Dom(Subst),
			  term_Copy(subst_Cod(Subst)),
			  *SubstOld);
      *SubstNew=subst_Add(subst_Dom(Subst),
			  term_Copy(cont_ContextBindingTerm(Context,
							       subst_Dom(Subst))),
			  *SubstNew);

    } else
      Result = subst_Add(subst_Dom(Subst),
			 unify_ComGenLinear(Context,
					    SubstNew,
					    cont_ContextBindingTerm(Context,
								       subst_Dom(Subst)),
					    SubstOld,
					    subst_Cod(Subst)),
			 Result);

    cont_CloseBinding(Context, subst_Dom(Subst));

    Subst = subst_Next(Subst);
  } while (subst_Exist(Subst));

  return Result;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  CLOSE BINDINGS                                        * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


void subst_CloseVariables(const CONTEXT Context, SUBST Subst)
{
  for (; subst_Exist(Subst); Subst = subst_Next(Subst))
    cont_CloseBinding(Context, subst_Dom(Subst));
}


SUBST subst_CloseOpenVariables(SUBST Result)
{
  while (cont_LastBinding()) {
    if (cont_LastIsBound())
      Result = subst_Add(cont_LastBindingSymbol(),
			 term_Copy(cont_LastBindingTerm()),
			 Result);
    cont_BackTrackLastBinding();
  }

  return Result;
}


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  EXTRACT UNIFIER                                       * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


void subst_ExtractUnifier(const CONTEXT CL,
			  SUBST* LeftSubst,
			  const CONTEXT CR,
			  SUBST* RightSubst)
/*********************************************************
  INPUT:   'LeftSubst', 'RightSubst' for the unifier,
           renaming the codomain variables starts at
           'MinimumCoVariable' excl., number of
           renamings are ADDED to 'Bindings'.
  RETURNS: Nothing.
  SUMMARY: Extracts the unifier into two substitutions
           with renamed variables in the codomain.
  CAUTION: DOES NOT RESET THE BINDINGS, CREATES EVEN
           MORE BINDINGS BECAUSE OF RENAMING.
**********************************************************/
{
  CONTEXT Scan;

  *LeftSubst  = subst_Nil();
  *RightSubst = subst_Nil();

  Scan        = cont_LastBinding();

  while (Scan) {
    if (cont_IsInContext(CL,
			    cont_BindingSymbol(Scan),
			    Scan))
      *LeftSubst = subst_Add(cont_BindingSymbol(Scan),
			     cont_CopyAndApplyBindings(cont_BindingContext(Scan),
							  cont_BindingTerm(Scan)),
			     *LeftSubst);
    else if (cont_IsInContext(CR,
				 cont_BindingSymbol(Scan),
				 Scan))
      *RightSubst = subst_Add(cont_BindingSymbol(Scan),
			      cont_CopyAndApplyBindings(cont_BindingContext(Scan),
							   cont_BindingTerm(Scan)),
			      *RightSubst);
    
    Scan = cont_BindingLink(Scan);
  }
}


void subst_ExtractUnifierCom(const CONTEXT Context, SUBST* Subst)
/*********************************************************
  INPUT:  'LeftSubst', 'RightSubst' for the unifier,
          renaming the codomain variables starts at
          'MinimumCoVariable' excl., number of
          renamings are ADDED to 'Bindings'.
 RETURNS: Nothing.
 SUMMARY: Extracts the unifier into two substitutions
          with renamed variables in the codomain.
 CAUTION: DOES NOT RESET THE BINDINGS, CREATES EVEN
          MORE BINDINGS BECAUSE OF RENAMING.
**********************************************************/
{
  CONTEXT Scan;

  *Subst = subst_Nil();

  Scan   = cont_LastBinding();

  while (Scan) {
    *Subst =
      subst_Add(cont_BindingSymbol(Scan),
		cont_CopyAndApplyBindingsCom(Context, cont_BindingTerm(Scan)),
		*Subst);

    Scan = cont_BindingLink(Scan);
  }
}


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  EXTRACT MATCHER                                       * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


SUBST subst_ExtractMatcher(void)
/*********************************************************
  INPUT:   None.
  RETURNS: The matcher.
  SUMMARY: Extracts the matcher without renaming.
  CAUTION: DOES NOT RESET THE BINDINGS, DOES NOT COPY
           THE CODOMAINS.
**********************************************************/
{
  CONTEXT     Scan;
  SUBST Result;

  for (Scan = cont_LastBinding(), Result = subst_Nil();
       Scan;
       Scan = cont_BindingLink(Scan))
    Result = subst_Add(cont_BindingSymbol(Scan),
		       cont_BindingTerm(Scan),
		       Result);

  return Result;
}


/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *  OUTPUT                                                * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


void subst_Print(SUBST Subst)
{
  fputs("{ ", stdout);
  for (; subst_Exist(Subst); Subst = subst_Next(Subst)) {
    symbol_Print(subst_Dom(Subst));
    if (subst_Cod(Subst)) {
      fputs(" -> ", stdout);
      term_PrintPrefix(subst_Cod(Subst));
    }
    if (subst_Next(Subst))
      fputs("; ", stdout);
  }
  fputs(" }", stdout);
}

