
module Appmap

  use map.Map
  use map.Const

  type key

  type t 'a = abstract { contents: map key 'a }

  val function create (x: 'a): t 'a
    ensures { result.contents = const x }

  val function ([]) (m: t 'a) (k: key): 'a
    ensures { result = m.contents[k] }

  val function ([<-]) (m: t 'a) (k: key) (v: 'a): t 'a
    ensures { result.contents = m.contents[k <- v] }

end

theory Heap

use int.Int
use int.ComputerDivision

function left(i:int) : int = 2*i+1
function right(i:int) : int = 2*i+2
function parent(i:int) : int = div (i-1) 2

lemma Parent_inf: forall i:int. 0 < i -> parent i < i
lemma Left_sup: forall i:int. 0 <= i -> i < left i
lemma Right_sup: forall i:int. 0 <= i -> i < right i
lemma Parent_right:forall i:int. 0 <= i -> parent (right i) = i
lemma Parent_left:forall i:int. 0 <= i ->  parent (left i) = i
lemma Inf_parent: forall i j: int. 0 < j <= right i -> parent j <= i
lemma Child_parent:forall i:int. 0 < i ->
        i = left (parent i) || i = right (parent i)
lemma Parent_pos: forall j: int. 0 < j -> 0 <= parent j

predicate parentChild (i: int) (j: int) =
    0 <= i < j -> (j = left i) || (j = right i)


clone import Appmap as A with type key = int
type map = A.t int
type logic_heap = (map, int)

predicate is_heap_array (a: map) (idx: int) (sz: int) =
    0 <= idx -> forall i j: int.
      idx <= i < j < sz ->
      parentChild i j ->
      a[i] <= a[j]

predicate is_heap (h : logic_heap) =
   let (a, sz) = h in sz >= 0 /\ is_heap_array a 0 sz

lemma Is_heap_when_no_element :
  forall a:map, idx  n: int.  0 <= n <= idx -> is_heap_array a idx n

lemma Is_heap_sub :
  forall a:map, i n :int.
    is_heap_array a i n ->
     forall j: int.  i <= j <= n -> is_heap_array a i j

lemma Is_heap_sub2 :
  forall a:map, n :int.
    is_heap_array a 0 n ->
     forall j: int.  0 <= j <= n -> is_heap_array a j n

lemma Is_heap_when_node_modified :
  forall a:map, n e idx i:int. 0 <= i < n ->
     is_heap_array a idx n ->
     (i > 0 -> a[parent i] <= e ) ->
     (left i < n -> e <= a[left i]) ->
     (right i < n -> e <= a[right i]) ->
     is_heap_array (a[i <- e]) idx n

lemma Is_heap_add_last :
  forall a:map, n e:int. n > 0 ->
    is_heap_array a 0 n /\ (e >= a[parent n]) ->
    is_heap_array (a[n <- e]) 0 (n + 1)

lemma Parent_inf_el:
     forall a: map, n: int.
       is_heap_array a 0 n ->
        forall j:int. 0 < j < n  -> a[parent j] <= a[j]

lemma Left_sup_el:
     forall a: map, n: int.
       is_heap_array a 0 n ->
        forall j: int. 0 <= j < n  -> left j < n ->
          a[j] <= a[left j]

lemma Right_sup_el:
     forall a: map, n: int.
       is_heap_array a 0 n ->
        forall j: int. 0 <= j < n -> right j < n ->
           a[j] <= a[right j]

let rec lemma is_heap_relation (a:map) (n :int) (j:int)
   requires { n > 0 }
   requires { is_heap_array a 0 n }
   requires { 0 <= j < n }
   variant { j }
   ensures  { a[0] <= a[j] }
 = if j > 0 then begin
     is_heap_relation a n (parent j)
   end

end

(*
Local Variables:
compile-command: "why3ide -I . proofs"
End:
*)
