Require Import Coq.Sorting.Permutation.
Require Import Logic.lib.List_Func_ext.
Require Import Logic.GeneralLogic.Base.
Require Import Logic.GeneralLogic.ProofTheory.BasicSequentCalculus.
Require Import Logic.MinimumLogic.Syntax.
Require Import Logic.MinimumLogic.ProofTheory.Minimum.
Require Import Logic.MinimumLogic.ProofTheory.RewriteClass.
Require Import Logic.MinimumLogic.ProofTheory.ProofTheoryPatterns.
Require Import Logic.MinimumLogic.ProofTheory.ExtensionTactic.
Require Import Logic.PropositionalLogic.Syntax.
Require Import Logic.PropositionalLogic.ProofTheory.Intuitionistic.
Require Import Logic.PropositionalLogic.ProofTheory.RewriteClass.

Local Open Scope logic_base.
Local Open Scope syntax.
Import PropositionalLanguageNotation.

Section PatternInstances0.

Context {L: Language}
        {minL: MinimumLanguage L}
        {pL: PropositionalLanguage L}
        {Gamma: Provable L}
        {minAX: MinimumAxiomatization L Gamma}
        {ipAX: IntuitionisticPropositionalLogic L Gamma}.

Lemma or_Comm: Commutativity L Gamma orp.
Proof.
  constructor.
  intros.
  rewrite orp_comm.
  apply provable_impp_refl.
Qed.

Lemma orp_Mono: Monotonicity L Gamma orp.
Proof.
  constructor; intros.
  apply orp_proper_impp; auto.
Qed.

End PatternInstances0.

Section DerivableRulesFromPatterns.

Context {L: Language}
        {minL: MinimumLanguage L}
        {pL: PropositionalLanguage L}
        {Gamma: Provable L}
        {minAX: MinimumAxiomatization L Gamma}
        {ipAX: IntuitionisticPropositionalLogic L Gamma}
        {prodp: expr -> expr -> expr}.

Lemma prodp_comm {Comm: Commutativity L Gamma prodp}: forall x y,
  |-- prodp x y <--> prodp y x.
Proof.
  intros.
  apply solve_andp_intros; apply prodp_comm_impp.
Qed.

Section UnitTheorems.

Context {e: expr}.

Lemma left_unit {LU: LeftUnit L Gamma e prodp} : forall x, |-- prodp e x <--> x.
Proof.
  intros.
  apply solve_andp_intros.
  + apply left_unit1.
  + apply left_unit2.
Qed.

Lemma right_unit {RU: RightUnit L Gamma e prodp} : forall x, |-- prodp x e <--> x.
Proof.
  intros.
  apply solve_andp_intros.
  + apply right_unit1.
  + apply right_unit2.
Qed.

End UnitTheorems.

Section DistrTheorems.

Context {sump: expr -> expr -> expr}.

Lemma prodp_sump_distr_l {LDistr: LeftDistr L Gamma prodp sump}: forall x y z,
  |-- prodp x (sump y z) <--> sump (prodp x y) (prodp x z).
Proof.
  intros.
  apply solve_andp_intros.
  + apply left_distr1.
  + apply left_distr2.
Qed.

Lemma prodp_sump_distr_r {RDistr: RightDistr L Gamma prodp sump}: forall x y z,
  |-- prodp (sump y z) x <--> sump (prodp y x) (prodp z x).
Proof.
  intros.
  apply solve_andp_intros.
  + apply right_distr1.
  + apply right_distr2.
Qed.

End DistrTheorems.

Section AdjointTheorems.

Context {funcp: expr -> expr -> expr}
        {Adj: Adjointness L Gamma prodp funcp}.

Lemma Adjoint2RDistr: RightDistr L Gamma prodp orp.
Proof.
  constructor; intros.
  + apply adjoint.
    apply solve_orp_impp; apply adjoint.
    - apply orp_intros1.
    - apply orp_intros2.
  + apply solve_orp_impp.
    - apply prodp_mono1.
      apply orp_intros1.
    - apply prodp_mono1.
      apply orp_intros2.
Qed.

Lemma Adjoint2LDistr {Comm: Commutativity L Gamma prodp}:
  LeftDistr L Gamma prodp orp.
Proof.
  apply @RightDistr2LeftDistr; auto.
  + apply orp_Mono.
  + apply Adjoint2RDistr.
Qed.

(* TODO: l/r wrong *)
Lemma prodp_orp_distr_l: forall x y z: expr,
  |-- prodp (x || y) z <--> (prodp x z || prodp y z).
Proof.
  intros.
  apply (@prodp_sump_distr_r _ Adjoint2RDistr).
Qed.

(* TODO: l/r wrong *)
Lemma prodp_orp_distr_r {Comm: Commutativity L Gamma prodp}: forall x y z: expr,
  |-- prodp x (y || z) <--> (prodp x y || prodp x z).
Proof.
  intros.
  apply (@prodp_sump_distr_l _ Adjoint2LDistr).
Qed.

Lemma orp_funcp {Comm: Commutativity L Gamma prodp}: forall x y z: expr,
  |-- funcp (x || y) z <--> (funcp x z && funcp y z).
Proof.
  pose proof Adjoint2Mono as Mono.
  intros.
  apply solve_andp_intros.
  + apply solve_impp_andp.
    - apply funcp_mono.
      * apply orp_intros1.
      * apply provable_impp_refl.
    - apply funcp_mono.
      * apply orp_intros2.
      * apply provable_impp_refl.
  + apply adjoint.
    rewrite prodp_orp_distr_r.
    apply solve_orp_impp; apply adjoint.
    - apply andp_elim1.
    - apply andp_elim2.
Qed.

(* TODO: l/r wrong *)
Lemma funcp_andp_distr_r: forall x y z: expr,
  |-- funcp x (y && z)  <--> (funcp x y && funcp x z).
Proof.
  intros.
  apply solve_andp_intros.
  + apply solve_impp_andp.
    - apply funcp_mono2.
      apply andp_elim1.
    - apply funcp_mono2.
      apply andp_elim2.
  + apply adjoint.
    apply solve_impp_andp; apply adjoint.
    - apply andp_elim1.
    - apply andp_elim2.
Qed.

Lemma falsep_prodp: forall x: expr,
  |-- prodp falsep x <--> falsep.
Proof.
  intros.
  apply solve_andp_intros.
  + apply adjoint.
    apply falsep_elim.
  + apply falsep_elim.
Qed.

Lemma prodp_falsep {Comm: Commutativity L Gamma prodp}: forall x: expr,
  |-- prodp x falsep <--> falsep.
Proof.
  intros.
  rewrite prodp_comm.
  rewrite falsep_prodp.
  apply provable_iffp_refl.
Qed.

End AdjointTheorems.

Section MonoTheorems.

Context {Mono: Monotonicity L Gamma prodp}.

Lemma prodp_iffp: forall x1 x2 y1 y2,
  |-- x1 <--> x2 ->
  |-- y1 <--> y2 ->
  |-- prodp x1 y1 <--> prodp x2 y2.
Proof.
  intros.
  apply solve_andp_intros.
  + apply prodp_mono.
    - eapply solve_andp_elim1; exact H.
    - eapply solve_andp_elim1; exact H0.
  + apply prodp_mono.
    - eapply solve_andp_elim2; exact H.
    - eapply solve_andp_elim2; exact H0.
Qed.

Lemma fold_left_iffp: forall x1 x2 xs1 xs2,
  (Forall2 (fun x1 x2 => |-- x1 <--> x2) xs1 xs2) ->
  |-- x1 <--> x2 ->
  |-- fold_left prodp xs1 x1 <--> fold_left prodp xs2 x2.
Proof.
  intros.
  apply solve_andp_intros.
  + apply fold_left_mono.
    - revert H; apply Forall2_impl.
      intros.
      eapply solve_andp_elim1; exact H.
    - eapply solve_andp_elim1; exact H0.
  + apply fold_left_mono.
    - apply Forall2_rev.
      revert H; apply Forall2_impl.
      intros.
      eapply solve_andp_elim2; exact H.
    - eapply solve_andp_elim2; exact H0.
Qed.

Context {e: expr}.

Lemma fold_left_prodp_unfold {LU: LeftUnit L Gamma e prodp}: forall xs,
  |-- fold_left prodp xs e <-->
      match xs with
      | nil => e
      | x :: xs0 => fold_left prodp xs0 x
      end.
Proof.
  intros.
  destruct xs.
  + simpl.
    apply provable_iffp_refl.
  + simpl.
    apply fold_left_iffp.
    - induction xs.
      * constructor.
      * constructor; auto.
        apply provable_iffp_refl.
    - apply left_unit.
Qed.

Lemma fold_right_prodp_unfold {RU: RightUnit L Gamma e prodp}: forall xs,
  |-- fold_right prodp e xs <-->
      (fix f xs :=
         match xs with
         | nil => e
         | x :: nil => x
         | x :: xs0 => prodp x (f xs0)
         end) xs.
Proof.
  intros.
  set (f := (fix f xs :=
             match xs with
             | nil => e
             | x :: nil => x
             | x :: xs0 => prodp x (f xs0)
             end)).
  destruct xs.
  + apply provable_iffp_refl.
  + simpl fold_right.
    revert e0; induction xs; intros.
    - simpl.
      rewrite right_unit.
      apply provable_iffp_refl.
    - change (f (e0 :: a :: xs)) with (prodp e0 (f (a :: xs))).
      apply prodp_iffp.
      * apply provable_iffp_refl.
      * apply IHxs.
Qed.

End MonoTheorems.

Section AssocTheorems.

Context {e: expr}
        {Mono: Monotonicity L Gamma prodp}
        {Assoc: Associativity L Gamma prodp}.

Lemma prodp_assoc: forall x y z, |-- prodp x (prodp y z) <--> prodp (prodp x y) z.
Proof.
  intros.
  apply solve_andp_intros.
  + apply prodp_assoc1.
  + apply prodp_assoc2.
Qed.

Context {LU: LeftUnit L Gamma e prodp}
        {RU: RightUnit L Gamma e prodp}.

