package ru.bmstu.datalog.algo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import ru.bmstu.datalog.data.Argument;
import ru.bmstu.datalog.data.Predicate;
/**
* Container for substitution with produceMGU function.
* @author art-vybor
*/
class Unifier {
private HashMap<Argument, Argument> substitution;
public Unifier() {
substitution = new HashMap<Argument, Argument>();
}
public Unifier(Unifier unifier) {
substitution = new HashMap<Argument, Argument>();
addAll(unifier);
}
public boolean isEqual(Argument a, Argument b) {
Argument subA = substitution.get(a);
Argument subB = substitution.get(b);
if (subA == null) subA = a;
if (subB == null) subB = b;
return subA.equals(subB);
}
public Argument unify(Argument key) {
Argument value = substitution.get(key);
if (value != null) return substitution.get(key);
return key;
}
public Argument get(Argument key) {
return substitution.get(key);
}
public Set<Argument> getKeySet() {
return substitution.keySet();
}
public void add(Argument key, Argument value) {
substitution.put(key, value);
}
@Override
public String toString() {
StringBuilder answerBuilder = new StringBuilder();
answerBuilder.append("{");
boolean f = false;
for (Argument key : substitution.keySet()) {
if (f) answerBuilder.append(", ");
else f = true;
answerBuilder.append(key.toString());
answerBuilder.append("/");
answerBuilder.append(substitution.get(key).toString());
}
answerBuilder.append("}");
return answerBuilder.toString();
}
public void addWithUnify(Argument key, Argument value) {
add(unify(key), unify(value));
}
public Predicate unifyPredicate(Predicate predicate) {
ArrayList<Argument> args = new ArrayList<Argument>();
for (int i = 0; i < predicate.size(); i++) {
args.add(unify(predicate.get(i)));
}
return new Predicate(predicate.getName(),args);
}
/**
* Proguce MGU from two predicate, if not return null.
* @param p1
* @param p2
* @return {@link Unifier}
*/
public static Unifier produceMostGeneralUnifier(Predicate p1, Predicate p2) {
//TODO переписать по человечески, как в курсаче
if (!p1.isSimiliar(p2)) return null;
Unifier unifier = new Unifier();
boolean unifies = true;
int lengthOfPredicate = p1.size();
for (int i = 0; i < lengthOfPredicate && unifies; ++i) {
Argument p1Arg = p1.get(i);
Argument p2Arg = p2.get(i);
if (unifier.isEqual(p1Arg, p2Arg) == false) {
if (unifier.unify(p1Arg).isVariable()) {
unifier.addWithUnify(p1Arg, p2Arg);
} else if (unifier.unify(p2Arg).isVariable()) {
unifier.addWithUnify(p2Arg, p1Arg);
} else unifies = false;
}
}
if (unifies) return unifier;
else return null;
}
public void addAll(Unifier unifier) {
substitution.putAll(unifier.getSubstitution());
}
private Map<Argument, Argument> getSubstitution() {
return substitution;
}
/**
* Returns a new unifier obtained merge this and source/dest.
* @param sourcePredicate
* @param destPredicate
* @return {@link Unifier}
*/
public Unifier getNewUnifier(Predicate sourcePredicate, Predicate destPredicate) {
Unifier unifier = new Unifier(this);
for (int i = 0; i < sourcePredicate.size(); i++) {
if (sourcePredicate.get(i).isVariable()) {
unifier.add(sourcePredicate.get(i), destPredicate.get(i));
}
}
return unifier;
}
}