(* This file is generated by Why3's Coq 8.4 driver *)
(* Beware! Only edit allowed sections below    *)
Require Import BuiltIn.
Require Import ZOdiv.
Require BuiltIn.
Require int.Int.
Require int.Abs.
Require int.MinMax.
Require int.ComputerDivision.
Require map.Map.

(* Why3 assumption *)
Definition unit := unit.

Axiom bag : forall (a:Type) {a_WT:WhyType a}, Type.
Parameter bag_WhyType : forall (a:Type) {a_WT:WhyType a}, WhyType (bag a).
Existing Instance bag_WhyType.

Parameter nb_occ: forall {a:Type} {a_WT:WhyType a}, a -> (bag a) -> Z.

Axiom occ_non_negative : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a))
  (x:a), (0%Z <= (nb_occ x b))%Z.

(* Why3 assumption *)
Definition mem {a:Type} {a_WT:WhyType a} (x:a) (b:(bag a)): Prop :=
  (0%Z < (nb_occ x b))%Z.

(* Why3 assumption *)
Definition eq_bag {a:Type} {a_WT:WhyType a} (a1:(bag a)) (b:(bag a)): Prop :=
  forall (x:a), ((nb_occ x a1) = (nb_occ x b)).

Axiom bag_extensionality : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag
  a)) (b:(bag a)), (eq_bag a1 b) -> (a1 = b).

Parameter empty_bag: forall {a:Type} {a_WT:WhyType a}, (bag a).

Axiom occ_empty : forall {a:Type} {a_WT:WhyType a}, forall (x:a), ((nb_occ x
  (empty_bag :(bag a))) = 0%Z).

Axiom is_empty : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a)),
  (forall (x:a), ((nb_occ x b) = 0%Z)) -> (b = (empty_bag :(bag a))).

Parameter singleton: forall {a:Type} {a_WT:WhyType a}, a -> (bag a).

Axiom occ_singleton : forall {a:Type} {a_WT:WhyType a}, forall (x:a) (y:a),
  ((x = y) /\ ((nb_occ y (singleton x)) = 1%Z)) \/ ((~ (x = y)) /\ ((nb_occ y
  (singleton x)) = 0%Z)).

Axiom occ_singleton_eq : forall {a:Type} {a_WT:WhyType a}, forall (x:a)
  (y:a), (x = y) -> ((nb_occ y (singleton x)) = 1%Z).

Axiom occ_singleton_neq : forall {a:Type} {a_WT:WhyType a}, forall (x:a)
  (y:a), (~ (x = y)) -> ((nb_occ y (singleton x)) = 0%Z).

Parameter union: forall {a:Type} {a_WT:WhyType a}, (bag a) -> (bag a) -> (bag
  a).

Axiom occ_union : forall {a:Type} {a_WT:WhyType a}, forall (x:a) (a1:(bag a))
  (b:(bag a)), ((nb_occ x (union a1 b)) = ((nb_occ x a1) + (nb_occ x b))%Z).

Axiom Union_comm : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag a))
  (b:(bag a)), ((union a1 b) = (union b a1)).

Axiom Union_identity : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag a)),
  ((union a1 (empty_bag :(bag a))) = a1).

Axiom Union_assoc : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag a))
  (b:(bag a)) (c:(bag a)), ((union a1 (union b c)) = (union (union a1 b) c)).

Axiom bag_simpl : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag a))
  (b:(bag a)) (c:(bag a)), ((union a1 b) = (union c b)) -> (a1 = c).

Axiom bag_simpl_left : forall {a:Type} {a_WT:WhyType a}, forall (a1:(bag a))
  (b:(bag a)) (c:(bag a)), ((union a1 b) = (union a1 c)) -> (b = c).

(* Why3 assumption *)
Definition add {a:Type} {a_WT:WhyType a} (x:a) (b:(bag a)): (bag a) :=
  (union (singleton x) b).

Axiom occ_add_eq : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a)) (x:a)
  (y:a), (x = y) -> ((nb_occ x (add x b)) = ((nb_occ x b) + 1%Z)%Z).

Axiom occ_add_neq : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a))
  (x:a) (y:a), (~ (x = y)) -> ((nb_occ y (add x b)) = (nb_occ y b)).

Parameter card: forall {a:Type} {a_WT:WhyType a}, (bag a) -> Z.

Axiom Card_empty : forall {a:Type} {a_WT:WhyType a}, ((card (empty_bag :(bag
  a))) = 0%Z).