Lemma assoc_fold_left_fold_right_equiv: forall xs,
  |-- fold_left prodp xs e <--> fold_right prodp e xs.
Proof.
  intros.
  apply solve_andp_intros.
  + apply assoc_fold_left_fold_right.
  + apply assoc_fold_right_fold_left.
Qed.

Lemma assoc_prodp_fold_left_equiv: forall xs1 xs2,
  |-- prodp (fold_left prodp xs1 e) (fold_left prodp xs2 e) <--> fold_left prodp (xs1 ++ xs2) e.
Proof.
  intros.
  apply solve_andp_intros.
  + apply assoc_prodp_fold_left.
  + apply assoc_fold_left_app.
Qed.

End AssocTheorems.

Section CommAssocTheorems.

Context {e: expr}
        {Mono: Monotonicity L Gamma prodp}
        {Comm: Commutativity L Gamma prodp}
        {Assoc: Associativity L Gamma prodp}.

Lemma assoc_fold_left_Permutation: forall x ys1 ys2,
  Permutation ys1 ys2 ->
  |-- fold_left prodp ys1 x <--> fold_left prodp ys2 x.
Proof.
  intros.
  pose proof @proper_permutation_fold_left _ _ _ _ prodp.
  assert (forall x y, |-- x <--> y -> forall z1 z2, z1 = z2 -> |-- prodp x z1 <--> prodp y z2)
    by (intros; subst; apply prodp_iffp; [auto | apply provable_iffp_refl]).
  specialize (H0 H1); clear H1.
  assert (forall x1 x2 y z, |-- x1 <--> x2 -> |-- prodp (prodp x1 y) z <--> prodp (prodp x2 z) y).
  {
    intros.
    rewrite <- !prodp_assoc.
    apply prodp_iffp; [auto | apply prodp_comm].
  }
  specialize (H0 H1); clear H1.
  apply H0; auto.
  apply provable_iffp_refl.
Qed.

End CommAssocTheorems.

End DerivableRulesFromPatterns.

Section ProofTheoryPatterns.

Context {L: Language}
        {minL: MinimumLanguage L}
        {pL: PropositionalLanguage L}
        {Gamma: Provable L}
        {minAX: MinimumAxiomatization L Gamma}
        {ipAX: IntuitionisticPropositionalLogic L Gamma}.

Lemma Build_LeftUnit': forall {e: expr} {prodp: expr -> expr -> expr},
  (forall x: expr, |-- prodp e x <--> x) ->
  LeftUnit L Gamma e prodp.
Proof.
  intros.
  constructor; intros; specialize (H x); revert H; AddSequentCalculus.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim1; eauto.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim2; eauto.
Qed.

Lemma Build_RightUnit': forall {e: expr} {prodp: expr -> expr -> expr},
  (forall x: expr, |-- prodp x e <--> x) ->
  RightUnit L Gamma e prodp.
Proof.
  intros.
  constructor; intros; specialize (H x); revert H; AddSequentCalculus.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim1; eauto.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim2; eauto.
Qed.

Lemma Build_Associativity': forall {prodp: expr -> expr -> expr},
  (forall x y z: expr, |-- prodp (prodp x y) z <--> prodp x (prodp y z)) ->
  Associativity L Gamma prodp.
Proof.
  intros.
  constructor; intros; specialize (H x y z); revert H; AddSequentCalculus.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim2; eauto.
  + rewrite !provable_derivable.
    intros.
    eapply deduction_andp_elim1; eauto.
Qed.

End ProofTheoryPatterns.

Section PatternInstances.

Context {L: Language}
        {minL: MinimumLanguage L}
        {pL: PropositionalLanguage L}
        {Gamma: Provable L}
        {minAX: MinimumAxiomatization L Gamma}
        {ipAX: IntuitionisticPropositionalLogic L Gamma}.

Lemma impp_andp_Adjoint: Adjointness L Gamma andp impp.
Proof.
  constructor; AddSequentCalculus.
  intros; split; intros.
  + eapply modus_ponens; [| exact H].
    apply impp_uncurry.
  + eapply modus_ponens; [| exact H].
    apply impp_curry.
Qed.

Lemma andp_Comm: Commutativity L Gamma andp.
Proof.
  constructor.
  AddSequentCalculus.
  intros.
  rewrite provable_derivable.
  eapply deduction_andp_elim1.
  rewrite <- provable_derivable.
  apply andp_comm.
Qed.

Lemma andp_Mono: Monotonicity L Gamma andp.
Proof.
  eapply @Adjoint2Mono.
  + auto.
  + apply impp_andp_Adjoint.
  + apply andp_Comm.
Qed.

Lemma andp_LU: LeftUnit L Gamma TT andp.
Proof.
  intros.
  apply Build_LeftUnit'.
  intros.
  apply truep_andp.
Qed.

Lemma andp_RU: RightUnit L Gamma TT andp.
Proof.
  intros.
  apply Build_RightUnit'.
  intros.
  apply andp_truep.
Qed.

Lemma andp_Assoc: Associativity L Gamma andp.
Proof.
  intros.
  apply Build_Associativity'.
  intros.
  apply andp_assoc.
Qed.

End PatternInstances.

Section DerivableRules.

Context {L: Language}
        {minL: MinimumLanguage L}
        {pL: PropositionalLanguage L}
        {Gamma: Provable L}
        {minAX: MinimumAxiomatization L Gamma}
        {ipAX: IntuitionisticPropositionalLogic L Gamma}.

Lemma falsep_andp: forall x: expr,
  |-- FF && x <--> FF.
Proof.
  intros.
  apply (@falsep_prodp _ _ _ _ _ _ _ _ impp_andp_Adjoint).
Qed.

Lemma andp_falsep: forall x: expr,
  |-- x && FF <--> FF.
Proof.
  intros.
  apply (@prodp_falsep _ _ _ _ _ _ _ _ impp_andp_Adjoint andp_Comm).
Qed.

Lemma andp_orp_distr_l: forall x y z: expr,
  |-- (x || y) && z <--> (x && z) || (y && z).
Proof.
  intros.
  apply (@prodp_orp_distr_l _ _ _ _ _ _ _ _ impp_andp_Adjoint).
Qed.

Lemma andp_orp_distr_r: forall x y z: expr,
  |-- x && (y || z) <--> (x && y) || (x && z).
Proof.
  intros.
  apply (@prodp_orp_distr_r _ _ _ _ _ _ _ _ impp_andp_Adjoint andp_Comm).
Qed.

Definition multi_and (xs: list expr): expr := fold_left andp xs truep.

Lemma multi_and_spec: forall (xs: list expr),
  |-- multi_and xs <--> fold_right andp TT xs.
Proof.
  intros.
  unfold multi_and.
  pose proof @assoc_fold_left_fold_right_equiv _ _ _ _ _ _ andp TT andp_Mono andp_Assoc andp_LU andp_RU.
  auto.
Qed.

Lemma multi_and_unfold_right_assoc:  forall (xs: list expr),
  |-- multi_and xs <-->
      (fix f xs :=
         match xs with
         | nil => TT
         | x :: nil => x
         | x :: xs0 => x && (f xs0)
         end) xs.
Proof.
  intros.
  rewrite multi_and_spec.
  pose proof @fold_right_prodp_unfold _ _ _ _ _ _ andp andp_Mono TT andp_RU.
  auto.
Qed.

Lemma multi_and_unfold_left_assoc:  forall (xs: list expr),
  |-- multi_and xs <-->
      match xs with
      | nil => TT
      | x :: xs0 =>
        (fix f xs x :=
           match xs with
           | nil => x
           | x0 :: xs0 => f xs0 (x && x0)
           end) xs0 x
      end.
Proof.
  intros.
  pose proof @fold_left_prodp_unfold _ _ _ _ _ _ andp andp_Mono TT andp_LU.
  apply H.
Qed.

Lemma multi_and_multi_imp: forall (xs: list expr) (y: expr),
  |-- (multi_and xs --> y) <--> (multi_imp xs y).
Proof.
  intros.
  unfold multi_imp.
  rewrite multi_and_spec.
  induction xs as [| x xs].
  + simpl.
    apply truep_impp.
  + simpl.
    rewrite <- impp_curry_uncurry.
    apply impp_proper_iffp; [apply provable_iffp_refl |].
    auto.
Qed.

End DerivableRules.


(**
CoLoR, a Coq library on rewriting and termination.

See the COPYRIGHTS and LICENSE files.

- Frederic Blanqui, 2012-04-05

* Simply-typed lambda-terms
*)

Set Implicit Arguments.

From Coq Require Import Relations.
From CoLoR Require Import VecUtil LogicUtil OrdUtil NatUtil.
From CoLoR Require Export LBeta.
From CoLoR Require FMapUtil.

(****************************************************************************)
(** * Simple types over a set [So] of type constants or sorts. *)

Section simple.

  Variable So : Type.

  Inductive Ty : Type :=
  | Base : So -> Ty
  | Arr : Ty -> Ty -> Ty.

  Infix "~~>" := Arr (at level 55, right associativity).

  Notation Tys := (vector Ty).

