/*
 * Scyther : An automatic verifier for security protocols.
 * Copyright (C) 2007-2013 Cas Cremers
 * 
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/** @file term.c \brief Term related base functions.
 *
 * Intended to be a standalone file, however during development it turned out
 * that a termlist structure was needed to define term types, so there is now a
 * dependency loop with termlists.c.
 *
 * Until now, symbols were unique and never deleted.  The same holds for basic
 * terms; leaves are equal when their pointers are equal.  We are looking to
 * extend this to whole terms. At that point, term equality is be reduced to
 * pointer comparison, which is what we want.
 */

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include "term.h"
#include "debug.h"
#include "error.h"
#include "ctype.h"
#include "specialterm.h"

/* public flag */
int rolelocal_variable;
char *RUNSEP;

/* external definitions */

extern int inTermlist ();	// suppresses a warning, but at what cost?

/* forward declarations */

void indent (void);

/* main code */

/* Two types of terms: general, and normalized. Normalized rewrites all
   tuples to (x,(y,z))..NULL form, making list traversal easy. */

//! Initialization of terms code.
void
termsInit (void)
{
  rolelocal_variable = 0;
  RUNSEP = "#";
  return;
}

//! Cleanup of terms code.
void
termsDone (void)
{
  return;
}

//! Allocate memory for a term.
/**
 *@return A pointer to the new term memory, which is not yet initialised.
 */
Term
makeTerm ()
{
  return (Term) malloc (sizeof (struct term));
}

//! Create a fresh encrypted term from two existing terms.
/**
 * The first argument is the message,
 * the second argument is the key.
 *
 *@return A pointer to the new term.
 */
Term
makeTermEncrypt (Term t1, Term t2)
{
  Term term = makeTerm ();
  term->type = ENCRYPT;
  term->stype = NULL;
  term->helper.fcall = false;
  term->subst = NULL;
  TermOp (term) = t1;
  TermKey (term) = t2;
  return term;
}

Term
makeTermFcall (Term t1, Term t2)
//! Create a fresh function application term from two existing terms.
/**
 * The first argument is the function argument,
 * the second argument is the function (name).
 *
 * These behave like encryptions in most cases.
 *
 *@return A pointer to the new term.
 */
{
  Term t;

  t = makeTermEncrypt (t1, t2);
  t->helper.fcall = true;
  return t;
}

//! Create a fresh term tuple from two existing terms.
/**
 *@return A pointer to the new term.
 */
Term
makeTermTuple (Term t1, Term t2)
{
  Term tt;

  if (t1 == NULL)
    {
      if (t2 == NULL)
	{
#ifdef DEBUG
	  debug (5, "Trying to make a tuple node with an empty term.");
#endif
	  return NULL;
	}
      else
	{
	  return t2;
	}
    }
  if (t2 == NULL)
    {
      return t1;
    }

  tt = makeTerm ();
  tt->type = TUPLE;
  tt->stype = NULL;
  tt->helper.roleVar = 0;
  tt->subst = NULL;
  TermOp1 (tt) = t1;
  TermOp2 (tt) = t2;
  return tt;
}

//! Make a term of the given type with run identifier and symbol.
/**
 *@return A pointer to the new term.
 *\sa GLOBAL, VARIABLE, LEAF, ENCRYPT, TUPLE
 */
Term
makeTermType (const int type, const Symbol symb, const int runid)
{
  Term term = makeTerm ();
  term->type = type;
  term->stype = NULL;
  if ((type == ENCRYPT) || (type == TUPLE))
    {
      // Non-leaf
      term->helper.fcall = false;
    }
  else
    {
      // Leaf
      term->helper.roleVar = 0;
    }
  term->subst = NULL;
  TermSymb (term) = symb;
  TermRunid (term) = runid;
  return term;
}

//! Unwrap any substitutions.
/**
 * For speed, it is also a macro. Sometimes it will call
 * deVarScan to do the actual unwinding.
 *@return A term that is either not a variable, or has a NULL substitution.
 *\sa deVar()
 */
__inline__ Term
deVarScan (Term t)
{
  while (realTermVariable (t) && t->subst != NULL)
    t = t->subst;
  return t;
}

//! Determine whether a term contains an unsubstituted variable as subterm.
/**
 *@return True iff there is an open variable as subterm.
 */
int
hasTermVariable (Term term)
{
  if (term == NULL)
    return 0;
  term = deVar (term);
  if (realTermLeaf (term))
    return realTermVariable (term);
  else
    {
      if (realTermTuple (term))
	return (hasTermVariable (TermOp1 (term))
		|| hasTermVariable (TermOp2 (term)));
      else
	return (hasTermVariable (TermOp (term))
		|| hasTermVariable (TermKey (term)));
    }
}

