/*
* Variable.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 java.nio.CharBuffer;
import nars.config.Parameters;
import static nars.io.Symbols.VAR_DEPENDENT;
import static nars.io.Symbols.VAR_INDEPENDENT;
import static nars.io.Symbols.VAR_QUERY;
import nars.io.Texts;
import static nars.language.Variable.newName;
/**
* A variable term, which does not correspond to a concept
*/
public class Variable extends Term {
/** caches the type character for faster lookup than charAt(0) */
private transient char type = 0;
private Term scope;
private transient int hash;
public Variable(final CharSequence name) {
this(name, null);
}
/**
* Constructor, from a given variable name
*
* @param name A String read from input
*/
protected Variable(final CharSequence name, final Term scope) {
super();
setScope(scope, name);
}
@Override protected void setName(CharSequence newName) { }
public Variable setScope(final Term scope, final CharSequence n) {
this.name = n;
this.type = n.charAt(0);
this.scope = scope != null ? scope : this;
this.hash = 0; //calculate lazily
if (!validVariableType(type))
throw new RuntimeException("Invalid variable type: " + n);
return this;
}
/**
* Clone a Variable
*
* @return The cloned Variable
*/
@Override
public Variable clone() {
Variable v = new Variable(name(), scope);
if (scope == this)
v.scope = v;
return v;
}
/**
* Get the type of the variable
*
* @return The variable type
*/
public char getType() {
return type;
}
/**
* A variable is not constant
*
* @return false
*/
@Override
public boolean isConstant() {
return false;
}
/**
* The syntactic complexity of a variable is 0, because it does not refer to
* any concept.
*
* @return The complexity of the term, an integer
*/
@Override public short getComplexity() {
return 0;
}
@Override public boolean hasVar() {
return true;
}
@Override public boolean hasVarIndep() {
return isIndependentVariable();
}
@Override public boolean hasVarDep() {
return isDependentVariable();
}
@Override public boolean hasVarQuery() {
return isQueryVariable();
}
@Override public boolean equals(final Object that) {
if (that == this) return true;
if (!(that instanceof Variable)) return false;
if (Parameters.TERM_ELEMENT_EQUIVALENCY) {
return equalsTerm(that);
}
else {
Variable v = (Variable)that;
if (!name().equals(v.name())) return false;
if (getScope() == this) {
if (v.getScope()!=v) return false;
}
return (v.getScope().name().equals(getScope().name()));
}
}
public boolean equalsTerm(Object that) {
//TODO factor these comparisons into 2 nested if's
Variable v = (Variable)that;
if ((v.scope == v) && (scope == this))
//both are unscoped, so compare by name only
return name().equals(v.name());
else if ((v.scope!=v) && (scope==this))
return false;
else if ((v.scope==v) && (scope!=this))
return false;
else {
if (!name().equals(v.name()))
return false;
if (scope == v.scope) return true;
if (scope.hashCode()!=v.scope.hashCode())
return false;
//WARNING infinnite loop can happen if the two scopes start equaling echother
//we need a special equals comparison which ignores variable scope when recursively
//called from this
//until then, we'll use the name for comparison because it wont
//invoke infinite recursion
return scope.name().equals(v.scope.name());
}
}
@Override
public int hashCode() {
if (hash == 0) {
if (scope!=this)
this.hash = 31 * name.hashCode() + scope.hashCode();
else
this.hash = name.hashCode();
}
return hash;
}
/**
* variable terms are listed first alphabetically
*
* @param that The Term to be compared with the current Term
* @return The same as compareTo as defined on Strings
*/
/*@Override
public final int compareTo(final AbstractTerm that) {
return (that instanceof Variable) ? ((Comparable)name()).compareTo(that.name()) : -1;
}*/
boolean isQueryVariable() { return getType() == VAR_QUERY; }
boolean isDependentVariable() { return getType() == VAR_DEPENDENT; }
boolean isIndependentVariable() { return getType() == VAR_INDEPENDENT; }
boolean isCommon() {
CharSequence n = name();
int l = n.length();
return n.charAt(l - 1) == '$';
}
public Term getScope() {
return scope;
}
//ported back from 1.7, sehs addition
public static int compare(final Variable a, final Variable b) {
//int i = a.name().compareTo(b.name());
int i=Texts.compareTo(a.name(), b.name());
if (i == 0) {
boolean ascoped = a.scope!=a;
boolean bscoped = b.scope!=b;
if (!ascoped && !bscoped) {
//if the two variables are each without scope, they are not equal.
//so use their identityHashCode to determine a stable ordering
int as = System.identityHashCode(a.scope);
int bs = System.identityHashCode(b.scope);
return Integer.compare(as, bs);
}
else if (ascoped && !bscoped) {
return -1;
}
else if (bscoped && !ascoped) {
return 1;
}
else {
return Texts.compareTo(a.getScope().name(), b.getScope().name());
// return Texts.compare(a.getScope().name(), b.getScope().name());
}
}
return i;
}
public static boolean validVariableType(final char c) {
return (c == VAR_QUERY) || (c == VAR_DEPENDENT) || (c == VAR_INDEPENDENT);
}
private static final int MAX_CACHED_VARNAME_INDEXES = 64;
private static final CharSequence[] vn1 = new CharSequence[MAX_CACHED_VARNAME_INDEXES];
private static final CharSequence[] vn2 = new CharSequence[MAX_CACHED_VARNAME_INDEXES];
private static final CharSequence[] vn3 = new CharSequence[MAX_CACHED_VARNAME_INDEXES];
public static CharSequence getName(char type, int index) {
if (index > MAX_CACHED_VARNAME_INDEXES)
return newName(type, index);
CharSequence[] cache;
switch (type) {
case VAR_INDEPENDENT: cache = vn1; break;
case VAR_DEPENDENT: cache = vn2; break;
case VAR_QUERY: cache = vn3; break;
default:
throw new RuntimeException("Invalid variable type");
}
CharSequence c = cache[index];
if (c == null) {
c = newName(type, index);
cache[index] = c;
}
return c;
}
protected static CharSequence newName(char type, int index) {
int digits = (index >= 256 ? 3 : ((index >= 16) ? 2 : 1));
CharBuffer cb = CharBuffer.allocate(1 + digits).append(type);
do {
cb.append( Character.forDigit(index % 16, 16) ); index /= 16;
} while (index != 0);
return cb.compact().toString();
}
}