package fj.data; import fj.Equal; import fj.F; import fj.F2; import fj.Monoid; import fj.Ord; import fj.P; import fj.P2; import fj.Show; import fj.data.fingertrees.FingerTree; import static fj.Function.compose; import static fj.data.Option.none; import static fj.data.Option.some; /** * A priority queue implementation backed by a * {@link fj.data.fingertrees.FingerTree}. The finger tree nodes are * annotated with type K, are combined using a monoid of K and both the * key and value are stored in the leaf. Priorities of the same value * are returned FIFO (first in, first out). * * Created by MarkPerry on 31 May 16. */ public final class PriorityQueue<K, A> { private final FingerTree<K, P2<K, A>> ftree; private final Equal<K> equal; private PriorityQueue(Equal<K> e, FingerTree<K, P2<K, A>> ft) { equal = e; ftree = ft; } /** * Creates a priority queue from a finger tree. */ public static <K, A> PriorityQueue<K, A> priorityQueue(Equal<K> e, FingerTree<K, P2<K, A>> ft) { return new PriorityQueue<>(e, ft); } /** * Creates an empty priority queue. * * @param m A monoid to combine node annotations. * @param e A value to compare key equality. */ public static <K, A> PriorityQueue<K, A> empty(Monoid<K> m, Equal<K> e) { return priorityQueue(e, FingerTree.empty(m, P2.__1())); } /** * An empty priority queue with integer priorities. */ public static <A> PriorityQueue<Integer, A> emptyInt() { return empty(Monoid.intMaxMonoid, Equal.intEqual); } /** * Maps the values in each node with function f. */ public <B> PriorityQueue<K, B> map(F<A, B> f) { return priorityQueue(equal, ftree.map(P2.map2_(f), FingerTree.measured(ftree.measured().monoid(), P2.__1()) ) ); } /** * Filters nodes based on the value inside each node. */ public PriorityQueue<K, A> filterValues(F<A, Boolean> f) { return priorityQueue(equal, ftree.filter(p2 -> f.f(p2._2()))); } /** * Filters the nodes based on the annotation of each node. */ public PriorityQueue<K, A> filterKeys(F<K, Boolean> f) { return priorityQueue(equal, ftree.filter(p2 -> f.f(p2._1()))); } /** * Is the tree empty? */ public boolean isEmpty() { return ftree.isEmpty(); } /** * If the tree is not empty, returns the node with highest priority otherwise returns nothing. */ public Option<P2<K, A>> top() { return unqueue(none(), (top, tail) -> some(top)); } /** * Returns all the elements of the queue with the highest (same) priority. */ public List<P2<K, A>> topN() { return toStream().uncons( List.nil(), top -> tail -> List.cons(top, tail._1().takeWhile(compose(equal.eq(top._1()), P2.__1())).toList()) ); } /** * Adds a node with priority k and value a. This operation take O(1). */ public PriorityQueue<K, A> enqueue(K k, A a) { return priorityQueue(equal, ftree.snoc(P.p(k, a))); } /** * Adds nodes using the list of products with priority k and value a. This operation takes O(list.length()). */ public PriorityQueue<K, A> enqueue(List<P2<K, A>> list) { return list.foldLeft((pq, p) -> pq.enqueue(p._1(), p._2()), this); } /** * Does the priority k exist already? */ public boolean contains(final K k) { return !ftree.split(equal.eq(k))._2().isEmpty(); } /** * Adds nodes using the iterable of products with priority k and value a. */ public PriorityQueue<K, A> enqueue(Iterable<P2<K, A>> it) { PriorityQueue<K, A> result = this; for (P2<K, A> p: it) { result = result.enqueue(p); } return result; } /** * Adds a node with priority k and value a. This operation take O(1). */ public PriorityQueue<K, A> enqueue(P2<K, A> p) { return enqueue(p._1(), p._2()); } /** * Removes the node with the highest priority. */ public PriorityQueue<K, A> dequeue() { return unqueue(this, (top, tail) -> tail); } /** * Returns a tuple of the node with the highest priority and the rest of the priority queue. */ public P2<Option<P2<K, A>>, PriorityQueue<K, A>> topDequeue() { return unqueue(P.p(none(), this), (top, tail) -> P.p(some(top), tail)); } /** * Performs a reduction on this priority queue using the given arguments. * * @param empty The value to return if this queue is empty. * @param topDequeue The function to apply to the top priority element and the tail of the queue (without its top element). * @return A reduction on this queue. */ public <B> B unqueue(B empty, F2<P2<K, A>, PriorityQueue<K, A>, B> topDequeue) { K top = ftree.measure(); P2<FingerTree<K, P2<K, A>>, FingerTree<K, P2<K, A>>> p = ftree.split(equal.eq(top)); return p._2().uncons( empty, (head, tail) -> topDequeue.f(head, priorityQueue(equal, p._1().append(tail))) ); } /** * Removes the top n elements with the highest priority. */ public PriorityQueue<K, A> dequeue(int n) { int i = n; PriorityQueue<K, A> result = this; while (i > 0) { i--; result = result.dequeue(); } return result; } /** * Does the top of the queue have lower priority than k? */ public boolean isLessThan(Ord<K> ok, K k) { return top().option(true, p -> ok.isLessThan(p._1(), k)); } public boolean isGreaterThan(Ord<K> ok, K k) { return top().option(false, p -> ok.isGreaterThan(p._1(), k)); } public boolean isEqual(Ord<K> ok, K k) { return top().option(false, p -> ok.eq(p._1(), k)); } /** * Returns a stream of products with priority k and value a. */ public Stream<P2<K, A>> toStream() { return unqueue(Stream.nil(), (top, tail) -> Stream.cons(top, () -> tail.toStream())); } /** * Returns a list of products with priority k and value a. */ public List<P2<K, A>> toList() { return toStream().toList(); } public String toString() { return Show.priorityQueueShow(Show.<K>anyShow(), Show.<A>anyShow()).showS(this); } }