//! Safe wrapper for isTermEqual

int
isTermEqualDebug (Term t1, Term t2)
{
  return isTermEqualFn (t1, t2);
}

//!Tests whether two terms are completely identical.
/**
 * This also includes
 * variables. This is the recursive function.
 * We assume the term is normalized, e.g. no tupling has direct
 * subtupling.
 *@return True iff the terms are equal.
 *\sa isTermEqual()
 */

int
isTermEqualFn (Term term1, Term term2)
{
  term1 = deVar (term1);
  term2 = deVar (term2);

  if (term1 == term2)
    return 1;
  if ((term1 == NULL) || (term2 == NULL))
    return 0;

  if (term1->type != term2->type)
    {
      return 0;
    }
  if (realTermLeaf (term1))
    {
      return (TermSymb (term1) == TermSymb (term2)
	      && TermRunid (term1) == TermRunid (term2));
    }
  else
    {
      /* ENCRYPT or TUPLE */

      if (realTermEncrypt (term1))
	{
	  /* for optimization of encryption equality, we compare
	     operator 2 first (we expect it to be a smaller term)
	   */
	  return (isTermEqualFn (TermKey (term1), TermKey (term2)) &&
		  isTermEqualFn (TermOp (term1), TermOp (term2)));
	}
      else
	{
	  /* tuple */

	  return (isTermEqualFn (TermOp1 (term1), TermOp1 (term2)) &&
		  isTermEqualFn (TermOp2 (term1), TermOp2 (term2)));
	}
    }
}

//! See if a term is a subterm of another.
/**
 *@param t Term to be checked for a subterm.
 *@param tsub Subterm.
 *	Note that if t is non-null and tsub is null, it is a valid subterm.
 *@return True iff tsub is a subterm of t.
 */
int
termSubTerm (Term t, Term tsub)
{
  t = deVar (t);
  tsub = deVar (tsub);

  if (isTermEqual (t, tsub))
    {
      return true;
    }
  else
    {
      if (t == NULL)
	{
	  return false;
	}
      else
	{
	  if (tsub == NULL)
	    {
	      return true;
	    }
	  else
	    {
	      if (realTermLeaf (t))
		{
		  return false;
		}
	      else
		{
		  if (realTermTuple (t))
		    return (termSubTerm (TermOp1 (t), tsub)
			    || termSubTerm (TermOp2 (t), tsub));
		  else
		    return (termSubTerm (TermOp (t), tsub)
			    || termSubTerm (TermKey (t), tsub));
		}
	    }
	}
    }
}

//! See if a term is an interm of another.
/**
 *@param t Term to be checked for a subterm.
 *@param tsub interm.
 *@return True iff tsub is an interm of t.
 */
int
termInTerm (Term t, Term tsub)
{
  t = deVar (t);
  tsub = deVar (tsub);

  if (isTermEqual (t, tsub))
    return true;
  if (realTermLeaf (t))
    return false;
  if (realTermTuple (t))
    return (termInTerm (TermOp1 (t), tsub) || termInTerm (TermOp2 (t), tsub));
  else
    return false;
}

//! Print a term to stdout.
/**
 * The tuple printing only works correctly for normalized terms.
 * If not, they might are displayed as "((x,y),z)". Maybe that is even
 * desirable to distinguish them.
 *\sa termTuplePrint()
 */
void
termPrintCustom (Term term, char *leftvar, char *rightvar, char *lefttup,
		 char *righttup, char *leftenc, char *rightenc,
		 void (*callback) (const Term t))
{
#ifdef DEBUG
  if (!DEBUGL (4))
    {
      term = deVar (term);
    }
#else
  term = deVar (term);
#endif
  if (term == NULL)
    {
      eprintf ("*");
      return;
    }
  if (realTermLeaf (term))
    {
      if (term->type == VARIABLE && TermRunid (term) >= 0)
	eprintf (leftvar);
      symbolPrint (TermSymb (term));
      if (term->type == VARIABLE && TermRunid (term) >= 0)
	eprintf (rightvar);
      if (TermRunid (term) >= 0)
	{
	  if (callback == NULL)
	    {
	      eprintf ("%s%i", RUNSEP, TermRunid (term));
	    }
	  else
	    {
	      callback (term);
	    }
	}
      if (term->subst != NULL)
	{
	  eprintf ("->");
	  termPrintCustom (term->subst, leftvar, rightvar, lefttup, righttup,
			   leftenc, rightenc, callback);
	}
    }
  if (realTermTuple (term))
    {
      eprintf ("(");
      termTuplePrintCustom (term, leftvar, rightvar, lefttup, righttup,
			    leftenc, rightenc, callback);
      eprintf (")");
      return;
    }
  if (realTermEncrypt (term))
    {
      if (term->helper.fcall)
	{
	  /* function application */
	  termPrintCustom (TermKey (term), leftvar, rightvar, lefttup,
			   righttup, leftenc, rightenc, callback);
	  eprintf (lefttup);
	  termTuplePrintCustom (TermOp (term), leftvar, rightvar, lefttup,
				righttup, leftenc, rightenc, callback);
	  eprintf (righttup);
	}
      else
	{
	  /* normal encryption */
	  eprintf (leftenc);
	  termTuplePrintCustom (TermOp (term), leftvar, rightvar, lefttup,
				righttup, leftenc, rightenc, callback);
	  eprintf (rightenc);
	  termPrintCustom (TermKey (term), leftvar, rightvar, lefttup,
			   righttup, leftenc, rightenc, callback);
	}
    }
}

