// FILE. . . . . d:/hak/hlt/src/hlt/fot/fuz/syntax/sources/fff-parser-javacode.grm
// EDIT BY . . . Hassan Ait-Kaci
// ON MACHINE. . Hak-Laptop
// STARTED ON. . Tue Aug 21 05:13:47 2018

// Last modified on Sat Dec 14 11:43:34 2019 by hak

%{
  /* ******************************************************************** */

  /**
   * Declare and initialize some global names and structures (accessible
   * by all methods in the parser).  More are declared and initialized
   * below just before or after the first method that uses them.
   */

  // temp structure used to process subterms
  Stack termStack = new Stack();
  // "raw" term structure - a tree of strings
  SyntacticTerm temp;

  // temps for holding the actual term read off the raw term and fot operations' operands
  FirstOrderTerm term, lhs, rhs;

  // the signature of functors some of which are similar
  SimilarFunctorSignature signature = new SimilarFunctorSignature();
  // a name -> functor table to retrieve functors by name:
  HashMap functorTable = new HashMap();

  // the similarity (when computed - null otherwise)
  SignatureSimilarity similarity;

  // the list of similarity degrees in the similarity (if not null) - sorted in ascending order
  DoubleArrayList degrees;

  // the current similarity degree - initialized to 1.0 (crisp true)
  double degree = 1.0;

  // the current tracing detail level - initialized to 0 (shut-up level)
  int tracingVerbosity = 0;

  /* ************************************************************************ */

  /**
   * Shorthands for tools to ease tagged interaction messaging.
   */

  // output a new line
  void line ()
  {
    out.println();
  }

  // output a new line containing msg
  void line (String msg)
  {
    out.println(msg);
  }

  // outputs a sequence of new lines containing an element of array msgs
  void lines (String[] msgs)
  {
    for (int i=0; i<msgs.length; i++)
      line(msgs[i]);
  }

  // output a new line starting with "*** " containing msg
  void say (String msg)
  {
    line("*** "+msg);
  }

  // outputs a sequence of new lines each starting with "*** " and
  // containing an element of array msgs
  void sayLines (String[] msgs)
  {
    for (int i=0; i<msgs.length; i++)
      say(msgs[i]);
  }

  // output a new line starting with ">>> " containing msg
  void note (String msg)
  {
    err.println(">>> "+msg);
  }

  // output a new line starting with "!!! " containing msg
  void warn (String msg)
  {
    err.println("!!! "+msg);
  }

  /* ************************************************************************ */
  
  /**
   * Pragma bookkeeping.
   */

  // current pragma being executed if one is; null otherwise
  private String pragma = null;

  String pragma ()
  {
    return pragma;
  }

  void setPragma (String pragma)
  {
    this.pragma = pragma;
  }

  // table of strings containing the formats of the defined pragmas
  ArrayList pragmaTable = initializePragmaTable();

  // method to initialize the pragma table
  ArrayList initializePragmaTable ()
  {
    pragmaTable = new ArrayList();
    
    pragmaTable.add("#info pragma;");
    pragmaTable.add("#fun f1/n1 ... fk/nk;");
    pragmaTable.add("#sig;");
    pragmaTable.add("#sim f1 g1 α1 ... fk gk αk;");
    pragmaTable.add("#close;");
    pragmaTable.add("#funclass f α;");
    pragmaTable.add("#termclass term α;");
    pragmaTable.add("#funrep f α;");
    pragmaTable.add("#termrep term α;");
    pragmaTable.add("#show;");
    pragmaTable.add("#map f g α i1:j1 ... ik:jk;");
    pragmaTable.add("#comp;");
    pragmaTable.add("#load \"file\";");
    pragmaTable.add("#trace n;");
    pragmaTable.add("#reset;");

    return pragmaTable;
  }

  /* ******************************************************************** */

  /**
   * Static global arrays of text strings containing detailed info per
   * pragma.  Each element is a line that will be printed with 'say' in
   * the order of indices.
   */
  
  private static String[] infoPragmaInfoLines = new String[]
  {
    "'#info;' will list all currently known info topics;",
    "'#info #foo;' will print info specific to known pragma '#foo'."
  };

  private static String[] funPragmaInfoLines = new String[]
  {
    "'#fun f1/n1 ... fk/nk;' will declare signature functor symbols,",
    "where fi is an identifier denoting a functor's name being declared,",
    "and ni is a natural number specifying its arity, for each i = 1,...,k,",
    "then print the current signature; the slashes '/' are optional and can",
    "be blank space; e.g., '#fun f1 n1 ... fk nk;'."
  };

  private static String[] sigPragmaInfoLines = new String[]
  {
    "'#sig;' will print the current signature (same as '#fun;')."
  };
  
  private static String[] simPragmaInfoLines = new String[]
  {
    "'#sim f1 g1 α1 ... fk gk αk;' will declare similar pairs of functors,",
    "where fi and gi denote (necessarily distinct and similar) functors' names",
    "and each αi is a value in the interval (0.0,1.0] defining their similariy",
    "degree, for each i = 1,...,k."
  };
  
  private static String[] closePragmaInfoLines = new String[]
  {
    "'#close;' will compute the similarity closure of the set of fuzzy pairs",
    "that have been declared for the current signature."
  };
  
  private static String[] showPragmaInfoLines = new String[]
  {
    "'#show;' will display the currently declared similar pairs on the current",
    "signature as a square matrix on the signature, of values in [0.0,1.0], its",
    "corresponding set of similarity degrees, and lists the partitions per",
    "similarity degrees."
  };
  
  private static String[] funclassPragmaInfoLines = new String[]
  {
    "'#funclass f α;' will, if the similarity has been closed, display the",
    "similarity class of functor f at similarity degree α."
  };
  
  private static String[] termclassPragmaInfoLines = new String[]
  {
    "'#termclass t α;' will, if the similarity has been closed, display the",
    "similarity class of FOT t at similarity degree α."
  };
  
  private static String[] funrepPragmaInfoLines = new String[]
  {
    "'#funrep f α;', will, if the similarity has been closed, show the",
    "similarity class representative of functor f at similarity degree α."
  };
  
  private static String[] termrepPragmaInfoLines = new String[]
  {
    "'#termrep t α;' will, if the similarity has been closed, show the",
    "similarity class representative of FOT t at similarity degree α."
  };
  
  private static String[] mapPragmaInfoLines = new String[]
  {
    "'#map f g α i1:j1 ... ik:jk;' will specify an argument-position map where:\n",
    "\t- f and g are (necessarily distinct and similar) functors;\n",
    "\t- α is a value in the interval (0.0,1.0] that belongs to the (finite)",
    "set of known approximation degrees in the current similarity on the current",
    "functor signature (if it is not, it will be set de facto to the greatest",
    "known positive degree in the similary that has a lesser value if one exists,",
    "otherwise this will generate an error);\n",
    "\t- k is a natural number such that 0 <= k <= Math.min(f.arity(),g.arity());\n",
    "\t- each i:j is a pair of non-zero natural numbers such that 1 <= i <= f.arity()",
    "and 1 <= j <= g.arity() indicating which argument positions of f and g are in",
    "(necessarily injective) mutual correspondence at approximation level α.\n\n",
    "This is therefore equivalent to '#map g f α j1:i1 ... jk:ik'.\n\n",
    "Note that when k=0 (i.e., just entering '#map f g α;'), the similarity between",
    "any two term structures whose root symbols are these respective functors will",
    "involve none of their subterms at approximation degree α or less, regardless of",
    "whether either of their respective arities is non-zero.\n\n",
    "N.B.: a #map pragma may be executed several times on the same two functors f and",
    "g for a same or different α. Upon invoking the pragma #comp, the compound effects",
    "on the same pair will result, in the order specified if these position maps are",
    "verified to be consistent, later ones overriding the effects of any earlier ones",
    "executed. Also, for any pair of functors (f,g) for which no #map is specified,",
    "the default is the identity on {1, ..., Math.min(f.arity(),g.arity())}."
  };

  private static String[] compPragmaInfoLines = new String[]
  {
    "'#comp;' will verify all the necessary consistency conditions for partial",
    "non-aligned similar functor arguments: namely, that all the declared",
    "argument-position maps between any similar pair of functors for the current",
    "signature's similarity have, for all the similarity's approximation degrees,",
    "monotonically decreasing domains, ranges, and similarity classes as the fuzzy",
    "approximation degree decreases and are consistent under composition (completing",
    "the signature and its similarity if necessary and if a consistent completion is",
    "possible), or indicates an existing map inconsistency."
  };

  private static String[] loadPragmaInfoLines = new String[]
  {
    "'#load \"file\";' will load and execute the clauses contained in the file."
  };

  private static String[] tracePragmaInfoLines = new String[]
  {
    "'#trace n;' where n is a natural number will turn on tracing mode at a verbosity",
    "detail level of n (the greater, the more verbose). To turn off tracing mode, one",
    "uses '#trace 0;' (or simply '#trace;'). When in tracing mode, FFF gives details of",
    "what it does (useful for debugging or if one wishes to see this sort of information)."
  };

  private static String[] resetPragmaInfoLines = new String[]
  {
    "'#reset;' will erase all declared functors and reset the signature and similarity."
  };

  /* ************************************************************************ */

  /**
   * Register the name of a functor, ensuring only one arity per functor name.
   */
  void registerFunctor (String name, Functor functor)
  {
    // if one is already registered under this name but not with this
    // arity the following test will raise a BadFunctorArityException:
    if (functorTable.put(name,signature.functor(name,functor.arity())) != null)
      // registered with same arity: no need to worry
      return;

    // // new functor; inform about this
    // warn("New functor added to signature: "+functor);

    // if similarity not yet defined, a new functor causes no harm: we're done!
    if (similarity == null)
      return;

    // similarity was defined; must update it on the fly on the augmented
    // signature as this may happen while evaluating a lattice operation
    // containing the new functor
    similarity = new SignatureSimilarity(signature,triplesToArray(declaredPairs));

    // inform of the deed
    warn("New functor: "+functor+
	 " ('#show;<CR>' to see new least similarity on "+signature+")");
  }

  /* ************************************************************************ */

  ArrayList declaredPairs = new ArrayList();

  void declareSimilarPair (String left, String right, double degree)
  {
    if (left == right)
      {
	warn("Bad similarity declaration: can only declare distinct similar pairs");
	warn("Similarity declaration ignored");
	return;
      }

    Functor L = (Functor)functorTable.get(left);
    boolean leftUnknown = (L == null);
    if (leftUnknown)
      warn("Unknown functor "+left);

    Functor R = (Functor)functorTable.get(right);
    boolean rightUnknown = (R == null);
    if (rightUnknown)
      warn("Unknown functor "+right);

    if (leftUnknown || rightUnknown)
      {
	warn("Similarity declaration ignored");
	return;
      }

    // record the declared pair and degree
    declaredPairs.add(new SignatureSimilarityTriple(L,R,degree));
    
    if (similarity != null)
      { // reset and warn of need to re-compute
	similarity = null;
	warn("New similarity declaration: re-compute closure with '#close;<CR>'");
      }
  }

  /* ************************************************************************ */

  ArrayList declaredMaps = new ArrayList();
  IntArrayList declaredPairMapList = new IntArrayList();

  void declareArgumentMapping (String left, String right, double degree)
  {
    // the following is commented out since already done when the FuzzyPair was read
    // declareSimilarPair(left,right,degree);

    if (declaredMaps.size() == 0)
      say("There are no declared argument mappings");
    else
      say("Declared argument mappings: ");
    
    for (int i = 0; i < declaredMaps.size(); i++)
      line("\t"+declaredMaps.get(i));
  }

  /* ************************************************************************ */

  /**
   * Transforms the set of declared fuzzy pairs, an <tt>ArrayList</tt>
   * of <tt>SignatureSimilarityTriple</tt> objects, into the same
   * information encoded as a functor-indexed 2D matrix of fuzzy
   * weights. This may be then closed to the least similarity containing
   * the declared fuzzy pairs.
   */
  double[][] triplesToArray (ArrayList declaredPairs)
  {
    double[][] data = new double[signature.size()][signature.size()];

    for (int i = 0; i < declaredPairs.size(); i++)
      {
	SignatureSimilarityTriple t  = (SignatureSimilarityTriple)declaredPairs.get(i);
	data[t.left().index()][t.right().index()] = t.degree();
      }

    return data;
  }

  /* ************************************************************************ */

  Stack fileStack = new Stack();

  boolean readingFromFile ()
  {
    return fileStack.size() > 0;
  }

  String currentFile ()
  {
    if (fileStack == null || fileStack.isEmpty())
      return "";

    return (String)fileStack.peek();
  }

  /* ************************************************************************ */

  String inputClause;
  
  private void echoFileReadClause (String clause)
  {
    if (readingFromFile())
      line(inputClause);
  }

  /**
   * Execute the current pragma.
   */
  void executePragma ()
  {
    note("Executing pragma: #"+pragma);

    switch (pragma)
      {
      case "info":
	inputClause = "#info"+inputClause+";";
	echoFileReadClause(inputClause);
	System.err.println("%%% READ inputClause = "+inputClause);
	executeInfoPragma();
	break;
      case "fun":
	inputClause = "#fun"+inputClause+";";
	echoFileReadClause(inputClause);
	executeFunPragma();
	break;
      case "sig":
	inputClause = "#sig;";
	echoFileReadClause(inputClause);
	executeSigPragma();
	break;
      case "sim":
	inputClause = "#sim"+inputClause+";";
	echoFileReadClause(inputClause);
	executeSimPragma();
	break;
      case "close":
	inputClause = "#close;";
	echoFileReadClause(inputClause);
	executeClosePragma();
	break;
      case "show":
	inputClause = "#show;";
	echoFileReadClause(inputClause);
	executeShowPragma();
	break;
      case "funclass":
	inputClause = "#funclass "+functorName+" "+classDegree+";";
	echoFileReadClause(inputClause);
	executeFunclassPragma();
	break;
      case "termclass":
	inputClause = "#termclass "+term+" "+classDegree+";";
	echoFileReadClause(inputClause);
	executeTermclassPragma();
	break;
      case "funrep":
	inputClause = "#funrep "+functorName+" "+classDegree+";";
	echoFileReadClause(inputClause);
	executeFunrepPragma();
	break;
      case "termrep":
	inputClause = "#termrep "+term+" "+classDegree+";";
	echoFileReadClause(inputClause);
	executeTermrepPragma();
	break;
      case "map":
	inputClause = "#map"+inputClause+";";
	echoFileReadClause(inputClause);
	executeMapPragma();
	break;
      case "comp":
	inputClause = "#comp;";
	echoFileReadClause(inputClause);
	executeCompPragma();
	break;
      case "load":
	inputClause = "#load "+(currentFile())+"\";";
	echoFileReadClause(inputClause);
	executeLoadPragma();
	break;
      case "trace":
	inputClause = "#trace "+tracingVerbosity+";";
	echoFileReadClause(inputClause);
	executeTracePragma();
	break;
      case "reset":
	inputClause = "#reset;";
	echoFileReadClause(inputClause);
	executeResetPragma();
	break;
      default:
	// actually never done: an undefined pragma causes a parsing exception in the tokenizer
	warn("Unknown pragma: '#"+pragma+"' (ignored)");
      }

    inputClause = "";
  }

  /* ******************************************************************** */

  String infoPragmaName;

  void executeInfoPragma ()
  { // System.err.println("$$$ executing pragma name = "+infoPragmaName);
    switch (infoPragmaName)
      {
      case "info":
	sayLines(infoPragmaInfoLines);
	break;
      case "fun":
	lines(funPragmaInfoLines);
	break;
      case "sig":
	lines(sigPragmaInfoLines);
	break;
      case "sim":
	lines(simPragmaInfoLines);
	break;
      case "close":
	lines(closePragmaInfoLines);
	break;
      case "show":
	lines(showPragmaInfoLines);
	break;
      case "funclass":
	lines(funclassPragmaInfoLines);
	break;
      case "termclass":
	lines(termclassPragmaInfoLines);
	break;
      case "funrep":
	lines(funrepPragmaInfoLines);
	break;
      case "termrep":
	lines(termrepPragmaInfoLines);
	break;
      case "map":
	lines(mapPragmaInfoLines);
	break;
      case "comp":
	lines(compPragmaInfoLines);
	break;
      case "load":
	lines(loadPragmaInfoLines);
	break;
      case "trace":
	lines(tracePragmaInfoLines);
	break;
      case "reset":
	lines(resetPragmaInfoLines);
	break;
      default:
	executeEmptyInfoPragma();
      }
  }

  /* ******************************************************************** */

  void executeEmptyInfoPragma ()
  {
    say("Currently supported FFF pragmas:\n");
    for (int i=0; i<pragmaTable.size(); i++)
      line("\t"+pragmaTable.get(i));
    line();
    say("Currently supported FFF operations:\n");
    line("\tt1 /\\ t2; computes the infimum of terms t1 and t2 (unification);");
    line("\tt1 \\/ t2; computes the supremum of terms t1 and t2 (generalization);");
    line("\tt1 ~ t2;  computes the similarity degree of terms t1 and t2 (value in [0.0,1.0]).");
    line();
    say("If a similarity is defined on the signature, these operations are fuzzy; otherwise, they are crisp.");
  }
  
  /* ******************************************************************** */

  void executeFunPragma ()
  {
    showSignature();
  }

  /* ******************************************************************** */

  void executeSigPragma ()
  {
    showSignature();
  }

  /* ******************************************************************** */

  void showSignature ()
  {
    say("The current signature has "+signature.size()+" functors: "+signature);
  }

  /* ******************************************************************** */

  void executeSimPragma ()
  {
    showDeclaredPairs();
  }

  /* ******************************************************************** */

  void showDeclaredPairs ()
  {
    if (declaredPairs.size() == 0)
      say("There are no declared similarities ");
    else
      say("Declared similarities: ");
    
    for (int i = 0; i < declaredPairs.size(); i++)
      line("\t"+declaredPairs.get(i));
  }

  void executeClosePragma ()
  {
    if (declaredPairs.isEmpty())
      warn("Cannot compute a similarity closure when no similar pairs have been declared");
    else
      {
	similarity = new SignatureSimilarity(signature,triplesToArray(declaredPairs));
	say("Computed similarity closure (enter '#show;<CR>' to see it)");
      }
  }

  /* ******************************************************************** */

  void executeShowPragma ()
  {
    showSignature();
    line();

    if (declaredPairs.isEmpty())
      {
	say("No functors have been declared similar");
	return;
      }

    // BUG!!!
    // In the following code beware that the size of declaredPairs is
    // reset to the size of the last list of position pairs that was
    // declared!

    say("The declared similar functor pairs are: ");
    for (int i=0; i<declaredPairs.size(); i++)
      line("\t"+declaredPairs.get(i));
    line();

    // FIX: there should be as many declaredPairs arrays as there are values in degree()!
    
    if (similarity == null)
      {
	say("No similarity closure has been computed yet ");
	line("(use pragma '#close;' to compute it)");
	return;
      }

    say("The similarity closure of the declared similar functor pairs is:\n");
    //    FuzzyMatrix.precision = "%4.1f ";
    similarity.show(); //("%4.1f ");

    degrees = similarity.degrees();
    say("It has "+degrees.size()+" similarity degrees: "+degrees);
    line();

    say("The "+degrees.size()+" corresponding fuzzy partitions are:");
    for (int index = 0; index < degrees.size(); index++)
      {
	double cut = degrees.get(index);
	line();
	// warn
	similarity.sayIfTracing
	  ("computing partition["+index+"] corresponding to degree "+cut);
	say(">= "+cut+": "+similarity.partitionToString(similarity.getPartition(index)));
      }
  }

  /* ******************************************************************** */

  private String functorName;
  private double classDegree;

  void executeFunrepPragma ()
  {
    Functor functor = (Functor)functorTable.get(functorName);
    if (functor == null)
      {
	warn("Unknown functor "+functorName+" (#funrep query ignored)");
	return;
      }

    say("The "+classDegree+"-similar functor representative of "+functorName+" is "+
	similarity.functorRep(functor,classDegree));
  }

  void executeTermrepPragma ()
  {
    FirstOrderTerm termrep = similarity.termRep(term,classDegree);
    say("The "+classDegree+"-similar term representative of "+term+" is "+termrep);
  }

  void executeFunclassPragma ()
  {
    if (similarity == null)
      {
	warn("No similarity has been defined yet; so functors are only similar to themselves:");
	say(classDegree+"-similarity class of functor "+functorName+": ["+functorName+"]");
	return;
      }

    Functor functor = (Functor)functorTable.get(functorName);
    if (functor == null)
      {
	warn("Unknown functor "+functorName+" (#funclass query ignored)");
	return;
      }

    say(classDegree+"-similarity class of functor "+functorName+": "+
	similarity.functorClass(functor,classDegree));
  }

  void executeTermclassPragma ()
  {
    if (similarity == null)
      {
	warn("No similarity has been defined yet; so terms are only similar to themselves:");
	say(classDegree+"-similarity class of term "+term+": ["+term+"]");
	return;
      }
    ArrayList termClass = similarity.termClass(term,classDegree);
    say(classDegree+"-similarity class of term "+term+" ("+termClass.size()+
	" term"+(termClass.size()>1?"s":"")+"): "+
	//		termClass+"\n");
	// write only the first 120 characters (because this can be and explosive number!)
	Misc.etc(120,termClass.toString())+"\n");
  }

  /* ******************************************************************** */

  // void executeMapPragma ()
  // {
  //   notyet("map");
  // }

  void executeMapPragma ()
  {
    // showDeclaredPairs();
    showDeclaredMaps();
  }

  void showDeclaredMaps ()
  {
    if (declaredMaps.size() == 0)
      say("There are no declared argument maps ");
    else
      say("Declared argument maps: ");
    
    for (int i = 0; i < declaredMaps.size(); i++)
      line("\t"+declaredMaps.get(i));

  }

  /* ******************************************************************** */

  void executeCompPragma ()
  {
    notyet("comp");
  }

  /* ******************************************************************** */

  void executeLoadPragma ()
  {
    try
      {
	((FuzzyFOTLatticeTokenizer)input).include(currentFile());
      }
    catch (FileNotFoundException e)
      {
	warn(syntaxError("file not found: "+currentFile(),currentNode()).toString());
      }
    catch (CircularInclusionException e)
      {
	warn(syntaxError("circular file inclusion of file: "+currentFile(),currentNode()).toString());
      }
  }

  // void doneLoading ()
  // {
  //   say("Done reading from file: "+currentFile());
  //   fileStack.pop();
  //   note("fileStack = "+fileStack);
  //   if (fileStack.isEmpty())
  //     FuzzyFOTLatticeTokenizer.interactive = true;
  //   note("#load pragma done");
  // }

  /* ******************************************************************** */

  void executeTracePragma ()
  {
    SignatureSimilarity.toggleTracing(tracingVerbosity);
  }

  /* ******************************************************************** */

  void executeResetPragma ()
  {
    functorTable.clear(); // = new HashMap();
    signature = new SimilarFunctorSignature();
    declaredPairs.clear(); // = new ArrayList();
    declaredMaps.clear(); // = new ArrayList();
    declaredPairMapList = new IntArrayList();
    similarity = null;
    degrees = null;
    say("All structures have been reset");
  }

  /* ******************************************************************** */

  private void notyet (String clause)
  {
    warn("Sorry: '"+clause+"' clause is not yet implemented");
  }

  /* ******************************************************************** */

  void processSimilarityDegree ()
  {
    try
      {
	inputClause = lhs+" ~ "+rhs+";";
	echoFileReadClause(inputClause);

	if (similarity == null)
	  {
	    warn
	      ("No similarity defined: similarity degree can only be 1.0 (equal) or 0.0 (unequal)");
	    if (lhs.equal(rhs))
	      say("These terms are equal (similarity degree = 1.0)");
	    else
	      say("These terms are not equal (similarity degree = 0.0)");
	  }
	else
	  // check term similarity:
	  say("These terms' similarity degree is "+similarity.similarityDegree(lhs,rhs));
      }
    catch (Exception e)
      {
	warn("Term similarity query aborted");
      }
  }

  /* ******************************************************************** */

  void processUnification ()
  {
    if (similarity == null)
      processCrispUnification();
    else
      processFuzzyUnification();
  }    

  void processCrispUnification ()
  {
    //    note("Processing crisp unification");

    try
      {
	inputClause = lhs+" /\\ "+rhs+";";
	echoFileReadClause(inputClause);
	if (FirstOrderTerm.unify(lhs,rhs))
	  {
	    say("Glb = "+lhs.derefToString());
	    showUnifier();
	  }
	else
	  say("These terms are not unifiable");
      }
    catch (Exception e)
      {
	note(e.getMessage());
	warn("Term unification aborted");
      }
  }    

  void processFuzzyUnification ()
  {
    //    note("Processing fuzzy unification");

    try
      {
	inputClause = lhs+" /\\ "+rhs+";";
	echoFileReadClause(inputClause);
	degree = similarity.unify(lhs,rhs);
	if (degree > 0.0)
	  {
	    say("The fuzzy glb is "+lhs.derefToString());
	    say("Its approximation degree is "+degree);
	    say("Its "+degree+"-similar term representative is "+
		similarity.termRep(lhs,degree).deref());
	    showUnifier();
	  }
	else
	  say("These terms are not fuzzy unifiable");
      }
    catch (Exception e)
      {
	note(e.getMessage());
	warn("Term fuzzy unification aborted");
      }
  }    

  void showUnifier ()
  {
    int count = 0;
    boolean isEmpty = true;
    
    SignatureSimilarity.sayIfTracing("registered variables: "+Variable.registeredVariableNames);
    for (int i = 0; i < Variable.registeredVariableNames.size(); i++)
      {
	String varName = (String)Variable.registeredVariableNames.get(i);
	Variable var = (Variable)Variable.registeredVariableTable.get(varName);

	if (var.isBound())
	  {	    
	    isEmpty = false;
	    if (count == 0)
	      say("Unifying substitution:");
	    count++;
	    if (similarity == null)
	      say("\t"+varName+" = "+var.deref().deref());
	    else
	      say("\t"+varName+" = "+similarity.termRep(var.deref().deref(),degree));
	  }
      }

    if (isEmpty)
      say("Empty unifying substitution");
  }

  /* ******************************************************************** */

  void processGeneralization ()
  {
    if (similarity == null)
      processCrispGeneralization();
    else
      processFuzzyGeneralization();
  }    

  void processCrispGeneralization ()
  {
    //    note("Processing crisp generalization");

    try
      {
	inputClause = lhs+" \\/ "+rhs+";";
	echoFileReadClause(inputClause);
	FirstOrderTerm.resetGeneralizer();
	say("Lub = "+FirstOrderTerm.generalize(lhs,rhs));
	showGeneralizers();
      }
    catch (Exception e)
      {
	warn("Term generalization aborted");
      }
  }    

  void processFuzzyGeneralization ()
  {
    //    note("Processing fuzzy generalization");

    try
      {
	inputClause = lhs+" \\/ "+rhs+";";
	echoFileReadClause(inputClause);
	similarity.resetGeneralizer();
	FuzzyFirstOrderTerm result = similarity.generalize(lhs,rhs,1.0);
	say("The fuzzy lub is "+result.term());
	say("Its approximation degree is "+result.degree());
	say("Its "+result.degree()+"-similar term representative is "+
	    similarity.termRep(result.term().deref(),result.degree()));
	showGeneralizers();
      }
    catch (Exception e)
      {
	e.printStackTrace();
	warn("Term fuzzy generalization aborted");
      }
  }    

  void showGeneralizers ()
  {
    SignatureSimilarity.sayIfTracing("registered variables: "+Variable.registeredVariableNames);
	
    if (similarity == null)
      showCrispGeneralizers();
    else
      showFuzzyGeneralizers();
  }

  void showCrispGeneralizers ()
  {
    Variable var;
    FirstOrderTerm term;

    if (!FirstOrderTerm.generatedVariables.isEmpty())
      {
	say("Left generalizing substitution:");
	for (int i = 0; i < FirstOrderTerm.generatedVariables.size(); i++)
	  {
	    var = (Variable)FirstOrderTerm.generatedVariables.get(i);
	    term = (FirstOrderTerm)FirstOrderTerm.lSubstitution.get(var);
	    say("\t"+var+" = "+term);
	  }
	
	say("Right generalizing substitution:");
	for (int i = 0; i < FirstOrderTerm.generatedVariables.size(); i++)
	  {
	    var = (Variable)FirstOrderTerm.generatedVariables.get(i);
	    term = (FirstOrderTerm)FirstOrderTerm.rSubstitution.get(var);
	    say("\t"+var+" = "+term);
	  }
      }

    // Map.Entry keyval;

    // say("Left substitution:");
    // for (Iterator i = FirstOrderTerm.lSubstitution.entrySet().iterator(); i.hasNext();)
    //   {
    // 	keyval = (Map.Entry)i.next();
    // 	var = (Variable)keyval.getKey();
    // 	term = (FirstOrderTerm)keyval.getValue();
    // 	say(var+" = "+term);
    //   }

    // say("Right substitution:");
    // for (Iterator i = FirstOrderTerm.rSubstitution.entrySet().iterator(); i.hasNext();)
    //   {
    // 	keyval = (Map.Entry)i.next();
    // 	var = (Variable)keyval.getKey();
    // 	term = (FirstOrderTerm)keyval.getValue();
    // 	say(var+" = "+term);
    //   }
  }

  void showFuzzyGeneralizers ()
  {
    Variable var;
    FirstOrderTerm term;

    if (!similarity.generatedVariables.isEmpty())
      {
	say("Left substitution:");
	for (int i = 0; i < similarity.generatedVariables.size(); i++)
	  {
	    var = (Variable)similarity.generatedVariables.get(i);
	    term = (FirstOrderTerm)similarity.lSubstitution.get(var);
	    say("\t"+var+" = "+similarity.termRep(term,degree));
	  }
	
	say("Right substitution:");
	for (int i = 0; i < similarity.generatedVariables.size(); i++)
	  {
	    var = (Variable)similarity.generatedVariables.get(i);
	    term = (FirstOrderTerm)similarity.rSubstitution.get(var);
	    say("\t"+var+" = "+similarity.termRep(term,degree));
	  }
      }
  }

  %}

/* ************************************************************************ */
