package org.archstudio.prolog.engine;
import java.util.Map.Entry;
import java.util.Set;
import org.archstudio.prolog.term.ComplexTerm;
import org.archstudio.prolog.term.ListTerm;
import org.archstudio.prolog.term.Term;
import org.archstudio.prolog.term.VariableTerm;
import com.google.common.collect.Sets;
/*
* A unifer based on http://www.univ-orleans.fr/lifo/software/stdprolog/unification.html
*/
public class MostGeneralUnifierEngine implements UnificationEngine {
@Override
public boolean unifies(ProofContext proofContext, UnificationContext context) {
for (int i = 0; i < context.equations.size(); i++) {
Equation e = context.equations.get(i);
Term f = e.term1;
Term g = e.term2;
// 2 & 3
if (f.equals(g)) {
continue;
}
// 5
if (g instanceof VariableTerm && !(f instanceof VariableTerm)) {
Term t = f;
f = g;
g = t;
context.equations.set(i, new Equation(f, g));
// fall through
}
// 6
if (f instanceof VariableTerm) {
VariableTerm v = (VariableTerm) f;
if (!PrologUtils.DONT_CARE_VARIABLE.equals(v) && !PrologUtils.DONT_CARE_VARIABLE.equals(g)) {
context.variables.put(v, g);
for (int j = i + 1; j < context.equations.size(); j++) {
Equation e2 = context.equations.get(j);
context.equations.set(j, new Equation(e2.term1.resolve(proofContext, context.variables),
e2.term2.resolve(proofContext, context.variables)));
}
}
continue;
}
// 4
if (f instanceof ComplexTerm) {
if (!(g instanceof ComplexTerm)) {
// 1.2b
return false;
}
ComplexTerm c = (ComplexTerm) f;
ComplexTerm d = (ComplexTerm) g;
if (!c.getFunctor().equals(d.getFunctor())) {
// 1.3
return false;
}
if (c.getArity() != d.getArity()) {
// 1.4
return false;
}
for (int j = 0; j < c.getArity(); j++) {
// 4
context.equations.add(new Equation(c.getTerm(j), d.getTerm(j)));
}
continue;
}
// added
if (f instanceof ListTerm) {
if (!(g instanceof ListTerm)) {
return false;
}
ListTerm c = (ListTerm) f;
ListTerm d = (ListTerm) g;
if (c.isEmpty()) {
if (d.isEmpty()) {
continue;
}
return false;
}
if (d.isEmpty()) {
return false;
}
context.equations.add(new Equation(c.getHead(), d.getHead()));
context.equations.add(new Equation(c.getTail(), d.getTail()));
continue;
}
if (g instanceof ComplexTerm && !(f instanceof VariableTerm)) {
// 1.2a
return false;
}
// 1.1
return false;
}
// Note: This is necessary otherwise, intermediate variables do not get resolved
// and results (especially with lists) end up looking weird and incomplete.
// FIXME: check for loops, self references, and do not simplify when those occur
for (Entry<VariableTerm, Term> entry : context.variables.entrySet()) {
Term value = entry.getValue();
int tries = 0;
Set<Term> seenTerms = Sets.newHashSet();
while (seenTerms.add(value) && tries++ < 20) {
value = value.resolve(proofContext, context.variables);
entry.setValue(value);
}
}
return true;
}
}