(** {1 Priority queues} *)

(** {2 Polymorphic mutable priority queues} *)

module Pqueue

  (** {3 Types} *)

  type elt
  (** the abstract type of elements *)

  predicate le elt elt
  (** `elt` is equipped with a total pre order `le` *)

  clone export relations.TotalPreOrder
    with type t = elt, predicate rel = le, axiom .

  use int.Int
  use seq.Seq
  use seq.Occ

  (** the priority queue is modeled as a sorted sequence of elements *)
  type t = abstract { mutable elts: seq elt }
    invariant { forall i1 i2.
                0 <= i1 <= i2 < length elts -> le elts[i1] elts[i2] }
  meta coercion function elts

  (** {3 Operations} *)

  val create () : t ensures { result = empty }
  (** create a fresh, empty queue *)

  val push (x: elt) (q: t) : unit
    writes  { q }
    ensures { length q = 1 + length (old q) }
    ensures { occ_all x q = 1 + occ_all x (old q) }
    ensures { forall y. y <> x -> occ_all y q = occ_all y (old q) }
  (** push an element in a queue *)

  exception Empty
  (** exception raised by `pop` and `peek` to signal an empty queue *)

  val pop (q: t) : elt
    writes {q}
    ensures { length (old q) > 0 }
    ensures { result = (old q)[0] }
    ensures { q = (old q)[1..] }
    raises  { Empty -> q = old q = empty }
  (** remove and return the minimal element *)

  val safe_pop (q: t) : elt
    requires { length q > 0 }
    writes   { q }
    ensures  { result = (old q)[0] }
    ensures  { q = (old q)[1..] }
  (** remove and return the minimal element *)

  val peek (q: t) : elt
    ensures { length q > 0 }
    ensures { result = q[0] }
    raises  { Empty -> q = empty }
  (** return the minimal element, without modifying the queue *)

  val safe_peek (q: t) : elt
    requires { length q > 0 }
    ensures { result = q[0] }
  (** return the minimal element, without modifying the queue *)

  val clear (q: t) : unit
    writes  { q }
    ensures { q = empty }
  (** empty the queue *)

  val copy (q: t) : t
    ensures { result == q }
  (** return a fresh copy of the queue *)

  val is_empty (q: t) : bool
    ensures { result <-> q = empty }
  (** check whether the queue is empty *)

  val length (q: t) : int
    ensures { result = length q }
  (** return the number of elements in the queue *)

end

(** Test the interface above using an external heapsort *)
module Harness

  use int.Int
  use array.Array
  use array.IntArraySorted
  use array.ArrayPermut
  use map.Occ as MO
  use seq.Seq
  use seq.FreeMonoid
  use seq.Occ as SO

  clone Pqueue as PQ with type elt = int, predicate le = (<=)

  let heapsort (a: array int) : unit
    ensures { sorted a }
    ensures { permut_all (old a) a }
  = let n = length a in
    let pq = PQ.create () in
    for i = 0 to n - 1 do
      invariant { length pq = i }
      invariant { forall x. MO.occ x a.elts 0 n =
                  MO.occ x a.elts i n + SO.occ_all x pq }
      PQ.push (Array.([]) a i) pq
    done;
    for i = 0 to n - 1 do
      invariant { length pq = n - i }
      invariant { sorted_sub a 0 i }
      invariant { forall j k. 0 <= j < i -> 0 <= k < length pq ->
                  Array.([]) a j <= pq[k] }
      invariant { forall x. MO.occ x (old a.elts) 0 n =
                  MO.occ x a.elts 0 i + SO.occ_all x pq }
      a[i] <- PQ.safe_pop pq
    done

end

(** {2 Simpler interface}

when duplicate elements are not allowed

*)

module PqueueNoDup

  (** {3 Types} *)

  type elt
  (** the abstract type of elements *)

  predicate le elt elt
  (** `elt` is equipped with a total pre order `le` *)

  clone export relations.TotalPreOrder
    with type t = elt, predicate rel = le, axiom .

  use set.Fset

  type t = abstract { mutable elts: fset elt }
  (** the priority queue is modeled as a finite set of elements *)
  meta coercion function elts

  (** {3 Operations} *)

  val create () : t
    ensures { result = empty }
  (** create a fresh, empty queue *)

  val push (x: elt) (q: t) : unit
    writes  { q }
    ensures { q = add x (old q) }
  (** push an element in a queue *)

  exception Empty
  (** exception raised by `pop` and `peek` to signal an empty queue *)

  predicate minimal_elt (x: elt) (s: fset elt) =
     mem x s /\ forall e: elt. mem e s -> le x e
  (** property of the element returned by `pop` and `peek` *)

  val pop (q: t) : elt
    writes  { q }
    ensures { q = remove result (old q) }
    ensures { minimal_elt result (old q) }
    raises  { Empty -> q = old q = empty }
  (** remove and returns the minimal element *)

  val safe_pop (q: t) : elt
    writes  { q }
    requires { not q = empty }
    ensures { q = remove result (old q) }
    ensures { minimal_elt result (old q) }
  (** remove and returns the minimal element *)

  val peek (q: t) : elt
    ensures { minimal_elt result q }
    raises  { Empty -> q = empty }
  (** return the minimal element, without modifying the queue *)

  val safe_peek (q: t) : elt
    requires { not q = empty }
    ensures { minimal_elt result q }
  (** return the minimal element, without modifying the queue *)

  val clear (q: t) : unit
    writes  { q }
    ensures { q = empty }
  (** empty the queue *)

  val copy (q: t) : t
    ensures { result = q }
  (** return a fresh copy of the queue *)

  val is_empty (q: t) : bool
    ensures { result <-> q = empty }
  (** check whether the queue is empty *)

  val length (q: t) : int
    ensures { result = cardinal q }
  (** return the number of elements in the queue *)

end
