package org.ggp.base.util.gdl.model.assignments;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlDistinct;
import org.ggp.base.util.gdl.grammar.GdlFunction;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.gdl.grammar.GdlVariable;
import com.google.common.collect.ImmutableList;
// Not thread-safe
public class AssignmentIteratorImpl implements AssignmentIterator {
private List<Integer> sourceTupleIndices = null;
//This time we just have integers to deal with
private List<Integer> valueIndices = null;
private List<GdlConstant> nextAssignment = new ArrayList<GdlConstant>();
private Map<GdlVariable, GdlConstant> assignmentMap = new HashMap<GdlVariable, GdlConstant>();
private boolean headOnly = false;
private boolean done = false;
private final AssignmentIterationPlan plan;
public AssignmentIteratorImpl(AssignmentIterationPlan plan) {
this.plan = plan;
//TODO: Handle this case with a separate class
if(plan.getVarsToAssign() == null) {
headOnly = true;
return;
}
//Set up source tuple...
sourceTupleIndices = new ArrayList<Integer>(plan.getTuplesBySource().size());
for(int i = 0; i < plan.getTuplesBySource().size(); i++) {
sourceTupleIndices.add(0);
}
//Set up...
valueIndices = new ArrayList<Integer>(plan.getVarsToAssign().size());
for(int i = 0; i < plan.getVarsToAssign().size(); i++) {
valueIndices.add(0);
nextAssignment.add(null);
}
assignmentMap.putAll(plan.getHeadAssignment());
//Update "nextAssignment" according to the values of the
//value indices
updateNextAssignment();
//Keep updating it until something really works
makeNextAssignmentValid();
}
private void makeNextAssignmentValid() {
if(nextAssignment == null)
return;
//Something new that can pop up with functional constants...
for(int i = 0; i < nextAssignment.size(); i++) {
if(nextAssignment.get(i) == null) {
//Some function doesn't agree with the answer here
//So what do we increment?
incrementIndex(plan.getIndicesToChangeWhenNull().get(i));
if(nextAssignment == null)
return;
i = -1;
}
}
//Find all the unsatisfied distincts
//Find the pair with the earliest var. that needs to be changed
List<GdlVariable> varsToChange = new ArrayList<GdlVariable>();
for(int d = 0; d < plan.getDistincts().size(); d++) {
GdlDistinct distinct = plan.getDistincts().get(d);
//The assignments must use the assignments implied by nextAssignment
GdlConstant term1 = replaceVariables(distinct.getArg1());
GdlConstant term2 = replaceVariables(distinct.getArg2());
if(term1.equals(term2)) {
//need to change one of these
varsToChange.add(plan.getVarsToChangePerDistinct().get(d));
}
}
if(!varsToChange.isEmpty()) {
GdlVariable varToChange = getLeftmostVar(varsToChange);
//We want just the one, as it is a full restriction on its
//own behalf
changeOneInNext(Collections.singleton(varToChange));
}
}
private GdlVariable getLeftmostVar(List<GdlVariable> vars) {
for(GdlVariable var : plan.getVarsToAssign())
if(vars.contains(var))
return var;
return null;
}
private GdlConstant replaceVariables(GdlTerm term) {
if(term instanceof GdlFunction)
throw new RuntimeException("Function in the distinct... not handled");
//Use the assignments implied by nextAssignment
if(plan.getHeadAssignment().containsKey(term))
return plan.getHeadAssignment().get(term); //Translated in head assignment
if(term instanceof GdlConstant)
return (GdlConstant) term;
int index = plan.getVarsToAssign().indexOf(term);
return nextAssignment.get(index);
}
private void incrementIndex(int index) {
if(index < 0) {
//Trash the iterator
nextAssignment = null;
return;
}
if(plan.getValuesToCompute() != null && plan.getValuesToCompute().containsKey(index)) {
//The constant at this index is functionally computed
incrementIndex(index - 1);
return;
}
if(plan.getSourceDefiningSlot().get(index) != -1) {
//This is set by a source; increment the source
incrementSource(plan.getSourceDefiningSlot().get(index));
return;
}
//We try increasing the var at index by 1.
//Everything to the right of it gets reset.
//If it can't be increased, increase the number
//to the left instead. If nothing can be
//increased, trash the iterator.
int curValue = valueIndices.get(index);
if(curValue == plan.getValuesToIterate().get(index).size() - 1) {
//We have no room to increase the value
incrementIndex(index - 1);
return;
}
//Increment the current value
valueIndices.set(index, curValue + 1);
//Reset everything to the right of the current value
for(int i = index + 1; i < valueIndices.size(); i++)
valueIndices.set(i, 0);
//Update the assignment
updateNextAssignment();
}
private void incrementSource(int source) {
if(source < 0) {
//Trash the iterator
nextAssignment = null;
return;
}
//If we can't increase this source, increase the one to the left instead
int curValue = sourceTupleIndices.get(source);
if(curValue == plan.getTuplesBySource().get(source).size() - 1) {
incrementSource(source - 1);
return;
}
//Increment the current source
sourceTupleIndices.set(source, curValue + 1);
//Reset all the sources to the right of it
for(int i = source + 1; i < sourceTupleIndices.size(); i++)
sourceTupleIndices.set(i, 0);
//Reset all the values set by iteration over domains
for(int i = 0; i < valueIndices.size(); i++)
valueIndices.set(i, 0);
//Update the assignment
updateNextAssignment();
}
private void updateNextAssignment() {
//Let's set according to the sources before we get to the remainder
for(int s = 0; s < sourceTupleIndices.size(); s++) {
ImmutableList<ImmutableList<GdlConstant>> tuples = plan.getTuplesBySource().get(s);
int curIndex = sourceTupleIndices.get(s);
if(tuples.size() == 0) {
// This could happen if e.g. there are no tuples that agree with
// the headAssignment.
nextAssignment = null;
return;
}
List<GdlConstant> tuple = tuples.get(curIndex);
List<Integer> varsChosen = plan.getVarsChosenBySource().get(s);
List<Boolean> putDontCheckTuple = plan.getPutDontCheckBySource().get(s);
for(int i = 0; i < tuple.size(); i++) {
GdlConstant value = tuple.get(i);
boolean putDontCheck = putDontCheckTuple.get(i);
int varSlotChosen = varsChosen.get(i);
if(putDontCheck) {
nextAssignment.set(varSlotChosen, value);
} else {
//It's only at this point that we get to check...
if(!nextAssignment.get(varSlotChosen).equals(value)) {
//We need to correct the value
//This is wrong! The current tuple may be the constraining tuple.
//But we might need it for performance reasons when there isn't that case...
//TODO: Restore this when we can tell it's appropriate
//incrementSourceToGetValueInSlot(s, nextAssignment.get(varSlotChosen), i);
incrementSource(s);
//updateNextAssignment(); (should be included at end of calling function)
return;
}
}
}
}
for(int i = 0; i < valueIndices.size(); i++) {
if((plan.getValuesToCompute() == null || !plan.getValuesToCompute().containsKey(i))
&& plan.getSourceDefiningSlot().get(i) == -1) {
nextAssignment.set(i, plan.getValuesToIterate().get(i).get(valueIndices.get(i)));
} else if(plan.getSourceDefiningSlot().get(i) == -1) {
//Fill in based on a function
//Note that the values on the left must already be filled in
GdlConstant valueFromFunction = plan.getValuesToCompute().get(i).getValue(nextAssignment);
// System.out.println("Setting based on a function: slot " + i + " to value " + valueFromFunction);
nextAssignment.set(i, valueFromFunction);
}
}
}
public void changeOneInNext(Collection<GdlVariable> vars) {
//Basically, we want to increment the rightmost one...
//Corner cases:
if(nextAssignment == null)
return;
if(vars.isEmpty()) {
if(headOnly) {
done = true;
return;
} else {
//Something currently constant is false
//The assignment is done
done = true;
return;
}
}
if(plan.getVarsToAssign() == null)
System.out.println("headOnly: " + headOnly);
GdlVariable rightmostVar = getRightmostVar(vars);
incrementIndex(plan.getVarsToAssign().indexOf(rightmostVar));
makeNextAssignmentValid();
}
@Override
public void changeOneInNext(Collection<GdlVariable> varsToChange,
Map<GdlVariable, GdlConstant> assignment) {
if (nextAssignment == null) {
return;
}
//First, we stop and see if any of these have already been
//changed (in nextAssignment)
for (GdlVariable varToChange : varsToChange) {
int index = plan.getVarsToAssign().indexOf(varToChange);
if (index != -1) {
GdlConstant assignedValue = assignment.get(varToChange);
if (assignedValue == null) {
throw new IllegalArgumentException("assignedValue is null; " +
"varToChange is " + varToChange +
" and assignment is " + assignment);
}
if (nextAssignment == null) {
throw new IllegalStateException("nextAssignment is null");
}
if (!assignedValue.equals(nextAssignment.get(index))) {
//We've already changed one of these
return;
}
}
}
//Okay, we actually need to change one of these
changeOneInNext(varsToChange);
}
@Override
public boolean hasNext() {
if (plan.getEmpty()) {
return false;
}
if (headOnly) {
return (!plan.getAllDone() && !done);
}
return (nextAssignment != null);
}
@Override
public Map<GdlVariable, GdlConstant> next() {
if(headOnly) {
if(plan.getAllDone() || done)
throw new RuntimeException("Asking for next when all done");
done = true;
return plan.getHeadAssignment();
}
updateMap(); //Sets assignmentMap
//Adds one to the nextAssignment
incrementIndex(valueIndices.size() - 1);
makeNextAssignmentValid();
return assignmentMap;
}
private void updateMap() {
//Sets the map to match the nextAssignment
for(int i = 0; i < plan.getVarsToAssign().size(); i++) {
assignmentMap.put(plan.getVarsToAssign().get(i), nextAssignment.get(i));
}
}
private GdlVariable getRightmostVar(Collection<GdlVariable> vars) {
GdlVariable rightmostVar = null;
for (GdlVariable var : plan.getVarsToAssign()) {
if(vars.contains(var)) {
rightmostVar = var;
}
}
return rightmostVar;
}
@Override
public void remove() {
//Not implemented
}
}