package org.archstudio.prolog.engine;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import org.archstudio.prolog.op.Executable;
import org.archstudio.prolog.op.iso.Abs;
import org.archstudio.prolog.op.iso.Add;
import org.archstudio.prolog.op.iso.AlphaGreaterThan;
import org.archstudio.prolog.op.iso.AlphaGreaterThanEqual;
import org.archstudio.prolog.op.iso.AlphaLessThan;
import org.archstudio.prolog.op.iso.AlphaLessThanEqual;
import org.archstudio.prolog.op.iso.BagOf;
import org.archstudio.prolog.op.iso.Conjunction;
import org.archstudio.prolog.op.iso.Cut;
import org.archstudio.prolog.op.iso.Disjunction;
import org.archstudio.prolog.op.iso.Equals;
import org.archstudio.prolog.op.iso.False;
import org.archstudio.prolog.op.iso.FindAll;
import org.archstudio.prolog.op.iso.IfThen;
import org.archstudio.prolog.op.iso.Is;
import org.archstudio.prolog.op.iso.IsAtom;
import org.archstudio.prolog.op.iso.IsAtomic;
import org.archstudio.prolog.op.iso.IsCallable;
import org.archstudio.prolog.op.iso.IsCompound;
import org.archstudio.prolog.op.iso.IsFloat;
import org.archstudio.prolog.op.iso.IsInteger;
import org.archstudio.prolog.op.iso.IsNonvar;
import org.archstudio.prolog.op.iso.IsNumber;
import org.archstudio.prolog.op.iso.IsVar;
import org.archstudio.prolog.op.iso.Length;
import org.archstudio.prolog.op.iso.Multiply;
import org.archstudio.prolog.op.iso.Neck;
import org.archstudio.prolog.op.iso.Not;
import org.archstudio.prolog.op.iso.NotEquals;
import org.archstudio.prolog.op.iso.NotUnifiable;
import org.archstudio.prolog.op.iso.SetOf;
import org.archstudio.prolog.op.iso.SoftCut;
import org.archstudio.prolog.op.iso.Sort;
import org.archstudio.prolog.op.iso.Subtract;
import org.archstudio.prolog.op.iso.True;
import org.archstudio.prolog.op.iso.Unifiable;
import org.archstudio.prolog.op.iso.ValueEquals;
import org.archstudio.prolog.op.iso.ValueGreaterThan;
import org.archstudio.prolog.op.iso.ValueGreaterThanEqual;
import org.archstudio.prolog.op.iso.ValueLessThan;
import org.archstudio.prolog.op.iso.ValueLessThanEqual;
import org.archstudio.prolog.op.iso.ValueNotEquals;
import org.archstudio.prolog.op.iso.Write;
import org.archstudio.prolog.op.iso.WriteLine;
import org.archstudio.prolog.op.iso.WriteNewLine;
import org.archstudio.prolog.term.ComplexTerm;
import org.archstudio.prolog.term.ConstantTerm;
import org.archstudio.prolog.term.ListTerm;
import org.archstudio.prolog.term.Term;
import org.archstudio.prolog.term.VariableTerm;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class ProofContext implements Cloneable {
private static final Map<String, Class<? extends Term>> operations = Maps.newHashMap();
{
// register the ISO operations
operations.put("+/2", Add.class);
operations.put("@>/2", AlphaGreaterThan.class);
operations.put("@>=/2", AlphaGreaterThanEqual.class);
operations.put("@</2", AlphaLessThan.class);
operations.put("@=</2", AlphaLessThanEqual.class);
operations.put(",/2", Conjunction.class);
operations.put("!/0", Cut.class);
operations.put(";/2", Disjunction.class);
operations.put("==/2", Equals.class);
operations.put("->/2", IfThen.class);
operations.put("./2", ListTerm.class);
operations.put("*/2", Multiply.class);
operations.put(":-/2", Neck.class);
operations.put("\\+/1", Not.class);
operations.put("\\=/2", NotUnifiable.class);
operations.put("\\==/2", NotEquals.class);
operations.put("*->/2", SoftCut.class);
operations.put("-/2", Subtract.class);
operations.put("=/2", Unifiable.class);
operations.put("=:=/2", ValueEquals.class);
operations.put(">/2", ValueGreaterThan.class);
operations.put(">=/2", ValueGreaterThanEqual.class);
operations.put("</2", ValueLessThan.class);
operations.put("=</2", ValueLessThanEqual.class);
operations.put("=\\=/2", ValueNotEquals.class);
operations.put("abs/1", Abs.class);
operations.put("atom/1", IsAtom.class);
operations.put("atomic/1", IsAtomic.class);
operations.put("bagof/3", BagOf.class);
operations.put("callable/1", IsCallable.class);
operations.put("compound/1", IsCompound.class);
operations.put("fail/0", False.class);
operations.put("false/0", False.class);
operations.put("findall/3", FindAll.class);
operations.put("float/1", IsFloat.class);
operations.put("integer/1", IsInteger.class);
operations.put("is/2", Is.class);
operations.put("length/2", Length.class);
operations.put("nonvar/1", IsNonvar.class);
operations.put("nl/0", WriteNewLine.class);
operations.put("number/1", IsNumber.class);
operations.put("setof/3", SetOf.class);
operations.put("sort/2", Sort.class);
operations.put("true/0", True.class);
operations.put("var/1", IsVar.class);
operations.put("write/1", Write.class);
operations.put("writeln/1", WriteLine.class);
// add additional operations
IExtensionRegistry reg = Platform.getExtensionRegistry();
if (reg != null) {
// The Extension Registry can be null if we're running outside of Eclipse
for (IConfigurationElement configurationElement : reg
.getConfigurationElementsFor("org.archstudio.prolog.operation")) {
String name = configurationElement.getAttribute("name");
String className = configurationElement.getAttribute("class");
if (name != null && className != null) {
String bundleName = configurationElement.getDeclaringExtension().getContributor().getName();
try {
@SuppressWarnings("unchecked")
Class<Executable> clazz = (Class<Executable>) Platform.getBundle(bundleName).loadClass(
className);
operations.put(name, clazz);
}
catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
}
}
}
private static class BaseContext {
ListMultimap<Signature, ComplexTerm> knowledgeBase = ArrayListMultimap.create();
Map<Signature, ListMultimap<Object, ComplexTerm>[]> valueBasedKnowledgeBase = Maps.newHashMap();
LoadingCache<Signature, Map<ComplexTerm, Integer>> knowledgeBaseIndeces = CacheBuilder.newBuilder().build(
new CacheLoader<Signature, Map<ComplexTerm, Integer>>() {
@Override
public Map<ComplexTerm, Integer> load(Signature key) throws Exception {
Map<ComplexTerm, Integer> indeces = Maps.newHashMap();
List<ComplexTerm> terms = knowledgeBase.get(key);
for (int i = 0; i < terms.size(); i++) {
indeces.put(terms.get(i), i);
}
return indeces;
}
});
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(System.out));
boolean cancelled = false;
}
private final ProofContext parentContext;
private final BaseContext baseContext;
private boolean cut = false;
public ProofContext() {
parentContext = null;
baseContext = new BaseContext();
}
public ProofContext(ProofContext parentContext) {
this.parentContext = parentContext;
baseContext = parentContext.baseContext;
}
@Override
public ProofContext clone() {
try {
return (ProofContext) super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void add(Iterable<ComplexTerm> terms) {
for (ComplexTerm term : terms) {
add(term, true);
}
}
@SuppressWarnings("unchecked")
public void add(ComplexTerm term, boolean atEnd) {
ComplexTerm head = term;
if (!(term instanceof Neck) && !PrologUtils.extractVariables(Sets.<VariableTerm> newHashSet(), term).isEmpty()) {
term = new Neck(":-", Lists.newArrayList(term, new True("true")));
}
if (term instanceof Neck) {
head = (ComplexTerm) ((Neck) term).getTerm(0);
}
add(baseContext.knowledgeBase.get(head.getSignature()), term, atEnd);
ListMultimap<Object, ComplexTerm>[] indexes = baseContext.valueBasedKnowledgeBase.get(head.getSignature());
if (indexes == null) {
baseContext.valueBasedKnowledgeBase.put(head.getSignature(), indexes = new ListMultimap[head.getArity()]);
for (int j = 0; j < head.getArity(); j++) {
indexes[j] = ArrayListMultimap.create();
}
}
for (int termIndex = 0; termIndex < head.getArity(); termIndex++) {
ListMultimap<Object, ComplexTerm> index = indexes[termIndex];
add(index.get(head.getTerm(termIndex)), term, atEnd);
}
baseContext.knowledgeBaseIndeces.invalidate(term.getSignature());
}
private void add(List<ComplexTerm> list, ComplexTerm term, boolean atEnd) {
if (atEnd) {
list.add(term);
}
else {
list.add(0, term);
}
}
public List<ComplexTerm> getKnowledgeBaseTerms(ComplexTerm goal, Map<VariableTerm, Term> variables) {
if (!baseContext.knowledgeBase.containsKey(goal.getSignature())) {
throw new IllegalArgumentException("Unrecognized signature: " + goal.getSignature());
}
List<ComplexTerm> result = baseContext.knowledgeBase.get(goal.getSignature());
for (int termIndex = 0; termIndex < goal.getArity(); termIndex++) {
if (goal.getTerm(termIndex) instanceof VariableTerm) {
if (variables.containsKey(goal.getTerm(termIndex))) {
Object value = variables.get(goal.getTerm(termIndex));
ListMultimap<Object, ComplexTerm>[] v = baseContext.valueBasedKnowledgeBase
.get(goal.getSignature());
if (v != null) {
List<ComplexTerm> t = v[termIndex].get(value);
if (t != null) {
// TODO: perform intersection instead of size comparison
if (t.size() < result.size()) {
result = t;
}
}
}
}
}
else {
Object value = goal.getTerm(termIndex);
ListMultimap<Object, ComplexTerm>[] v = baseContext.valueBasedKnowledgeBase.get(goal.getSignature());
if (v != null) {
List<ComplexTerm> t = (List<ComplexTerm>) v[termIndex].asMap().get(value);
if (t != null) {
// TODO: perform intersection instead of size comparison
if (t.size() < result.size()) {
result = t;
}
}
}
}
}
return Lists.newArrayList(result);
}
public Integer getIndex(ComplexTerm complexTerm) {
return baseContext.knowledgeBaseIndeces.getUnchecked(complexTerm.getSignature()).get(complexTerm);
}
public Term create(String name, List<Term> terms) {
@SuppressWarnings("unchecked")
Class<Executable> operationClass = (Class<Executable>) operations.get(name + "/" + terms.size());
if (operationClass != null) {
try {
if (terms.size() > 0) {
Constructor<Executable> c = operationClass.getConstructor(String.class, List.class);
return c.newInstance(name, terms);
}
else {
Constructor<Executable> c = operationClass.getConstructor(String.class);
return c.newInstance(name);
}
}
catch (Throwable exc) {
throw new RuntimeException(exc);
}
}
if (terms.size() > 0) {
return new ComplexTerm(name, terms);
}
return new ConstantTerm(name);
}
public BufferedWriter getOutput() {
return baseContext.output;
}
public void setOutput(BufferedWriter output) {
baseContext.output = output;
}
public boolean isCancelled() {
return baseContext.cancelled;
}
public void cancel() {
baseContext.cancelled = true;
}
public boolean isCut() {
return cut;
}
public void cut() {
cut = true;
if (parentContext != null) {
parentContext.cut();
}
}
public void reset() {
baseContext.cancelled = false;
cut = false;
}
}