/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.constraint.xnative;
import x10.constraint.XFormula;
import x10.constraint.XTerm;
import x10.constraint.XVar;
import x10.util.CollectionFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A representation of an atomic formula op(t1,..., tn).
*
* @author vj
*
*/
public class XNativeFormula<T> extends XNativeTerm implements XFormula<T> {
private static final long serialVersionUID = -124049106759891751L;
public final T op;
public final T asExprOp;
public XNativeTerm[] arguments;
public final boolean isAtomicFormula;
@Override
public XNativeTerm accept(TermVisitor visitor) {
XNativeTerm res = (XNativeTerm)visitor.visit(this);
if (res!=null) return res;
XNativeTerm[] newArgs = new XNativeTerm[arguments.length];
boolean wasNew = false;
for (int i = 0; i < arguments.length; ++i ) {
XNativeTerm xTerm = arguments[i];
final XNativeTerm newArg = xTerm.accept(visitor);
wasNew |= newArg!=xTerm;
newArgs[i] = newArg;
}
if (!wasNew) return this;
XNativeFormula<T> newThis = (XNativeFormula<T>)this.clone();
newThis.arguments = newArgs;
return newThis;
}
/**
* Create a formula with the given op and given list of arguments.
* @param op
* @param args
*/
public XNativeFormula(T op, T opAsExpr, boolean isAtomicFormula, XTerm... args) {
this.op = op;
this.asExprOp = opAsExpr;
this.isAtomicFormula = isAtomicFormula;
XNativeTerm[] newargs = new XNativeTerm[args.length];
for (int i = 0; i < args.length; ++i) {
newargs[i] = (XNativeTerm)args[i];
}
this.arguments = newargs;
}
@Override
public boolean isAtomicFormula() {
return isAtomicFormula;
}
@Override
public XTermKind kind() { return XTermKind.FN_APPLICATION;}
@Override
public List<XNativeEQV> eqvs() {
List<XNativeEQV> eqvs = new ArrayList<XNativeEQV>();
for (XNativeTerm arg : arguments) {
eqvs.addAll(arg.eqvs());
}
return eqvs;
}
@Override
public XNativeTerm subst(XTerm y, XVar x) {
return subst((XNativeTerm)y, x, true);
}
@Override
public XNativeTerm subst(XTerm y, XVar x, boolean propagate) {
XNativeTerm[] newArgs = new XNativeTerm[this.arguments().length];
boolean changed = false;
for (int i = 0; i < arguments().length; ++i) {
XNativeTerm arg = arguments[i];
if (arg == null)
assert arg != null : "Argument to a formula cannot be null.";
XNativeTerm a = arg.subst(y, x, propagate);
if (a != arg)
changed = true;
newArgs[i] = a;
}
XNativeFormula<?> n = (XNativeFormula<?>) super.subst(y, x, propagate);
if (! changed) return n;
if (n == this) n = (XNativeFormula<?>) clone();
n.arguments = newArgs;
return n;
}
@Override
public T operator() {
return op;
}
@Override
public boolean isUnary() {
return arguments.length == 1;
}
@Override
public boolean isBinary() {
return arguments.length == 2;
}
@Override
public XNativeTerm unaryArg() {
if (isUnary())
return arguments[0];
return null;
}
@Override
public XNativeTerm left() {
if (isBinary())
return arguments[0];
return null;
}
@Override
public XNativeTerm right() {
if (isBinary())
return arguments[1];
return null;
}
@Override
public XNativeTerm[] arguments() { return arguments; }
@Override
public boolean hasVar(XVar v) {
for (XNativeTerm arg : arguments)
if (arg.hasVar(v)) return true;
return false;
}
@Override
public XPromise internIntoConstraint(XNativeConstraint c, XPromise last) {
assert last == null;
// Evaluate left == right, if both are literals.
XPromise result = nfp(c);
if (result != null) return result; // this term has already been interned.
Map<Object, XPromise> fields = CollectionFactory.newHashMap();
for (int i = 0; i < arguments.length; i++) {
XNativeTerm arg = arguments[i];
if (arg == null) continue;
if (c == null) return null;
XPromise child = c.intern(arg);
fields.put(new Integer(i), child);
}
// C_Local_c v = new C_Local_c(op);
XNativeTerm v = this;
// fields.put(new C_NameWrapper<Integer>(-1), c.intern(v));
// create a new promise and return it.
XPromise p = new XPromise(fields, v);
c.addPromise(v, p);
result = p;
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(op);
sb.append("(");
String sep = "";
for (XNativeTerm arg : arguments) {
sb.append(sep);
sep = ", ";
sb.append(arg);
}
sb.append(")");
return sb.toString();
}
@Override
public int hashCode() {
int hash = 29;
for (XNativeTerm arg: arguments)
if (arg != null) hash += arg.hashCode();
return hash;
}
@Override
public boolean hasEQV() {
for (XNativeTerm arg: arguments) if (arg.hasEQV()) return true;
return false;
}
/**
* An XFormula is equal t another object if it is == to it, or the other object
* is an XFormula with equal ops and equal args.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof XNativeFormula) {
XNativeFormula<?> c = (XNativeFormula<?>) o;
if (! c.op.equals(op)) return false;
if (c.arguments().length == arguments().length) {
for (int i = 0; i < arguments().length; i++) {
XNativeTerm ti = arguments()[i];
XNativeTerm ci = c.arguments()[i];
if (! ti.equals(ci)) return false;
}
return true;
}
}
return false;
}
@Override
public boolean okAsNestedTerm() { return false;}
@Override
public XPromise nfp(XNativeConstraint c) {
assert c!=null;
XPromise p;
if (c.roots == null) {
c.roots = CollectionFactory.<XNativeTerm, XPromise> newHashMap();
p = new XPromise(this);
c.roots.put(this, p);
return p;
} else {
p = c.roots.get(this);
if (p == null) {
p = new XPromise(this);
c.roots.put(this, p);
return p;
}
}
return p.lookup();
}
@Override
public T asExprOperator() {
return asExprOp;
}
}