(** Basic functions on simple types. *)

  Fixpoint arity (T : Ty) :=
    match T with
      | Base _ => 0
      | Arr _ T' => S (arity T')
    end.

  Fixpoint inputs (T : Ty) :=
    match T as T return Tys (arity T) with
      | Base _ => Vnil
      | Arr T1 T2 => Vcons T1 (inputs T2)
    end.

  Fixpoint output (T : Ty) k {struct k} :=
    match k, T with
      | S k', Arr U V => output V k'
      | _, _ => T
    end.

  Fixpoint output_base (T : Ty) :=
    match T with
      | Base b => b
      | Arr _ T' => output_base T'
    end.

  Lemma output_arity : forall T, output T (arity T) = Base (output_base T).

  Proof. induction T; fo. Qed.

  Lemma arity_output : forall p T, arity (output T p) = arity T - p.

  Proof. induction p; destruct T; simpl; try refl. rewrite IHp. refl. Qed.

  Lemma output_output : forall p q T, output (output T p) q = output T (p+q).

  Proof.
    induction p; destruct T; simpl; try refl.
    destruct q; refl. rewrite IHp. refl.
  Qed.

  Lemma inputs_output_aux : forall p T,
    p <= arity T -> p + arity (output T p) <= arity T.

  Proof. intros p T. rewrite arity_output. lia. Qed.

  Arguments inputs_output_aux [p T] _.

  Lemma inputs_output : forall p T (h : p <= arity T),
    inputs (output T p) = Vsub (inputs T) (inputs_output_aux h).

  Proof.
    induction p; destruct T; simpl; intro h. refl.
    apply Vtail_eq. rewrite Vsub_cons, Vsub_id. refl.
    lia.
    assert (h' : p <= arity T2). lia.
    rewrite IHp with (h:=h'), Vsub_cons. apply Vsub_pi.
  Qed.

  Lemma output_Base (s : So) n : output (Base s) n = Base s.

  Proof. destruct n; refl. Qed.

  Lemma output_arrow_elim : forall n (A B C : Ty), output A n = B ~~> C
    -> output A (S n) = C /\ exists h : S n <= arity A, Vnth (inputs A) h = B.

  Proof.
    induction n; destruct A; intros B C; rewrite ?output_Base; try discr;
      simpl; intro e. inversion e; subst. split. refl.
    ex (le_n_S (le_0_n (arity C))). refl.
    destruct (IHn _ _ _ e) as [h1 [h2 h3]]. split.
    revert h1. destruct A2; auto.
    ex (le_n_S h2). rewrite <- h3. apply Vnth_eq. refl.
  Qed.

(** Building the type [T1 ~~> .. ~~> Tn -> U] from the type vector
[Ts] and the type [U]. *)

  Fixpoint arrow n (Ts : Tys n) U :=
    match Ts with
      | Vnil => U
      | Vcons T Ts' => T ~~> arrow Ts' U
    end.

  Lemma arrow_cast : forall n (Ts : Tys n) U n' (h:n=n'),
    arrow (Vcast Ts h) U = arrow Ts U.

  Proof. induction Ts; intros U n' e; subst; rewrite Vcast_refl; refl. Qed.

  Lemma arrow_output : forall T p q (h : p+q<=arity T),
    arrow (Vsub (inputs T) h) (output T (p+q)) = output T p.

  Proof.
    induction T; simpl; intros p q h.
    assert (p=0). lia. assert (q=0). lia. subst. refl.
    destruct p; simpl.
    destruct q; simpl. refl. rewrite Vsub_cons, IHT2. refl.
    rewrite Vsub_cons, IHT2. refl.
  Qed.

(** Decidability of equality on types. *)

  Section dec.

    Variable eq_so_dec : forall a b : So, {a=b}+{~a=b}.

    Lemma eq_typ_dec : forall A B : Ty, {A=B}+{~A=B}.

    Proof. decide equality. Qed.

  End dec.

(** [occurs a T] says if [T] contains some [a]. *)

  Section occurs.

    Fixpoint occurs a T :=
      match T with
        | Base b => a = b
        | Arr A B => occurs a A \/ occurs a B
      end.

    Variable eq_dec : forall x y : So, {x = y}+{~x = y}.

    Lemma occurs_dec : forall a T, {occurs a T}+{~occurs a T}.

    Proof.
      intro a. induction T; simpl. apply eq_dec.
      destruct IHT1. fo. destruct IHT2. fo. right. fo.
    Qed.

  End occurs.

(** Predicate saying that a type constant occurs only
positively/negatively in a simple type. *)

  Section pos.

    Variable a : So.

    Fixpoint pos T :=
      match T with
        | Base _ => True
        | Arr A B => neg A /\ pos B
      end

    with neg T :=
      match T with
        | Base b => b <> a
        | Arr A B => pos A /\ neg B
      end.

    (*COQ: [pos] and [neg] not [simpl]ified outside the section. See bug
    report https://coq.inria.fr/bugs/show_bug.cgi?id=3097 *)

    Lemma pos_base : forall b, pos (Base b) <-> True.

    Proof. refl. Qed.

    Lemma pos_arrow : forall A B, pos (A ~~> B) <-> neg A /\ pos B.

    Proof. refl. Qed.

    Lemma neg_base : forall b, neg (Base b) <-> b <> a.

    Proof. refl. Qed.

    Lemma neg_arrow : forall A B, neg (A ~~> B) <-> pos A /\ neg B.

    Proof. refl. Qed.

(** Some properties of [occurs], [pos] and [neg]. *)

    Lemma not_occurs_pos_neg : forall {T}, ~occurs a T -> pos T /\ neg T.

    Proof. induction T; simpl; fo. Qed.

    Lemma not_occurs_pos : forall {T}, ~occurs a T -> pos T.

    Proof. intros T h. gen (not_occurs_pos_neg h). fo. Qed.

    Lemma not_occurs_neg : forall {T}, ~occurs a T -> neg T.

    Proof. intros T h. gen (not_occurs_pos_neg h). fo. Qed.

  End pos.

End simple.

Arguments output_arrow_elim [So n A B C] _.

Infix "~~>" := Arr (at level 55, right associativity).

Global Hint Rewrite pos_base pos_arrow neg_base neg_arrow : pos.

Ltac simpl_pos := autorewrite with pos; simpl.

(****************************************************************************)
(** ** Functor building a Cmp structure for simple types from a Cmp
structure for type constants. *)

Module ST_Cmp (Export BCmp : Cmp) <: Cmp.

  Definition t := Ty BCmp.t.

  Fixpoint cmp A B :=
    match A, B with
      | Base a, Base b => BCmp.cmp a b
      | Base _, Arr _ _ => Lt
      | Arr _ _, Base _ => Gt
      | Arr A1 A2, Arr B1 B2 =>
          match cmp A1 B1 with
            | Eq => cmp A2 B2
            | c => c
          end
    end.

  Lemma cmp_opp : forall x y, cmp x y = CompOpp (cmp y x).

  Proof.
    induction x; destruct y; simpl; auto.
    apply BCmp.cmp_opp.
    rewrite IHx1. destruct (cmp y1 x1); simpl; auto.
  Qed.

End ST_Cmp.

(****************************************************************************)
(** ** Functor building a CmpTransLeibniz structure for simple types
from a CmpTransLeibniz structure for type constants. *)

Module ST_CmpTransLeibniz (Export BCmpTransLeibniz : CmpTransLeibniz)
  <: CmpTransLeibniz.

  Module C := ST_Cmp BCmpTransLeibniz.

  Include C.

  Lemma cmp_eq : forall x y, cmp x y = Eq -> x = y.

  Proof.
    induction x; destruct y; simpl; try discr.
    intro e. apply BCmpTransLeibniz.cmp_eq in e. subst. refl.
    case_eq (cmp x1 y1); intros e1 e2; f_equal;
      try apply IHx1 in e1; try apply IHx2 in e2; subst; (refl||discr).
  Qed.

  Lemma cmp_lt_trans :
    forall x y z, cmp x y = Lt -> cmp y z = Lt -> cmp x z = Lt.

  Proof.
    induction x; destruct y; destruct z; simpl; try (refl||discr).
    apply BCmpTransLeibniz.cmp_lt_trans.
    case_eq (cmp x1 y1); intro e1; try (refl||discr).
    apply cmp_eq in e1. subst.
    case_eq (cmp y1 z1); intro e2; try (refl||discr).
    apply cmp_eq in e2. subst. fo.
    intros _. case_eq (cmp y1 z1); intro e2; try (refl||discr).
    apply cmp_eq in e2. subst.
    case_eq (cmp y2 z2); intro e2; try (refl||discr).
    intros _. case_eq (cmp x1 z1); intro e3; try (refl||discr).
    apply cmp_eq in e3. subst.
    Module CF := CmpFacts C. rewrite CF.eq_refl in e1. discr.
    rewrite e1 in e3. auto.
    intros _. case_eq (cmp x1 z1); intro e3; try (refl||discr).
    apply cmp_eq in e3. subst.
    rewrite C.cmp_opp, e2 in e1. discr.
    rewrite (IHx1 _ _ e1 e2) in e3. discr.
  Qed.

End ST_CmpTransLeibniz.

(****************************************************************************)
(** * Typing relation

Note that, for typing an abstraction [Lam x u] in [E], we do not
assume that [x] does not occur in [E], but overrides its type in
[E]. This is a restricted form of weakening. *)

Section typing.

  Variables F X So : Type.

  Notation Te := (@Te F X).
  Notation Tes := (vector Te).
  Notation Var := (@Var F X).
  Notation Fun := (@Fun F X).
  Notation Ty := (@Ty So).

  Variable typ : F -> Ty.

  Definition TypArgs f := vector Te (arity (typ f)).

  Record Env := mk_Env {
    Env_type : Type;
    Env_empty : Env_type;
    Env_add : X -> Ty -> Env_type -> Env_type;
    Env_In : X -> Env_type -> Prop;
    Env_MapsTo : X -> Ty -> Env_type -> Prop;
    Env_Equal : relation Env_type }.

  Variable env : Env.

  Notation En := (Env_type env).
  Notation MapsTo := (Env_MapsTo env).
  Notation add := (Env_add env).

  Inductive tr : En -> Te -> Ty -> Prop :=
  | tr_var : forall E x T, MapsTo x T E -> tr E (Var x) T
  | tr_fun : forall E f, tr E (Fun f) (typ f)
  | tr_app : forall E u v V T, tr E u (V ~~> T) -> tr E v V -> tr E (App u v) T
  | tr_lam : forall E x X v V, tr (add x X E) v V -> tr E (Lam x v) (X ~~> V).

  Lemma tr_apps : forall n (us : Tes n) E t T (hn : n <= arity T),
    tr E t T
    -> (forall i (hi : i < n),
           tr E (Vnth us hi) (Vnth (inputs T) (lt_le_trans hi hn)))
    -> tr E (apps t us) (output T n).

  Proof.
    induction us; intros E t T hn ht g. hyp.
    rename h into u. simpl. destruct T as [b|A B]; simpl in hn. lia.
    apply IHus with (hn := le_S_n hn).
    apply tr_app with A. hyp. gen (g _ (lt_0_Sn n)). simpl. auto.
    intros i hi. gen (g _ (lt_n_S hi)). simpl.
    rewrite lt_unique with (h1 := lt_S_n (lt_n_S hi)) (h2 := hi).
    rewrite lt_unique with (h1 := lt_S_n _)
                             (h2 := lt_le_trans hi (le_S_n hn)). auto.
  Qed.

  Lemma tr_apps_fun_inv E f :
    forall n (ts : Tes n) T, tr E (apps (Fun f) ts) T
    -> T = output (typ f) n
       /\ exists nf : n <= arity (typ f), forall i (hi : i < n),
          tr E (Vnth ts hi) (Vnth (inputs (typ f)) (lt_le_trans hi nf)).

  Proof.
    induction n; intros ts T hT.
    VOtac. inversion hT; subst. split. refl. ex (le_0_n (arity (typ f))). lia.
    revert hT. VSntac ts. simpl Def.apps. rewrite apps_app.
    set (us := Vremove_last (Vcons (Vhead ts) (Vtail ts))). rewrite <- !H.
    intro hT. inversion hT. subst E0 T0. destruct (IHn _ _ H3) as [e [p h]].
    symmetry in e. destruct (output_arrow_elim e) as [h1 [h2 h3]]. split. hyp.
    ex h2. intros i hi. destruct (lt_dec i n) as [l|l].
    rewrite Vnth_remove_last_intro with (h1:=l). rewrite H. fold us.
    rewrite (lt_unique (lt_le_trans hi h2) (lt_le_trans l p)). apply h.
    assert (i=n). lia. subst i. rewrite <- Vlast_nth with (x := Vhead ts).
    rewrite Vnth_eq with (h2 := h2).
    setoid_rewrite h3.
    rewrite <- Vlast_tail. hyp. refl.
  Qed.

End typing.

Arguments tr_apps_fun_inv [F X So typ env E f n ts T] _.

Ltac env :=
  unfold Env_type, Env_empty, Env_add, Env_In, Env_MapsTo, Env_Equal in *.

(****************************************************************************)
(** * Structure over which we will define typing. *)

From Coq Require FMapInterface.

Module Type ST_Struct.

  Declare Module Export L : L_Struct.

  Parameter So : Type.

  Notation Ty := (Ty So).
  Notation Tys := (vector Ty).

  Parameter typ : F -> Ty.

  Notation TypArgs := (@TypArgs F X So typ).

  (** Module providing finite maps on variables. *)

  Declare Module Export XMap : FMapInterface.S with Module E := XOrd.

  Notation En := (@XMap.t Ty).
  Notation empty := (@XMap.empty Ty).
  Notation add := (@XMap.add Ty).
  Notation In := (@XMap.In Ty).
  Notation MapsTo := (@XMap.MapsTo Ty).
  Notation Equal := (@XMap.Equal Ty).
  Notation env := (mk_Env empty add In MapsTo Equal).

End ST_Struct.

(****************************************************************************)
(** * Typing relation over an ST structure. *)

Module Make (Export ST : ST_Struct).

  Module Export B := LBeta.Make ST.L.

(****************************************************************************)
(** ** Typing environments

are finite maps from variables to types. *)

  Module XMapUtil := FMapUtil.Make XMap.
  Module Export Domain := XMapUtil.Domain XSet.
  Export XMapUtil.

(****************************************************************************)
(** ** Typing. *)

  Notation tr := (@tr F X So typ env).

  Notation "E '|-' v '~:' V" := (tr E v V) (at level 70).

(** If a term [v] is typable in [E], then its free variable are in
[E]. In fact, any subterm of [v] is typable in some extension of
[restrict_dom E (fv v)]. *)

  Lemma tr_fv_dom : forall E v V, E |- v ~: V -> fv v [<=] dom E.

  Proof.
    induction 1; simpl; intro z; set_iff.
    intro e. subst z. rewrite In_dom. exists T. hyp.
    tauto.
    intros [h|h]. apply IHtr1. hyp. apply IHtr2. hyp.
    intros [h1 h2]. rewrite In_dom, <- add_neq_in_iff. 2: apply h2.
    rewrite <- In_dom. apply IHtr. hyp.
  Qed.

(** Weakening: [tr] is compatible with [le]. *)

  Global Instance tr_le : Proper (le ==> Logic.eq ==> Logic.eq ==> impl) tr.

  Proof.
    intros E F EF t v tv T V TV ht. subst v V. revert E t T ht F EF.
    induction 1; intros F EF.
    apply tr_var. rewrite EF in H. hyp.
    apply tr_fun.
    apply tr_app with V. apply IHht1. hyp. apply IHht2. hyp.
    apply tr_lam. apply IHht. rewrite EF. refl.
  Qed.

(** Contraction: only the type of free variables need to be given. *)

  Lemma tr_contraction : forall E v V,
    E |- v ~: V -> forall y, ~XSet.In y (fv v) -> remove y E |- v ~: V.

  Proof.
    induction 1 ; intro y; simpl; set_iff; intro hy.
    apply tr_var. apply remove_2; auto.
    apply tr_fun.
    apply tr_app with V. apply IHtr1. tauto. apply IHtr2. tauto.
    apply tr_lam.
    try rename X into X0. (* compat: variable is X0 before coq 8.15 but X after *)
    eq_dec x y.
    (* x = y *)
    eapply tr_le with (x:=add x X0 E). 2: refl. 2: refl.
    intros z hz. env. do 2 rewrite find_mapsto_iff, add_o. eq_dec x z; auto.
    rewrite remove_o. eq_dec y z. exfalso. apply n. trans y; hyp. auto. hyp.
    (* x <> y *)
    eapply tr_le with (x:= remove y (add x X0 E)). 2: refl. 2: refl.
    intros z hz. env. rewrite !find_mapsto_iff, add_o, !remove_o, add_o.
    eq_dec y z. discr.
    eq_dec x z; auto.
    apply IHtr. tauto.
  Qed.

  Lemma tr_restrict : forall E v V,
    E |- v ~: V -> restrict_dom E (fv v) |- v ~: V.

  Proof.
    induction 1; simpl.
    apply tr_var. apply mapsto_restrict_dom_singleton. hyp.
    apply tr_fun.
    apply tr_app with V; eapply tr_le.
    eapply restrict_dom_s. refl. apply union_subset_1. refl. refl. hyp.
    eapply restrict_dom_s. refl. apply union_subset_2. refl. refl. hyp.
    apply tr_lam. eapply tr_le. 2: refl. 2: refl. 2: apply IHtr.
    intros y hy.
    env. rewrite mapsto_restrict_dom, !add_mapsto_iff, mapsto_restrict_dom.
    set_iff. tauto.
  Qed.

(****************************************************************************)
(** ** Well-typed substitutions. *)

  Definition wt s E F := forall x T, MapsTo x T E -> F |- s x ~: T.

  Notation "F |-s s ~: E" := (wt s E F) (at level 70).

  Global Instance wt_le : Proper (Logic.eq ==> le --> le ==> impl) wt.

  Proof.
    intros s t st E E' E'E F F' FF' hs x T hx. subst t. eapply tr_le.
    apply FF'. refl. refl. apply hs. rewrite E'E. hyp.
  Qed.

  Lemma tr_subs : forall E v V, E |- v ~: V ->
    forall s F, F |-s s ~: E -> F |- subs s v ~: V.

  Proof.
    (* Assume that [t = Lam x v] and [T = U ~~> V]. Then, [subs s t =
      Lam x' (subs s' v)] and it may be the case that there is [y]
      such that [In y E] but [~In y (fv v)]. Then, it may be the case
      that there is [U' <> U] such that [MapsTo x' U' (restrict_dom F
      (fv (s y)))], in which case we cannot pove that [add x' U F |- s
      y ~: T]. We therefore need to restrict [E] to [restrict_dom E
      (fv t)] to prove the lemma. *)
    cut (forall E v V, E |- v ~: V ->
      forall s F, F |-s s ~: restrict_dom E (fv v) -> F |- subs s v ~: V).
    intros h E v V ht s F hs. eapply h. apply ht.
    intros x T. rewrite mapsto_restrict_dom. intros [h1 h2]. apply hs. hyp.
    (* end cut *)
    induction 1; simpl; intros s F hs.
    (* var *)
    apply hs. rewrite mapsto_restrict_dom. set_iff. tauto.
    (* fun *)
    apply tr_fun.
    (* app *)
    apply tr_app with V.
    apply IHtr1. intros x A. rewrite mapsto_restrict_dom. intros [h1 h2].
    apply hs. rewrite mapsto_restrict_dom. set_iff. tauto.
    apply IHtr2. intros x A. rewrite mapsto_restrict_dom. intros [h1 h2].
    apply hs. rewrite mapsto_restrict_dom. set_iff. tauto.
    (* lam *)
    set (x' := var x v s). set (s' := S.update x (Var x') s).
    apply tr_lam. apply IHtr. intros y T.
    env. rewrite mapsto_restrict_dom, add_mapsto_iff.
    intros [[[h1 h2]|[h1 h2]] h3]; unfold s', Def.update; eq_dec y x.
    (* (y,T) = (x,U) *)
    subst y T. apply tr_var. env. rewrite add_mapsto_iff. intuition. intuition.
    (* y <> x /\ MapsTo y T E *)
    intuition.
    assert (h2' : MapsTo y T (restrict_dom E (XSet.remove x (fv v)))).
    rewrite mapsto_restrict_dom. set_iff. intuition.
    gen (hs _ _ h2'); intro h. apply tr_restrict in h.
    set (F' := restrict_dom F (fv (s y))) in h. case_eq (find x' F').
    (* find x' F' = Some U' *)
    intro U'. rewrite <- find_mapsto_iff. unfold F'.
    rewrite mapsto_restrict_dom. intros [i1 i2].
    exfalso. eapply var_notin_fv_subs. apply h3. apply n. apply i2.
    (* find x' F' = None *)
    intro i. eapply tr_le with (x:=add x' _ F'); try refl.
    unfold F'. rewrite restrict_dom_le. refl. rewrite <- le_add; hyp.
  Qed.

(****************************************************************************)
(** ** Typing is compatible with alpha-equivalence. *)

  Global Instance tr_aeq_impl : Proper (Equal ==> aeq ==> Logic.eq ==> impl) tr.

  Proof.
    intros E E' EE' v v' vv' V V' VV' h. subst V'. revert v E V h E' EE' v' vv'.
    ind_size1 v; intros E V ht E' EE' v' vv'; inversion ht; inv_aeq vv'; subst.
    (* var *)
    apply tr_var. rewrite <- EE'. hyp.
    (* fun *)
    apply tr_fun.
    (* app *)
    apply tr_app with V0.
    eapply hu. apply H2. hyp. hyp.
    eapply hv. apply H4. hyp. hyp.
    (* lam *)
    try rename X into X0. (* compat: variable is X0 before coq 8.15 but X after *)
    apply tr_lam. eapply hu with (u':=rename x x1 v) (E:=add x1 X0 E).
    rewrite size_rename. refl. unfold Def.rename. eapply tr_subs.
    eapply tr_restrict. apply H3. 2: rewrite EE'; refl. 2: hyp.

    intros y V. unfold Def.single, Def.update.
    env. rewrite mapsto_restrict_dom, add_mapsto_iff. intros [h1 h2].
    eq_dec y x. unfold XSet.E.eq in e. subst.
    assert (b : X0 = V). tauto. subst V.
    apply tr_var. env. rewrite add_mapsto_iff. auto.
    assert (b : MapsTo y V E). destruct h1. exfalso. apply n. sym. tauto. tauto.
    apply tr_var. env. rewrite add_mapsto_iff. eq_dec x1 y.
    unfold XSet.E.eq in e. subst x1. tauto. tauto.
  Qed.

  Global Instance tr_aeq : Proper (Equal ==> aeq ==> Logic.eq ==> iff) tr.

  Proof.
    intros E E' EE' v v' vv' V V' VV'. subst V'.
    split; apply tr_aeq_impl; (auto || sym; auto).
  Qed.

(****************************************************************************)
(** ** Subject reduction for beta. *)

  Global Instance tr_beta_aeq :
    Proper (Logic.eq ==> beta_aeq ==> Logic.eq ==> impl) tr.

  Proof.
    intros E E' EE' v v' vv' V V' VV' ht. subst E' V'. revert E v V ht v' vv'.
    induction 1; intros v' b; inv_beta_aeq b; subst.
    (* app_l *)
    rewrite h. apply tr_app with V. apply IHht1. hyp. hyp.
    (* app_r *)
    rewrite h. apply tr_app with V. hyp. apply IHht2. hyp.
    (* top *)
    rewrite h0. inversion ht1; subst. eapply tr_subs. apply H1.
    intros z B. env. rewrite add_mapsto_iff. intros [[h1 h2]|[h1 h2]].
    subst z B. rewrite single_eq. hyp.
    rewrite single_neq. 2: hyp. apply tr_var. hyp.
    (* lam *)
    destruct (fv_R_notin_fv_lam _ b).
    subst. apply tr_lam. apply IHht. rewrite rename_id in h0. hyp.
    rewrite (aeq_alpha x). 2: hyp. apply tr_lam. apply IHht. hyp.
  Qed.

End Make.


Require Import Coq.Program.Basics.
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Program.Combinators.
Require Import omega.Omega.
Require Import Setoid.
Require Import ZArith.

Require Import FinProof.Common.
Require Import FinProof.CommonInstances.
Require Import FinProof.StateMonad2.
Require Import FinProof.StateMonadInstances.
Require Import FinProof.ProgrammingWith.

Local Open Scope struct_scope.

Require Import FinProof.CommonProofs.
Require Import depoolContract.ProofEnvironment.
Require Import depoolContract.DePoolClass.
Require Import depoolContract.SolidityNotations.

Require Import depoolContract.DePoolFunc.
Module DePoolFuncs := DePoolFuncs XTypesSig StateMonadSig.
Import DePoolFuncs.
Import DePoolSpec.
Import LedgerClass.

Require Import depoolContract.Lib.CommonModelProofs.
Module CommonModelProofs := CommonModelProofs StateMonadSig.
Import CommonModelProofs.
Require Import depoolContract.Lib.Tactics.
Require Import depoolContract.Lib.ErrorValueProofs.
(*Require Import depoolContract.Lib.CommonCommon.*)
(* Require Import depoolContract.Lib.tvmFunctionsProofs. *)

Import DePoolSpec.LedgerClass.SolidityNotations.

Local Open Scope struct_scope.
Local Open Scope Z_scope.
Local Open Scope solidity_scope.
Require Import Lists.List.
Import ListNotations.
Local Open Scope list_scope.

Set Typeclasses Iterative Deepening.
Set Typeclasses Depth 100.

Require Export depoolContract.Scenarios.Common.CommonDefinitions.
Require Export depoolContract.Scenarios.Common.RoundDefinitions.

Definition _participantCorrectPositive (p : DePoolLib_ι_Participant) :=
    0 <= DePoolLib_ι_Participant_ι_roundQty p /\
    0 <= DePoolLib_ι_Participant_ι_reward p /\
    0 <= DePoolLib_ι_Participant_ι_withdrawValue p.

Definition participantCorrectLocally (p : DePoolLib_ι_Participant) :=
    _participantCorrectPositive p.

Definition _participantCorrectlyExists (l : Ledger) (p : DePoolLib_ι_Participant) :=
    (participantIn l p /\ 0 < DePoolLib_ι_Participant_ι_roundQty p) \/
    (participantOut l p /\ 0 = DePoolLib_ι_Participant_ι_roundQty p).

Definition _participantInRound (l : Ledger) (p : DePoolLib_ι_Participant) :=
    participantIn l p <->
    (exists r addr, roundIn l r /\ addrInRound l r addr /\
    isSome (eval_state ( ParticipantBase_Ф_fetchParticipant addr) l) = true).

Definition participantCorrectGlobally (l : Ledger) :=
    forall p,
    participantIn l p ->
    (
        participantCorrectLocally p /\
        _participantCorrectlyExists l p /\
        _participantInRound l p
    ).


(* This file is generated by Why3's Coq driver *)
(* Beware! Only edit allowed sections below    *)
Require Import ZArith.
Require Import Rbase.
Require int.Int.
Require int.Abs.
Require int.EuclideanDivision.
Require real.Real.
(*
Require real.RealInfix.
*)
Require real.FromInt.

Definition implb(x:bool) (y:bool): bool := match (x,
  y) with
  | (true, false) => false
  | (_, _) => true
  end.

Parameter pow2: Z -> Z.

Axiom Power_0 : ((pow2 0%Z) = 1%Z).

Axiom Power_s : forall (n:Z), (0%Z <= n)%Z ->
  ((pow2 (n + 1%Z)%Z) = (2%Z * (pow2 n))%Z).

Axiom Power_1 : ((pow2 1%Z) = 2%Z).

Axiom Power_sum : forall (n:Z) (m:Z), ((0%Z <= n)%Z /\ (0%Z <= m)%Z) ->
  ((pow2 (n + m)%Z) = ((pow2 n) * (pow2 m))%Z).

Axiom pow2_0 : ((pow2 0%Z) = 1%Z).

Axiom pow2_1 : ((pow2 1%Z) = 2%Z).

Axiom pow2_2 : ((pow2 2%Z) = 4%Z).

Axiom pow2_3 : ((pow2 3%Z) = 8%Z).

Axiom pow2_4 : ((pow2 4%Z) = 16%Z).

Axiom pow2_5 : ((pow2 5%Z) = 32%Z).

Axiom pow2_6 : ((pow2 6%Z) = 64%Z).

Axiom pow2_7 : ((pow2 7%Z) = 128%Z).

Axiom pow2_8 : ((pow2 8%Z) = 256%Z).

Axiom pow2_9 : ((pow2 9%Z) = 512%Z).

Axiom pow2_10 : ((pow2 10%Z) = 1024%Z).

Axiom pow2_11 : ((pow2 11%Z) = 2048%Z).

Axiom pow2_12 : ((pow2 12%Z) = 4096%Z).

Axiom pow2_13 : ((pow2 13%Z) = 8192%Z).

Axiom pow2_14 : ((pow2 14%Z) = 16384%Z).

Axiom pow2_15 : ((pow2 15%Z) = 32768%Z).

Axiom pow2_16 : ((pow2 16%Z) = 65536%Z).

Axiom pow2_17 : ((pow2 17%Z) = 131072%Z).

Axiom pow2_18 : ((pow2 18%Z) = 262144%Z).

Axiom pow2_19 : ((pow2 19%Z) = 524288%Z).

Axiom pow2_20 : ((pow2 20%Z) = 1048576%Z).

Axiom pow2_21 : ((pow2 21%Z) = 2097152%Z).

Axiom pow2_22 : ((pow2 22%Z) = 4194304%Z).

Axiom pow2_23 : ((pow2 23%Z) = 8388608%Z).

Axiom pow2_24 : ((pow2 24%Z) = 16777216%Z).

Axiom pow2_25 : ((pow2 25%Z) = 33554432%Z).

Axiom pow2_26 : ((pow2 26%Z) = 67108864%Z).

Axiom pow2_27 : ((pow2 27%Z) = 134217728%Z).

Axiom pow2_28 : ((pow2 28%Z) = 268435456%Z).

Axiom pow2_29 : ((pow2 29%Z) = 536870912%Z).

Axiom pow2_30 : ((pow2 30%Z) = 1073741824%Z).

Axiom pow2_31 : ((pow2 31%Z) = 2147483648%Z).

Axiom pow2_32 : ((pow2 32%Z) = 4294967296%Z).

Axiom pow2_33 : ((pow2 33%Z) = 8589934592%Z).

Axiom pow2_34 : ((pow2 34%Z) = 17179869184%Z).

Axiom pow2_35 : ((pow2 35%Z) = 34359738368%Z).

Axiom pow2_36 : ((pow2 36%Z) = 68719476736%Z).

Axiom pow2_37 : ((pow2 37%Z) = 137438953472%Z).

Axiom pow2_38 : ((pow2 38%Z) = 274877906944%Z).

Axiom pow2_39 : ((pow2 39%Z) = 549755813888%Z).

Axiom pow2_40 : ((pow2 40%Z) = 1099511627776%Z).

Axiom pow2_41 : ((pow2 41%Z) = 2199023255552%Z).

Axiom pow2_42 : ((pow2 42%Z) = 4398046511104%Z).

Axiom pow2_43 : ((pow2 43%Z) = 8796093022208%Z).

Axiom pow2_44 : ((pow2 44%Z) = 17592186044416%Z).

Axiom pow2_45 : ((pow2 45%Z) = 35184372088832%Z).

Axiom pow2_46 : ((pow2 46%Z) = 70368744177664%Z).

Axiom pow2_47 : ((pow2 47%Z) = 140737488355328%Z).

Axiom pow2_48 : ((pow2 48%Z) = 281474976710656%Z).

Axiom pow2_49 : ((pow2 49%Z) = 562949953421312%Z).

Axiom pow2_50 : ((pow2 50%Z) = 1125899906842624%Z).

Axiom pow2_51 : ((pow2 51%Z) = 2251799813685248%Z).

Axiom pow2_52 : ((pow2 52%Z) = 4503599627370496%Z).

Axiom pow2_53 : ((pow2 53%Z) = 9007199254740992%Z).

Axiom pow2_54 : ((pow2 54%Z) = 18014398509481984%Z).

Axiom pow2_55 : ((pow2 55%Z) = 36028797018963968%Z).

Axiom pow2_56 : ((pow2 56%Z) = 72057594037927936%Z).

Axiom pow2_57 : ((pow2 57%Z) = 144115188075855872%Z).

Axiom pow2_58 : ((pow2 58%Z) = 288230376151711744%Z).

Axiom pow2_59 : ((pow2 59%Z) = 576460752303423488%Z).

Axiom pow2_60 : ((pow2 60%Z) = 1152921504606846976%Z).

Axiom pow2_61 : ((pow2 61%Z) = 2305843009213693952%Z).

Axiom pow2_62 : ((pow2 62%Z) = 4611686018427387904%Z).

Axiom pow2_63 : ((pow2 63%Z) = 9223372036854775808%Z).

Parameter bv : Type.

Axiom size_positive : (1%Z <  32%Z)%Z.

Parameter nth: bv -> Z -> bool.

Parameter bvzero: bv.

Axiom Nth_zero : forall (n:Z), ((0%Z <= n)%Z /\ (n <  32%Z)%Z) ->
  ((nth bvzero n) = false).

Parameter bvone: bv.

Axiom Nth_one : forall (n:Z), ((0%Z <= n)%Z /\ (n <  32%Z)%Z) -> ((nth bvone
  n) = true).

(* Why3 assumption *)
Definition eq(v1:bv) (v2:bv): Prop := forall (n:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((nth v1 n) = (nth v2 n)).

Axiom extensionality : forall (v1:bv) (v2:bv), (eq v1 v2) -> (v1 = v2).

Parameter bw_and: bv -> bv -> bv.

Axiom Nth_bw_and : forall (v1:bv) (v2:bv) (n:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((nth (bw_and v1 v2) n) = (andb (nth v1 n) (nth v2 n))).

Parameter bw_or: bv -> bv -> bv.

Axiom Nth_bw_or : forall (v1:bv) (v2:bv) (n:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((nth (bw_or v1 v2) n) = (orb (nth v1 n) (nth v2 n))).

Parameter bw_xor: bv -> bv -> bv.

Axiom Nth_bw_xor : forall (v1:bv) (v2:bv) (n:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((nth (bw_xor v1 v2) n) = (xorb (nth v1 n) (nth v2 n))).

Axiom Nth_bw_xor_v1true : forall (v1:bv) (v2:bv) (n:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ ((nth v1 n) = true)) -> ((nth (bw_xor v1 v2)
  n) = (negb (nth v2 n))).

Axiom Nth_bw_xor_v1false : forall (v1:bv) (v2:bv) (n:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ ((nth v1 n) = false)) -> ((nth (bw_xor v1 v2)
  n) = (nth v2 n)).

Axiom Nth_bw_xor_v2true : forall (v1:bv) (v2:bv) (n:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ ((nth v2 n) = true)) -> ((nth (bw_xor v1 v2)
  n) = (negb (nth v1 n))).

Axiom Nth_bw_xor_v2false : forall (v1:bv) (v2:bv) (n:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ ((nth v2 n) = false)) -> ((nth (bw_xor v1 v2)
  n) = (nth v1 n)).

Parameter bw_not: bv -> bv.

Axiom Nth_bw_not : forall (v:bv) (n:Z), ((0%Z <= n)%Z /\ (n <  32%Z)%Z) ->
  ((nth (bw_not v) n) = (negb (nth v n))).

Parameter lsr: bv -> Z -> bv.

Axiom lsr_nth_low : forall (b:bv) (n:Z) (s:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ (((0%Z <= s)%Z /\ (s <  32%Z)%Z) /\
  ((n + s)%Z <  32%Z)%Z)) -> ((nth (lsr b s) n) = (nth b (n + s)%Z)).

Axiom lsr_nth_high : forall (b:bv) (n:Z) (s:Z), (((0%Z <= n)%Z /\
  (n <  32%Z)%Z) /\ (((0%Z <= s)%Z /\ (s <  32%Z)%Z) /\
  (32%Z <= (n + s)%Z)%Z)) -> ((nth (lsr b s) n) = false).

Parameter asr: bv -> Z -> bv.

Axiom asr_nth_low : forall (b:bv) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((0%Z <= s)%Z -> (((n + s)%Z <  32%Z)%Z -> ((nth (asr b
  s) n) = (nth b (n + s)%Z)))).

Axiom asr_nth_high : forall (b:bv) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((0%Z <= s)%Z -> ((32%Z <= (n + s)%Z)%Z -> ((nth (asr b
  s) n) = (nth b (32%Z - 1%Z)%Z)))).

Parameter lsl: bv -> Z -> bv.

Axiom lsl_nth_high : forall (b:bv) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((0%Z <= s)%Z -> ((0%Z <= (n - s)%Z)%Z -> ((nth (lsl b s)
  n) = (nth b (n - s)%Z)))).

Axiom lsl_nth_low : forall (b:bv) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  32%Z)%Z) -> ((0%Z <= s)%Z -> (((n - s)%Z <  0%Z)%Z -> ((nth (lsl b s)
  n) = false))).

Parameter to_nat_sub: bv -> Z -> Z -> Z.

Axiom to_nat_sub_zero : forall (b:bv) (j:Z) (i:Z), (((0%Z <= i)%Z /\
  (i <= j)%Z) /\ (j <  32%Z)%Z) -> (((nth b j) = false) -> ((to_nat_sub b j
  i) = (to_nat_sub b (j - 1%Z)%Z i))).

Axiom to_nat_sub_one : forall (b:bv) (j:Z) (i:Z), (((0%Z <= i)%Z /\
  (i <= j)%Z) /\ (j <  32%Z)%Z) -> (((nth b j) = true) -> ((to_nat_sub b j
  i) = ((pow2 (j - i)%Z) + (to_nat_sub b (j - 1%Z)%Z i))%Z)).

Axiom to_nat_sub_high : forall (b:bv) (j:Z) (i:Z), (j <  i)%Z ->
  ((to_nat_sub b j i) = 0%Z).

Axiom to_nat_of_zero2 : forall (b:bv) (i:Z) (j:Z), (((j <  32%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <  k)%Z) -> ((nth b k) = false)) -> ((to_nat_sub b j
  0%Z) = (to_nat_sub b i 0%Z))).

Axiom to_nat_of_zero : forall (b:bv) (i:Z) (j:Z), (((j <  32%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <= k)%Z) -> ((nth b k) = false)) -> ((to_nat_sub b j i) = 0%Z)).

Axiom to_nat_of_one : forall (b:bv) (i:Z) (j:Z), (((j <  32%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <= k)%Z) -> ((nth b k) = true)) -> ((to_nat_sub b j
  i) = ((pow2 ((j - i)%Z + 1%Z)%Z) - 1%Z)%Z)).

Axiom to_nat_sub_footprint : forall (b1:bv) (b2:bv) (j:Z) (i:Z),
  ((j <  32%Z)%Z /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((i <= k)%Z /\
  (k <= j)%Z) -> ((nth b1 k) = (nth b2 k))) -> ((to_nat_sub b1 j
  i) = (to_nat_sub b2 j i))).

Parameter from_int: Z -> bv.

Axiom Div_inf1 : forall (x:Z) (y:Z), ((0%Z <= x)%Z /\ (x <  y)%Z) ->
  ((int.EuclideanDivision.div x y) = 0%Z).

Axiom Div_inf_neg : forall (x:Z) (y:Z), ((0%Z <  x)%Z /\ (x <= y)%Z) ->
  ((int.EuclideanDivision.div (-x)%Z y) = (-1%Z)%Z).

Axiom Div_pow : forall (x:Z) (i:Z), (((pow2 (i - 1%Z)%Z) <= x)%Z /\
  (x <  (pow2 i))%Z) -> ((int.EuclideanDivision.div x
  (pow2 (i - 1%Z)%Z)) = 1%Z).

Axiom Div_pow2 : forall (x:Z) (i:Z), (((-(pow2 i))%Z <= x)%Z /\
  (x <  (-(pow2 (i - 1%Z)%Z))%Z)%Z) -> ((int.EuclideanDivision.div x
  (pow2 (i - 1%Z)%Z)) = (-2%Z)%Z).

Axiom Mod_01 : forall (y:Z), (~ (y = 0%Z)) ->
  ((int.EuclideanDivision.mod1 0%Z y) = 0%Z).

Axiom Mod_1y : forall (y:Z), (1%Z <  y)%Z -> ((int.EuclideanDivision.mod1 1%Z
  y) = 1%Z).

Axiom Mod_neg1y : forall (y:Z), (1%Z <  y)%Z ->
  ((int.EuclideanDivision.mod1 (-1%Z)%Z y) = 1%Z).

Axiom Mod_pow2 : forall (x:Z) (i:Z),
  ((int.EuclideanDivision.mod1 (x + (pow2 i))%Z
  2%Z) = (int.EuclideanDivision.mod1 x 2%Z)).

Axiom nth_from_int_high_even : forall (n:Z) (i:Z), (((i <  32%Z)%Z /\
  (0%Z <= i)%Z) /\ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n
  (pow2 i)) 2%Z) = 0%Z)) -> ((nth (from_int n) i) = false).

Axiom nth_from_int_high_odd : forall (n:Z) (i:Z), (((i <  32%Z)%Z /\
  (0%Z <= i)%Z) /\
  ~ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth (from_int n) i) = true).

Axiom nth_from_int_low_even : forall (n:Z), ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z) -> ((nth (from_int n) 0%Z) = false).

Axiom nth_from_int_low_odd : forall (n:Z), (~ ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z)) -> ((nth (from_int n) 0%Z) = true).