void
termPrint (Term term)
{
  termPrintCustom (term, "", "V", "(", ")", "{ ", " }", NULL);
}

//! Print an inner (tuple) term to stdout, without brackets.
/**
 * The tuple printing only works correctly for normalized terms.
 * If not, they might are displayed as "((x,y),z)". Maybe that is even
 * desirable to distinguish them.
 */
void
termTuplePrintCustom (Term term, char *leftvar, char *rightvar, char *lefttup,
		      char *righttup, char *leftenc, char *rightenc,
		      void (*callback) (const Term t))
{
  if (term == NULL)
    {
      eprintf ("Empty term");
      return;
    }
  term = deVar (term);
  while (realTermTuple (term))
    {
      // To remove any brackets, change this into termTuplePrint.
      termPrintCustom (TermOp1 (term), leftvar, rightvar, lefttup, righttup,
		       leftenc, rightenc, callback);
      eprintf (",");
      term = deVar (TermOp2 (term));
    }
  termPrintCustom (term, leftvar, rightvar, lefttup, righttup, leftenc,
		   rightenc, callback);
  return;
}

//! Print inner tuple
void
termTuplePrint (Term term)
{
  termTuplePrintCustom (term, "", "V", "(", ")", "{ ", " }", NULL);
}

//! Make a deep copy of a term.
/**
 * Leaves are not copied.
 *@return If the original was a leaf, then the pointer is simply returned. Otherwise, new memory is allocated and the node is copied recursively.
 *\sa termDuplicateDeep()
 */

Term
termDuplicate (const Term term)
{
  Term newterm;

  if (term == NULL)
    return NULL;
  if (realTermLeaf (term))
    return term;

  newterm = (Term) malloc (sizeof (struct term));
  memcpy (newterm, term, sizeof (struct term));
  if (realTermEncrypt (term))
    {
      TermOp (newterm) = termDuplicate (TermOp (term));
      TermKey (newterm) = termDuplicate (TermKey (term));
    }
  else
    {
      TermOp1 (newterm) = termDuplicate (TermOp1 (term));
      TermOp2 (newterm) = termDuplicate (TermOp2 (term));
    }
  return newterm;
}

//! Make a deep copy of a term node (one-level)
/**
 * Leaves are not copied.
 *@return If the original was a leaf, then the pointer is simply returned. Otherwise, new memory is allocated and the node is copied recursively.
 *\sa termDuplicateDeep()
 */

Term
termNodeDuplicate (const Term term)
{
  Term newterm;

  if (term == NULL)
    return NULL;
  if (realTermLeaf (term))
    return term;

  newterm = (Term) malloc (sizeof (struct term));
  memcpy (newterm, term, sizeof (struct term));
  return newterm;
}

//! Make a true deep copy of a term.
/**
 * Currently, this function is not to be used, so we can be sure leaf nodes occur only once in the system.
 *@return New memory is allocated and the node is copied recursively.
 *\sa termDuplicate()
 */


Term
termDuplicateDeep (const Term term)
{
  Term newterm;

  if (term == NULL)
    return NULL;

  newterm = (Term) malloc (sizeof (struct term));
  memcpy (newterm, term, sizeof (struct term));
  if (!realTermLeaf (term))
    {
      if (realTermEncrypt (term))
	{
	  TermOp (newterm) = termDuplicateDeep (TermOp (term));
	  TermKey (newterm) = termDuplicateDeep (TermKey (term));
	}
      else
	{
	  TermOp1 (newterm) = termDuplicateDeep (TermOp1 (term));
	  TermOp2 (newterm) = termDuplicateDeep (TermOp2 (term));
	}
    }
  return newterm;
}