Axiom Card_zero_empty : forall {a:Type} {a_WT:WhyType a}, forall (x:(bag a)),
  ((card x) = 0%Z) -> (x = (empty_bag :(bag a))).

Axiom Card_singleton : forall {a:Type} {a_WT:WhyType a}, forall (x:a),
  ((card (singleton x)) = 1%Z).

Axiom Card_union : forall {a:Type} {a_WT:WhyType a}, forall (x:(bag a))
  (y:(bag a)), ((card (union x y)) = ((card x) + (card y))%Z).

Axiom Card_add : forall {a:Type} {a_WT:WhyType a}, forall (x:a) (b:(bag a)),
  ((card (add x b)) = (1%Z + (card b))%Z).

Parameter diff: forall {a:Type} {a_WT:WhyType a}, (bag a) -> (bag a) -> (bag
  a).

Axiom Diff_occ : forall {a:Type} {a_WT:WhyType a}, forall (b1:(bag a))
  (b2:(bag a)) (x:a), ((nb_occ x (diff b1 b2)) = (Zmax 0%Z ((nb_occ x
  b1) - (nb_occ x b2))%Z)).

Axiom Diff_empty_right : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag
  a)), ((diff b (empty_bag :(bag a))) = b).

Axiom Diff_empty_left : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a)),
  ((diff (empty_bag :(bag a)) b) = (empty_bag :(bag a))).

Axiom Diff_add : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a)) (x:a),
  ((diff (add x b) (singleton x)) = b).

Axiom Diff_comm : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a))
  (b1:(bag a)) (b2:(bag a)), ((diff (diff b b1) b2) = (diff (diff b b2) b1)).

Axiom Add_diff : forall {a:Type} {a_WT:WhyType a}, forall (b:(bag a)) (x:a),
  (mem x b) -> ((add x (diff b (singleton x))) = b).

(* Why3 assumption *)
Definition array (a:Type) {a_WT:WhyType a} := (map.Map.map Z a).

Parameter elements: forall {a:Type} {a_WT:WhyType a}, (map.Map.map Z a) -> Z
  -> Z -> (bag a).

Axiom Elements_empty : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (j <= i)%Z -> ((elements a1 i
  j) = (empty_bag :(bag a))).

Axiom Elements_add : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (i < j)%Z -> ((elements a1 i
  j) = (add (map.Map.get a1 (j - 1%Z)%Z) (elements a1 i (j - 1%Z)%Z))).

Axiom Elements_singleton : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (j = (i + 1%Z)%Z) ->
  ((elements a1 i j) = (singleton (map.Map.get a1 i))).

Axiom Elements_union : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z) (k:Z), ((i <= j)%Z /\
  (j <= k)%Z) -> ((elements a1 i k) = (union (elements a1 i j) (elements a1 j
  k))).

Axiom Elements_add1 : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (i < j)%Z -> ((elements a1 i
  j) = (add (map.Map.get a1 i) (elements a1 (i + 1%Z)%Z j))).

Axiom Elements_remove_last : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (i < (j - 1%Z)%Z)%Z ->
  ((elements a1 i (j - 1%Z)%Z) = (diff (elements a1 i j)
  (singleton (map.Map.get a1 (j - 1%Z)%Z)))).

Axiom Occ_elements : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z) (n:Z), ((i <= j)%Z /\
  (j < n)%Z) -> (0%Z < (nb_occ (map.Map.get a1 j) (elements a1 i n)))%Z.

Axiom Elements_set_outside : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z), (i <= j)%Z -> forall (k:Z),
  ((k < i)%Z \/ (j <= k)%Z) -> forall (e:a), ((elements (map.Map.set a1 k e)
  i j) = (elements a1 i j)).

Axiom Elements_set_inside : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z) (n:Z) (e:a) (b:(bag a)),
  ((i <= j)%Z /\ (j < n)%Z) -> (((elements a1 i n) = (add (map.Map.get a1 j)
  b)) -> ((elements (map.Map.set a1 j e) i n) = (add e b))).

Axiom Elements_set_inside2 : forall {a:Type} {a_WT:WhyType a},
  forall (a1:(map.Map.map Z a)) (i:Z) (j:Z) (n:Z) (e:a), ((i <= j)%Z /\
  (j < n)%Z) -> ((elements (map.Map.set a1 j e) i n) = (add e
  (diff (elements a1 i n) (singleton (map.Map.get a1 j))))).

