/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *                STANDARD UNIFICATION                    * */
/* *                                                        * */
/* *  $Module:   UNIFY                                      * */ 
/* *                                                        * */
/* *  Copyright (C) 1996, 1997, 1998, 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 "unify.h"

/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  INITIALIZATION           			            * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


void unify_Init(void)
/**********************************************************
  INPUT:   None.
  RETURNS: None.
  EFFECT:  Initializes the unify module.
********************************************************/
{

}

void unify_Free(void)
/**********************************************************
  INPUT:   None.
  RETURNS: None.
  EFFECT:  Frees internal structures of the unify module.
********************************************************/
{

}


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  MISC FUNCTIONS                     	            * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  OCCUR CHECK            			            * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


BOOL unify_OccurCheckCom(SYMBOL Top, CONTEXT Context, TERM Term)
/**********************************************************
  INPUT:   A variable symbol, a context, and a term.
  RETURNS: TRUE if there is a occur check failure with respect
           to the variable symbol <Top> and <Term>.
	   The search is started in <Term>
***********************************************************/
{
  int  Bottom;
  LIST Args;

  Bottom = stack_Bottom();

  for (;;) {

    if (term_IsVariable(Term)) {
      if (symbol_Equal(Top, term_TopSymbol(Term))) {
	stack_SetBottom(Bottom);
	return TRUE;
      } else if (cont_VarIsBound(Context, term_TopSymbol(Term))) {
	Term = cont_ContextBindingTerm(Context, term_TopSymbol(Term));
	continue;
      }

    } else if (term_IsComplex(Term)) {

      Args = term_ArgumentList(Term);
      if (!list_Empty(list_Cdr(Args)))
	stack_Push(list_Cdr(Args));

      Term = list_Car(Args);
      continue;
    }

    if (stack_Empty(Bottom))
      return FALSE;
    else {
      Args = (LIST)stack_PopResult();
      Term = list_Car(Args);
      if (!list_Empty(list_Cdr(Args)))
	stack_Push(list_Cdr(Args));
    }
  }
}


BOOL unify_OccurCheck(CONTEXT CTop, SYMBOL Top, CONTEXT CTerm, TERM Term)
/**********************************************************
  INPUT:   A context, a variable symbol, a context, and a term.
  RETURNS: TRUE if there is a occur check failure with respect
           to the variable symbol <Top> and <Term>.
	   The search is started in <Term>
***********************************************************/
{
  int    Bottom;
  LIST   Args;
  SYMBOL TermTop;

  Bottom = stack_Bottom();

  for (;;) {

    if (term_IsVariable(Term)) {

      TermTop = term_TopSymbol(Term);

      if ((CTop == CTerm) && (symbol_Equal(Top, TermTop))) {
	stack_SetBottom(Bottom);
	return TRUE;
      } else if (cont_VarIsBound(CTerm, TermTop)) {
	Term  = cont_ContextBindingTerm(CTerm, TermTop);
	CTerm = cont_ContextBindingContext(CTerm, TermTop);
	continue;
      }
    } else if (term_IsComplex(Term)) {

      Args = term_ArgumentList(Term);
      if (!list_Empty(list_Cdr(Args))) {
	stack_Push(CTerm);
	stack_Push(list_Cdr(Args));
      }
      Term = list_Car(Args);
      continue;

    }

    if (stack_Empty(Bottom))
      return FALSE;
    else {
      Args = (LIST)stack_PopResult();
      Term = list_Car(Args);
      CTerm = (CONTEXT)stack_Top();
      if (list_Empty(list_Cdr(Args)))
	stack_Pop();
      else
	stack_Push(list_Cdr(Args));
    }
  }
}


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  UNIFICATION           			            * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