Axiom pow2i : forall (i:Z), (0%Z <= i)%Z -> ~ ((pow2 i) = 0%Z).

Axiom nth_from_int_0 : forall (i:Z), ((i <  32%Z)%Z /\ (0%Z <= i)%Z) ->
  ((nth (from_int 0%Z) i) = false).

Parameter from_int2c: Z -> bv.

Axiom nth_sign_positive : forall (n:Z), (0%Z <= n)%Z -> ((nth (from_int2c n)
  (32%Z - 1%Z)%Z) = false).

Axiom nth_sign_negative : forall (n:Z), (n <  0%Z)%Z -> ((nth (from_int2c n)
  (32%Z - 1%Z)%Z) = true).

Axiom nth_from_int2c_high_even : forall (n:Z) (i:Z),
  (((i <  (32%Z - 1%Z)%Z)%Z /\ (0%Z <= i)%Z) /\
  ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth (from_int2c n) i) = false).

Axiom nth_from_int2c_high_odd : forall (n:Z) (i:Z),
  (((i <  (32%Z - 1%Z)%Z)%Z /\ (0%Z <= i)%Z) /\
  ~ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth (from_int2c n) i) = true).

Axiom nth_from_int2c_low_even : forall (n:Z), ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z) -> ((nth (from_int2c n) 0%Z) = false).

