package models.algebra;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
public class Term extends Expression {
protected Symbol symbol = null;
protected List<Expression> children = new ArrayList<>();
protected Type type = null;
public Term(Symbol symbol) {
super();
this.symbol = symbol;
}
public Term(Symbol symbol, List<Expression> children) {
super();
this.symbol = symbol;
this.children = children;
}
public Term(Symbol symbol, Expression[] children) {
super();
this.symbol = symbol;
this.children = new ArrayList<>(Arrays.asList(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 boolean setChild(int n, Expression child) {
if (getArity() != -1 && n >= getArity()) return false;
children.set(n, 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 List<Expression> getChildren() {
return children;
}
public <T extends Expression> HashMap<Position, T> getSubTerms(Class<T> clazz) {
HashMap<Position, T> subTerms = new HashMap<>();
Class thisClass = this.getClass();
while (thisClass != null) {
if (clazz == thisClass) {
subTerms.put(new Position(), (T) this);
break;
}
thisClass = thisClass.getSuperclass();
}
for (int i = 0; i < children.size(); i++) {
if (children.get(i) != null) {
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 (this instanceof Constant) return (Expression) this.clone();
if (another instanceof Constant) return (Expression) another.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;
}
}
public Expression reduce() {
if (symbol.isLambda()) {
// Lambda beta-reduction
LambdaAbstraction newSymbol = ((LambdaAbstraction) symbol);
Term newTerm = newSymbol.getTerm();
List<Variable> newVariables = newSymbol.getVariables();
List<Expression> newChildren = children;
while (newVariables.size() > 0 && newChildren.size() > 0) {
newTerm = newTerm.substitute(newVariables.get(0), newChildren.get(0));
newVariables = newVariables.subList(1, newVariables.size());
newChildren = newChildren.subList(1, newChildren.size());
newSymbol = new LambdaAbstraction(newVariables, newTerm);
}
if (newSymbol.arity == 0 && newChildren.size() == 0) {
return newTerm;
} else {
return new Term(newSymbol, newChildren);
}
} else if (symbol.isCalculatable()) {
List<Expression> newChildren = new ArrayList<>();
for (Expression child: children) {
if (child instanceof Term) {
child = ((Term) child).reduce();
}
newChildren.add(child);
}
Expression newTerm = symbol.calculate(newChildren);
if (newTerm == null) return this;
return newTerm;
} else {
// Calculate inverse map
List<Expression> newChildren = new ArrayList<>();
boolean bReduced = false;
for (Expression child: children) {
if (child instanceof Term && !(child instanceof Constant)) {
Expression newChild = ((Term) (child)).reduce();
if (newChild != child) {
bReduced = true;
child = newChild;
}
}
newChildren.add(child);
}
if (symbol.arity == 1 && newChildren.size() == 1) {
Expression child = newChildren.get(0);
if (child instanceof Term && !(child instanceof Constant)) {
Symbol childSymbol = ((Term) child).getSymbol();
if (childSymbol.getInverses() != null) {
for (int i = 0; i < childSymbol.getInverses().length; i++) {
if (symbol.equals(childSymbol.getInverses()[i])) {
return ((Term) child).getChild(i);
}
}
}
}
}
if (!bReduced) return this;
Term newTerm = (Term) this.clone();
newTerm.children = newChildren;
return newTerm;
}
}
@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) {
if (e != null) {
newTerm.addChild((Expression) e.clone());
} else {
newTerm.addChild(null);
}
}
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 || 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) {
if (e != null) {
exp += (delimiter + e.toString());
} else {
exp += (delimiter + "null");
}
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];
String receiver = "";
if (implParamOrder == null) {
receiver = children.get(0).toImplementation(sideEffects);
exp = exp.replace(params[0], receiver);
for (int i = 1; i < params.length; i++) {
exp = exp.replace(params[i], children.get(i).toImplementation(sideEffects));
}
} else {
receiver = children.get(implParamOrder[0]).toImplementation(sideEffects);
exp = exp.replace(params[0], receiver);
for (int i = 1; i < params.length; i++) {
exp = exp.replace(params[i], children.get(implParamOrder[i]).toImplementation(sideEffects));
}
}
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp + ";\n";
exp = receiver;
}
return exp;
}
if (symbol.isImplGenerative()) {
Type childrenTypes[] = new Type[children.size()];
String childrenImpl[] = new String[children.size()];
String childrenSideEffects[] = new String[children.size()];
if (implParamOrder == null) {
for (int i = 0; i < children.size(); i++) {
Expression child = children.get(i);
if (child instanceof Variable) {
childrenTypes[i] = ((Variable) child).getType();
} else if (child instanceof Term) {
childrenTypes[i] = ((Term) child).getType();
}
String childSideEffect[] = new String[] {""};
childrenImpl[i] = child.toImplementation(childSideEffect);
childrenSideEffects[i] = childSideEffect[0];
}
String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects);
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp;
exp = childrenImpl[0]; // the value of this term
}
return exp;
} else {
for (int i = 0; i < children.size(); i++) {
Expression child = children.get(implParamOrder[i]);
if (child instanceof Variable) {
childrenTypes[i] = ((Variable) child).getType();
} else if (child instanceof Term) {
childrenTypes[i] = ((Term) child).getType();
}
String childSideEffect[] = new String[] {""};
childrenImpl[i] = child.toImplementation(childSideEffect);
childrenSideEffects[i] = childSideEffect[0];
}
String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects);
if (symbol.isImplWithSideEffect()) {
sideEffects[0] = sideEffects[0] + exp;
exp = childrenImpl[0]; // the value of this term
}
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 || getArity() == -1) && symbol.isImplMethod()) {
if (implParamOrder == null) {
String exp = null;
String receiver = "";
if (children.get(0) != null) {
receiver = children.get(0).toImplementation(sideEffects);
exp = receiver + "." + symbol.toImplementation() + "(";
} else {
exp = 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 + ";\n";
exp = receiver;
}
return exp;
} else {
String receiver = children.get(implParamOrder[0]).toImplementation(sideEffects);
String exp = receiver + "." + 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 + ";\n";
exp = receiver;
}
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 + ")";
}
}
}
}