BOOL unify_Unify(CONTEXT CtL, TERM TermL, CONTEXT CtR, TERM TermR)
/**********************************************************
  INPUT:   Two terms which may contain the same variable symbols.
           Equal variable symbols are interpreted different in
	   <TermL> and <TermR>, respectively.
  RETURNS: TRUE, if <TermL> and <TermR> are unifiable, FALSE otherwise.
  CAUTION: None.
***********************************************************/
{
  CONTEXT CHelp;
  LIST    ArgsL, ArgsR;
  int     Bottom;
  BOOL    Bound;

  Bound  = FALSE;
  Bottom = stack_Bottom();

  for (;;) {

    while (term_IsVariable(TermL) &&
	   cont_VarIsBound(CtL, term_TopSymbol(TermL))) {
      CHelp = cont_ContextBindingContext(CtL, term_TopSymbol(TermL));
      TermL = cont_ContextBindingTerm(CtL, term_TopSymbol(TermL));
      CtL   = CHelp;
    }

    while (term_IsVariable(TermR) &&
	   cont_VarIsBound(CtR, term_TopSymbol(TermR))) {
      CHelp = cont_ContextBindingContext(CtR, term_TopSymbol(TermR));
      TermR = cont_ContextBindingTerm(CtR, term_TopSymbol(TermR));
      CtR   = CHelp;
    }

    /* Caution: Bindings from variable to variable are made with priority
       from the right context into the left context. */

    if (term_IsVariable(TermL)) {
      if (term_IsVariable(TermR)) {
	if (!(CtL == CtR && term_EqualTopSymbols(TermL, TermR)))
	  Bound = cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);
      } else if (Bound && unify_OccurCheck(CtL, term_TopSymbol(TermL), CtR, TermR)) {
	stack_SetBottom(Bottom);
	return FALSE;
      }	else
	Bound = cont_CreateBinding(CtL, term_TopSymbol(TermL), CtR, TermR);

    } else if (term_IsVariable(TermR)) {
      if (Bound && unify_OccurCheck(CtR, term_TopSymbol(TermR), CtL, TermL)) {
	stack_SetBottom(Bottom);
	return FALSE;
      }	else
	Bound = cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);

    } else if (term_EqualTopSymbols(TermL, TermR)) {
      if (term_IsComplex(TermL) && TermL != TermR) {
	ArgsL = term_ArgumentList(TermL);
	ArgsR = term_ArgumentList(TermR);
	if (!list_Empty(list_Cdr(ArgsL))) {
	  stack_Push(CtL);
	  stack_Push(CtR);
	  stack_Push(list_Cdr(ArgsL));
	  stack_Push(list_Cdr(ArgsR));
	}
	TermL = list_Car(ArgsL);
	TermR = list_Car(ArgsR);
	continue;
      }
    } else {
      stack_SetBottom(Bottom);
      return FALSE;
    }

    if (stack_Empty(Bottom))
      return TRUE;
    else {
      ArgsR = stack_PopResult();
      ArgsL = stack_PopResult();
      TermR = list_Car(ArgsR);
      TermL = list_Car(ArgsL);
      CtR   = (CONTEXT)stack_Top();
      CtL   = (CONTEXT)stack_NthTop(1);
      if (list_Empty(list_Cdr(ArgsL)))
	stack_NPop(2);
      else {
	stack_Push(list_Cdr(ArgsL));
	stack_Push(list_Cdr(ArgsR));
      }
    }
  }
}