Axiom nth_from_int2c_low_odd : forall (n:Z),
  (~ ((int.EuclideanDivision.mod1 n 2%Z) = 0%Z)) -> ((nth (from_int2c n)
  0%Z) = true).

Axiom nth_from_int2c_0 : forall (i:Z), ((i <  32%Z)%Z /\ (0%Z <= i)%Z) ->
  ((nth (from_int2c 0%Z) i) = false).

Axiom nth_from_int2c_plus_pow2 : forall (x:Z) (k:Z) (i:Z), ((0%Z <= k)%Z /\
  (k <  i)%Z) -> ((nth (from_int2c (x + (pow2 i))%Z) k) = (nth (from_int2c x)
  k)).

Parameter bv1 : Type.

Axiom size_positive1 : (1%Z <  64%Z)%Z.

Parameter nth1: bv1 -> Z -> bool.

Parameter bvzero1: bv1.

Axiom Nth_zero1 : forall (n:Z), ((0%Z <= n)%Z /\ (n <  64%Z)%Z) ->
  ((nth1 bvzero1 n) = false).

Parameter bvone1: bv1.

Axiom Nth_one1 : forall (n:Z), ((0%Z <= n)%Z /\ (n <  64%Z)%Z) ->
  ((nth1 bvone1 n) = true).