//! Make a copy of a term, but remove substituted variable nodes.
/**
 * Remove all instantiated variables on the way down.
 *\sa termDuplicate()
 */
Term
termDuplicateUV (Term term)
{
  Term newterm;

  term = deVar (term);
  if (term == NULL)
    return NULL;
  if (realTermLeaf (term))
    return term;

  newterm = (Term) malloc (sizeof (struct term));
  memcpy (newterm, term, sizeof (struct term));
  if (realTermEncrypt (term))
    {
      TermOp (newterm) = termDuplicateUV (TermOp (term));
      TermKey (newterm) = termDuplicateUV (TermKey (term));
    }
  else
    {
      TermOp1 (newterm) = termDuplicateUV (TermOp1 (term));
      TermOp2 (newterm) = termDuplicateUV (TermOp2 (term));
    }
  return newterm;
}

//! Make a deep copy of a term, also of leaves
Term
realTermDuplicate (const Term term)
{
  Term newterm;

  if (term == NULL)
    return NULL;

  newterm = (Term) malloc (sizeof (struct term));
  if (realTermLeaf (term))
    {
      memcpy (newterm, term, sizeof (struct term));
    }
  else
    {
      newterm->type = term->type;
      if (realTermEncrypt (term))
	{
	  TermOp (newterm) = realTermDuplicate (TermOp (term));
	  TermKey (newterm) = realTermDuplicate (TermKey (term));
	}
      else
	{
	  TermOp1 (newterm) = realTermDuplicate (TermOp1 (term));
	  TermOp2 (newterm) = realTermDuplicate (TermOp2 (term));
	}
    }
  return newterm;
}

//!Removes a term and deallocates memory.
/**
 * Is meant to remove terms make with termDuplicate. Only deallocates memory
 * of nodes, not of leaves.
 *\sa termDuplicate(), termDuplicateUV()
 */

void
termDelete (const Term term)
{
  if (term != NULL && !realTermLeaf (term))
    {
      if (realTermEncrypt (term))
	{
	  termDelete (TermOp (term));
	  termDelete (TermKey (term));
	}
      else
	{
	  termDelete (TermOp1 (term));
	  termDelete (TermOp2 (term));
	}
      free (term);
    }
}

//! Normalize a term with respect to tupling.
/**
 * Avoids problems with associativity by rewriting every ((x,y),z) to
 * (x,(y,z)), i.e. a normal form for terms, after which equality is
 * okay. No memory was allocated or deallocated, as only pointers are swapped.
 *
 *@return After execution, the term pointed at has been normalized. */

void
termNormalize (Term term)
{
  term = deVar (term);
  if (term == NULL || realTermLeaf (term))
    return;

  if (realTermEncrypt (term))
    {
      termNormalize (TermOp (term));
      termNormalize (TermKey (term));
    }
  else
    {
      /* normalize left hand first,both for tupling and for
         encryption */
      termNormalize (TermOp1 (term));
      /* check for ((x,y),z) construct */
      if (realTermTuple (TermOp1 (term)))
	{
	  /* temporarily store the old terms */
	  Term tx = TermOp1 (TermOp1 (term));
	  Term ty = TermOp2 (TermOp1 (term));
	  Term tz = TermOp2 (term);
	  /* move node */
	  TermOp2 (term) = TermOp1 (term);
	  /* construct (x,(y,z)) version */
	  TermOp1 (term) = tx;
	  TermOp1 (TermOp2 (term)) = ty;
	  TermOp2 (TermOp2 (term)) = tz;
	}
      termNormalize (TermOp2 (term));
    }
}

//! Copy a term, and ensure all run identifiers are set to the new value.
/**
 * Strange code. Only to be used on locals, as is stupidly replaces all run identifiers.
 *@return The new term.
 *\sa termDuplicate()
 */
Term
termRunid (Term term, int runid)
{
  if (term == NULL)
    return NULL;
  if (realTermLeaf (term))
    {
      /* leaf */
      if (TermRunid (term) == runid)
	return term;
      else
	{
	  Term newt = termDuplicate (term);
	  TermRunid (newt) = runid;
	  return newt;
	}
    }
  else
    {
      /* anything else, recurse */
      if (realTermEncrypt (term))
	{
	  Term t;

	  t = makeTermEncrypt (termRunid (TermOp (term), runid),
			       termRunid (TermKey (term), runid));
	  t->helper.fcall = term->helper.fcall;
	  return t;
	}
      else
	{
	  return makeTermTuple (termRunid (TermOp1 (term), runid),
				termRunid (TermOp2 (term), runid));
	}
    }
}