BOOL unify_UnifyCom(CONTEXT Context, TERM TermL, TERM TermR)
/**********************************************************
  INPUT:   Two terms which may contain the same variable symbols.
           Equal variable symbols are interpreted equally in
	   <TermL> and <TermR>, respectively.
  RETURNS: TRUE, if <TermL> and <TermR> are unifiable, FALSE otherwise.
  CAUTION: None.
***********************************************************/
{
  LIST ArgsL, ArgsR;
  int  Bottom;

  Bottom = stack_Bottom();

  for (;;) {

    while (term_IsVariable(TermL) &&
	   cont_VarIsBound(Context, term_TopSymbol(TermL)))
      TermL = cont_ContextBindingTerm(Context, term_TopSymbol(TermL));

    while (term_IsVariable(TermR) &&
	   cont_VarIsBound(Context, term_TopSymbol(TermR)))
      TermR = cont_ContextBindingTerm(Context, term_TopSymbol(TermR));

    if (term_EqualTopSymbols(TermL, TermR)) {
      if (term_IsComplex(TermL) && TermL != TermR) {
	ArgsL = term_ArgumentList(TermL);
	ArgsR = term_ArgumentList(TermR);
	if (!list_Empty(list_Cdr(ArgsL))) {
	  stack_Push(list_Cdr(ArgsL));
	  stack_Push(list_Cdr(ArgsR));
	}
	TermL = list_Car(ArgsL);
	TermR = list_Car(ArgsR);
	continue;
      }
    } else if (term_IsVariable(TermL)) {
      if (term_IsVariable(TermR))
	cont_CreateBinding(Context, term_TopSymbol(TermL), Context, TermR);
      else if (unify_OccurCheckCom(term_TopSymbol(TermL), Context, TermR)) {
	stack_SetBottom(Bottom);
	return FALSE;
      } else
	cont_CreateBinding(Context, term_TopSymbol(TermL), Context, TermR);
      
    } else if (term_IsVariable(TermR)) {
      if (unify_OccurCheckCom(term_TopSymbol(TermR), Context, TermL)) {
	stack_SetBottom(Bottom);
	return FALSE;
      }	else
	cont_CreateBinding(Context, term_TopSymbol(TermR), Context, TermL);

    } else {
      stack_SetBottom(Bottom);
      return FALSE;
    }

    if (stack_Empty(Bottom))
      return TRUE;
    else {
      ArgsR = stack_PopResult();
      ArgsL = stack_PopResult();
      TermR = list_Car(ArgsR);
      TermL = list_Car(ArgsL);
      if (!list_Empty(list_Cdr(ArgsL))) {
	stack_Push(list_Cdr(ArgsL));
	stack_Push(list_Cdr(ArgsR));
      }
    }
  }
}



BOOL unify_UnifyNoOC(CONTEXT CtL, TERM TermL, CONTEXT CtR, TERM TermR)
/**********************************************************
  INPUT:   Two terms which may contain the same variable symbols.
           Equal variable symbols are interpreted different in
	   <TermL> and <TermR>, respectively.
  RETURNS: TRUE, if <TermL> and <TermR> are unifiable, FALSE otherwise.
  CAUTION: None.
***********************************************************/
{
  CONTEXT CHelp;
  LIST    ArgsL, ArgsR;
  int     Bottom;

  Bottom = stack_Bottom();

  for (;;) {

    while (term_IsVariable(TermL) &&
	   cont_VarIsBound(CtL, term_TopSymbol(TermL))) {
      CHelp = cont_ContextBindingContext(CtL, term_TopSymbol(TermL));
      TermL = cont_ContextBindingTerm(CtL, term_TopSymbol(TermL));
      CtL   = CHelp;
    }

    while (term_IsVariable(TermR) &&
	   cont_VarIsBound(CtR, term_TopSymbol(TermR))) {
      CHelp = cont_ContextBindingContext(CtR, term_TopSymbol(TermR));
      TermR = cont_ContextBindingTerm(CtR, term_TopSymbol(TermR));
      CtR   = CHelp;
    }

    /* Caution: Bindings from variable to variable are made with priority
       from the right context into the left context. */

    if (term_IsVariable(TermL)) {
      if (term_IsVariable(TermR)) {
	if (!(CtL == CtR && term_EqualTopSymbols(TermL, TermR)))
	  cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);
      } else 
	cont_CreateBinding(CtL, term_TopSymbol(TermL), CtR, TermR);

    } else if (term_IsVariable(TermR))
      cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);

    else if (term_EqualTopSymbols(TermL, TermR)) {
      if (term_IsComplex(TermL) && TermL != TermR) {
	ArgsL = term_ArgumentList(TermL);
	ArgsR = term_ArgumentList(TermR);
	if (!list_Empty(list_Cdr(ArgsL))) {
	  stack_Push(CtL);
	  stack_Push(CtR);
	  stack_Push(list_Cdr(ArgsL));
	  stack_Push(list_Cdr(ArgsR));
	}
	TermL = list_Car(ArgsL);
	TermR = list_Car(ArgsR);
	continue;
      }
    } else {
      stack_SetBottom(Bottom);
      return FALSE;
    }

    if (stack_Empty(Bottom))
      return TRUE;
    else {
      ArgsR = stack_PopResult();
      ArgsL = stack_PopResult();
      TermR = list_Car(ArgsR);
      TermL = list_Car(ArgsL);
      CtR   = (CONTEXT) stack_Top();
      CtL   = (CONTEXT) stack_NthTop(1);
      if (list_Empty(list_Cdr(ArgsL)))
	stack_NPop(2);
      else {
	stack_Push(list_Cdr(ArgsL));
	stack_Push(list_Cdr(ArgsR));
      }
    }
  }
}


