/*
* Copyright © 2010 by Ondrej Skalicka. All Rights Reserved
*/
package cz.cvut.felk.cig.jcop.problem.bucket;
import cz.cvut.felk.cig.jcop.problem.Configuration;
import cz.cvut.felk.cig.jcop.problem.Operation;
import cz.cvut.felk.cig.jcop.problem.OperationIterator;
import cz.cvut.felk.cig.jcop.util.JcopRandom;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Iterator over bucket operations.
* <p/>
* Operations are iterated in this way: <ol> <li>{@link PourOperation Pour operations} <li>{@link SpillOperation Spill
* operations} <li>{@link FillOperation Fill operations} </ol>
* <p/>
* Each operation type is sorted by source bucket. Eg. first filled bucket is bucket with index 0, then index 1 etc.
* Pouring is first pour bucket with index 0 to buckets 1 ... n, then pour bucket 1 to other etc.
*
* @author Ondrej Skalicka
*/
public class BucketIterator implements OperationIterator {
/**
* Main counter. For fill and spill operations used to indicate which bucket to fill/spill. For pour operations
* indicates destination bucket.
*/
protected int counter = 0;
/**
* Secondary counter. Not used for fill/spill operations, for pour operations indicates source bucket.
*/
protected int counter2 = 0;
/**
* Stage of iterator.
* <p/>
* 0 = prepare stage (move counter not yet called), 1 = pour operations, 2 = spill operations, 3 = fill operations,
* .
*/
protected int stage = 0;
/**
* Configuration to iterate operations over.
*/
protected Configuration configuration;
/**
* Problem for configuration, required to get bucket capacities.
*/
protected Bucket problem;
/**
* Creates new bucket iterator for certain configuration
*
* @param configuration configuration to create iterator for
* @param problem problem that configuration is part of
*/
public BucketIterator(Configuration configuration, Bucket problem) {
this.configuration = configuration;
this.problem = problem;
this.moveCounter();
}
/**
* Moves internal counter to next value (can change counter or both counter and stage).
*/
protected void moveCounter() {
// Prepare stage, just init
if (this.stage == 0) {
this.stage = 1;
this.counter = -1;
this.counter2 = 0;
}
// First stage. Try to pour every non-empty bucket to every non-full
if (this.stage == 1) {
List<PourOperation> sourceBucketOperations;
// iterating over source buckets
while (this.counter2 < this.problem.pourOperations.size()) {
sourceBucketOperations = this.problem.pourOperations.get(this.counter2);
// source bucket is empty, skip it right away. fetch info from
// first operation in list
if (sourceBucketOperations.size() > 0
&& this.configuration.valueAt(sourceBucketOperations.get(0).sourceBucket.getIndex()) > 0) {
// move to next destination bucket
this.counter++;
while (this.counter < sourceBucketOperations.size()) {
// non-full destination
if (this.configuration.valueAt(sourceBucketOperations.get(counter).destinationBucket.getIndex())
< sourceBucketOperations.get(counter).destinationBucket.getCapacity())
return;
// try next bucket
this.counter++;
}
}
// depleted all possibilities for this source bucket (or source
// is empty), move to next source bucket
this.counter2++;
this.counter = -1;
}
this.stage = 2;
this.counter = -1;
}
// Second stage. Try to spill any non-empty bucket
if (this.stage == 2) {
// move to next bucket
this.counter++;
while (this.counter < this.problem.getDimension()) {
// non-empty bucket
if (this.configuration.valueAt(this.problem.fillOperations.get(counter).bucketItem.getIndex()) > 0)
return;
// try next bucket
this.counter++;
}
// tried all buckets, move to next stage
this.stage = 3;
this.counter = -1;
}
// Third stage. Try to fill any non-full bucket.
if (this.stage == 3) {
// move to next bucket
this.counter++;
while (this.counter < this.problem.getDimension()) {
// non-full bucket
if (this.configuration.valueAt(this.problem.fillOperations.get(counter).bucketItem.getIndex())
< this.problem.buckets.get(counter).getCapacity())
return;
// try next bucket
this.counter++;
}
// tried all buckets, move to next stage
this.stage = 4;
this.counter = -1;
}
}
public boolean hasNext() {
return this.stage < 4 && this.stage > 0;
}
public Operation next() throws NoSuchElementException {
int oldCounter = this.counter;
int oldCounter2 = this.counter2;
int oldStage = this.stage;
this.moveCounter();
// stage one - pour operations
if (oldStage == 1)
return this.problem.pourOperations.get(oldCounter2).get(oldCounter);
// stage two - spill operations
if (oldStage == 2) return this.problem.spillOperations.get(oldCounter);
// stage three - fill operations
if (oldStage == 3) return this.problem.fillOperations.get(oldCounter);
throw new NoSuchElementException("Bucket iterator has no more operations");
}
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException("BucketIterator does not support remove()");
}
public Operation getRandomOperation() {
int backupCounter = this.counter;
int backupStage = this.stage;
int backupCounter2 = this.counter2;
this.stage = JcopRandom.nextInt(4);
this.counter = JcopRandom.nextInt(this.problem.getDimension());
this.counter2 = JcopRandom.nextInt(this.problem.getDimension());
this.moveCounter();
if (!this.hasNext()) {
this.stage = 0;
this.moveCounter();
}
Operation operation;
try {
operation = this.next();
} finally {
this.counter = backupCounter;
this.stage = backupStage;
this.counter2 = backupCounter2;
}
return operation;
}
}