//! Determine tuple width of a given term.
/**
 *\sa tupleProject()
 */
int
tupleCount (Term tt)
{
  if (tt == NULL)
    {
      return 0;
    }
  else
    {
      tt = deVar (tt);
      if (!realTermTuple (tt))
	{
	  return 1;
	}
      else
	{
	  return (tupleCount (TermOp1 (tt)) + tupleCount (TermOp2 (tt)));
	}
    }
}

//! Yield the projection Pi(n) of a term.
/**
 *@param tt Term
 *@param n The index in the tuple [0..tupleCount-1]
 *@return Returns either a pointer to a term, or NULL if the index is out of range.
 *\sa tupleCount()
 */
Term
tupleProject (Term tt, int n)
{
  if (tt == NULL)
    {
      return NULL;
    }
  tt = deVar (tt);
  if (!realTermTuple (tt))
    {
      if (n > 0)
	{
	  /* no tuple, adressing error */
	  return NULL;
	}
      else
	{
	  /* no tuple */
	  return tt;
	}
    }
  else
    {
      /* there is a tuple to traverse */
      int left = tupleCount (TermOp1 (tt));
      if (n >= left)
	{
	  /* it's in the right hand side */
	  return tupleProject (TermOp2 (tt), n - left);
	}
      else
	{
	  /* left hand side */
	  return tupleProject (TermOp1 (tt), n);
	}
    }
}

//! Determine size of term.
/**
 * Determines the size of a term according to some heuristic.
 * Currently, the encryption operator is weighed as well.
 *@return Returns a nonnegative integer.
 *\sa termDistance()
 */

int
termSize (Term t)
{
  if (t == NULL)
    {
      return 0;
    }

  t = deVar (t);
  if (realTermLeaf (t))
    {
      return 1;
    }
  else
    {
      if (realTermEncrypt (t))
	{
	  return 1 + termSize (TermOp (t)) + termSize (TermKey (t));
	}
      else
	{
	  return termSize (TermOp1 (t)) + termSize (TermOp2 (t));
	}
    }
}

//! Determine distance between two terms.
/**
 *@return A float value between 0, completely dissimilar, and 1, equal.
 *\sa termSize()
 */

float
termDistance (Term t1, Term t2)
{
  int t1s;
  int t2s;

  /* First the special cases: no equal subterms, completely equal */
  if (isTermEqual (t1, t2))
    return 1;

  t1 = deVar (t1);
  t2 = deVar (t2);

  t1s = termSize (t1);
  t2s = termSize (t2);

  if (t1 == NULL || t2 == NULL)
    {
      return 0;
    }
  if (t1->type != t2->type)
    {
      /* unequal type, maybe one is a subterm of the other? */
      if (t1s > t2s && termSubTerm (t1, t2))
	{
	  return (float) t2s / t1s;
	}
      if (t2s > t1s && termSubTerm (t2, t1))
	{
	  return (float) t1s / t2s;
	}
      return 0;
    }
  else
    {
      /* equal types */
      if (isTermLeaf (t1))
	{
	  /* we had established before that they are not equal */
	  return 0;
	}
      else
	{
	  /* non-leaf recurse */
	  if (isTermEncrypt (t1))
	    {
	      /* encryption */
	      return (termDistance (TermOp (t1), TermOp (t2)) +
		      termDistance (TermKey (t1), TermKey (t2))) / 2;
	    }
	  else
	    {
	      return (termDistance (TermOp1 (t1), TermOp1 (t2)) +
		      termDistance (TermOp2 (t1), TermOp2 (t2))) / 2;
	    }
	}
    }
}

/**
 * Enforce a (arbitrary) ordering on terms
 * <0 means a<b, 0 means a=b, >0 means a>b.
 */
