package util.gdl.transforms;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import util.gdl.grammar.Gdl;
import util.gdl.grammar.GdlLiteral;
import util.gdl.grammar.GdlPool;
import util.gdl.grammar.GdlRule;
import util.gdl.grammar.GdlSentence;
import util.gdl.grammar.GdlVariable;
import util.gdl.model.MoveMutexFinder;
import util.gdl.model.Mutex;
import util.gdl.model.SentenceModel;
public class CrudeSplitter {
public static List<Gdl> run(List<Gdl> description) {
//Recommend running CI, then this, then CI again
Set<Mutex> mutexes = MoveMutexFinder.findMutexes(description);
//Go through the rules looking for mutex body literals
List<Gdl> newDescription = new ArrayList<Gdl>();
for(Gdl gdl : description) {
if(gdl instanceof GdlRule) {
//Does it contain a mutex?
if(containsMutex((GdlRule)gdl, mutexes))
newDescription.add(splittableRule((GdlRule)gdl, mutexes));
else
newDescription.add(gdl);
} else {
newDescription.add(gdl);
}
}
return newDescription;
}
private static GdlRule splittableRule(GdlRule rule, Set<Mutex> mutexes) {
//System.out.println("Trying to get splittable rule for " + rule);
//Find the mutex (assume there's only one)
//Split it based on how its variables are used in the rest of the rule
//We're basically looking for connected components
GdlLiteral mutexComponent = null;
for(GdlLiteral literal : rule.getBody()) {
if(literal instanceof GdlSentence) {
for(Mutex mutex : mutexes) {
if(mutex.matches((GdlSentence)literal))
mutexComponent = literal;
}
}
}
if(mutexComponent == null) throw new RuntimeException(":" + rule);
//Want to form connected components from variables in the rule...
List<GdlVariable> varsInMutex = SentenceModel.getVariables(mutexComponent);
Map<GdlVariable, Set<GdlVariable>> map = new HashMap<GdlVariable, Set<GdlVariable>>();
//Also do this for head, not just body literals
{
List<GdlVariable> vars = SentenceModel.getVariables(rule.getHead());
for(GdlVariable var : vars) {
if(!map.containsKey(var))
map.put(var, new HashSet<GdlVariable>());
map.get(var).addAll(vars);
}
}
for(GdlLiteral literal : rule.getBody()) {
if(literal == mutexComponent)
continue;
List<GdlVariable> vars = SentenceModel.getVariables(literal);
for(GdlVariable var : vars) {
if(!map.containsKey(var))
map.put(var, new HashSet<GdlVariable>());
map.get(var).addAll(vars);
}
}
//TODO: Can't explain why this is right right now
for(GdlVariable key : map.keySet()) {
if(!varsInMutex.contains(key))
map.get(key).clear();
else
map.get(key).retainAll(varsInMutex);
}
List<Set<GdlVariable>> connectedComponents;
connectedComponents = getConnectedComponents(map);
//So now what?
//Find ccs overlapping the mutex's variables
//Where that happens, make a copy of the mutex sharing only those
//variables with the rest of the rule
//The remainder become new variables
List<String> variableNames = SentenceModel.getVariableNames(rule);
int varName = 0;
List<GdlLiteral> mutexCopies = new ArrayList<GdlLiteral>();
for(Set<GdlVariable> cc : connectedComponents) {
if(!Collections.disjoint(cc, varsInMutex)) {
Set<GdlVariable> varsToReplace = new HashSet<GdlVariable>(varsInMutex);
varsToReplace.removeAll(cc);
//Replace all these vars with new variables
Map<GdlVariable, GdlVariable> renaming = new HashMap<GdlVariable, GdlVariable>();
for(GdlVariable toReplace : varsToReplace) {
String newCandidateName = "?a" + (varName++);
while(variableNames.contains(newCandidateName))
newCandidateName = "?a" + (varName++);
renaming.put(toReplace, GdlPool.getVariable(newCandidateName));
variableNames.add(newCandidateName);
}
mutexCopies.add(CommonTransforms.replaceVariables(mutexComponent, renaming));
}
}
//We also want to make sure whatever parts are
if(mutexCopies.size() <= 1)
return rule; //No benefit to the renaming
List<GdlLiteral> newBody = new ArrayList<GdlLiteral>();
for(GdlLiteral literal : rule.getBody()) {
if(literal != mutexComponent)
newBody.add(literal);
}
newBody.addAll(mutexCopies);
GdlRule newRule = GdlPool.getRule(rule.getHead(), newBody);
//System.out.println("Split " + rule + " into " + newRule);
return newRule;
}
private static List<Set<GdlVariable>> getConnectedComponents(
Map<GdlVariable, Set<GdlVariable>> graph) {
List<Set<GdlVariable>> components = new ArrayList<Set<GdlVariable>>();
Set<GdlVariable> varsAdded = new HashSet<GdlVariable>();
for(GdlVariable key : graph.keySet()) {
Set<GdlVariable> component = new HashSet<GdlVariable>();
Queue<GdlVariable> varsToAdd = new LinkedList<GdlVariable>();
if(!varsAdded.contains(key))
varsToAdd.add(key);
while(!varsToAdd.isEmpty()) {
GdlVariable curVar = varsToAdd.remove();
if(varsAdded.contains(curVar))
continue;
//Find the children
Set<GdlVariable> children = graph.get(curVar);
//Add those children that have not been handled
for(GdlVariable child : children) {
if(!varsAdded.contains(child))
varsToAdd.add(child);
}
component.add(curVar);
varsAdded.add(curVar);
}
if(!component.isEmpty())
components.add(component);
}
return components;
}
private static boolean containsMutex(GdlRule rule, Set<Mutex> mutexes) {
for(GdlLiteral literal : rule.getBody()) {
if(literal instanceof GdlSentence) {
for(Mutex mutex : mutexes) {
if(mutex.matches((GdlSentence)literal))
return true;
}
}
}
return false;
}
}