/*
* CompoundTerm.java
*
* Copyright (C) 2008 Pei Wang
*
* This file is part of Open-NARS.
*
* Open-NARS 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.
*
* Open-NARS 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 Open-NARS. If not, see <http://www.gnu.org/licenses/>.
*/
package nars.language;
import com.google.common.collect.Iterators;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import nars.storage.Memory;
import nars.config.Parameters;
import nars.entity.TermLink;
import nars.inference.TemporalRules;
import nars.io.Symbols;
import nars.io.Symbols.NativeOperator;
import static nars.io.Symbols.NativeOperator.COMPOUND_TERM_CLOSER;
import static nars.io.Symbols.NativeOperator.COMPOUND_TERM_OPENER;
import static nars.language.CompoundTerm.makeCompoundName;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
import static nars.language.Interval.interval;
public abstract class CompoundTerm extends Term implements Iterable<Term> {
/**
* list of (direct) term
* TODO make final again
*/
public final Term[] term;
/**
* syntactic complexity of the compound, the sum of those of its term
plus 1
TODO make final again
*/
transient public short complexity;
/** Whether contains a variable */
transient private boolean hasVariables, hasVarQueries, hasVarIndeps, hasVarDeps;
transient int containedTemporalRelations = -1;
int hash;
private boolean normalized;
/**
* Abstract method to get the operator of the compound
*/
@Override public abstract NativeOperator operator();
/**
* Abstract clone method
*
* @return A clone of the compound term
*/
@Override public abstract CompoundTerm clone();
/** subclasses should be sure to call init() in their constructors; it is not done here
to allow subclass constructors to set data before calling init() */
public CompoundTerm(final Term[] components) {
super();
this.term = components;
}
/** call this after changing Term[] contents */
protected void init(Term[] term) {
this.complexity = 1;
this.hasVariables = this.hasVarDeps = this.hasVarIndeps = this.hasVarQueries = false;
for (final Term t : term) {
this.complexity += t.getComplexity();
hasVariables |= t.hasVar();
hasVarDeps |= t.hasVarDep();
hasVarIndeps |= t.hasVarIndep();
hasVarQueries |= t.hasVarQuery();
}
invalidateName();
if (!hasVar())
setNormalized(true);
}
public void invalidateName() {
this.name = null; //invalidate name so it will be (re-)created lazily
for (Term t : term) {
if (t.hasVar())
if (t instanceof CompoundTerm)
((CompoundTerm)t).invalidateName();
}
setNormalized(false);
}
/** Must be Term return type because the type of Term may change with different arguments */
abstract public Term clone(final Term[] replaced);
@Override
public CompoundTerm cloneDeep() {
Term c = clone(cloneTermsDeep());
if (c == null)
throw new UnableToCloneException("Unable to cloneDeep: " + this);
if (c.getClass()!=getClass())
throw new UnableToCloneException("cloneDeep resulted in different class: " + c + " from " + this);
if (isNormalized())
((CompoundTerm)c).setNormalized(true);
return (CompoundTerm)c;
}
protected void transformIndependentVariableToDependent(HashMap<String,Variable> vars, CompoundTerm T) { //a special instance of transformVariableTermsDeep in 1.7
Term[] term=T.term;
for (int i = 0; i < term.length; i++) {
Term t = term[i];
if (t.hasVar()) {
if (t instanceof CompoundTerm) {
transformIndependentVariableToDependent(vars, (CompoundTerm) t);
} else if (t instanceof Variable) { /* it's a variable */
term[i] = vars.get(t.toString());
}
}
}
}
static Interval conceptival = interval(1);
private static void ReplaceIntervals(CompoundTerm comp) {
for(int i=0; i<comp.term.length; i++) {
Term t = comp.term[i];
if(t instanceof Interval) {
comp.term[i] = conceptival;
comp.invalidateName();
}
else
if(t instanceof CompoundTerm) {
ReplaceIntervals((CompoundTerm) t);
}
}
}
public static Term cloneDeepReplaceIntervals(Term T) {
T=T.cloneDeep(); //we will operate on a copy
if(T instanceof CompoundTerm) {
ReplaceIntervals((CompoundTerm) T);
}
return T;
}
public CompoundTerm transformIndependentVariableToDependentVar(CompoundTerm T) {
T=T.cloneDeep(); //we will operate on a copy
int counter = 0;
for(char c : T.toString().toCharArray()) {
if(c==Symbols.VAR_INDEPENDENT) {
counter++;
}
}
HashMap<String,Variable> vars = new HashMap<>();
for(int i=1;i<=counter;i++) {
vars.put(Symbols.VAR_INDEPENDENT+String.valueOf(i), new Variable(Symbols.VAR_DEPENDENT+String.valueOf(i)));
}
transformIndependentVariableToDependent(vars, T);
return T;
}
public static class UnableToCloneException extends RuntimeException {
public UnableToCloneException(String message) {
super(message);
}
@Override
public synchronized Throwable fillInStackTrace() {
if (Parameters.DEBUG) {
return super.fillInStackTrace();
}
else {
//avoid recording stack trace for efficiency reasons
return this;
}
}
}
public CompoundTerm cloneDeepVariables() {
Term c = clone( cloneVariableTermsDeep() );
if (c == null)
throw new UnableToCloneException("clone(cloneVariableTermsDeep()) resulted in null: " + this);
if (c.getClass()!=getClass())
throw new UnableToCloneException("cloneDeepVariables resulted in different class: " + c + " from " + this);
CompoundTerm cc = (CompoundTerm)c;
cc.setNormalized(isNormalized());
return cc;
}
/** override in subclasses to avoid unnecessary reinit */
/*public CompoundTerm _clone(final Term[] replaced) {
if (Terms.equals(term, replaced)) {
return this;
}
return clone(replaced);
}*/
//TODO not used yet
private Term[] ensureValidComponents(final Term[] components) {
if (components.length < getMinimumRequiredComponents()) {
throw new RuntimeException(getClass().getSimpleName() + " requires >=" + getMinimumRequiredComponents() + " components, invalid argument:" + Arrays.toString(components));
}
//return Collections.unmodifiableList( term );
return components;
}
/**
* default value, override in subclasses.
* may not be enforced yet.
*/
public int getMinimumRequiredComponents() {
return 2;
}
@Override
public int containedTemporalRelations() {
if (containedTemporalRelations == -1) {
containedTemporalRelations = 0;
if ((this instanceof Equivalence) || (this instanceof Implication)) {
int temporalOrder = ((Statement)this).getTemporalOrder();
switch (temporalOrder) {
case TemporalRules.ORDER_FORWARD:
case TemporalRules.ORDER_CONCURRENT:
case TemporalRules.ORDER_BACKWARD:
containedTemporalRelations = 1;
}
}
for (final Term t : term)
containedTemporalRelations += t.containedTemporalRelations();
}
return this.containedTemporalRelations;
}
/**
* build a component list from terms
* @return the component list
*/
public static Term[] termArray(final Term... t) {
return t;
}
public static List<Term> termList(final Term... t) {
return Arrays.asList((Term[])t);
}
/* ----- utilities for oldName ----- */
/**
* default method to make the oldName of the current term from existing
* fields. needs overridden in certain subclasses
*
* @return the oldName of the term
*/
protected CharSequence makeName() {
return makeCompoundName(operator(), term);
}
@Override
public CharSequence name() {
if (this.name == null) {
this.name = makeName();
}
return this.name;
}
// @Override
// public boolean equals(final Object that) {
// if (!(that instanceof CompoundTerm))
// return false;
//
// final CompoundTerm t = (CompoundTerm)that;
// return name().equals(t.name());
//
// /*if (hashCode() != t.hashCode())
// return false;
//
// if (operator() != t.operator())
// return false;
//
// if (size() != t.size())
// return false;
//
// for (int i = 0; i < term.size(); i++) {
// final Term c = term.get(i);
// if (!c.equals(t.componentAt(i)))
// return false;
// }
//
// return true;*/
//
// }
/**
* default method to make the oldName of a compound term from given fields
*
* @param op the term operator
* @param arg the list of term
* @return the oldName of the term
*/
protected static CharSequence makeCompoundName(final NativeOperator op, final Term... arg) {
int size = 1 + 1;
String opString = op.toString();
size += opString.length();
for (final Term t : arg)
size += 1 + t.name().length();
final CharBuffer n = CharBuffer.allocate(size)
.append(COMPOUND_TERM_OPENER.ch).append(opString);
for (final Term t : arg) {
n.append(Symbols.ARGUMENT_SEPARATOR).append(t.name());
}
n.append(COMPOUND_TERM_CLOSER.ch);
return n.compact().toString();
}
/* ----- utilities for other fields ----- */
/**
* report the term's syntactic complexity
*
* @return the complexity value
*/
@Override public short getComplexity() {
return complexity;
}
/**
* Check if the order of the term matters
* <p>
* commutative CompoundTerms: Sets, Intersections Commutative Statements:
* Similarity, Equivalence (except the one with a temporal order)
* Commutative CompoundStatements: Disjunction, Conjunction (except the one
* with a temporal order)
*
* @return The default value is false
*/
public boolean isCommutative() {
return false;
}
/* ----- extend Collection methods to component list ----- */
/**
* get the number of term
*
* @return the size of the component list
*/
final public int size() {
return term.length;
}
/** Gives a set of all contained term, recursively */
public Set<Term> getContainedTerms() {
Set<Term> s = new HashSet(getComplexity());
for (Term t : term) {
s.add(t);
if (t instanceof CompoundTerm)
s.addAll( ((CompoundTerm)t).getContainedTerms() );
}
return s;
}
/**
* Clone the component list
*
* @return The cloned component list
*/
public Term[] cloneTerms(final Term... additional) {
return cloneTermsAppend(term, additional);
}
/**
* Cloned array of Terms, except for one or more Terms.
* @param toRemove
* @return the cloned array with the missing terms removed, OR null if no terms were actually removed when requireModification=true
*/
public Term[] cloneTermsExcept(final boolean requireModification, final Term[] toRemove) {
//TODO if deep, this wastes created clones that are then removed. correct this inefficiency?
List<Term> l = asTermList();
boolean removed = false;
for (final Term t : toRemove) {
if (l.remove(t))
removed = true;
}
if ((!removed) && (requireModification))
return null;
return l.toArray(new Term[l.size()]);
}
/**
* Deep clone an array list of terms
*
* @param original The original component list
* @return an identical and separate copy of the list
*/
public static Term[] cloneTermsAppend(final Term[] original, final Term[] additional) {
if (original == null) {
return null;
}
int L = original.length + additional.length;
if (L == 0)
return original;
//TODO apply preventUnnecessaryDeepCopy to more cases
final Term[] arr = new Term[L];
int i;
int j = 0;
Term[] srcArray = original;
for (i = 0; i < L; i++) {
if (i == original.length) {
srcArray = additional;
j = 0;
}
arr[i] = srcArray[j++];
}
return arr;
}
public List<Term> asTermList() {
ArrayList l = new ArrayList(term.length);
addTermsTo(l);
return l;
}
/** forced deep clone of terms */
public Term[] cloneTermsDeep() {
Term[] l = new Term[term.length];
for (int i = 0; i < l.length; i++)
l[i] = term[i].cloneDeep();
return l;
}
public Term[] cloneVariableTermsDeep() {
Term[] l = new Term[term.length];
for (int i = 0; i < l.length; i++) {
Term t = term[i];
if (t.hasVar()) {
if (t instanceof CompoundTerm) {
t = ((CompoundTerm)t).cloneDeepVariables();
}
else /* it's a variable */
t = t.clone();
}
l[i] = t;
}
return l;
}
/** forced deep clone of terms */
public ArrayList<Term> cloneTermsListDeep() {
ArrayList<Term> l = new ArrayList(term.length);
for (final Term t : term)
l.add(t.clone());
return l;
}
/*static void shuffle(final Term[] list, final Random randomNumber) {
if (list.length < 2) {
return;
}
int n = list.length;
for (int i = 0; i < n; i++) {
// between i and n-1
int r = i + (randomNumber.nextInt() % (n-i));
Term tmp = list[i]; // swap
list[i] = list[r];
list[r] = tmp;
}
}*/
static void shuffle(final Term[] ar,final Random randomNumber)
{
if (ar.length < 2) {
return;
}
for (int i = ar.length - 1; i > 0; i--)
{
int index = randomNumber.nextInt(i + 1);
// Simple swap
Term a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
/**
* Check whether the compound contains a certain component
* Also matches variables, ex: (&&,<a --> b>,<b --> c>) also contains <a --> #1>
* ^^^ is this right? if so then try containsVariablesAsWildcard
*
* @param t The component to be checked
* @return Whether the component is in the compound
*/
@Override
public boolean containsTerm(final Term t) {
return Terms.contains(term, t);
//return Terms.containsVariablesAsWildcard(term, t);
}
/**
* Recursively check if a compound contains a term
*
* @param target The term to be searched
* @return Whether the target is in the current term
*/
@Override
public boolean containsTermRecursively(final Term target) {
if (super.containsTermRecursively(target))
return true;
for (final Term term : term) {
if (term.containsTermRecursively(target)) {
return true;
}
}
return false;
}
/**
* Check whether the compound contains all term of another term, or
that term as a whole
*
* @param t The other term
* @return Whether the term are all in the compound
*/
public boolean containsAllTermsOf(final Term t) {
if (getClass() == t.getClass()) { //(t instanceof CompoundTerm) {
return Terms.containsAll(term, ((CompoundTerm) t).term );
} else {
return Terms.contains(term, t);
}
}
// /**
// * Try to add a component into a compound
// *
// * @param t1 The compound
// * @param t2 The component
// * @param memory Reference to the memory
// * @return The new compound
// */
// public static Term addComponents(final CompoundTerm t1, final Term t2, final Memory memory) {
// if (t2 == null)
// return t1;
//
// boolean success;
// Term[] terms;
// if (t2 instanceof CompoundTerm) {
// terms = t1.cloneTerms(((CompoundTerm) t2).term);
// } else {
// terms = t1.cloneTerms(t2);
// }
// return Memory.make(t1, terms, memory);
// }
/**
* Try to replace a component in a compound at a given index by another one
*
* @param compound The compound
* @param index The location of replacement
* @param t The new component
* @param memory Reference to the memory
* @return The new compound
*/
public Term setComponent(final int index, final Term t, final Memory memory) {
List<Term> list = asTermList();//Deep();
list.remove(index);
if (t != null) {
if (getClass() != t.getClass()) {
list.add(index, t);
} else {
//final List<Term> list2 = ((CompoundTerm) t).cloneTermsList();
Term[] tt = ((CompoundTerm)t).term;
for (int i = 0; i < tt.length; i++) {
list.add(index + i, tt[i]);
}
}
}
return Terms.term(this, list);
}
/* ----- variable-related utilities ----- */
/**
* Whether this compound term contains any variable term
*
* @return Whether the name contains a variable
*/
@Override
public boolean hasVar() {
return hasVariables;
}
@Override
public boolean hasVarDep() {
return hasVarDeps;
}
@Override
public boolean hasVarIndep() {
return hasVarIndeps;
}
@Override
public boolean hasVarQuery() {
return hasVarQueries;
}
// /** caches a static copy of commonly uesd index variables of each variable type */
// public static final int maxCachedVariableIndex = 32;
// public static final Variable[][] varCache = (Variable[][]) Array.newInstance(Variable.class, 3, maxCachedVariableIndex);
//
// public static Variable getIndexVariable(final char type, final int i) {
// int typeI;
// switch (type) {
// case '#': typeI = 0; break;
// case '$': typeI = 1; break;
// case '?': typeI = 2; break;
// default: throw new RuntimeException("Invalid variable type: " + type + ", index " + i);
// }
//
// if (i < maxCachedVariableIndex) {
// Variable existing = varCache[typeI][i];
// if (existing == null)
// existing = varCache[typeI][i] = new Variable(type + String.valueOf(i));
// return existing;
// }
// else
// return new Variable(type + String.valueOf(i));
// }
// /**
// * Recursively rename the variables in the compound
// *
// * @param map The substitution established so far
// * @return an array of terms, normalized; may return the original Term[] array if nothing changed,
// * otherwise a clone of the array will be returned
// */
// public static Term[] normalizeVariableNames(String prefix, final Term[] s, final HashMap<Variable, Variable> map) {
//
// boolean renamed = false;
// Term[] t = s.clone();
// char c = 'a';
// for (int i = 0; i < t.length; i++) {
// final Term term = t[i];
//
//
// if (term instanceof Variable) {
//
// Variable termV = (Variable)term;
// Variable var;
//
// var = map.get(termV);
// if (var == null) {
// //var = getIndexVariable(termV.getType(), map.size() + 1);
// var = new Variable(termV.getType() + /*prefix + */String.valueOf(map.size() + 1));
// }
//
// if (!termV.equals(var)) {
// t[i] = var;
// renamed = true;
// }
//
// map.put(termV, var);
//
// } else if (term instanceof CompoundTerm) {
// CompoundTerm ct = (CompoundTerm)term;
// if (ct.containVar()) {
// Term[] d = normalizeVariableNames(prefix + Character.toString(c), ct.term, map);
// if (d!=ct.term) {
// t[i] = ct.clone(d, true);
// renamed = true;
// }
// }
// }
// c++;
// }
//
// if (renamed) {
// return t;
// }
// else
// return s;
// }
/** NOT TESTED YET */
public boolean containsAnyTermsOf(final Collection<Term> c) {
return Terms.containsAny(term, c);
}
/**
* Recursively apply a substitute to the current CompoundTerm
* May return null if the term can not be created
* @param subs
*/
public Term applySubstitute(final Map<Term, Term> subs) {
if ((subs == null) || (subs.isEmpty())) {
return this;//.clone();
}
Term[] tt = new Term[term.length];
boolean modified = false;
for (int i = 0; i < tt.length; i++) {
Term t1 = tt[i] = term[i];
if (subs.containsKey(t1)) {
Term t2 = subs.get(t1);
while (subs.containsKey(t2)) {
t2 = subs.get(t2);
}
//prevents infinite recursion
if (!t2.containsTerm(t1)) {
tt[i] = t2; //t2.clone();
modified = true;
}
} else if (t1 instanceof CompoundTerm) {
Term ss = ((CompoundTerm) t1).applySubstitute(subs);
if (ss!=null) {
tt[i] = ss;
if (!tt[i].equals(term[i]))
modified = true;
}
}
}
if (!modified)
return this;
if (this.isCommutative()) {
Arrays.sort(tt);
}
return this.clone(tt);
}
/** returns result of applySubstitute, if and only if it's a CompoundTerm.
* otherwise it is null */
public CompoundTerm applySubstituteToCompound(Map<Term, Term> substitute) {
Term t = applySubstitute(substitute);
if (t instanceof CompoundTerm)
return ((CompoundTerm)t);
return null;
}
/* ----- link CompoundTerm and its term ----- */
/**
* Build TermLink templates to constant term and subcomponents
* <p>
* The compound type determines the link type; the component type determines
* whether to build the link.
*
* @return A list of TermLink templates
*/
public ArrayList<TermLink> prepareComponentLinks() {
//complexity seems like an upper bound for the resulting number of componentLinks.
//so use it as an initial size for the array list
final ArrayList<TermLink> componentLinks = new ArrayList<>( getComplexity() );
return Terms.prepareComponentLinks(componentLinks, this);
}
final public void addTermsTo(final Collection<Term> c) {
for (final Term t : term)
c.add(t);
}
@Override
public int hashCode() {
if (!Parameters.TERM_ELEMENT_EQUIVALENCY) {
return name().hashCode();
}
else {
return hash;
}
}
@Override
public int compareTo(final AbstractTerm that) {
if (that==this) return 0;
if (Parameters.TERM_ELEMENT_EQUIVALENCY) {
if (that instanceof CompoundTerm) {
CompoundTerm t = (CompoundTerm)that;
int h = Integer.compare(hashCode(), t.hashCode());
if (h != 0) return h;
int o = operator().compareTo(t.operator());
if (o != 0) return o;
//same operator
int c = Integer.compare(getComplexity(), t.getComplexity());
if (c!=0) return c;
//should almost never reach here, the hashcode above will handle > 99% of comparisons
if (!equals(that)) {
return Integer.compare(System.identityHashCode(this), System.identityHashCode(that));
}
return 0;
}
else
return super.compareTo(that);
}
return
super.compareTo(that);
}
@Override
public boolean equals(final Object that) {
if (that==this) return true;
if (!(that instanceof Term))
return false;
if (Parameters.TERM_ELEMENT_EQUIVALENCY)
return equalsByTerm(that);
return name().equals(((Term)that).name());
}
public boolean equalsByTerm(final Object that) {
if (!(that instanceof CompoundTerm)) return false;
final CompoundTerm t = (CompoundTerm)that;
if (operator() != t.operator())
return false;
if (getComplexity()!= t.getComplexity())
return false;
if (getTemporalOrder()!=t.getTemporalOrder())
return false;
if (!equals2(t))
return false;
if (term.length!=t.term.length)
return false;
for (int i = 0; i < term.length; i++) {
if (!term[i].equals(t.term[i]))
return false;
}
return true;
}
/** additional equality checks, in subclasses*/
public boolean equals2(final CompoundTerm other) {
return true;
}
// /** may be overridden in subclass to include other details */
// protected int calcHash() {
// //return Objects.hash(operator(), Arrays.hashCode(term), getTemporalOrder());
// return name().hashCode();
// }
//
// /**
// * Orders among terms: variable < atomic < compound
// *
// * @param that The Term to be compared with the current Term
//\ * @return The order of the two terms
// */
// @Override
// public int compareTo(final AbstractTerm that) {
// if (this == that) return 0;
//
// if (that instanceof CompoundTerm) {
// final CompoundTerm t = (CompoundTerm) that;
// if (size() == t.size()) {
// int opDiff = this.operator().ordinal() - t.operator().ordinal(); //should be faster faster than Enum.compareTo
// if (opDiff != 0) {
// return opDiff;
// }
//
// int tDiff = this.getTemporalOrder() - t.getTemporalOrder(); //should be faster faster than Enum.compareTo
// if (tDiff != 0) {
// return tDiff;
// }
//
// for (int i = 0; i < term.length; i++) {
// final int diff = term[i].compareTo(t.term[i]);
// if (diff != 0) {
// return diff;
// }
// }
//
// return 0;
// } else {
// return size() - t.size();
// }
// } else {
// return 1;
// }
// }
/*
@Override
public boolean equals(final Object that) {
return (that instanceof Term) && (compareTo((Term) that) == 0);
}
*/
//
//
//
//
// /**
// * Orders among terms: variable < atomic < compound
// *
// * @param that The Term to be compared with the current Term
//\ * @return The order of the two terms
// */
// @Override
// public int compareTo(final Term that) {
// /*if (!(that instanceof CompoundTerm)) {
// return getClass().getSimpleName().compareTo(that.getClass().getSimpleName());
// }
// */
// return -name.compareTo(that.name());
// /*
// if (size() == t.size()) {
// int opDiff = this.operator().ordinal() - t.operator().ordinal(); //should be faster faster than Enum.compareTo
// if (opDiff != 0) {
// return opDiff;
// }
//
// for (int i = 0; i < term.length; i++) {
// final int diff = term[i].compareTo(t.term[i]);
// if (diff != 0) {
// return diff;
// }
// }
//
// return 0;
// } else {
// return size() - t.size();
// }
// } else {
// return 1;
// */
// }
public void setNormalized(boolean b) {
this.normalized = b;
}
public boolean isNormalized() {
return normalized;
}
/** compare subterms where any variables matched are not compared */
public boolean equalsVariablesAsWildcards(final CompoundTerm c) {
if (operator()!=c.operator()) return false;
if (size()!=c.size()) return false;
for (int i = 0; i < size(); i++) {
Term a = term[i];
Term b = c.term[i];
if ((a instanceof Variable) && (a.hasVarDep()) ||
((b instanceof Variable) && (b.hasVarDep())))
continue;
if (!a.equals(b)) return false;
}
return true;
}
public Term[] cloneTermsReplacing(Term from, Term to) {
Term[] y = new Term[term.length];
int i = 0;
for (Term x : term) {
if (x.equals(from))
x = to;
y[i++] = x;
}
return y;
}
@Override
public Iterator<Term> iterator() {
return Iterators.forArray(term);
}
}