package fj.data.properties; import fj.Ord; import fj.P; import fj.P2; import fj.data.List; import fj.data.Option; import fj.data.PriorityQueue; import fj.data.Set; import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; import org.junit.runner.RunWith; import static fj.data.Option.some; import static fj.data.PriorityQueue.emptyInt; import static fj.test.Arbitrary.arbAlphaNumString; import static fj.test.Arbitrary.arbInteger; import static fj.test.Arbitrary.arbList; import static fj.test.Arbitrary.arbP2; import static fj.test.Arbitrary.arbSet; import static fj.test.Property.impliesBoolean; import static fj.test.Property.prop; import static fj.test.Property.property; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; /** * Created by MarkPerry on 18 Jun 16. */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 100) public class PriorityQueueProperties { public static Gen<PriorityQueue<Integer, String>> arbPriorityQueueIntegerString = arbQueue(arbAlphaNumString); /** * Returns a queue with unique integer priorities. */ public static <A> Gen<PriorityQueue<Integer, A>> arbUniqueQueue(Gen<A> aa) { Gen<Set<Integer>> as = arbSet(Ord.intOrd, arbInteger); Gen<List<Integer>> ints = (as.map(si -> si.toList())); Gen<List<P2<Integer, A>>> alp = ( ints.bind(li -> aa.map(s -> li.map(i -> P.p(i, s)))) ); return (alp.map(l -> PriorityQueue.<A>emptyInt().enqueue(l))); } public static <A> Gen<PriorityQueue<Integer, A>> arbQueue(Gen<A> aa) { Gen<List<P2<Integer, A>>> g = arbList(arbP2(arbInteger, aa)); return g.map(l -> PriorityQueue.<A>emptyInt().enqueue(l)); } Property empty() { PriorityQueue<Integer, Object> pq = emptyInt(); return prop(pq.isEmpty()); } /** * Adding a priority that is at the top and then removing it returns the original top. */ Property addRemove() { return property(arbPriorityQueueIntegerString, arbInteger, arbAlphaNumString, (q, i, s) -> { Option<P2<Integer, String>> t1 = q.top(); Option<P2<Integer, String>> t2 = q.enqueue(i, s).dequeue().top(); return prop(q.isLessThan(Ord.intOrd, i) ? t1.equals(t2) : t2.map(p -> p._1() >= i).orSome(true) ); }); } /** * An empty queue has no top. */ Property emptyTop() { return prop(emptyInt().top().isNone()); } /** * Adding a value with the highest priority makes it the top item. */ Property addTop() { return property(arbPriorityQueueIntegerString, arbInteger, arbAlphaNumString, (q, i, s) -> { Option<P2<Integer, String>> actual = q.enqueue(i, s).top(); return prop(q.isLessThan(Ord.intOrd, i) ? actual.equals(some(P.p(i, s))) : actual.equals(q.top())); }); } /** * Sorting a list returns the same as putting the list into a priority queue and getting the queue as a list. */ public Property sorted() { return property(arbPriorityQueueIntegerString, pq -> { List<P2<Integer, String>> expected = pq.toList().sort(Ord.p2Ord1(Ord.intOrd.reverse())); List<P2<Integer, String>> actual = pq.toList(); return prop(actual.equals(expected)); }); } /** * Where the top n of the queue has just one element then: * - Enqueueing and then topN of the queue should return a list of the top and the new item * - Enqueuing and then dequeueing and then topping the queue should return the new item */ Property singleTopSame() { return property(arbPriorityQueueIntegerString, arbAlphaNumString, (pq, s) -> { Option<Integer> o1 = pq.top().map(p -> p._1()); return o1.map(j -> { boolean b = pq.topN().length() == 1; Property p1 = impliesBoolean(b, () -> pq.enqueue(j, s).dequeue().top().equals(some(P.p(j, s)))); Property p2 = impliesBoolean(b, () -> pq.enqueue(j, s).topN().equals(List.list(pq.top().some(), P.p(j, s)))); return p1.and(p2); }).orSome(prop(true)); }); } }