int
termOrder (Term t1, Term t2)
{
  t1 = deVar (t1);
  t2 = deVar (t2);
  if (isTermEqual (t1, t2))
    {
      /* equal terms */
      return 0;
    }

  /* differ */
  if (t1->type != t2->type)
    {
      /* different types, so ordering on types first */
      if (t1->type < t2->type)
	return -1;
      else
	return 1;
    }

  /* same type
   * distinguish cases
   */
  if (realTermLeaf (t1))
    {
      /* compare names */
      int comp;

      comp = strcmp (TermSymb (t1)->text, TermSymb (t2)->text);
      if (comp != 0)
	{
	  /* names differ */
	  return comp;
	}
      else
	{
	  /* equal names, compare run identifiers */
	  if (TermRunid (t1) == TermRunid (t2))
	    {
	      error
		("termOrder: two terms seem to be identical although local precondition says they aren't.");
	    }
	  else
	    {
	      if (TermRunid (t1) < TermRunid (t2))
		return -1;
	      else
		return 1;
	    }
	}
    }
  else
    {
      /* non-leaf */
      int compL, compR;

      if (isTermEncrypt (t1))
	{
	  compL = termOrder (TermOp (t1), TermOp (t2));
	  compR = termOrder (TermKey (t1), TermKey (t2));
	}
      else
	{
	  compL = termOrder (TermOp1 (t1), TermOp1 (t2));
	  compR = termOrder (TermOp2 (t1), TermOp2 (t2));
	}
      if (compL != 0)
	return compL;
      else
	return compR;
    }
  // @TODO Should be considered an error
  return 0;
}

//! Generic term iteration
int
term_iterate (const Term term, int (*leaf) (Term t), int (*nodel) (Term t),
	      int (*nodem) (Term t), int (*noder) (Term t))
{
  if (term != NULL)
    {
      if (realTermLeaf (term))
	{
	  // Leaf
	  if (leaf != NULL)
	    {
	      return leaf (term);
	    }
	}
      else
	{
	  int flag;

	  flag = 1;

	  if (nodel != NULL)
	    flag = flag && nodel (term);

	  // Left part
	  if (realTermTuple (term))
	    flag = flag
	      && (term_iterate (TermOp1 (term), leaf, nodel, nodem, noder));
	  else
	    flag = flag
	      && (term_iterate (TermOp (term), leaf, nodel, nodem, noder));

	  if (nodem != NULL)
	    flag = flag && nodem (term);

	  // Right part
	  if (realTermTuple (term))
	    flag = flag
	      && (term_iterate (TermOp2 (term), leaf, nodel, nodem, noder));
	  else
	    flag = flag
	      && (term_iterate (TermKey (term), leaf, nodel, nodem, noder));

	  if (noder != NULL)
	    flag = flag && noder (term);

	  return flag;
	}
    }
  return 1;
}

//! Generic term iteration
int
term_iterate_deVar (Term term, int (*leaf) (Term t), int (*nodel) (Term t),
		    int (*nodem) (Term t), int (*noder) (Term t))
{
  term = deVar (term);
  if (term != NULL)
    {
      if (realTermLeaf (term))
	{
	  // Leaf
	  if (leaf != NULL)
	    {
	      return leaf (term);
	    }
	  else
	    {
	      return true;
	    }
	}
      else
	{
	  int flag;

	  flag = true;

	  if (nodel != NULL)
	    flag = flag && nodel (term);

	  // Left part
	  if (realTermTuple (term))
	    flag = flag
	      &&
	      (term_iterate_deVar
	       (TermOp1 (term), leaf, nodel, nodem, noder));
	  else
	    flag = flag
	      &&
	      (term_iterate_deVar (TermOp (term), leaf, nodel, nodem, noder));

	  // Center

	  if (nodem != NULL)
	    flag = flag && nodem (term);

	  // right part
	  if (realTermTuple (term))
	    flag = flag
	      &&
	      (term_iterate_deVar
	       (TermOp2 (term), leaf, nodel, nodem, noder));
	  else
	    flag = flag
	      &&
	      (term_iterate_deVar
	       (TermKey (term), leaf, nodel, nodem, noder));

	  if (noder != NULL)
	    flag = flag && noder (term);

	  return flag;
	}
    }
  return true;
}

//! Iterate over the leaves in a term
/**
 * Note that this function iterates over real leaves; thus closed variables can occur as
 * well. It is up to func to decide wether or not to recurse.
 */
int
term_iterate_leaves (const Term term, int (*func) (Term t))
{
  if (term != NULL)
    {
      if (realTermLeaf (term))
	{
	  if (!func (term))
	    return 0;
	}
      else
	{
	  if (realTermTuple (term))
	    return (term_iterate_leaves (TermOp1 (term), func)
		    && term_iterate_leaves (TermOp2 (term), func));
	  else
	    return (term_iterate_leaves (TermOp (term), func)
		    && term_iterate_leaves (TermKey (term), func));
	}
    }
  return 1;
}

//! Iterate over open leaves (i.e. respect variable closure)
int
term_iterate_open_leaves (const Term term, int (*func) (Term t))
{
  int testleaf (const Term t)
  {
    if (substVar (t))
      {
	return term_iterate_open_leaves (t->subst, func);
      }
    else
      {
	return func (t);
      }
  }

  return term_iterate_leaves (term, testleaf);
}