(* Why3 assumption *)
Definition left1 (i:Z): Z := ((2%Z * i)%Z + 1%Z)%Z.

(* Why3 assumption *)
Definition right1 (i:Z): Z := ((2%Z * i)%Z + 2%Z)%Z.

(* Why3 assumption *)
Definition parent (i:Z): Z := (ZOdiv (i - 1%Z)%Z 2%Z).

Axiom Parent_inf : forall (i:Z), (0%Z < i)%Z -> ((parent i) < i)%Z.

Axiom Left_sup : forall (i:Z), (0%Z <= i)%Z -> (i < (left1 i))%Z.

Axiom Right_sup : forall (i:Z), (0%Z <= i)%Z -> (i < (right1 i))%Z.

Axiom Parent_right : forall (i:Z), (0%Z <= i)%Z -> ((parent (right1 i)) = i).

Axiom Parent_left : forall (i:Z), (0%Z <= i)%Z -> ((parent (left1 i)) = i).

Axiom Inf_parent : forall (i:Z) (j:Z), ((0%Z < j)%Z /\
  (j <= (right1 i))%Z) -> ((parent j) <= i)%Z.

Axiom Child_parent : forall (i:Z), (0%Z < i)%Z ->
  ((i = (left1 (parent i))) \/ (i = (right1 (parent i)))).

Axiom Parent_pos : forall (j:Z), (0%Z < j)%Z -> (0%Z <= (parent j))%Z.

(* Why3 assumption *)
Definition parentChild (i:Z) (j:Z): Prop := ((0%Z <= i)%Z /\ (i < j)%Z) ->
  ((j = (left1 i)) \/ (j = (right1 i))).

(* Why3 assumption *)
Definition map := (map.Map.map Z Z).

(* Why3 assumption *)
Definition logic_heap := ((map.Map.map Z Z)* Z)%type.

(* Why3 assumption *)
Definition is_heap_array (a:(map.Map.map Z Z)) (idx:Z) (sz:Z): Prop :=
  (0%Z <= idx)%Z -> forall (i:Z) (j:Z), (((idx <= i)%Z /\ (i < j)%Z) /\
  (j < sz)%Z) -> ((parentChild i j) -> ((map.Map.get a i) <= (map.Map.get a
  j))%Z).

(* Why3 assumption *)
Definition is_heap (h:((map.Map.map Z Z)* Z)%type): Prop :=
  match h with
  | (a, sz) => (0%Z <= sz)%Z /\ (is_heap_array a 0%Z sz)
  end.

Axiom Is_heap_when_no_element : forall (a:(map.Map.map Z Z)) (idx:Z) (n:Z),
  ((0%Z <= n)%Z /\ (n <= idx)%Z) -> (is_heap_array a idx n).

Axiom Is_heap_sub : forall (a:(map.Map.map Z Z)) (i:Z) (n:Z), (is_heap_array
  a i n) -> forall (j:Z), ((i <= j)%Z /\ (j <= n)%Z) -> (is_heap_array a i
  j).

Axiom Is_heap_sub2 : forall (a:(map.Map.map Z Z)) (n:Z), (is_heap_array a 0%Z
  n) -> forall (j:Z), ((0%Z <= j)%Z /\ (j <= n)%Z) -> (is_heap_array a j n).

Axiom Is_heap_when_node_modified : forall (a:(map.Map.map Z Z)) (n:Z) (e:Z)
  (idx:Z) (i:Z), ((0%Z <= i)%Z /\ (i < n)%Z) -> ((is_heap_array a idx n) ->
  (((0%Z < i)%Z -> ((map.Map.get a (parent i)) <= e)%Z) ->
  ((((left1 i) < n)%Z -> (e <= (map.Map.get a (left1 i)))%Z) ->
  ((((right1 i) < n)%Z -> (e <= (map.Map.get a (right1 i)))%Z) ->
  (is_heap_array (map.Map.set a i e) idx n))))).

Axiom Is_heap_add_last : forall (a:(map.Map.map Z Z)) (n:Z) (e:Z),
  (0%Z < n)%Z -> (((is_heap_array a 0%Z n) /\ ((map.Map.get a
  (parent n)) <= e)%Z) -> (is_heap_array (map.Map.set a n e) 0%Z
  (n + 1%Z)%Z)).

