package models.algebra;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
public class Term extends Expression {
protected Symbol symbol = null;
protected ArrayList<Expression> children = new ArrayList<>();
protected Type type = null;
public Term(Symbol symbol) {
super();
this.symbol = symbol;
}
public Term(Symbol symbol, ArrayList<Expression> children) {
super();
this.symbol = symbol;
this.children = children;
}
public Symbol getSymbol() {
return symbol;
}
public int getArity() {
return symbol.getArity();
}
public void setType(Type type) {
this.type = type;
}
public Type getType() {
if (type == null) {
if (symbol.getSignature() == null) return null;
return symbol.getSignature()[0];
}
return type;
}
public boolean addChild(Expression child) {
if (getArity() != -1 && children.size() >= getArity()) return false;
children.add(child);
return true;
}
public void addChild(Expression child, boolean bForced) {
if (!bForced && getArity() != -1 && children.size() >= getArity()) return;
children.add(child);
}
public Expression getChild(int n) {
return children.get(n);
}
public ArrayList<Expression> getChildren() {
return children;
}
public <T extends Expression> HashMap<Position, T> getSubTerms(Class<T> clazz) {
HashMap<Position, T> subTerms = new HashMap<>();
if (clazz == this.getClass()) {
subTerms.put(new Position(), (T) this);
}
for (int i = 0; i < children.size(); i++) {
HashMap<Position, T> terms = children.get(i).getSubTerms(clazz);
for (Entry<Position, T> term: terms.entrySet()) {
Position pos = term.getKey();
pos.addHeadOrder(i);
subTerms.put(pos, term.getValue());
}
}
return subTerms;
}
public Expression getSubTerm(Position pos) {
if (pos.isEmpty()) return this;
pos = (Position) pos.clone();
int i = pos.removeHeadOrder();
if (i >= children.size()) return null;
return children.get(i).getSubTerm(pos);
}
public Term substitute(Variable variable, Expression value) {
Term newTerm = (Term) this.clone();
HashMap<Position, Variable> variables = getVariables();
for (Entry<Position, Variable> varEnt: variables.entrySet()) {
if (varEnt.getValue().equals(variable)) {
newTerm.replaceSubTerm(varEnt.getKey(), value);
}
}
return newTerm;
}
public void replaceSubTerm(Position pos, Expression newSubTerm) {
if (pos.isEmpty()) return;
pos = (Position) pos.clone();
int i = pos.removeHeadOrder();
if (pos.isEmpty()) {
children.set(i, newSubTerm);
} else {
if (!(children.get(i) instanceof Term)) return;
((Term) children.get(i)).replaceSubTerm(pos, newSubTerm);
}
}
@Override
public Expression unify(Expression another) {
if (another instanceof Variable) return (Expression) this.clone();
if (another instanceof Term) {
Term anotherTerm = (Term) another;
if (!symbol.equals(anotherTerm.symbol)) return null;
if (children.size() != anotherTerm.children.size()) return null;
Term unifiedTerm = new Term(symbol);
for (int i = 0; i < children.size(); i++) {
unifiedTerm.addChild(children.get(i).unify(anotherTerm.children.get(i)));
}
return unifiedTerm;
} else {
return null;
}
}
@Override
public Expression getInverseMap(Expression outputValue, Position targetPos) {
if (targetPos.isEmpty()) return outputValue;
targetPos = (Position) targetPos.clone();
int i = targetPos.removeHeadOrder();
Symbol[] inverseSymbols = symbol.getInverses();
if (inverseSymbols == null || i >= inverseSymbols.length || inverseSymbols[i] == null) return null;
Term inverseMap = new Term(inverseSymbols[i]);
inverseMap.addChild(outputValue);
for (int n = 0; n < inverseSymbols[i].getArity(); n++) {
if (n != i) {
inverseMap.addChild(children.get(n));
}
}
return children.get(i).getInverseMap(inverseMap, targetPos);
}
@Override
public boolean contains(Expression exp) {
if (equals(exp)) return true;
for (Expression e: children) {
if (e.contains(exp)) return true;
}
return false;
}
@Override
public boolean equals(Object another) {
if (!(another instanceof Term)) return false;
if (this == another) return true;
Term anotherTerm = (Term) another;
if (!symbol.equals(anotherTerm.symbol)) return false;
if (children.size() != anotherTerm.children.size()) return false;
if (type != anotherTerm.type) return false;
for (int i = 0; i < children.size(); i++) {
Expression e = children.get(i);
Expression e2 = anotherTerm.children.get(i);
if (!e.equals(e2)) return false;
}
return true;
}
@Override
public int hashCode() {
return symbol.hashCode();
}
@Override
public Object clone() {
Term newTerm = new Term(symbol);
for (Expression e: children) {
newTerm.addChild((Expression) e.clone());
}
newTerm.type = type;
return newTerm;
}
public String toString() {
if (getArity() == 2 && symbol.isInfix()) {
return "(" + children.get(0) + symbol.toString() + children.get(1) + ")";
}
if (getArity() >= 1 && symbol.isMethod()) {
String exp = children.get(0).toString() + "." + symbol.toString() + "(";
String delimiter = "";
for (int i = 1; i < children.size(); i++) {
Expression e = children.get(i);
exp += (delimiter + e.toString());
delimiter = ",";
}
return exp + ")";
} else {
String exp = symbol.toString() + "(";
String delimiter = "";
for (Expression e: children) {
exp += (delimiter + e.toString());
delimiter = ",";
}
return exp + ")";
}
}
public String toImplementation(String[] sideEffects) {
int[] implParamOrder = symbol.getImplParamOrder();
if (symbol.isImplLambda()) {
String[] components = symbol.getImplName().split("->");
String component0 = components[0].replace("(", "").replace(")", "");
String[] params = component0.split(",");
String exp = components[1];
if (implParamOrder == null) {
for (int i = 0; i < params.length; i++) {
exp = exp.replace(params[i], children.get(i).toImplementation(sideEffects));
}
} else {
for (int i = 0; i < params.length; i++) {
exp = exp.replace(params[i], children.get(implParamOrder[i]).toImplementation(sideEffects));
}
}
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp + ";";
if (implParamOrder == null) {
exp = children.get(0).toImplementation(new String[] {""});
} else {
exp = children.get(implParamOrder[0]).toImplementation(new String[] {""});
}
}
return exp;
}
if (symbol.isImplGenerative()) {
String childrenImpl[] = new String[children.size()];
if (implParamOrder == null) {
for (int i = 0; i < children.size(); i++) {
childrenImpl[i] = children.get(i).toImplementation(sideEffects);
}
String exp = symbol.generate(getType(), childrenImpl, sideEffects);
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp;
exp = children.get(0).toImplementation(new String[] {""});
}
return exp;
} else {
for (int i = 0; i < children.size(); i++) {
childrenImpl[i] = children.get(implParamOrder[i]).toImplementation(sideEffects);
}
String exp = symbol.generate(getType(), childrenImpl, sideEffects);
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp;
exp = children.get(implParamOrder[0]).toImplementation(new String[] {""});
}
return exp;
}
}
if (getArity() == 2 && symbol.isImplInfix()) {
if (implParamOrder == null) {
return "(" + children.get(0).toImplementation(sideEffects) + symbol.toImplementation() + children.get(1).toImplementation(sideEffects) + ")";
} else {
return "(" + children.get(implParamOrder[0]).toImplementation(sideEffects) + symbol.toImplementation() + children.get(implParamOrder[1]).toImplementation(sideEffects) + ")";
}
}
if (getArity() >= 1 && symbol.isImplMethod()) {
if (implParamOrder == null) {
String exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "(";
String delimiter = "";
for (int i = 1; i < children.size(); i++) {
Expression e = children.get(i);
exp += (delimiter + e.toImplementation(sideEffects));
delimiter = ",";
}
exp += ")";
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp + ";";
exp = children.get(0).toImplementation(new String[] {""});
}
return exp;
} else {
String exp = children.get(implParamOrder[0]).toImplementation(sideEffects) + "." + symbol.toImplementation() + "(";
String delimiter = "";
for (int i = 1; i < children.size(); i++) {
Expression e = children.get(implParamOrder[i]);
exp += (delimiter + e.toImplementation(sideEffects));
delimiter = ",";
}
exp += ")";
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp + ";";
exp = children.get(implParamOrder[0]).toImplementation(new String[] {""});
}
return exp;
}
} else {
if (implParamOrder == null) {
String exp = symbol.toImplementation() + "(";
String delimiter = "";
for (Expression e: children) {
exp += (delimiter + e.toImplementation(sideEffects));
delimiter = ",";
}
return exp + ")";
} else {
String exp = symbol.toImplementation() + "(";
String delimiter = "";
for (int i = 0; i < children.size(); i++) {
Expression e = children.get(implParamOrder[i]);
exp += (delimiter + e.toImplementation(sideEffects));
delimiter = ",";
}
return exp + ")";
}
}
}
}