package com.tuplejump.stargate.lucene.query.fsm;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
/**
* A priority queue for breadth-first search that can backtrack the list of states that lead to
* some particular state.
* <p>
* Items are queued by their offsets, i.e., higher offsets are queued later.
*
* @author Florian Leitner
*/
final class BFSQueue<E> {
private QueueItem<Node<E>> start;
private Map<QueueItem<Node<E>>, QueueItem<Node<E>>> moves;
private Map<QueueItem<Node<E>>, Double> weights;
private Queue<QueueItem<Node<E>>> queue;
BFSQueue() {
start = null;
moves = new HashMap<QueueItem<Node<E>>, QueueItem<Node<E>>>();
weights = new HashMap<QueueItem<Node<E>>, Double>();
queue = new PriorityQueue<QueueItem<Node<E>>>();
}
BFSQueue(int offset, Node<E> init) {
this();
setStart(offset, init);
}
/**
* Set the initial start state of search.
*
* @param offset of this state in the input sequence
* @param init the starting state to backtrack too
*/
void setStart(int offset, Node<E> init) {
if (init == null) throw new IllegalArgumentException("init state may never be null");
if (queue.size() != 0 || moves.size() != 0)
throw new IllegalStateException("tracer already running");
start = new QueueItem<Node<E>>(offset, init);
queue.add(start);
}
/**
* Add the all target states that can be reached to the queue.
*
* @param off of the target states in the input sequence
* @param src queue item from where the transitions were made from
* @param trgts states to where the source state transitioned to
* @param w weighted IC gained from the {@link Transition}; should be zero for epsilon
* transitions
*/
void addTransistions(int off, QueueItem<Node<E>> src, Set<Node<E>> trgts, double w) {
w += src.equals(start) ? 0.0 : weights.get(src);
for (Node<E> t : trgts) {
if (t != null) {
QueueItem<Node<E>> target = new QueueItem<Node<E>>(off, t);
if (!moves.containsKey(target)) {
moves.put(target, src);
weights.put(target, w);
queue.add(target);
} else if (weights.get(target) < w) {
// found a better move to the target with a higher IC weight
moves.put(target, src);
weights.put(target, w);
}
}
}
}
/**
* Find the list of states that were visited at each offset to reach this particular state.
*
* @param from queue item from which to start backtracking to the start state
* @return the list of queue items, starting with start, and ending with the <code>from</code>
* state
*/
List<QueueItem<Node<E>>> backtrack(QueueItem<Node<E>> from) {
List<QueueItem<Node<E>>> bt = new LinkedList<QueueItem<Node<E>>>();
while (!from.equals(start)) {
bt.add(from);
if (!moves.containsKey(from))
throw new NullPointerException("illegal item " + from.toString());
from = moves.get(from);
}
bt.add(from);
Collections.reverse(bt);
return bt;
}
/** Return <code>true</code> if the queue is empty. */
boolean isEmpty() {
return queue.isEmpty();
}
/** Return the head of the queue. */
QueueItem<Node<E>> remove() {
return queue.remove();
}
}