package urban.transformers;
import static org.apache.commons.collections15.CollectionUtils.collect;
import static org.apache.commons.collections15.CollectionUtils.disjunction;
import static org.apache.commons.collections15.CollectionUtils.intersection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections15.Transformer;
import urban.model.Agent;
import urban.model.Model;
import urban.model.Rule;
import urban.model.Site;
import urban.model.Statement;
/**
* Identifies rules that may have 'pinches' and adds extra rules to represent them.
*
* <p>For example a rule like:<br/>
* <code> A(f~0,x!1,y!2),B(x!1),B(y!2) <-> A(f~1,x!1,y!2),B(x!1),B(y!2) </code><br/>
* will produce an extra rule of the form<br/>
* <code> A(f~0,x!1,y!2),B(x!1,y!2) <-> A(f~1,x!1,y!2),B(x!1,y!2)</code>
*/
public class AddPinchesModelTransformer implements ModelTransformer {
@Override
public Model transform(Model in) {
Model out = new Model();
for(Statement s : in.getLines()){
out.addStatement(s);
if (s instanceof Rule){
out.addStatements(pinches((Rule)s,1));
}
}
return out;
}
/**
* This represents the possibility of bindings going to the same agent instead of different ones
* @param searchAfter
* @param name2
* @param i
* @param before2
* @param after2
* @param sc
* @return the generated collection of rules that are 'pinches' of the given rule
*/
protected static Collection<Rule> pinches(Rule rule, int searchAfter) {
Set<Rule> list = new HashSet<Rule>();
List<Agent> added = new ArrayList<Agent>(rule.getLhs()).subList(searchAfter,rule.getLhs().size());
Collections.sort(added, new Comparator<Agent>() {
public int compare(Agent o1, Agent o2) {
return o1.getName().compareTo(o2.getName());
}
});
List<List<Agent>> permutations = new ArrayList<List<Agent>>();
List<Agent> empty = Collections.emptyList();
addPinchPermutations(permutations, empty , added);
int i=0;
for (List<Agent> lc : permutations) {
List<Agent> btmp = new ArrayList<Agent>(rule.getLhs()).subList(0, searchAfter);
btmp.addAll(lc);
List<Agent> atmp = new ArrayList<Agent>(rule.getRhs()).subList(0, searchAfter);
atmp.addAll(lc);
try {
list.add( new Rule(rule.getName() == null ? null : rule.getName()+"."+i,btmp,atmp, rule.isBidirectional(), rule.getForwardRate(), rule.getBackwardRate()));
} catch (Exception e) {
e.printStackTrace();
}
i++;
}
return list;
}
/**
* Look for constraints with the same agent, check that they could be merged together
* if they can then join them and recursively check the rest of the constraints
* @param permutations
* @param checked
* @param added
*/
private static void addPinchPermutations(List<List<Agent>> permutations, List<Agent> checked, List<Agent> added) {
Agent previous = null;
List<Agent> checked2 = new ArrayList<Agent>();
int i=0;
for (Agent constraint : added) {
if (previous == null){
previous = constraint;
} else {
if (constraint.getName().equals(previous.getName())){
if (canBeMerged(previous, constraint)){
// (A.x, A.z) plus A.x (A.z .. from continuing loop
List<Agent> todo = new ArrayList<Agent>();
todo.add(merge(previous,constraint)); // could still be merged further
todo.addAll(added.subList(i+1, added.size()));
ArrayList<Agent> tmp = new ArrayList<Agent>();
tmp.addAll(checked);
tmp.addAll(checked2);
addPinchPermutations(permutations, tmp, todo);
tmp.addAll(todo);
permutations.add(tmp);
// A.z (A.x ... A.y
todo = new ArrayList<Agent>();
todo.add(previous);
todo.addAll(added.subList(i+1, added.size()));
tmp = new ArrayList<Agent>();
tmp.addAll(checked);
tmp.addAll(checked2);
tmp.add(constraint);
addPinchPermutations(permutations, tmp, todo);
}
}
checked2.add(previous);
previous = constraint;
}
i++;
}
}
private static Agent merge(Agent previous, Agent constraint) {
Set<Site> set = new HashSet<Site>();
set.addAll(previous.getSites());
set.addAll(constraint.getSites());
return new Agent(previous.getName(), set);
}
private static boolean canBeMerged(Agent previous, Agent constraint) {
Collection<Site> theSame = intersection(previous.getSites(), constraint.getSites());
return Collections.disjoint(
collect(disjunction(previous.getSites(), theSame), toName),
collect(disjunction(constraint.getSites(), theSame), toName));
}
static Transformer<Site,String> toName = new Transformer<Site,String>(){
@Override
public String transform(Site arg0) {
return arg0.getName();
}
};
}