(* Why3 assumption *)
Definition eq1(v1:bv1) (v2:bv1): Prop := forall (n:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((nth1 v1 n) = (nth1 v2 n)).

Axiom extensionality1 : forall (v1:bv1) (v2:bv1), (eq1 v1 v2) -> (v1 = v2).

Parameter bw_and1: bv1 -> bv1 -> bv1.

Axiom Nth_bw_and1 : forall (v1:bv1) (v2:bv1) (n:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((nth1 (bw_and1 v1 v2) n) = (andb (nth1 v1 n) (nth1 v2
  n))).

Parameter bw_or1: bv1 -> bv1 -> bv1.

Axiom Nth_bw_or1 : forall (v1:bv1) (v2:bv1) (n:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((nth1 (bw_or1 v1 v2) n) = (orb (nth1 v1 n) (nth1 v2
  n))).

Parameter bw_xor1: bv1 -> bv1 -> bv1.

Axiom Nth_bw_xor1 : forall (v1:bv1) (v2:bv1) (n:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((nth1 (bw_xor1 v1 v2) n) = (xorb (nth1 v1 n) (nth1 v2
  n))).

Axiom Nth_bw_xor_v1true1 : forall (v1:bv1) (v2:bv1) (n:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ ((nth1 v1 n) = true)) -> ((nth1 (bw_xor1 v1 v2)
  n) = (negb (nth1 v2 n))).

Axiom Nth_bw_xor_v1false1 : forall (v1:bv1) (v2:bv1) (n:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ ((nth1 v1 n) = false)) -> ((nth1 (bw_xor1 v1 v2)
  n) = (nth1 v2 n)).

Axiom Nth_bw_xor_v2true1 : forall (v1:bv1) (v2:bv1) (n:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ ((nth1 v2 n) = true)) -> ((nth1 (bw_xor1 v1 v2)
  n) = (negb (nth1 v1 n))).

Axiom Nth_bw_xor_v2false1 : forall (v1:bv1) (v2:bv1) (n:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ ((nth1 v2 n) = false)) -> ((nth1 (bw_xor1 v1 v2)
  n) = (nth1 v1 n)).

Parameter bw_not1: bv1 -> bv1.

Axiom Nth_bw_not1 : forall (v:bv1) (n:Z), ((0%Z <= n)%Z /\ (n <  64%Z)%Z) ->
  ((nth1 (bw_not1 v) n) = (negb (nth1 v n))).

Parameter lsr1: bv1 -> Z -> bv1.

Axiom lsr_nth_low1 : forall (b:bv1) (n:Z) (s:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ (((0%Z <= s)%Z /\ (s <  64%Z)%Z) /\
  ((n + s)%Z <  64%Z)%Z)) -> ((nth1 (lsr1 b s) n) = (nth1 b (n + s)%Z)).

Axiom lsr_nth_high1 : forall (b:bv1) (n:Z) (s:Z), (((0%Z <= n)%Z /\
  (n <  64%Z)%Z) /\ (((0%Z <= s)%Z /\ (s <  64%Z)%Z) /\
  (64%Z <= (n + s)%Z)%Z)) -> ((nth1 (lsr1 b s) n) = false).

Parameter asr1: bv1 -> Z -> bv1.

Axiom asr_nth_low1 : forall (b:bv1) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((0%Z <= s)%Z -> (((n + s)%Z <  64%Z)%Z -> ((nth1 (asr1 b
  s) n) = (nth1 b (n + s)%Z)))).

Axiom asr_nth_high1 : forall (b:bv1) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((0%Z <= s)%Z -> ((64%Z <= (n + s)%Z)%Z -> ((nth1 (asr1 b
  s) n) = (nth1 b (64%Z - 1%Z)%Z)))).

Parameter lsl1: bv1 -> Z -> bv1.

Axiom lsl_nth_high1 : forall (b:bv1) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((0%Z <= s)%Z -> ((0%Z <= (n - s)%Z)%Z -> ((nth1 (lsl1 b
  s) n) = (nth1 b (n - s)%Z)))).

Axiom lsl_nth_low1 : forall (b:bv1) (n:Z) (s:Z), ((0%Z <= n)%Z /\
  (n <  64%Z)%Z) -> ((0%Z <= s)%Z -> (((n - s)%Z <  0%Z)%Z -> ((nth1 (lsl1 b
  s) n) = false))).

Parameter to_nat_sub1: bv1 -> Z -> Z -> Z.

Axiom to_nat_sub_zero1 : forall (b:bv1) (j:Z) (i:Z), (((0%Z <= i)%Z /\
  (i <= j)%Z) /\ (j <  64%Z)%Z) -> (((nth1 b j) = false) -> ((to_nat_sub1 b j
  i) = (to_nat_sub1 b (j - 1%Z)%Z i))).

Axiom to_nat_sub_one1 : forall (b:bv1) (j:Z) (i:Z), (((0%Z <= i)%Z /\
  (i <= j)%Z) /\ (j <  64%Z)%Z) -> (((nth1 b j) = true) -> ((to_nat_sub1 b j
  i) = ((pow2 (j - i)%Z) + (to_nat_sub1 b (j - 1%Z)%Z i))%Z)).

Axiom to_nat_sub_high1 : forall (b:bv1) (j:Z) (i:Z), (j <  i)%Z ->
  ((to_nat_sub1 b j i) = 0%Z).

Axiom to_nat_of_zero21 : forall (b:bv1) (i:Z) (j:Z), (((j <  64%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <  k)%Z) -> ((nth1 b k) = false)) -> ((to_nat_sub1 b j
  0%Z) = (to_nat_sub1 b i 0%Z))).

Axiom to_nat_of_zero1 : forall (b:bv1) (i:Z) (j:Z), (((j <  64%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <= k)%Z) -> ((nth1 b k) = false)) -> ((to_nat_sub1 b j i) = 0%Z)).

Axiom to_nat_of_one1 : forall (b:bv1) (i:Z) (j:Z), (((j <  64%Z)%Z /\
  (i <= j)%Z) /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((k <= j)%Z /\
  (i <= k)%Z) -> ((nth1 b k) = true)) -> ((to_nat_sub1 b j
  i) = ((pow2 ((j - i)%Z + 1%Z)%Z) - 1%Z)%Z)).

Axiom to_nat_sub_footprint1 : forall (b1:bv1) (b2:bv1) (j:Z) (i:Z),
  ((j <  64%Z)%Z /\ (0%Z <= i)%Z) -> ((forall (k:Z), ((i <= k)%Z /\
  (k <= j)%Z) -> ((nth1 b1 k) = (nth1 b2 k))) -> ((to_nat_sub1 b1 j
  i) = (to_nat_sub1 b2 j i))).

Parameter from_int1: Z -> bv1.

Axiom nth_from_int_high_even1 : forall (n:Z) (i:Z), (((i <  64%Z)%Z /\
  (0%Z <= i)%Z) /\ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n
  (pow2 i)) 2%Z) = 0%Z)) -> ((nth1 (from_int1 n) i) = false).