/**************************************************************/
/* ********************************************************** */
/* *							    * */
/* *  UNIFICATION WITH FULL OCCUR CHECK	(recursive)         * */
/* *							    * */
/* ********************************************************** */
/**************************************************************/


BOOL unify_UnifyAllOC(CONTEXT IndexContext, CONTEXT CtL, TERM TermL, CONTEXT CtR, TERM TermR)
{
  while (term_IsVariable(TermL)) {
    SYMBOL TermTop;

    TermTop = term_TopSymbol(TermL);

    if (cont_VarIsBound(CtL, TermTop)) {
      CONTEXT CHelp;

      CHelp = cont_ContextBindingContext(CtL, TermTop);
      TermL = cont_ContextBindingTerm(CtL, TermTop);
      CtL   = CHelp;
    } else
      break;
  }

  while (term_IsVariable(TermR)) {
    SYMBOL TermTop;

    TermTop = term_TopSymbol(TermR);

    if (cont_VarIsBound(CtR, TermTop)) {
      CONTEXT CHelp;

      CHelp = cont_ContextBindingContext(CtR, TermTop);
      TermR = cont_ContextBindingTerm(CtR, TermTop);
      CtR   = CHelp;
    } else
      break;
  }
  
  if (term_IsVariable(TermL)) {
    
    if (term_IsVariable(TermR)) {
      if ((CtL != CtR || !term_EqualTopSymbols(TermL, TermR))) {
	if (term_IsIndexVariable(TermL))
	  cont_CreateBinding(CtL, term_TopSymbol(TermL), CtR, TermR);
	else 
	  if (term_IsIndexVariable(TermR) || (CtR == IndexContext))
	    cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);
	  else
	    cont_CreateBinding(CtL, term_TopSymbol(TermL), CtR, TermR);
      }
      return TRUE;
    } 
    else 
      if (unify_OccurCheck(CtL, term_TopSymbol(TermL), CtR, TermR))
	return FALSE;
      else {
	cont_CreateBinding(CtL, term_TopSymbol(TermL), CtR, TermR);
	return TRUE;
      }
  } 
  else 
    if (term_IsVariable(TermR)) { 
      if (unify_OccurCheck(CtR, term_TopSymbol(TermR), CtL, TermL))
	return FALSE;
      else {
	cont_CreateBinding(CtR, term_TopSymbol(TermR), CtL, TermL);
	return TRUE;
      }
    } 
    else 
      if (term_EqualTopSymbols(TermL, TermR)) {
	if (term_IsComplex(TermL)) {
	  LIST ArgL, ArgR;
	  for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	       !list_Empty(ArgL);
	       ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	    if (!unify_UnifyAllOC(IndexContext, CtL, list_Car(ArgL), CtR, list_Car(ArgR)))
	      return FALSE;
	}
	return TRUE;
      } 
      else
	return FALSE;
}


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


BOOL unify_Match(CONTEXT Context, TERM TermL, TERM TermR)
{
  if (term_IsVariable(TermL)) {
    if (cont_VarIsBound(Context, term_TopSymbol(TermL)))
      return term_Equal(cont_ContextBindingTerm(Context, term_TopSymbol(TermL)), TermR);
    else {
      cont_CreateBinding(Context, term_TopSymbol(TermL), cont_InstanceContext(), TermR);
      return TRUE;
    }
  } else if (term_EqualTopSymbols(TermL, TermR)) {
    if (term_IsComplex(TermL)) {
      LIST ArgL;
      LIST ArgR;
      for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	   !list_Empty(ArgL);
	   ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	if (!unify_Match(Context, list_Car(ArgL), list_Car(ArgR)))
	  return FALSE;
    }
    return TRUE;
  } else
    return FALSE;
}