//! Turn all rolelocals into variables
void
term_rolelocals_are_variables ()
{
  rolelocal_variable = 1;
}

//! Count the encryption level of a term
/**
 * Note that this stops at any variable that is of ticket type.
 */
int
term_encryption_level (const Term term)
{
  int iter_maxencrypt (Term t)
  {
    if (isTicketTerm (t))
      {
	return 0;
      }
    t = deVar (t);
    if (t == NULL)
      {
#ifdef DEBUG
	if (DEBUGL (2))
	  {
	    eprintf ("Warning: Term encryption level finds a NULL for term ");
	    termPrint (term);
	    eprintf ("\n");
	  }
#endif
	return 0;
      }
    if (realTermLeaf (t))
      {
	return 0;
      }
    else
      {
	int l, r;

	if (realTermTuple (t))
	  {
	    l = iter_maxencrypt (TermOp1 (t));
	    r = iter_maxencrypt (TermOp2 (t));
	  }
	else
	  {
	    // encrypt
	    l = 1 + iter_maxencrypt (TermOp (t));
	    r = iter_maxencrypt (TermKey (t));
	  }
	if (l > r)
	  return l;
	else
	  return r;
      }
  }

  return iter_maxencrypt (term);
}

//! Determine 'constrained factor' of a term
/**
 * Actually this is (number of vars/structure).
 * Thus, 0 means very constrained, no variables.
 * Everything else has higher float, but always <=1. In fact, only a single variable has a level 1.
 */
float
term_constrain_level (const Term term)
{
  int vars;
  int structure;

  void tcl_iterate (Term t)
  {
    t = deVar (t);
    structure++;
    if (realTermLeaf (t))
      {
	if (realTermVariable (t))
	  vars++;
      }
    else
      {
	if (realTermTuple (t))
	  {
	    tcl_iterate (TermOp1 (t));
	    tcl_iterate (TermOp2 (t));
	  }
	else
	  {
	    tcl_iterate (TermOp (t));
	    tcl_iterate (TermKey (t));
	  }
      }
  }

  if (term == NULL)
    error ("Cannot determine constrain level of empty term.");

  vars = 0;
  structure = 0;
  tcl_iterate (term);
  return ((float) vars / (float) structure);
}

//! Adjust the keylevels of the symbols in a term.
/**
 * This is used to scan the roles. For each symbol, this function does the bookkeeping of the keylevels at which they occur.
 */
void
term_set_keylevels (const Term term)
{
  void scan_levels (int level, Term t)
  {
#ifdef DEBUG
    if (DEBUGL (5))
      {
	int c;

	c = 0;
	while (c < level)
	  {
	    eprintf ("  ");
	    c++;
	  }
	eprintf ("Scanning keylevel %i for term ", level);
	termPrint (t);
	eprintf ("\n");
      }
#endif
    if (realTermLeaf (t))
      {
	Symbol sym;

	// So, it occurs at 'level' as key. If that is less than known, store.
	sym = TermSymb (t);
	if (level < sym->keylevel)
	  {
	    // New minimum level
	    sym->keylevel = level;
	  }
      }
    else
      {
	if (realTermTuple (t))
	  {
	    scan_levels (level, TermOp1 (t));
	    scan_levels (level, TermOp2 (t));
	  }
	else
	  {
	    scan_levels (level, TermOp (t));
	    scan_levels ((level + 1), TermKey (t));
	  }
      }
  }

  scan_levels (0, term);
}

//! Print the term diff of two terms
/**
 * This is not correct yet. We need to add function application and correct tuple handing.
 */
void
termPrintDiff (Term t1, Term t2)
{
  void termFromTo (Term t1, Term t2)
  {
    t1 = deVar (t1);
    t2 = deVar (t2);

    eprintf (" [");
    termPrint (t1);
    eprintf ("]->[");
    termPrint (t2);
    eprintf ("] ");
  }

  t1 = deVar (t1);
  t2 = deVar (t2);

  if (isTermEqual (t1, t2))
    {
      // Equal, simply print
      termPrint (t1);
    }
  else
    {
      if (t1->type != t2->type)
	{
	  // Different types
	  termFromTo (t1, t2);
	}
      else
	{
	  // Equal types, but not the same
	  // If component type, but both components different, we simply do moveto at the node level.
	  if (realTermLeaf (t1))
	    {
	      // Different constants
	      termFromTo (t1, t2);
	    }
	  else
	    {
	      if (realTermEncrypt (t1))
		{
		  // Encryption
		  if (isTermEqual (TermOp (t1), TermOp (t2))
		      || isTermEqual (TermKey (t1), TermKey (t2)))
		    {
		      eprintf ("{");
		      termPrintDiff (TermOp (t1), TermOp (t2));
		      eprintf ("}");
		      termPrintDiff (TermKey (t1), TermKey (t2));
		    }
		  else
		    {
		      termFromTo (t1, t2);
		    }
		}
	      else
		{
		  // Tupling
		  if (isTermEqual (TermOp1 (t1), TermOp1 (t2))
		      || isTermEqual (TermOp2 (t1), TermOp2 (t2)))
		    {
		      eprintf ("(");
		      termPrintDiff (TermOp1 (t1), TermOp1 (t2));
		      eprintf (")");
		      termPrintDiff (TermOp2 (t1), TermOp2 (t2));
		    }
		  else
		    {
		      termFromTo (t1, t2);
		    }
		}
	    }
	}
    }
}