Axiom nth_from_int_high_odd1 : forall (n:Z) (i:Z), (((i <  64%Z)%Z /\
  (0%Z <= i)%Z) /\
  ~ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth1 (from_int1 n) i) = true).

Axiom nth_from_int_low_even1 : forall (n:Z), ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z) -> ((nth1 (from_int1 n) 0%Z) = false).

Axiom nth_from_int_low_odd1 : forall (n:Z), (~ ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z)) -> ((nth1 (from_int1 n) 0%Z) = true).

Axiom pow2i1 : forall (i:Z), (0%Z <= i)%Z -> ~ ((pow2 i) = 0%Z).

Axiom nth_from_int_01 : forall (i:Z), ((i <  64%Z)%Z /\ (0%Z <= i)%Z) ->
  ((nth1 (from_int1 0%Z) i) = false).

Parameter from_int2c1: Z -> bv1.

Axiom nth_sign_positive1 : forall (n:Z), (0%Z <= n)%Z ->
  ((nth1 (from_int2c1 n) (64%Z - 1%Z)%Z) = false).

Axiom nth_sign_negative1 : forall (n:Z), (n <  0%Z)%Z ->
  ((nth1 (from_int2c1 n) (64%Z - 1%Z)%Z) = true).

Axiom nth_from_int2c_high_even1 : forall (n:Z) (i:Z),
  (((i <  (64%Z - 1%Z)%Z)%Z /\ (0%Z <= i)%Z) /\
  ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth1 (from_int2c1 n) i) = false).