BOOL unify_MatchFlexible(CONTEXT Context, TERM TermL, TERM TermR)
/**************************************************************
  INPUT:   Two terms where symbols with flexible arity are allowed.
  RETURNS: TRUE if <TermL> matches <TermR>.        
***************************************************************/
{
  if (term_IsVariable(TermL)) {
    if (cont_VarIsBound(Context, term_TopSymbol(TermL)))
      return term_Equal(cont_ContextBindingTerm(Context, term_TopSymbol(TermL)), TermR);
    else {
      cont_CreateBinding(Context, term_TopSymbol(TermL), cont_InstanceContext(), TermR);
      return TRUE;
    }
  } else 
    if (term_EqualTopSymbols(TermL, TermR) 
	&& list_Length(term_ArgumentList(TermL)) == list_Length(term_ArgumentList(TermR))) {
      if (term_IsComplex(TermL)) {
	LIST ArgL;
	LIST ArgR;
	for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	     !list_Empty(ArgL);
	     ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	  if (!unify_MatchFlexible(Context, list_Car(ArgL), list_Car(ArgR)))
	    return FALSE;
      }
      return TRUE;
    } 
    else
      return FALSE;
}


void unify_EstablishMatcher(CONTEXT Context, SUBST Subst)
{
  while (subst_Exist(Subst)) {
    /* Index to query */
    cont_CreateBinding(Context, subst_Dom(Subst), cont_InstanceContext(), subst_Cod(Subst));
    Subst = subst_Next(Subst);
  }
}


BOOL unify_MatchBindingsHelp(const CONTEXT IndexContext, TERM TermL, CONTEXT CtR, TERM TermR)
{
  while (term_IsVariable(TermR)) {
    SYMBOL TermTop;

    TermTop = term_TopSymbol(TermR);

    if (symbol_IsIndexVariable(TermTop))
      CtR = IndexContext;
    else if (CtR == cont_InstanceContext())
      break;

    if (cont_VarIsBound(CtR, TermTop)) {
      CONTEXT CHelp;

      CHelp = cont_ContextBindingContext(CtR, TermTop);
      TermR = cont_ContextBindingTerm(CtR, TermTop);
      CtR   = CHelp;
    } else
      break;
  }

  if (term_IsVariable(TermL)) {
    /* Assertion: Variables of 'TermL' are bound in the index context only. */

    if (cont_VarIsBound(IndexContext, term_TopSymbol(TermL)))
      return
	cont_TermEqualModuloBindings(IndexContext,
					cont_ContextBindingContext(IndexContext,
								      term_TopSymbol(TermL)),
					cont_ContextBindingTerm(IndexContext,
								   term_TopSymbol(TermL)),
					CtR,
					TermR);
    else {
      cont_CreateBinding(IndexContext, term_TopSymbol(TermL), CtR, TermR);
      return TRUE;
    }
  } else if (term_EqualTopSymbols(TermL, TermR)) {
    if (term_IsComplex(TermL)) {
      LIST ArgL;
      LIST ArgR;

      for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	   !list_Empty(ArgL);
	   ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	if (!unify_MatchBindingsHelp(IndexContext, list_Car(ArgL), CtR, list_Car(ArgR)))
	  return FALSE;
    }

    return TRUE;
  } else
    return FALSE;
}


BOOL unify_MatchBindings(const CONTEXT IndexContext, TERM TermL, TERM TermR)
{
  return unify_MatchBindingsHelp(IndexContext, TermL, cont_InstanceContext(), TermR);
}


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


BOOL unify_MatchReverse(const CONTEXT IndexContext, TERM TermL,	CONTEXT CtR, 
			TERM TermR)
