(* This file is generated by Why3's Coq driver *)
(* Beware! Only edit allowed sections below    *)
Require Import BuiltIn.
Require BuiltIn.
Require HighOrd.
Require int.Int.
Require map.Map.
Require bool.Bool.
Require list.List.

(* Why3 assumption *)
Inductive datatype :=
  | TYunit : datatype
  | TYint : datatype
  | TYbool : datatype.
Axiom datatype_WhyType : WhyType datatype.
Existing Instance datatype_WhyType.

(* Why3 assumption *)
Inductive value :=
  | Vvoid : value
  | Vint : Numbers.BinNums.Z -> value
  | Vbool : Init.Datatypes.bool -> value.
Axiom value_WhyType : WhyType value.
Existing Instance value_WhyType.

(* Why3 assumption *)
Inductive operator :=
  | Oplus : operator
  | Ominus : operator
  | Omult : operator
  | Ole : operator.
Axiom operator_WhyType : WhyType operator.
Existing Instance operator_WhyType.

Axiom mident : Type.
Parameter mident_WhyType : WhyType mident.
Existing Instance mident_WhyType.

Axiom mident_decide :
  forall (m1:mident) (m2:mident), (m1 = m2) \/ ~ (m1 = m2).

Axiom ident : Type.
Parameter ident_WhyType : WhyType ident.
Existing Instance ident_WhyType.

Axiom ident_decide : forall (m1:ident) (m2:ident), (m1 = m2) \/ ~ (m1 = m2).

(* Why3 assumption *)
Inductive term :=
  | Tvalue : value -> term
  | Tvar : ident -> term
  | Tderef : mident -> term
  | Tbin : term -> operator -> term -> term.
Axiom term_WhyType : WhyType term.
Existing Instance term_WhyType.

(* Why3 assumption *)
Inductive fmla :=
  | Fterm : term -> fmla
  | Fand : fmla -> fmla -> fmla
  | Fnot : fmla -> fmla
  | Fimplies : fmla -> fmla -> fmla
  | Flet : ident -> term -> fmla -> fmla
  | Fforall : ident -> datatype -> fmla -> fmla.
Axiom fmla_WhyType : WhyType fmla.
Existing Instance fmla_WhyType.

(* Why3 assumption *)
Inductive stmt :=
  | Sskip : stmt
  | Sassign : mident -> term -> stmt
  | Sseq : stmt -> stmt -> stmt
  | Sif : term -> stmt -> stmt -> stmt
  | Sassert : fmla -> stmt
  | Swhile : term -> fmla -> stmt -> stmt.
Axiom stmt_WhyType : WhyType stmt.
Existing Instance stmt_WhyType.

Axiom decide_is_skip : forall (s:stmt), (s = Sskip) \/ ~ (s = Sskip).

(* Why3 assumption *)
Definition env := mident -> value.

(* Why3 assumption *)
Definition stack := Init.Datatypes.list (ident* value)%type.

Parameter get_stack:
  ident -> Init.Datatypes.list (ident* value)%type -> value.

Axiom get_stack'def :
  forall (i:ident) (pi:Init.Datatypes.list (ident* value)%type),
  match pi with
  | Init.Datatypes.nil => ((get_stack i pi) = Vvoid)
  | Init.Datatypes.cons (x, v) r =>
      ((x = i) -> ((get_stack i pi) = v)) /\
      (~ (x = i) -> ((get_stack i pi) = (get_stack i r)))
  end.

Axiom get_stack_eq :
  forall (x:ident) (v:value) (r:Init.Datatypes.list (ident* value)%type),
  ((get_stack x (Init.Datatypes.cons (x, v) r)) = v).

Axiom get_stack_neq :
  forall (x:ident) (i:ident) (v:value)
    (r:Init.Datatypes.list (ident* value)%type),
  ~ (x = i) ->
  ((get_stack i (Init.Datatypes.cons (x, v) r)) = (get_stack i r)).

Parameter eval_bin: value -> operator -> value -> value.

Axiom eval_bin'def :
  forall (x:value) (op:operator) (y:value),
  match (x, y) with
  | (Vint x1, Vint y1) =>
      match op with
      | Oplus => ((eval_bin x op y) = (Vint (x1 + y1)%Z))
      | Ominus => ((eval_bin x op y) = (Vint (x1 - y1)%Z))
      | Omult => ((eval_bin x op y) = (Vint (x1 * y1)%Z))
      | Ole =>
          ((x1 <= y1)%Z -> ((eval_bin x op y) = (Vbool Init.Datatypes.true))) /\
          (~ (x1 <= y1)%Z ->
           ((eval_bin x op y) = (Vbool Init.Datatypes.false)))
      end
  | (_, _) => ((eval_bin x op y) = Vvoid)
  end.

(* Why3 assumption *)
Fixpoint eval_term (sigma:mident -> value)
  (pi:Init.Datatypes.list (ident* value)%type) (t:term) {struct t}: value :=
  match t with
  | Tvalue v => v
  | Tvar id => get_stack id pi
  | Tderef id => sigma id
  | Tbin t1 op t2 =>
      eval_bin (eval_term sigma pi t1) op (eval_term sigma pi t2)
  end.

(* Why3 assumption *)
Fixpoint eval_fmla (sigma:mident -> value)
  (pi:Init.Datatypes.list (ident* value)%type) (f:fmla) {struct f}: Prop :=
  match f with
  | Fterm t => ((eval_term sigma pi t) = (Vbool Init.Datatypes.true))
  | Fand f1 f2 => eval_fmla sigma pi f1 /\ eval_fmla sigma pi f2
  | Fnot f1 => ~ eval_fmla sigma pi f1
  | Fimplies f1 f2 => eval_fmla sigma pi f1 -> eval_fmla sigma pi f2
  | Flet x t f1 =>
      eval_fmla sigma (Init.Datatypes.cons (x, eval_term sigma pi t) pi) f1
  | Fforall x TYint f1 =>
      forall (n:Numbers.BinNums.Z),
      eval_fmla sigma (Init.Datatypes.cons (x, Vint n) pi) f1
  | Fforall x TYbool f1 =>
      forall (b:Init.Datatypes.bool),
      eval_fmla sigma (Init.Datatypes.cons (x, Vbool b) pi) f1
  | Fforall x TYunit f1 =>
      eval_fmla sigma (Init.Datatypes.cons (x, Vvoid) pi) f1
  end.

(* Why3 assumption *)
Definition valid_fmla (p:fmla) : Prop :=
  forall (sigma:mident -> value) (pi:Init.Datatypes.list (ident* value)%type),
  eval_fmla sigma pi p.

(* Why3 assumption *)
Inductive one_step: (mident -> value) ->
  Init.Datatypes.list (ident* value)%type -> stmt -> (mident -> value) ->
  Init.Datatypes.list (ident* value)%type -> stmt -> Prop :=
  | one_step_assign :
      forall (sigma:mident -> value) (sigma':mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (x:mident) (t:term),
      (sigma' = (map.Map.set sigma x (eval_term sigma pi t))) ->
      one_step sigma pi (Sassign x t) sigma' pi Sskip
  | one_step_seq_noskip :
      forall (sigma:mident -> value) (sigma':mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type)
        (pi':Init.Datatypes.list (ident* value)%type) (s1:stmt) (s1':stmt)
        (s2:stmt),
      one_step sigma pi s1 sigma' pi' s1' ->
      one_step sigma pi (Sseq s1 s2) sigma' pi' (Sseq s1' s2)
  | one_step_seq_skip :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (s:stmt),
      one_step sigma pi (Sseq Sskip s) sigma pi s
  | one_step_if_true :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (t:term) (s1:stmt)
        (s2:stmt),
      ((eval_term sigma pi t) = (Vbool Init.Datatypes.true)) ->
      one_step sigma pi (Sif t s1 s2) sigma pi s1
  | one_step_if_false :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (t:term) (s1:stmt)
        (s2:stmt),
      ((eval_term sigma pi t) = (Vbool Init.Datatypes.false)) ->
      one_step sigma pi (Sif t s1 s2) sigma pi s2
  | one_step_assert :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (f:fmla),
      eval_fmla sigma pi f -> one_step sigma pi (Sassert f) sigma pi Sskip
  | one_step_while_true :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (cond:term) (inv:fmla)
        (body:stmt),
      eval_fmla sigma pi inv /\
      ((eval_term sigma pi cond) = (Vbool Init.Datatypes.true)) ->
      one_step sigma pi (Swhile cond inv body) sigma pi
      (Sseq body (Swhile cond inv body))
  | one_step_while_false :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (cond:term) (inv:fmla)
        (body:stmt),
      eval_fmla sigma pi inv /\
      ((eval_term sigma pi cond) = (Vbool Init.Datatypes.false)) ->
      one_step sigma pi (Swhile cond inv body) sigma pi Sskip.

(* Why3 assumption *)
Inductive many_steps: (mident -> value) ->
  Init.Datatypes.list (ident* value)%type -> stmt -> (mident -> value) ->
  Init.Datatypes.list (ident* value)%type -> stmt -> Numbers.BinNums.Z ->
  Prop :=
  | many_steps_refl :
      forall (sigma:mident -> value)
        (pi:Init.Datatypes.list (ident* value)%type) (s:stmt),
      many_steps sigma pi s sigma pi s 0%Z
  | many_steps_trans :
      forall (sigma1:mident -> value) (sigma2:mident -> value)
        (sigma3:mident -> value)
        (pi1:Init.Datatypes.list (ident* value)%type)
        (pi2:Init.Datatypes.list (ident* value)%type)
        (pi3:Init.Datatypes.list (ident* value)%type) (s1:stmt) (s2:stmt)
        (s3:stmt) (n:Numbers.BinNums.Z),
      one_step sigma1 pi1 s1 sigma2 pi2 s2 ->
      many_steps sigma2 pi2 s2 sigma3 pi3 s3 n ->
      many_steps sigma1 pi1 s1 sigma3 pi3 s3 (n + 1%Z)%Z.

Axiom steps_non_neg :
  forall (sigma1:mident -> value) (sigma2:mident -> value)
    (pi1:Init.Datatypes.list (ident* value)%type)
    (pi2:Init.Datatypes.list (ident* value)%type) (s1:stmt) (s2:stmt)
    (n:Numbers.BinNums.Z),
  many_steps sigma1 pi1 s1 sigma2 pi2 s2 n -> (0%Z <= n)%Z.

(* Why3 assumption *)
Definition reductible (sigma:mident -> value)
    (pi:Init.Datatypes.list (ident* value)%type) (s:stmt) : Prop :=
  exists sigma':mident -> value, exists pi':
  Init.Datatypes.list (ident* value)%type, exists s':stmt,
  one_step sigma pi s sigma' pi' s'.

(* Why3 assumption *)
Definition type_value (v:value) : datatype :=
  match v with
  | Vvoid => TYunit
  | Vint _ => TYint
  | Vbool _ => TYbool
  end.

(* Why3 assumption *)
Inductive type_operator: operator -> datatype -> datatype -> datatype ->
  Prop :=
  | Type_plus : type_operator Oplus TYint TYint TYint
  | Type_minus : type_operator Ominus TYint TYint TYint
  | Type_mult : type_operator Omult TYint TYint TYint
  | Type_le : type_operator Ole TYint TYint TYbool.

(* Why3 assumption *)
Definition type_stack := Init.Datatypes.list (ident* datatype)%type.

Parameter get_vartype:
  ident -> Init.Datatypes.list (ident* datatype)%type -> datatype.

Axiom get_vartype'def :
  forall (i:ident) (pi:Init.Datatypes.list (ident* datatype)%type),
  match pi with
  | Init.Datatypes.nil => ((get_vartype i pi) = TYunit)
  | Init.Datatypes.cons (x, ty) r =>
      ((x = i) -> ((get_vartype i pi) = ty)) /\
      (~ (x = i) -> ((get_vartype i pi) = (get_vartype i r)))
  end.

(* Why3 assumption *)
Definition type_env := mident -> datatype.

(* Why3 assumption *)
Inductive type_term: (mident -> datatype) ->
  Init.Datatypes.list (ident* datatype)%type -> term -> datatype -> Prop :=
  | Type_value :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (v:value),
      type_term sigma pi (Tvalue v) (type_value v)
  | Type_var :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (v:ident)
        (ty:datatype),
      ((get_vartype v pi) = ty) -> type_term sigma pi (Tvar v) ty
  | Type_deref :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (v:mident)
        (ty:datatype),
      ((sigma v) = ty) -> type_term sigma pi (Tderef v) ty
  | Type_bin :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (t1:term) (t2:term)
        (op:operator) (ty1:datatype) (ty2:datatype) (ty:datatype),
      type_term sigma pi t1 ty1 /\
      type_term sigma pi t2 ty2 /\ type_operator op ty1 ty2 ty ->
      type_term sigma pi (Tbin t1 op t2) ty.

(* Why3 assumption *)
Inductive type_fmla: (mident -> datatype) ->
  Init.Datatypes.list (ident* datatype)%type -> fmla -> Prop :=
  | Type_term :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (t:term),
      type_term sigma pi t TYbool -> type_fmla sigma pi (Fterm t)
  | Type_conj :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (f1:fmla) (f2:fmla),
      type_fmla sigma pi f1 /\ type_fmla sigma pi f2 ->
      type_fmla sigma pi (Fand f1 f2)
  | Type_neg :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (f:fmla),
      type_fmla sigma pi f -> type_fmla sigma pi (Fnot f)
  | Type_implies :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (f1:fmla) (f2:fmla),
      type_fmla sigma pi f1 -> type_fmla sigma pi f2 ->
      type_fmla sigma pi (Fimplies f1 f2)
  | Type_let :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (x:ident) (t:term)
        (f:fmla) (ty:datatype),
      type_term sigma pi t ty ->
      type_fmla sigma (Init.Datatypes.cons (x, ty) pi) f ->
      type_fmla sigma pi (Flet x t f)
  | Type_forall :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (x:ident) (f:fmla)
        (ty:datatype),
      type_fmla sigma (Init.Datatypes.cons (x, ty) pi) f ->
      type_fmla sigma pi (Fforall x ty f).

(* Why3 assumption *)
Inductive type_stmt: (mident -> datatype) ->
  Init.Datatypes.list (ident* datatype)%type -> stmt -> Prop :=
  | Type_skip :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type),
      type_stmt sigma pi Sskip
  | Type_seq :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (s1:stmt) (s2:stmt),
      type_stmt sigma pi s1 -> type_stmt sigma pi s2 ->
      type_stmt sigma pi (Sseq s1 s2)
  | Type_assigns :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (x:mident) (t:term)
        (ty:datatype),
      ((sigma x) = ty) -> type_term sigma pi t ty ->
      type_stmt sigma pi (Sassign x t)
  | Type_if :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (t:term) (s1:stmt)
        (s2:stmt),
      type_term sigma pi t TYbool -> type_stmt sigma pi s1 ->
      type_stmt sigma pi s2 -> type_stmt sigma pi (Sif t s1 s2)
  | Type_assert :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (p:fmla),
      type_fmla sigma pi p -> type_stmt sigma pi (Sassert p)
  | Type_while :
      forall (sigma:mident -> datatype)
        (pi:Init.Datatypes.list (ident* datatype)%type) (cond:term)
        (body:stmt) (inv:fmla),
      type_fmla sigma pi inv -> type_term sigma pi cond TYbool ->
      type_stmt sigma pi body -> type_stmt sigma pi (Swhile cond inv body).

(* Why3 assumption *)
Definition compatible_env (sigma:mident -> value) (sigmat:mident -> datatype)
    (pi:Init.Datatypes.list (ident* value)%type)
    (pit:Init.Datatypes.list (ident* datatype)%type) : Prop :=
  (forall (id:mident), ((type_value (sigma id)) = (sigmat id))) /\
  (forall (id:ident),
   ((type_value (get_stack id pi)) = (get_vartype id pit))).

Axiom type_inversion :
  forall (v:value),
  match type_value v with
  | TYbool => exists b:Init.Datatypes.bool, (v = (Vbool b))
  | TYint => exists n:Numbers.BinNums.Z, (v = (Vint n))
  | TYunit => (v = Vvoid)
  end.

(* Why3 goal *)
Theorem eval_type_term :
  forall (t:term), forall (x:term) (x1:operator) (x2:term),
  (t = (Tbin x x1 x2)) ->
  (forall (sigma:mident -> value)
     (pi:Init.Datatypes.list (ident* value)%type) (sigmat:mident -> datatype)
     (pit:Init.Datatypes.list (ident* datatype)%type) (ty:datatype),
   compatible_env sigma sigmat pi pit -> type_term sigmat pit x2 ty ->
   ((type_value (eval_term sigma pi x2)) = ty)) ->
  (forall (sigma:mident -> value)
     (pi:Init.Datatypes.list (ident* value)%type) (sigmat:mident -> datatype)
     (pit:Init.Datatypes.list (ident* datatype)%type) (ty:datatype),
   compatible_env sigma sigmat pi pit -> type_term sigmat pit x ty ->
   ((type_value (eval_term sigma pi x)) = ty)) ->
  forall (sigma:mident -> value) (pi:Init.Datatypes.list (ident* value)%type)
    (sigmat:mident -> datatype)
    (pit:Init.Datatypes.list (ident* datatype)%type) (ty:datatype),
  compatible_env sigma sigmat pi pit -> type_term sigmat pit t ty ->
  ((type_value (eval_term sigma pi t)) = ty).
(* Why3 intros t x x1 x2 h1 h2 h3 sigma pi sigmat pit ty h4 h5. *)
intros t x x1 x2 H;rewrite H in *;clear H.
simpl; intros.
inversion H2; subst; clear H2.
destruct H9 as (h1 & h2 & h3).
generalize (type_inversion (eval_term sigma pi x)).
rewrite (H0 sigma pi sigmat pit ty1 H1 h1).
generalize (type_inversion (eval_term sigma pi x2)).
rewrite (H sigma pi sigmat pit ty2 H1 h2).
destruct h3; intros (n2 & hh2) (n1 & hh1); rewrite hh1 ; rewrite hh2.
now rewrite (eval_bin'def (Vint n1) Oplus (Vint n2)).
now rewrite (eval_bin'def (Vint n1) Ominus (Vint n2)).
now rewrite (eval_bin'def (Vint n1) Omult (Vint n2)).
generalize (eval_bin'def (Vint n1) Ole (Vint n2)).
intros (ha,hb).
destruct (Z_le_dec n1 n2) as [ l | l].
now rewrite (ha l).
now rewrite (hb l).
Qed.