Axiom nth_from_int2c_high_odd1 : forall (n:Z) (i:Z),
  (((i <  (64%Z - 1%Z)%Z)%Z /\ (0%Z <= i)%Z) /\
  ~ ((int.EuclideanDivision.mod1 (int.EuclideanDivision.div n (pow2 i))
  2%Z) = 0%Z)) -> ((nth1 (from_int2c1 n) i) = true).

Axiom nth_from_int2c_low_even1 : forall (n:Z), ((int.EuclideanDivision.mod1 n
  2%Z) = 0%Z) -> ((nth1 (from_int2c1 n) 0%Z) = false).

Axiom nth_from_int2c_low_odd1 : forall (n:Z),
  (~ ((int.EuclideanDivision.mod1 n 2%Z) = 0%Z)) -> ((nth1 (from_int2c1 n)
  0%Z) = true).

Axiom nth_from_int2c_01 : forall (i:Z), ((i <  64%Z)%Z /\ (0%Z <= i)%Z) ->
  ((nth1 (from_int2c1 0%Z) i) = false).

Axiom nth_from_int2c_plus_pow21 : forall (x:Z) (k:Z) (i:Z), ((0%Z <= k)%Z /\
  (k <  i)%Z) -> ((nth1 (from_int2c1 (x + (pow2 i))%Z)
  k) = (nth1 (from_int2c1 x) k)).

Parameter concat: bv -> bv -> bv1.

Axiom concat_low : forall (b1:bv) (b2:bv), forall (i:Z), ((0%Z <= i)%Z /\
  (i <  32%Z)%Z) -> ((nth1 (concat b1 b2) i) = (nth b2 i)).

Axiom concat_high : forall (b1:bv) (b2:bv), forall (i:Z), ((32%Z <= i)%Z /\
  (i <  64%Z)%Z) -> ((nth1 (concat b1 b2) i) = (nth b1 (i - 32%Z)%Z)).

Parameter pow21: Z -> R.

Axiom Power_01 : ((pow21 0%Z) = 1%R).

Axiom Power_s1 : forall (n:Z), (0%Z <= n)%Z ->
  ((pow21 (n + 1%Z)%Z) = (2%R * (pow21 n))%R).

Axiom Power_p : forall (n:Z), (n <= 0%Z)%Z ->
  ((pow21 (n - 1%Z)%Z) = ((05 / 10)%R * (pow21 n))%R).

Axiom Power_s_all : forall (n:Z),
  ((pow21 (n + 1%Z)%Z) = (2%R * (pow21 n))%R).

Axiom Power_p_all : forall (n:Z),
  ((pow21 (n - 1%Z)%Z) = ((05 / 10)%R * (pow21 n))%R).

Axiom Power_1_2 : ((05 / 10)%R = (Rdiv 1%R 2%R)%R).

Axiom Power_11 : ((pow21 1%Z) = 2%R).

Axiom Power_neg1 : ((pow21 (-1%Z)%Z) = (05 / 10)%R).

Axiom Power_non_null_aux : forall (n:Z), (0%Z <= n)%Z -> ~ ((pow21 n) = 0%R).

Axiom Power_neg_aux : forall (n:Z), (0%Z <= n)%Z ->
  ((pow21 (-n)%Z) = (Rdiv 1%R (pow21 n))%R).

Axiom Power_non_null : forall (n:Z), ~ ((pow21 n) = 0%R).

Axiom Power_neg : forall (n:Z), ((pow21 (-n)%Z) = (Rdiv 1%R (pow21 n))%R).

Axiom Power_sum_aux : forall (n:Z) (m:Z), (0%Z <= m)%Z ->
  ((pow21 (n + m)%Z) = ((pow21 n) * (pow21 m))%R).

Axiom Power_sum1 : forall (n:Z) (m:Z),
  ((pow21 (n + m)%Z) = ((pow21 n) * (pow21 m))%R).

Axiom Pow2_int_real : forall (x:Z), (0%Z <= x)%Z ->
  ((pow21 x) = (IZR (pow2 x))).

Parameter double_of_bv64: bv1 -> R.

Parameter sign_value: bool -> R.

Axiom sign_value_false : ((sign_value false) = 1%R).

Axiom sign_value_true : ((sign_value true) = (-1%R)%R).

Axiom zero : forall (b:bv1), (((to_nat_sub1 b 62%Z 52%Z) = 0%Z) /\
  ((to_nat_sub1 b 51%Z 0%Z) = 0%Z)) -> ((double_of_bv64 b) = 0%R).

Axiom sign_of_double_positive : forall (b:bv1), ((nth1 b 63%Z) = false) ->
  (0%R <= (double_of_bv64 b))%R.

Axiom sign_of_double_negative : forall (b:bv1), ((nth1 b 63%Z) = true) ->
  ((double_of_bv64 b) <= 0%R)%R.

Axiom double_of_bv64_value : forall (b:bv1), ((0%Z <  (to_nat_sub1 b 62%Z
  52%Z))%Z /\ ((to_nat_sub1 b 62%Z 52%Z) <  2047%Z)%Z) ->
  ((double_of_bv64 b) = (((sign_value (nth1 b 63%Z)) * (pow21 ((to_nat_sub1 b
  62%Z 52%Z) - 1023%Z)%Z))%R * (1%R + ((IZR (to_nat_sub1 b 51%Z
  0%Z)) * (pow21 (-52%Z)%Z))%R)%R)%R).

Axiom jp0_30 : forall (i:Z), ((0%Z <= i)%Z /\ (i <  30%Z)%Z) ->
  ((nth (from_int 2147483648%Z) i) = false).

Axiom nth_const1 : forall (i:Z), ((0%Z <= i)%Z /\ (i <= 30%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z))
  i) = false).

Axiom nth_const2 : ((nth1 (concat (from_int 1127219200%Z)
  (from_int 2147483648%Z)) 31%Z) = true).

Axiom nth_const3 : forall (i:Z), ((32%Z <= i)%Z /\ (i <= 51%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z))
  i) = false).

Axiom nth_const4 : forall (i:Z), ((52%Z <= i)%Z /\ (i <= 53%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z)) i) = true).

Axiom nth_const5 : forall (i:Z), ((54%Z <= i)%Z /\ (i <= 55%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z))
  i) = false).

Axiom nth_const6 : forall (i:Z), ((56%Z <= i)%Z /\ (i <= 57%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z)) i) = true).

Axiom nth_const7 : forall (i:Z), ((58%Z <= i)%Z /\ (i <= 61%Z)%Z) ->
  ((nth1 (concat (from_int 1127219200%Z) (from_int 2147483648%Z))
  i) = false).

Axiom nth_const8 : ((nth1 (concat (from_int 1127219200%Z)
  (from_int 2147483648%Z)) 62%Z) = true).

Axiom nth_const9 : ((nth1 (concat (from_int 1127219200%Z)
  (from_int 2147483648%Z)) 63%Z) = false).

Axiom sign_const : ((nth1 (concat (from_int 1127219200%Z)
  (from_int 2147483648%Z)) 63%Z) = false).

Open Scope Z_scope.

(* Why3 goal *)
Theorem exp_const : ((to_nat_sub1 (concat (from_int 1127219200%Z)
  (from_int 2147483648%Z)) 62%Z 52%Z) = 1075%Z).
(* YOU MAY EDIT THE PROOF BELOW *)
rewrite to_nat_sub_one1; auto with zarith.
  2: apply nth_const8.
replace (62 - 52) with 10 by omega.
 rewrite pow2_10.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const7; auto with zarith.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const7; auto with zarith.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const7; auto with zarith.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const7; auto with zarith.
rewrite to_nat_sub_one1; auto with zarith.
  2: apply nth_const6; auto with zarith.
replace (62 - 1 - 1 - 1 - 1 - 1 - 52) with 5 by omega.
 rewrite pow2_5.
rewrite to_nat_sub_one1; auto with zarith.
  2: apply nth_const6; auto with zarith.
replace (62 - 1 - 1 - 1 - 1 - 1 - 1 -  52) with 4 by omega.
 rewrite pow2_4.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const5; auto with zarith.
rewrite to_nat_sub_zero1; auto with zarith.
  2: apply nth_const5; auto with zarith.
rewrite to_nat_of_one1; auto with zarith.
replace (62 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 52) with 1 by omega.
replace (1+1) with 2 by omega.
 rewrite pow2_2; auto.
intros.
apply nth_const4; auto with zarith.
Qed.