/*********************************************************
  INPUT:   'TermL' is in IndexContext and the codomain of a subst.,
           'CtR' is the context of 'TermR' which is the codomain of a subst.
           obtained by a variable binding, 'Bindings' is
           the number of established bindings.
  RETURNS: TRUE, if 'TermL' is an instance of 'TermR';
           FALSE, otherwise.
**********************************************************/
{
  while (term_IsVariable(TermR)) {
    SYMBOL TermTop;

    TermTop = term_TopSymbol(TermR);

    if (symbol_IsIndexVariable(TermTop))
      CtR = IndexContext;
    else if (CtR == cont_InstanceContext())
      break;

    if (cont_VarIsBound(CtR, TermTop)) {
      CONTEXT CHelp;

      CHelp = cont_ContextBindingContext(CtR, TermTop);
      TermR = cont_ContextBindingTerm(CtR, TermTop);
      CtR   = CHelp;
    } else
      break;
  }

  if (term_IsVariable(TermL)) {
    if ((CtR == cont_InstanceContext()) && term_EqualTopSymbols(TermL, TermR))
      /* 'TermL' and 'TermR' are exactly the same variables (via bindings),
	 therefore do not bind them, just return positive. */
      return TRUE;

    else if (term_IsIndexVariable(TermL)) {
      cont_CreateBinding(IndexContext, term_TopSymbol(TermL), CtR, TermR);
      return TRUE;

    } else if (term_IsVariable(TermR) &&
	       (term_IsIndexVariable(TermR) || (CtR == IndexContext))) {
      cont_CreateBinding(IndexContext, term_TopSymbol(TermR), cont_InstanceContext(), TermL);
      return TRUE;

    } else
      return FALSE;

  } else if (term_IsVariable(TermR)) {
    if (term_IsIndexVariable(TermR) || (CtR == IndexContext)) {
      cont_CreateBinding(IndexContext, term_TopSymbol(TermR), cont_InstanceContext(), TermL);
      return TRUE;
    } else
      return FALSE;

  } else if (term_EqualTopSymbols(TermL, TermR)) {
    
    if (term_IsComplex(TermL)) {
      LIST ArgL, ArgR;
      for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	   !list_Empty(ArgL);
	   ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	if (!unify_MatchReverse(IndexContext,
				list_Car(ArgL),
				CtR,
				list_Car(ArgR)))
	  return FALSE;
    }
    return TRUE;
  } else
    return FALSE;
}


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


BOOL unify_Variation(const CONTEXT Context, TERM TermL, TERM TermR)
{
  if (term_IsVariable(TermL)) {
    if (term_EqualTopSymbols(TermL, TermR))
      /* TermL and TermR are in different contexts
	 but both are term variables which do not need to be variated.
	 Index variables cannot occur in TermR
	 which is the term to be inserted. */
      return TRUE;
    else if (term_IsIndexVariable(TermL)) {
      if (cont_VarIsBound(Context, term_TopSymbol(TermL)))
	return term_Equal(cont_ContextBindingTerm(Context, term_TopSymbol(TermL)), TermR);
      else {
	/* Index to query */
	cont_CreateBinding(Context, term_TopSymbol(TermL), Context, TermR);
	return TRUE;
      }
    }
    else
      return FALSE;

  } else if (term_EqualTopSymbols(TermL, TermR)) {
    if (term_IsComplex(TermL)) {
      LIST ArgL;
      LIST ArgR;
      for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	   !list_Empty(ArgL);
	   ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
	if (!unify_Variation(Context, list_Car(ArgL), list_Car(ArgR)))
	  return FALSE;
    }
    return TRUE;
  } else 
    return FALSE;
}


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


TERM unify_ComGenLinear(const CONTEXT IndexContext,
			SUBST* SubstL, TERM TermL, SUBST* SubstR, TERM TermR)
{
  if (term_IsIndexVariable(TermR)) {
    *SubstL = subst_Add(term_TopSymbol(TermR), term_Copy(TermL), *SubstL);

    return term_Copy(TermR);
  
  } else if (term_EqualTopSymbols(TermL, TermR)) {
   
    LIST ArgList, ArgL, ArgR;

    ArgList   = list_Nil();
    for (ArgL = term_ArgumentList(TermL), ArgR = term_ArgumentList(TermR);
	 !list_Empty(ArgL);
	 ArgL = list_Cdr(ArgL), ArgR = list_Cdr(ArgR))
      ArgList = list_Nconc(ArgList,
			   list_List(unify_ComGenLinear(IndexContext,
							SubstL,
							list_Car(ArgL),
							SubstR,
							list_Car(ArgR))));
    return term_Create(term_TopSymbol(TermL), ArgList);
    
  } else {
    
    SYMBOL Symbol;
    
    Symbol  = cont_NextIndexVariable(IndexContext);

    *SubstL = subst_Add(Symbol, term_Copy(TermL), *SubstL);
    *SubstR = subst_Add(Symbol, term_Copy(TermR), *SubstR);
    
    return term_Create(Symbol, list_Nil());
  }
}