Axiom Parent_inf_el : forall (a:(map.Map.map Z Z)) (n:Z), (is_heap_array a
  0%Z n) -> forall (j:Z), ((0%Z < j)%Z /\ (j < n)%Z) -> ((map.Map.get a
  (parent j)) <= (map.Map.get a j))%Z.

Axiom Left_sup_el : forall (a:(map.Map.map Z Z)) (n:Z), (is_heap_array a 0%Z
  n) -> forall (j:Z), ((0%Z <= j)%Z /\ (j < n)%Z) -> (((left1 j) < n)%Z ->
  ((map.Map.get a j) <= (map.Map.get a (left1 j)))%Z).

Axiom Right_sup_el : forall (a:(map.Map.map Z Z)) (n:Z), (is_heap_array a 0%Z
  n) -> forall (j:Z), ((0%Z <= j)%Z /\ (j < n)%Z) -> (((right1 j) < n)%Z ->
  ((map.Map.get a j) <= (map.Map.get a (right1 j)))%Z).

Axiom Is_heap_relation : forall (a:(map.Map.map Z Z)) (n:Z), (0%Z < n)%Z ->
  ((is_heap_array a 0%Z n) -> forall (j:Z), (0%Z <= j)%Z -> ((j < n)%Z ->
  ((map.Map.get a 0%Z) <= (map.Map.get a j))%Z)).

(* Why3 assumption *)
Definition model (h:((map.Map.map Z Z)* Z)%type): (bag Z) :=
  match h with
  | (a, n) => (elements a 0%Z n)
  end.

Axiom Model_empty : forall (a:(map.Map.map Z Z)), ((model (a,
  0%Z)) = (empty_bag :(bag Z))).

Axiom Model_singleton : forall (a:(map.Map.map Z Z)), ((model (a,
  1%Z)) = (singleton (map.Map.get a 0%Z))).

Axiom Model_set : forall (a:(map.Map.map Z Z)) (v:Z) (i:Z) (n:Z),
  ((0%Z <= i)%Z /\ (i < n)%Z) -> ((add (map.Map.get a i) (model (
  (map.Map.set a i v), n))) = (add v (model (a, n)))).

Axiom Model_add_last : forall (a:(map.Map.map Z Z)) (n:Z), (0%Z <= n)%Z ->
  ((model (a, (n + 1%Z)%Z)) = (add (map.Map.get a n) (model (a, n)))).

Parameter min_bag: (bag Z) -> Z.

Axiom Min_bag_singleton : forall (x:Z), ((min_bag (singleton x)) = x).

Axiom Min_bag_union : forall (x:(bag Z)) (y:(bag Z)), ((min_bag (union x
  y)) = (Zmin (min_bag x) (min_bag y))).

Axiom Min_bag_union1 : forall (x:(bag Z)) (y:(bag Z)) (a:Z), (x = (add a
  y)) -> ((min_bag x) = (Zmin a (min_bag y))).

Axiom Min_bag_union2 : forall (x:(bag Z)) (a:Z), (a <= (min_bag x))%Z ->
  (a <= (min_bag (add a x)))%Z.

Open Scope Z_scope.

(* Why3 goal *)
Theorem Is_heap_min : forall (a:(map.Map.map Z Z)) (n:Z), (0%Z < n)%Z ->
  ((is_heap_array a 0%Z n) -> ((map.Map.get a 0%Z) = (min_bag (model (a,
  n))))).
(* Why3 intros a n h1 h2. *)
Proof.
intros a n H_n Heap.
unfold model.
generalize Heap.
generalize (H_n).
apply (Z_lt_induction (fun n => ( 0 < n -> is_heap_array a 0 n -> Map.get a 0 = min_bag (elements a 0 n)))); [idtac | omega].
intros i H_induc.
intro.
assert (h:i=1 \/ i> 1) by omega.
  destruct h.
  (*case i = 1*)
  intro.
  subst.
  rewrite Elements_singleton; auto.
  rewrite Min_bag_singleton; auto.
  (*case i > 1*)
  intro.
  rewrite Elements_add; [idtac | omega].
  rewrite Min_bag_union1 with (a := Map.get a (i - 1)) (y := elements a 0 (i - 1)); auto.
  rewrite <- (H_induc (i-1)).
  rewrite Zmin_r; auto.
  apply Is_heap_relation with (n:=i); auto with *.
  split.
  omega.
  omega.
  omega.
  apply Is_heap_sub with (n:=i); auto.
  split; omega.
Qed.


