package ai.domain.boxes;
import java.util.ArrayList;
import java.util.Arrays;
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.Set;
import ai.common.CollectionUtils;
import ai.common.CollectionUtils.Predicate;
import ai.common.Pair;
import ai.domain.Variable;
import ai.domain.generic.NonRelationalDomain;
import ai.domain.interval.Interval;
import ai.domain.interval.IntervalValue;
public class IntegerBoxesHelper {
static abstract class GenericOperation {
protected abstract ArrayList<SequencePair> zeroCase(ArrayList<SequencePair> left, ArrayList<SequencePair> right);
private static double getNextValue(int idx, ArrayList<SequencePair> data) {
if (idx>=data.size())
return Double.POSITIVE_INFINITY;
return data.get(idx).key;
}
private ArrayList<SequencePair> recursiveCompute(ArrayList<SequencePair> left,
ArrayList<SequencePair> right, int varIdx) {
//here 0 dimensional version??
if (left == null || right == null || varIdx == 0)
return zeroCase(left, right);
ArrayList<SequencePair> currentLeftValue = null;
ArrayList<SequencePair> currentRightValue = null;
ArrayList<SequencePair> lastAddedValue = null; // normalization here
int leftIdx = -1;
int rightIdx = -1;
ArrayList<SequencePair> result = new ArrayList<SequencePair>();
while(true) {
if (leftIdx == left.size()-1 && rightIdx == right.size()-1)
break;
double nextLeftKey = getNextValue(leftIdx+1, left);
double nextRightKey = getNextValue(rightIdx+1, right);
if (nextLeftKey <= nextRightKey) {
leftIdx += 1;
currentLeftValue = left.get(leftIdx).value;
}
if (nextRightKey <= nextLeftKey) {
rightIdx += 1;
currentRightValue = right.get(rightIdx).value;
}
//compute value
double key = Math.min(nextLeftKey, nextRightKey);
ArrayList<SequencePair> val = recursiveCompute(currentLeftValue, currentRightValue, varIdx-1);
//normalization
if (!IntegerBoxesHelper.equals(lastAddedValue, val)) {
result.add(new SequencePair(key, val));
lastAddedValue = val;
}
}
return (result.size() == 0) ? null : result;
}
public ArrayList<SequencePair> apply(ArrayList<SequencePair> left, ArrayList<SequencePair> right, int varCount) {
return recursiveCompute(left, right, varCount);
}
}
private static class JoinOperation extends GenericOperation {
@Override
protected ArrayList<SequencePair> zeroCase(ArrayList<SequencePair> left,
ArrayList<SequencePair> right) {
return (left == null) ? right : left;
}
}
private static class MeetOperation extends GenericOperation {
@Override
protected ArrayList<SequencePair> zeroCase(ArrayList<SequencePair> left,
ArrayList<SequencePair> right) {
return (left == null) ? left : right;
}
}
private static class LeqOperation extends GenericOperation {
@Override
protected ArrayList<SequencePair> zeroCase(ArrayList<SequencePair> left,
ArrayList<SequencePair> right) {
return (left != null && right == null) ? left : null;
}
}
static GenericOperation joinOp = new JoinOperation();
static GenericOperation meetOp = new MeetOperation();
static GenericOperation leqOp = new LeqOperation();
static IntegerBoxes applyOpOnCommonVariables(IntegerBoxes left,
IntegerBoxes right, GenericOperation op) {
Pair<ArrayList<Variable>, Pair<ArrayList<Integer>, ArrayList<Integer>>> x = findVariablesToRemove(left, right);
ArrayList<SequencePair> leftData = IntegerBoxesHelper.removeVariables(x.right.left, left.data, left.variables.size());
ArrayList<SequencePair> rightData = IntegerBoxesHelper.removeVariables(x.right.right, right.data, right.variables.size());
ArrayList<SequencePair> opData = op.apply(leftData, rightData, x.left.size());
return (opData == null) ? IntegerBoxes.BOTTOM : new IntegerBoxes(x.left, opData);
}
static ArrayList<SequencePair> removeHelper(int currentVar, int varToRemove, ArrayList<SequencePair> data, int variablesCount) {
if (data == null || currentVar > varToRemove)
return data;
ArrayList<SequencePair> result;
if (currentVar == varToRemove) {
result = null;
for(SequencePair item: data)
result = joinOp.apply(result, item.value, variablesCount-1);
} else {//currentVar < varToRemove - apply recursive and normalize result
result = new ArrayList<SequencePair>();
ArrayList<SequencePair> previous = null;
for(SequencePair item: data) {
ArrayList<SequencePair> newItemData = removeHelper(currentVar+1, varToRemove, item.value, variablesCount);
if (!equals(previous, newItemData)) {
result.add(new SequencePair(item.key, newItemData));
previous = newItemData;
}
}
}
return result;
}
static ArrayList<SequencePair> removeVariables(ArrayList<Integer> variables, ArrayList<SequencePair> data, int variablesCount) {
int removedCount = 0;
for(int i: variables) {
if (data == null)
return data;
data = removeHelper(0, i - removedCount, data, variablesCount-removedCount);
removedCount ++;
}
return data;
}
static Pair<ArrayList<Variable>, Pair<ArrayList<Integer>, ArrayList<Integer>>> findVariablesToRemove(IntegerBoxes left, IntegerBoxes right) {
final Set<Variable> commonVars = getCommonVariables(left, right);
Predicate<Variable> filterPredicate = new CollectionUtils.Predicate<Variable>() {
@Override
public boolean meets(Variable var) {
return commonVars.contains(var);
}
};
ArrayList<Variable> leftVarsOrdered = CollectionUtils.filter(left.variables, filterPredicate);
ArrayList<Variable> rightVarsOrdered = CollectionUtils.filter(right.variables, filterPredicate);
if (!leftVarsOrdered.equals(rightVarsOrdered))
throw new IntegerBoxes.IntegerBoxesException("Not the same variable order!!");
//find indexes of variables to remove
ArrayList<Integer> toRemoveLeft = new ArrayList<Integer>();
for(int i=0; i< left.variables.size(); i++)
if (!commonVars.contains(left.variables.get(i)))
toRemoveLeft.add(i);
ArrayList<Integer> toRemoveRight = new ArrayList<Integer>();
for(int i=0; i< right.variables.size(); i++)
if (!commonVars.contains(right.variables.get(i)))
toRemoveRight.add(i);
return Pair.create(leftVarsOrdered, Pair.create(toRemoveLeft, toRemoveRight));
}
/**
* Returns set of variables present in both arguments
* Assume arguments are not bottom.
*
* @param left
* @param right
* @return
*/
static Set<Variable> getCommonVariables(IntegerBoxes left, IntegerBoxes right) {
Set<Variable> commonVars = new HashSet<Variable>(left.variables);
commonVars.retainAll(right.variables);
return commonVars;
}
public static boolean equals(ArrayList<SequencePair> left, ArrayList<SequencePair> right) {
if (left == null)
return right == null;
if (right == null)
return false;
if (left == right)
return true;
if (left.size() != right.size())
return false;
for(int i=0; i< left.size(); i++) {
SequencePair leftItem = left.get(i);
SequencePair rightItem = right.get(i);
if (!leftItem.key.equals(rightItem.key))
return false;
if (!equals(leftItem.value, rightItem.value))
return false;
}
return true;
}
private static ArrayList<SequencePair> getSequenceFor(IntervalValue interval, ArrayList<SequencePair> inner) {
ArrayList<SequencePair> result = new ArrayList<SequencePair>(interval.right < Double.POSITIVE_INFINITY ? 2 : 1);
result.add(new SequencePair(interval.left, inner));
if (interval.right < Double.POSITIVE_INFINITY && interval.right != interval.right+1)
result.add(new SequencePair(interval.right+1, null));
return result;
}
public static IntegerBoxes fromIntervalBox(NonRelationalDomain<Interval> in) {
//FIXME: should we reverse variables??
if (in.isBottom())
return IntegerBoxes.BOTTOM;
//NOTE: we sort variables according to their order they appear in
ArrayList<Variable> variables = new ArrayList<Variable>(in.getVariables());
Collections.sort(variables);
ArrayList<SequencePair> result = new ArrayList<SequencePair>(0);
for(Variable var: variables)
result = getSequenceFor(in.getValueFor(var).getValue(), result);
Collections.reverse(variables);
return new IntegerBoxes(variables, result);
}
private static class Split {
private static final NonRelationalDomain<Interval> INITIAL_INTV = NonRelationalDomain.getInitialValue();
@SuppressWarnings("unchecked")
private static final List<NonRelationalDomain<Interval>> FINAL_LIST = Arrays.asList(INITIAL_INTV);
private final ArrayList<Variable> variables;
private Split(ArrayList<Variable> variables){
this.variables = variables;
}
private void process(List<NonRelationalDomain<Interval>> result,
double start, double end, int varIdx, ArrayList<SequencePair> subData) {
Variable currentVar = variables.get(varIdx);
for(NonRelationalDomain<Interval> smaller: apply(subData, varIdx+1))
result.add(smaller.addNewVariable(currentVar, new Interval(start, end), false));
}
// private final FINISH = new LinkedList<NonRelationalDomain<Interval>>
private List<NonRelationalDomain<Interval>> apply(ArrayList<SequencePair> data, int varIdx) {
if (varIdx >= variables.size())
return FINAL_LIST;
double start = Double.NEGATIVE_INFINITY;
ArrayList<SequencePair> lastValue = null;
List<NonRelationalDomain<Interval>> result = new LinkedList<NonRelationalDomain<Interval>>();
for(SequencePair elem: data){
double end = elem.key-1;
if (lastValue != null)
process(result, start, end, varIdx, lastValue);
start = elem.key;
lastValue = elem.value;
}
if (lastValue != null)
process(result, start, Double.POSITIVE_INFINITY, varIdx, lastValue);
return result;
}
public static List<NonRelationalDomain<Interval>> execute(IntegerBoxes boxes) {
Split x = new Split(boxes.variables);
return x.apply(boxes.data, 0);
}
}
public static IntegerBoxes removeVariables(IntegerBoxes input, List<Variable> variables){
if (input.isBottom())
return input;
final Set<Variable> varSet = new HashSet<Variable>(input.variables);
varSet.retainAll(variables);
if (varSet.size() == 0)
return input;
//find indexes to remove
ArrayList<Integer> variablesToRemove = new ArrayList<Integer>();
for(int i=0; i< input.variables.size(); i++)
if (varSet.contains(input.variables.get(i)))
variablesToRemove.add(i);
ArrayList<SequencePair> newData = IntegerBoxesHelper.removeVariables(variablesToRemove, input.data, input.variables.size());
ArrayList<Variable> vars = CollectionUtils.filter(input.variables, new Predicate<Variable>() {
@Override
public boolean meets(Variable var) {
return !varSet.contains(var);
}
});
return (newData == null) ? IntegerBoxes.BOTTOM : new IntegerBoxes(vars, newData);
}
/**
* NOTE: when boxes is not bottom but empty we return initial value for intervals
* @param boxes
* @return
*/
public static List<NonRelationalDomain<Interval>> split(IntegerBoxes boxes) {
if (boxes.isBottom())
return null;
// if (boxes.variables.size() == 0 && boxes.data.size() == 0)
// return new LinkedList<NonRelationalDomain<Interval>>();
return Split.execute(boxes);
}
public static IntegerBoxes join(IntegerBoxes... arguments) {
IntegerBoxes result = IntegerBoxes.BOTTOM;
for(IntegerBoxes arg: arguments)
result = result.join(arg);
return result;
}
private static void processHelp(ArrayList<SequencePair> data, StringBuffer result, String indent) {
if (data == null) {
result.append("BOT");
return;
}
if (data.isEmpty()){
result.append("TOP");
return;
}
for(SequencePair item: data) {
result.append("\n");
result.append(indent);
result.append(item.key + ":");
processHelp(item.value, result, indent+ " ");
}
}
public static String formatData(IntegerBoxes boxes) {
StringBuffer result = new StringBuffer();
processHelp(boxes.data, result, "");
return result.toString();
}
private static void getSurroundingBoxHelper(ArrayList<SequencePair> data, int variable,
ArrayList<Pair<Double, Double>> restricions) {
if (data == null || data.size() == 0)
return;
Pair<Double, Double> restriction = restricions.get(variable);
SequencePair first = data.get(0);
SequencePair last = data.get(data.size()-1);
if (first.key < restriction.left)
restriction = restriction.setLeft(first.key);
restriction = restriction.setRight((last.value == null) ? last.key : Double.POSITIVE_INFINITY);
restricions.set(variable, restriction);
for(SequencePair sp: data)
getSurroundingBoxHelper(sp.value, variable+1, restricions);
}
public static NonRelationalDomain<Interval> getSurroundingBox(IntegerBoxes boxes) {
if (boxes.isBottom())
return NonRelationalDomain.getStaticBottom();
ArrayList<Pair<Double, Double>> restrictions = new ArrayList<Pair<Double, Double>>(boxes.variables.size());
for(Variable var: boxes.variables)
restrictions.add(Pair.create(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
getSurroundingBoxHelper(boxes.data, 0, restrictions);
NonRelationalDomain<Interval> result = NonRelationalDomain.getInitialValue();
for(int i=0; i< boxes.variables.size(); i++) {
Pair<Double, Double> r = restrictions.get(i);
result = result.addNewVariable(boxes.variables.get(i),
new Interval(r.left, r.right-1), false);
}
return result;
}
private static void getLocalPointsHelper(ArrayList<SequencePair> data, ArrayList<Variable> variables,
int variable, Map<Variable, Set<Double>> result) {
if (data == null || data.size() == 0)
return;
Set<Double> x = result.get(variables.get(variable));
for(SequencePair sp: data) {
x.add(sp.key);
getLocalPointsHelper(sp.value, variables, variable+1, result);
}
}
public static Map<Variable, Set<Double>> getLocalPoints(IntegerBoxes boxes) {
Map<Variable, Set<Double>> result = new HashMap<Variable, Set<Double>>();
if (boxes.isBottom())
return result;
for(Variable var: boxes.variables)
result.put(var, new HashSet<Double>());
getLocalPointsHelper(boxes.data, boxes.variables, 0, result);
return result;
}
}