//! Test two leaf terms for abstract equality (abstracting from the run identifiers)
int
isLeafNameEqual (Term t1, Term t2)
{
  return (TermSymb (t1) == TermSymb (t2));
}

//! Generate a fresh term, that does not occur yet, prefixed with the string of the given term.
Term
freshTermPrefix (Term prefixterm)
{
  Symbol prefixsymbol;
  Symbol freshsymbol;

  prefixsymbol = NULL;
  if (prefixterm != NULL && realTermLeaf (prefixterm))
    {
      prefixsymbol = TermSymb (prefixterm);
    }
  freshsymbol = symbolNextFree (prefixsymbol);
  return makeTermType (GLOBAL, freshsymbol, -1);
}

//! Generate a term from an int, prefixed with the string of the given term.
Term
intTermPrefix (const int n, Term prefixterm)
{
  Symbol prefixsymbol;
  Symbol freshsymbol;

  prefixsymbol = NULL;
  if (prefixterm != NULL && realTermLeaf (prefixterm))
    {
      prefixsymbol = TermSymb (prefixterm);
    }
  freshsymbol = symbolFromInt (n, prefixsymbol);
  return makeTermType (GLOBAL, freshsymbol, -1);
}

//! Determine whether a term is a functor
int
isTermFunctionName (Term t)
{
  t = deVar (t);
  if (t != NULL && isTermLeaf (t) && t->stype != NULL
      && inTermlist (t->stype, TERM_Function))
    return 1;
  return 0;
}

//! Determine whether a term is a function application. Returns the function term.
Term
getTermFunction (Term t)
{
  t = deVar (t);
  if (realTermEncrypt (t))
    {
      if (t->helper.fcall)
	{
	  return TermKey (t);
	}
    }
  return NULL;
}

//! Return hidelevel for a single term within another
unsigned int
termHidelevel (const Term tsmall, Term tbig)
{
  tbig = deVar (tbig);
  if (isTermEqual (tsmall, tbig))
    {
      // It's simply here: not hidden at any level
      return 0;
    }
  else
    {
      if (realTermLeaf (tbig))
	{
	  // Not here: infinitely hidden
	  return INT_MAX;
	}
      else
	{
	  // Nested term
	  unsigned int t1, t2;

	  if (realTermTuple (tbig))
	    {
	      // Tupling
	      t1 = termHidelevel (tsmall, TermOp1 (tbig));
	      t2 = termHidelevel (tsmall, TermOp2 (tbig));
	    }
	  else
	    {
	      // Encryption
	      t1 = termHidelevel (tsmall, TermOp (tbig));
	      t2 = termHidelevel (tsmall, TermKey (tbig));
	      if (t2 < INT_MAX)
		t2++;
	    }
	  // Return minimum
	  if (t1 < t2)
	    {
	      return t1;
	    }
	  else
	    {
	      return t2;
	    }
	}
    }
}

//! Show a substitution of t
void
termSubstPrint (Term t)
{
  if (realTermVariable (t))
    {
      Term tbuf;

      tbuf = t->subst;
      t->subst = NULL;
      termPrint (t);
      t->subst = tbuf;
      eprintf (":=");
      termSubstPrint (t->subst);
    }
  else
    {
      termPrint (t);
    }
}

// Iterate over subterm constants of other runs in a term
// Callback should return true to progress. This is reported in the final thing.
int
iterateTermOther (const int myrun, Term t, int (*callback) (Term t))
{
  int testOther (Term t)
  {
    int run;

    run = TermRunid (t);
    if (run >= 0 && run != myrun)
      {
	return callback (t);
      }
    return true;
  }
  return term_iterate_deVar (t, testOther, NULL, NULL, NULL);
}
