Require Import GradebookModel. (* change this module name or move to here *)
Require Import List.

Set Implicit Arguments.

(*
Module Config.
  (* privacy respecting queries can probably stay the same *)
  Definition P (cfg: Config) (q: Command) := True.
  (* likewise with correct queries *)
  Definition E (cfg: Config) (q: Command) := True.
  (* todo add definition of privacy and wellformedness of q *)
  Parameter P_dec : forall cfg q, {P cfg q} + {~P cfg q}. (* privacy *)
  Parameter E_dec : forall cfg q, {E cfg q} + {~E cfg q}. (* valid query *)
End Config.
*)

Module Type Model.

  Parameter M : Type.
  Parameter I : Config -> M -> Prop.

  Parameter mutate : forall (cfg: Config) (m : M) (pfI : I cfg m) (q: Command), (Status * M).
  Parameter PreservesI : forall (cfg: Config) (m : M) (pfI: I cfg m) (q: Command),
    I cfg (snd (mutate pfI q)).

End Model.

(* equivalences of models *)
Module Type ModelIso.
  Declare Module S : Model.
  Declare Module T : Model.

  Parameter f : forall cfg, sigT (S.I cfg) -> sigT (T.I cfg).

  Parameter Correct : forall (cfg : Config) (m : S.M) (pfI : S.I cfg m) (q : Command),
    let t    := f (existT (S.I cfg) m pfI) in
    let sres := @S.mutate cfg m pfI q in
    let tres := @T.mutate cfg (projT1 t) (projT2 t) q in
    let t'   := @f cfg (@existT S.M (S.I cfg) (snd sres) (@S.PreservesI cfg m pfI q)) in
    fst sres = fst tres /\ projT1 t' = snd tres.

End ModelIso.

Module Spec <: Model.
  Definition M : Type := list (ID * list Grade).
  Definition I (cfg : Config) (m : M) : Prop :=
    store_inv m cfg = true.

  Definition mutate (cfg: Config) (m : M) (pfI : I cfg m) (q: Command) : (Status * M) :=
    if private cfg q
    then match q with
           | SetGrade id pass x a g =>
             if inbounds g a (totals cfg)
             then match lookup x m with
                    | None => (ERR_NOINV, m)
                    | Some g' => (OK, insert x (nth_set g a g') m)
                  end
             else (ERR_BADGRADE, m)
           | GetGrade id pass x a =>
             match lookup x m with
               | None => (ERR_NOINV, m)
               | Some g' => match nth_get a g' with
                              | None => (ERR_BADGRADE, m)
                              | Some g'' => (RET g'', m)
                            end
             end
           | Average  id pass a =>
             if validAssignment cfg a then
               match proj a m with
                 | None => (ERR_NOINV, m)
                 | Some x => (RET (avg x), m)
               end
               else (ERR_BADGRADE, m)
         end
      else (ERR_NOTPRIVATE, m).

  Theorem PreservesI : forall (cfg: Config) (m : M) (pfI: I cfg m) (q: Command),
    I cfg (snd (mutate pfI q)).
  Proof.
    unfold I. intros. unfold mutate.
    destruct q;
      [ exhaustive assumption
      | simpl in *; exhaustive assumption
      | simpl in *; exhaustive assumption ].
    unfold store_inv in *. all.
    Hint Resolve insert_preserves_noduples.
    Hint Resolve insert_preserves_total_dec.
    Hint Resolve insert_preserves_inv.
    each; auto.
  Qed.

End Spec.

(**
Module StoreModel <: Model.
  Require Import GradebookStoreImpl.
  Require Import Store.
  Export GradesStoreMapping.

  Definition M : Type := forall (cfg : Config), Table (S (length (totals cfg))).
  Definition I (cfg : Config) (m : M) : Prop :=
    True. (** TODO **)

  (** TODO **)
  Definition mutate (cfg: Config) (m : M) (pfI : I cfg m) (q: Command) : (Status * M) :=
    if private cfg q
    then match q with
           | SetGrade id pass x a g => (ERR_BADGRADE, m)
(*
             if inbounds g a (totals cfg)
             then match lookup x m with
                    | None => (ERR_NOINV, m)
                    | Some g' => (OK, insert x (nth_set g a g') m)
                  end
             else (ERR_BADGRADE, m)
*)
           | GetGrade id pass x a =>
             match nthget a (select (get_query id cfg) (m cfg)) with
               | None => (ERR_NOINV, m) (** MAYBE **)
               | Some None => (ERR_BADGRADE, m) (** MAYBE **)
               | Some (Some g') => (RET g', m)
             end
           | Average  id pass a =>  (ERR_BADGRADE, m)
(*
             if validAssignment cfg a then
               match proj a m with
                 | None => (ERR_NOINV, m)
                 | Some x => (RET (avg x), m)
               end
               else (ERR_BADGRADE, m)
*)
         end
      else (ERR_NOTPRIVATE, m).

  Theorem PreservesI : forall (cfg: Config) (m : M) (pfI: I cfg m) (q: Command),
    I cfg (snd (mutate pfI q)).
  Proof.
  Admitted.

End StoreModel.
**)

(**
(* our spec is a distinguished model *)
Module Spec : Model.
  Definition  M := ID -> Assignment -> option Grade.

  Definition I (c : Config) (m : M) : Prop :=
    fold_right (fun (p : Prop) (rr : Prop) => p /\ rr) True
      (flat_map (fun id : ID =>
        map (fun x : Assignment * Grade =>
          match m id (fst x) with
            | Some g => g <= (snd x)
            | None => False
          end) (combine (seq 0 (length (totals c))) (totals c)))
      (students c)).

  Parameter mutate : forall (cfg: Config) (q: Command) (m: M), (Status * M).

  Theorem pres_I : forall cfg q m,
    I cfg (snd (mutate cfg q m)).
  Admitted.
End Spec.

(* The model of the imperative implementation *)
Module TupleModel : Model.
   Definition  M := ID -> Assignment -> option Grade.

   Definition  I : Config -> M -> Prop.
   Admitted.

  Parameter mutate : forall (cfg: Config) (q: Command) (m: M), (Status * M).

    Theorem pres_I : forall cfg q m,
    I cfg (snd (mutate cfg q m)).
    Admitted.
End TupleModel.

Module CorrectRefl (X: Model) : ModelIso with Module S := X
                                         with Module T := X.
  Module S := X.
  Module T := X.

  Definition f cfg (x: sigT (S.I cfg)) := x.
  Definition g cfg (y: sigT (T.I cfg)) := y.

  Theorem Correct : forall cfg t,
   projT1 (f (g (cfg:=cfg) t)) = projT1 t.
  Proof.
    compute. intros. eauto. Qed.
End CorrectRefl.

(* todo the isomorphism of the models isn't quite implementation
   correctness.  technically we need to take into account
   the outer wrapping where the invariant is checked, where
   privacy is checked, etc.  But that should be easy enough
   to build off of this. *)
Module ImplCorrect : ModelIso with Module S := Spec
                              with Module T := TupleModel.
  Module S := Spec.
  Module T := TupleModel.

  Definition f cfg (x: sigT (S.I cfg)) : sigT (T.I cfg).
  Admitted.
  Definition g cfg (y: sigT (T.I cfg)) : sigT (S.I cfg).
  Admitted.

  Theorem Correct : forall cfg t,
   projT1 (f (g (cfg:=cfg) t)) = projT1 t.
  Proof.
  Admitted.
End ImplCorrect.

(* we want to say that given a model and
   an imperative realization of that model, that
   we can build an app
*)

Module Type Impl.
  Declare Module M : Model.
End Impl.

Require Import Ynot.

Module Type ImplF (X: Model).
 Export X.
  Parameter T: Set.
  Parameter rep : forall (t: T) (m: M), hprop.

  Open Local Scope hprop_scope.
(*
  Parameter imp_mutate : forall cfg t q (m: [M]),
    STsep (m ~~ rep t m * [I cfg m] * [P cfg q] * [E cfg q])
          (fun r : Status => m ~~ let (r', m') := (mutate cfg q m)
                                  in  [r' = r] * rep t m').
*)
End ImplF.

Require Import AppServer.

Module StoreImpl <: Impl with Module M := TupleModel.
  Module M := TupleModel.
End StoreImpl.

Module BuildApp (Y: Impl) : App.
 Module X := Y.M.

 Print X.
 Export C.
 Open Local Scope hprop_scope.
 Definition Q := Command.
 Definition T : Set := (Config * C.T)%type.
 Definition RR : Set := Status.
 Definition M := (Config * X.M)%type.
 Definition rep (t: T) (m: M) : hprop := [fst t = fst m] * C.rep (snd t) (snd m).
 Definition I (m: M) : Prop := X.I (fst m) (snd m).

 Definition func := X.mutate.

End BuildApp.

(* these wrappers may come in handy
   for above. *)
Module WrapModel (X: Model).
  Export X.

  Definition wrap cfg q m (pf_I: I cfg m) :=
    match P_dec cfg q with
      | left  pf_P => match E_dec cfg q with
                        | left  pf_E => mutate cfg q m
                        | right pf_badgrade => (ERR_BADGRADE, m)
                      end
      | right pf_notpriv => (ERR_NOTPRIVATE, m)
    end.
End WrapModel.

Require Import Ynot.
Module WrapImpl (X: Model) (Y: Impl).
 Module Z := Y X.
 Export Z.
 Module W := WrapModel X.
 Export W.

 Open Local Scope stsepi_scope.
 Open Local Scope hprop_scope.
 Definition imp_wrap cfg t q m :
  STsep (m ~~ rep t m * [I cfg m] * [P cfg q] * [E cfg q])
   (fun r : Status => m ~~ Exists pf1 :@ I cfg m,
                           let (r', m') := (wrap q pf1 )
                           in  [r' = r] * rep t m').
 Admitted.
End WrapImpl.

Require Import Ynot.

Require Import Store.

(* And we need to do this as well
Module StoreImpl (s: Store) : Impl TupleModel .

End StoreImpl.
*)
**)


(** * Induction: Proof by Induction *)

(* 10152160137 陈弈君 *)

(** Before getting started, we need to import all of our
    definitions from the previous chapter: *)

Require Export Basics.

(** For the [Require Export] to work, you first need to use
    [coqc] to compile [Basics.v] into [Basics.vo].  This is like
    making a [.class] file from a [.java] file, or a [.o] file from a
    [.c] file.  There are two ways to do it:

     - In CoqIDE:

         Open [Basics.v].  In the "Compile" menu, click on "Compile
         Buffer".

     - From the command line: Either

         [make Basics.vo]

       (assuming you've downloaded the whole LF directory and have a
       working [make] command) or

         [coqc Basics.v]

       (which should work regardless).

    If you have trouble (e.g., if you get complaints about missing
    identifiers later in the file), it may be because the "load path"
    for Coq is not set up correctly.  The [Print LoadPath.] command may
    be helpful in sorting out such issues.

    In particular, if you see a message like

        [Compiled library Foo makes inconsistent assumptions over
        library Coq.Init.Bar]

    you should check whether you have multiple installations of Coq on
    your machine.  If so, it may be that commands (like [coqc]) that
    you execute in a terminal window are getting a different version of
    Coq than commands executed by Proof General or CoqIDE.

    One more tip for CoqIDE users: If you see messages like [Error:
    Unable to locate library Basics], a likely reason is
    inconsistencies between compiling things _within CoqIDE_ vs _using
    coqc_ from the command line.  This typically happens when there are
    two incompatible versions of [coqc] installed on your system (one
    associated with CoqIDE, and one associated with [coqc] from the
    terminal).  The workaround for this situation is compiling using
    CoqIDE only (i.e. choosing "make" from the menu), and avoiding
    using [coqc] directly at all. *)

(* ################################################################# *)
(** * Proof by Induction *)

(** We proved in the last chapter that [0] is a neutral element
    for [+] on the left, using an easy argument based on
    simplification.  We also observed that proving the fact that it is
    also a neutral element on the _right_... *)

Theorem plus_n_O_firsttry : forall n:nat,
  n = n + 0.

(** ... can't be done in the same simple way.  Just applying
  [reflexivity] doesn't work, since the [n] in [n + 0] is an arbitrary
  unknown number, so the [match] in the definition of [+] can't be
  simplified.  *)

Proof.
  intros n.
  simpl. (* Does nothing! *)
Abort.

(** And reasoning by cases using [destruct n] doesn't get us much
    further: the branch of the case analysis where we assume [n = 0]
    goes through fine, but in the branch where [n = S n'] for some [n'] we
    get stuck in exactly the same way. *)

Theorem plus_n_O_secondtry : forall n:nat,
  n = n + 0.
Proof.
  intros n. destruct n as [| n'].
  - (* n = 0 *)
    reflexivity. (* so far so good... *)
  - (* n = S n' *)
    simpl.       (* ...but here we are stuck again *)
Abort.

(** We could use [destruct n'] to get one step further, but,
    since [n] can be arbitrarily large, if we just go on like this
    we'll never finish. *)

(** To prove interesting facts about numbers, lists, and other
    inductively defined sets, we usually need a more powerful
    reasoning principle: _induction_.

    Recall (from high school, a discrete math course, etc.) the
    _principle of induction over natural numbers_: If [P(n)] is some
    proposition involving a natural number [n] and we want to show
    that [P] holds for all numbers [n], we can reason like this:
         - show that [P(O)] holds;
         - show that, for any [n'], if [P(n')] holds, then so does
           [P(S n')];
         - conclude that [P(n)] holds for all [n].

    In Coq, the steps are the same: we begin with the goal of proving
    [P(n)] for all [n] and break it down (by applying the [induction]
    tactic) into two separate subgoals: one where we must show [P(O)]
    and another where we must show [P(n') -> P(S n')].  Here's how
    this works for the theorem at hand: *)

Theorem plus_n_O : forall n:nat, n = n + 0.
Proof.
  intros n. induction n as [| n' IHn'].
  - (* n = 0 *)    reflexivity.
  - (* n = S n' *) simpl. rewrite <- IHn'. reflexivity.  Qed.

(** Like [destruct], the [induction] tactic takes an [as...]
    clause that specifies the names of the variables to be introduced
    in the subgoals.  Since there are two subgoals, the [as...] clause
    has two parts, separated by [|].  (Strictly speaking, we can omit
    the [as...] clause and Coq will choose names for us.  In practice,
    this is a bad idea, as Coq's automatic choices tend to be
    confusing.)

    In the first subgoal, [n] is replaced by [0].  No new variables
    are introduced (so the first part of the [as...] is empty), and
    the goal becomes [0 = 0 + 0], which follows by simplification.

    In the second subgoal, [n] is replaced by [S n'], and the
    assumption [n' + 0 = n'] is added to the context with the name
    [IHn'] (i.e., the Induction Hypothesis for [n']).  These two names
    are specified in the second part of the [as...] clause.  The goal
    in this case becomes [S n' = (S n') + 0], which simplifies to
    [S n' = S (n' + 0)], which in turn follows from [IHn']. *)

Theorem minus_diag : forall n,
  minus n n = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n. induction n as [| n' IHn'].
  - (* n = 0 *)
    simpl. reflexivity.
  - (* n = S n' *)
    simpl. rewrite -> IHn'. reflexivity.  Qed.

(** (The use of the [intros] tactic in these proofs is actually
    redundant.  When applied to a goal that contains quantified
    variables, the [induction] tactic will automatically move them
    into the context as needed.) *)

(** **** Exercise: 2 stars, recommended (basic_induction)  *)
(** Prove the following using induction. You might need previously
    proven results. *)

Theorem mult_0_r : forall n:nat,
  n * 0 = 0.
Proof.
  induction n as [| n' IHn'].
  - (* n = 0 *)    reflexivity.
  - (* n = S n' *) simpl. rewrite -> IHn'. reflexivity.
Qed.

Theorem plus_n_Sm : forall n m : nat,
  S (n + m) = n + (S m).
Proof.
  intros n m.
  induction n as [| n' IHn'].
  - (* n = 0 *)
    reflexivity.
  - (* n = S n' *)
    simpl. rewrite -> IHn'. reflexivity.
Qed.

Theorem plus_comm : forall n m : nat,
  n + m = m + n.
Proof.
  intros n m.
  induction n as [| n' IHn'].
  - (* n = 0 *)
    simpl. rewrite <- plus_n_O. reflexivity.
  - (* n = S n' *)
    simpl. rewrite <- plus_n_Sm. rewrite <- IHn'. reflexivity.
Qed.

Theorem plus_assoc : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof.
  intros n m p.
  induction n as [| n' IHn'].
  - (* n = 0 *)
    reflexivity.
  - (* n = S n' *)
    simpl. rewrite -> IHn'. reflexivity.
Qed.

(** **** Exercise: 2 stars (double_plus)  *)
(** Consider the following function, which doubles its argument: *)

Fixpoint double (n:nat) :=
  match n with
  | O => O
  | S n' => S (S (double n'))
  end.

(** Use induction to prove this simple fact about [double]: *)

Lemma double_plus : forall n, double n = n + n .
Proof.
  intros n. induction n as [|n' IHn'].
  - simpl. reflexivity.
  - simpl. rewrite IHn'. rewrite <- plus_n_Sm. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (evenb_S)  *)
(** One inconvenient aspect of our definition of [evenb n] is the
    recursive call on [n - 2]. This makes proofs about [evenb n]
    harder when done by induction on [n], since we may need an
    induction hypothesis about [n - 2]. The following lemma gives an
    alternative characterization of [evenb (S n)] that works better
    with induction: *)

Theorem evenb_S : forall n : nat,
  evenb (S n) = negb (evenb n).
Proof.
  intros n. induction n as [|n IHn'].
  - simpl. reflexivity.
  - rewrite -> IHn'. rewrite -> negb_involutive. simpl. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 1 star (destruct_induction)  *)
(** Briefly explain the difference between the tactics [destruct]
    and [induction].

(* [destruct] decompose its value to what it is defined as. *)
(* [induction] adds an Induction Hypothesis to the context which is helpful *)
*)

(* Do not modify the following line: *)
Definition manual_grade_for_destruct_induction : option (prod nat string) := None.
(** [] *)

(* ################################################################# *)
(** * Proofs Within Proofs *)

(** In Coq, as in informal mathematics, large proofs are often
    broken into a sequence of theorems, with later proofs referring to
    earlier theorems.  But sometimes a proof will require some
    miscellaneous fact that is too trivial and of too little general
    interest to bother giving it its own top-level name.  In such
    cases, it is convenient to be able to simply state and prove the
    needed "sub-theorem" right at the point where it is used.  The
    [assert] tactic allows us to do this.  For example, our earlier
    proof of the [mult_0_plus] theorem referred to a previous theorem
    named [plus_O_n].  We could instead use [assert] to state and
    prove [plus_O_n] in-line: *)

Theorem mult_0_plus' : forall n m : nat,
  (0 + n) * m = n * m.
Proof.
  intros n m.
  assert (H: 0 + n = n). { reflexivity. }
  rewrite -> H.
  reflexivity.  Qed.

(** The [assert] tactic introduces two sub-goals.  The first is
    the assertion itself; by prefixing it with [H:] we name the
    assertion [H].  (We can also name the assertion with [as] just as
    we did above with [destruct] and [induction], i.e., [assert (0 + n
    = n) as H].)  Note that we surround the proof of this assertion
    with curly braces [{ ... }], both for readability and so that,
    when using Coq interactively, we can see more easily when we have
    finished this sub-proof.  The second goal is the same as the one
    at the point where we invoke [assert] except that, in the context,
    we now have the assumption [H] that [0 + n = n].  That is,
    [assert] generates one subgoal where we must prove the asserted
    fact and a second subgoal where we can use the asserted fact to
    make progress on whatever we were trying to prove in the first
    place. *)

(** Another example of [assert]... *)

(** For example, suppose we want to prove that [(n + m) + (p + q)
    = (m + n) + (p + q)]. The only difference between the two sides of
    the [=] is that the arguments [m] and [n] to the first inner [+]
    are swapped, so it seems we should be able to use the
    commutativity of addition ([plus_comm]) to rewrite one into the
    other.  However, the [rewrite] tactic is not very smart about
    _where_ it applies the rewrite.  There are three uses of [+] here,
    and it turns out that doing [rewrite -> plus_comm] will affect
    only the _outer_ one... *)

Theorem plus_rearrange_firsttry : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  (* We just need to swap (n + m) for (m + n)... seems
     like plus_comm should do the trick! *)
  rewrite -> plus_comm.
  (* Doesn't work...Coq rewrote the wrong plus! *)
Abort.

(** To use [plus_comm] at the point where we need it, we can introduce
    a local lemma stating that [n + m = m + n] (for the particular [m]
    and [n] that we are talking about here), prove this lemma using
    [plus_comm], and then use it to do the desired rewrite. *)

Theorem plus_rearrange : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  assert (H: n + m = m + n).
  { rewrite -> plus_comm. reflexivity. }
  rewrite -> H. reflexivity.  Qed.

(* ################################################################# *)
(** * Formal vs. Informal Proof *)

(** "_Informal proofs are algorithms; formal proofs are code_." *)

(** What constitutes a successful proof of a mathematical claim?
    The question has challenged philosophers for millennia, but a
    rough and ready definition could be this: A proof of a
    mathematical proposition [P] is a written (or spoken) text that
    instills in the reader or hearer the certainty that [P] is true --
    an unassailable argument for the truth of [P].  That is, a proof
    is an act of communication.

    Acts of communication may involve different sorts of readers.  On
    one hand, the "reader" can be a program like Coq, in which case
    the "belief" that is instilled is that [P] can be mechanically
    derived from a certain set of formal logical rules, and the proof
    is a recipe that guides the program in checking this fact.  Such
    recipes are _formal_ proofs.

    Alternatively, the reader can be a human being, in which case the
    proof will be written in English or some other natural language,
    and will thus necessarily be _informal_.  Here, the criteria for
    success are less clearly specified.  A "valid" proof is one that
    makes the reader believe [P].  But the same proof may be read by
    many different readers, some of whom may be convinced by a
    particular way of phrasing the argument, while others may not be.
    Some readers may be particularly pedantic, inexperienced, or just
    plain thick-headed; the only way to convince them will be to make
    the argument in painstaking detail.  But other readers, more
    familiar in the area, may find all this detail so overwhelming
    that they lose the overall thread; all they want is to be told the
    main ideas, since it is easier for them to fill in the details for
    themselves than to wade through a written presentation of them.
    Ultimately, there is no universal standard, because there is no
    single way of writing an informal proof that is guaranteed to
    convince every conceivable reader.

    In practice, however, mathematicians have developed a rich set of
    conventions and idioms for writing about complex mathematical
    objects that -- at least within a certain community -- make
    communication fairly reliable.  The conventions of this stylized
    form of communication give a fairly clear standard for judging
    proofs good or bad.

    Because we are using Coq in this course, we will be working
    heavily with formal proofs.  But this doesn't mean we can
    completely forget about informal ones!  Formal proofs are useful
    in many ways, but they are _not_ very efficient ways of
    communicating ideas between human beings. *)

(** For example, here is a proof that addition is associative: *)

Theorem plus_assoc' : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof. intros n m p. induction n as [| n' IHn']. reflexivity.
  simpl. rewrite -> IHn'. reflexivity.  Qed.

(** Coq is perfectly happy with this.  For a human, however, it
    is difficult to make much sense of it.  We can use comments and
    bullets to show the structure a little more clearly... *)

Theorem plus_assoc'' : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof.
  intros n m p. induction n as [| n' IHn'].
  - (* n = 0 *)
    reflexivity.
  - (* n = S n' *)
    simpl. rewrite -> IHn'. reflexivity.
Qed.

(** ... and if you're used to Coq you may be able to step
    through the tactics one after the other in your mind and imagine
    the state of the context and goal stack at each point, but if the
    proof were even a little bit more complicated this would be next
    to impossible.

    A (pedantic) mathematician might write the proof something like
    this: *)

(** - _Theorem_: For any [n], [m] and [p],

      n + (m + p) = (n + m) + p.

    _Proof_: By induction on [n].

    - First, suppose [n = 0].  We must show

        0 + (m + p) = (0 + m) + p.

      This follows directly from the definition of [+].

    - Next, suppose [n = S n'], where

        n' + (m + p) = (n' + m) + p.

      We must show

        (S n') + (m + p) = ((S n') + m) + p.

      By the definition of [+], this follows from

        S (n' + (m + p)) = S ((n' + m) + p),

      which is immediate from the induction hypothesis.  _Qed_. *)

(** The overall form of the proof is basically similar, and of
    course this is no accident: Coq has been designed so that its
    [induction] tactic generates the same sub-goals, in the same
    order, as the bullet points that a mathematician would write.  But
    there are significant differences of detail: the formal proof is
    much more explicit in some ways (e.g., the use of [reflexivity])
    but much less explicit in others (in particular, the "proof state"
    at any given point in the Coq proof is completely implicit,
    whereas the informal proof reminds the reader several times where
    things stand). *)

(** **** Exercise: 2 stars, advanced, recommended (plus_comm_informal)  *)
(** Translate your solution for [plus_comm] into an informal proof:

    Theorem: Addition is commutative.

    Proof: (* FILL IN HERE *)
*)

(* Do not modify the following line: *)
Definition manual_grade_for_plus_comm_informal : option (prod nat string) := None.
(** [] *)

(** **** Exercise: 2 stars, optional (beq_nat_refl_informal)  *)
(** Write an informal proof of the following theorem, using the
    informal proof of [plus_assoc] as a model.  Don't just
    paraphrase the Coq tactics into English!

    Theorem: [true = beq_nat n n] for any [n].

    Proof: (* FILL IN HERE *)
*)
(** [] *)

(* ################################################################# *)
(** * More Exercises *)

(** **** Exercise: 3 stars, recommended (mult_comm)  *)
(** Use [assert] to help prove this theorem.  You shouldn't need to
    use induction on [plus_swap]. *)

Theorem plus_swap : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros n m p.
  rewrite -> plus_assoc.
  rewrite -> plus_assoc.
  assert (H: n + m = m + n).
  { rewrite -> plus_comm. reflexivity. }
  rewrite -> H. reflexivity.
Qed.

(** Now prove commutativity of multiplication.  (You will probably
    need to define and prove a separate subsidiary theorem to be used
    in the proof of this one.  You may find that [plus_swap] comes in
    handy.) *)

Theorem mult_comm : forall m n : nat,
  m * n = n * m.
Proof.
  intros m n. destruct m as [| m'].
  - simpl. rewrite -> mult_0_r. reflexivity.
  - induction n as [|n' IHn'].
    + simpl. rewrite -> mult_0_r. reflexivity.
    + simpl. rewrite <- IHn'.
      simpl. rewrite <- mult_n_Sm.
      assert (H1: m' + (n' + m' * n') = n' + (m' + m' * n')).
      { rewrite -> plus_swap. reflexivity. }
      assert (H2: m' * n' + m' = m' + m' * n').
      { rewrite -> plus_comm. reflexivity. }
      rewrite -> H1. rewrite -> H2.
      reflexivity.
Qed.
(** [] *)

(** **** Exercise: 3 stars, optional (more_exercises)  *)
(** Take a piece of paper.  For each of the following theorems, first
    _think_ about whether (a) it can be proved using only
    simplification and rewriting, (b) it also requires case
    analysis ([destruct]), or (c) it also requires induction.  Write
    down your prediction.  Then fill in the proof.  (There is no need
    to turn in your piece of paper; this is just to encourage you to
    reflect before you hack!) *)

Check leb.

Theorem leb_refl : forall n:nat,
  true = leb n n.
Proof.
  induction n as [| n' IHn'].
  - simpl. reflexivity.
  - simpl. rewrite <- IHn'. reflexivity.
Qed.

Theorem zero_nbeq_S : forall n:nat,
  beq_nat 0 (S n) = false.
Proof.
  simpl. reflexivity.
Qed.

Theorem andb_false_r : forall b : bool,
  andb b false = false.
Proof.
  intros b. destruct b.
  - simpl. reflexivity.
  - simpl. reflexivity.
Qed.

Theorem plus_ble_compat_l : forall n m p : nat,
  leb n m = true -> leb (p + n) (p + m) = true.
Proof.
  intros n m p H. induction p as [| p' IHp'].
  - simpl. rewrite H. reflexivity.
  - simpl. rewrite -> IHp'. reflexivity.
Qed.

Theorem S_nbeq_0 : forall n:nat,
  beq_nat (S n) 0 = false.
Proof.
  simpl. reflexivity. Qed.

Theorem mult_1_l : forall n:nat, 1 * n = n.
Proof.
  intros n. simpl. rewrite -> plus_n_O. reflexivity. Qed.

Theorem all3_spec : forall b c : bool,
    orb
      (andb b c)
      (orb (negb b)
               (negb c))
  = true.
Proof.
  intros b c. destruct b.
  - simpl.
    destruct c.
    + simpl. reflexivity.
    + simpl. reflexivity.
  - simpl. reflexivity.
Qed.

Theorem mult_plus_distr_r : forall n m p : nat,
  (n + m) * p = (n * p) + (m * p).
Proof.
  intros n m p. induction p as [| p' IHp'].
  - rewrite -> mult_0_r. rewrite -> mult_0_r. rewrite -> mult_0_r.
    reflexivity.
  - rewrite <- mult_n_Sm.
    rewrite <- mult_n_Sm.
    rewrite <- mult_n_Sm.
    rewrite -> IHp'.
    assert (H1: n * p' + m * p' + (n + m) = n * p' + (m * p' + (n + m))).
    { rewrite <- plus_assoc. reflexivity. }
    rewrite -> H1.
    assert (H2: n * p' + n + (m * p' + m) = n * p' + (n + (m * p' + m))).
    { rewrite <- plus_assoc. reflexivity. }
    rewrite -> H2.
    assert (H3: n + (m * p' + m) = n + m * p' + m).
    { rewrite <- plus_assoc. reflexivity. }
    rewrite -> H3.
    assert (H4: n + m * p' = m * p' + n).
    { rewrite -> plus_comm. reflexivity. }
    rewrite -> H4.
    rewrite -> plus_assoc.
    rewrite -> plus_assoc.
    rewrite -> plus_assoc.
    rewrite -> plus_assoc.
    reflexivity.
Qed.

Theorem mult_assoc : forall n m p : nat,
  n * (m * p) = (n * m) * p.
Proof.
  intros n m p. induction n as [|n'].
  - simpl. reflexivity.
  - simpl.
    rewrite -> IHn'.
    rewrite -> mult_plus_distr_r.
    reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (beq_nat_refl)  *)
(** Prove the following theorem.  (Putting the [true] on the left-hand
    side of the equality may look odd, but this is how the theorem is
    stated in the Coq standard library, so we follow suit.  Rewriting
    works equally well in either direction, so we will have no problem
    using the theorem no matter which way we state it.) *)

Theorem beq_nat_refl : forall n : nat,
  true = beq_nat n n.
Proof.
  intros n. induction n.
  - reflexivity.
  - simpl. rewrite <- IHn. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (plus_swap')  *)
(** The [replace] tactic allows you to specify a particular subterm to
   rewrite and what you want it rewritten to: [replace (t) with (u)]
   replaces (all copies of) expression [t] in the goal by expression
   [u], and generates [t = u] as an additional subgoal. This is often
   useful when a plain [rewrite] acts on the wrong part of the goal.

   Use the [replace] tactic to do a proof of [plus_swap'], just like
   [plus_swap] but without needing [assert (n + m = m + n)]. *)

Theorem plus_swap' : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros n m p.
  rewrite -> plus_assoc.
  rewrite -> plus_assoc.
  replace (n + m) with (m + n). reflexivity.
  rewrite -> plus_comm. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 3 stars, recommended (binary_commute)  *)
(** Recall the [incr] and [bin_to_nat] functions that you
    wrote for the [binary] exercise in the [Basics] chapter.  Prove
    that the following diagram commutes:

                            incr
              bin ----------------------> bin
               |                           |
    bin_to_nat |                           |  bin_to_nat
               |                           |
               v                           v
              nat ----------------------> nat
                             S

    That is, incrementing a binary number and then converting it to
    a (unary) natural number yields the same result as first converting
    it to a natural number and then incrementing.
    Name your theorem [bin_to_nat_pres_incr] ("pres" for "preserves").

    Before you start working on this exercise, copy the definitions
    from your solution to the [binary] exercise here so that this file
    can be graded on its own.  If you want to change your original
    definitions to make the property easier to prove, feel free to
    do so! *)

Lemma S_S_plus : forall a b : nat,
  S a + S b = S (S (a + b)).
Proof.
  intros a b. induction a.
  - simpl. reflexivity.
  - simpl. rewrite <- IHa. reflexivity. Qed.

Theorem bin_to_nat_pres_incr : forall b : bin,
  bin_to_nat (incr b) = S (bin_to_nat b).
Proof.
  intros b. induction b as [|b'|b'].
  - simpl. reflexivity.
  - simpl. reflexivity.
  - simpl. rewrite -> IHb'. rewrite -> S_S_plus. reflexivity.
Qed.

(* Do not modify the following line: *)
Definition manual_grade_for_binary_commute : option (prod nat string) := None.
(** [] *)

(** **** Exercise: 5 stars, advanced (binary_inverse)  *)
(** This exercise is a continuation of the previous exercise about
    binary numbers.  You will need your definitions and theorems from
    there to complete this one; please copy them to this file to make
    it self contained for grading.

    (a) First, write a function to convert natural numbers to binary
        numbers.  Then prove that starting with any natural number,
        converting to binary, then converting back yields the same
        natural number you started with.

    (b) You might naturally think that we should also prove the
        opposite direction: that starting with a binary number,
        converting to a natural, and then back to binary yields the
        same number we started with.  However, this is not true!
        Explain what the problem is.

    (c) Define a "direct" normalization function -- i.e., a function
        [normalize] from binary numbers to binary numbers such that,
        for any binary number b, converting to a natural and then back
        to binary yields [(normalize b)].  Prove it.  (Warning: This
        part is tricky!)

    Again, feel free to change your earlier definitions if this helps
    here. *)

Fixpoint nat_to_bin (n : nat) : bin :=
  match n with
  | 0    => B0
  | S n' => incr (nat_to_bin n')
  end.

Theorem bin_to_nat_nat_to_bin : forall n : nat,
  bin_to_nat(nat_to_bin(n)) = n.
Proof.
  induction n as [|n'].
  - reflexivity.
  - simpl.
    rewrite -> bin_to_nat_pres_incr.
    rewrite -> IHn'.
    reflexivity.
Qed.

(* Fixpoint normalize (b : bin) : bin :=
  match b with
  | B0    => B0
  | B2 b' => match normalize b' with
             | B0 => B0
             | b'' => B2 b''
             end
  | B21 b' => B21 (normalize b')
  end.

Example test_normalize0_1 : normalize B0 = B0.
Proof. reflexivity. Qed.

Example test_normalize0_2 : normalize (B2 B0) = B0.
Proof. reflexivity. Qed.

Example test_normalize0_3 : normalize (B2 (B2 B0)) = B0.
Proof. reflexivity. Qed.

Example test_normalize0_4 : normalize (B2 (B2 (B2 B0))) = B0.
Proof. reflexivity. Qed.

Example test_normalize1 : bin_to_nat (normalize (B21 B0)) = 1.
Proof. reflexivity. Qed.

Example test_normalize2 : bin_to_nat (normalize (B2 (B21 B0))) = 2.
Proof. reflexivity. Qed.

Example test_normalize3 : bin_to_nat (normalize (B21 (B21 B0))) = 3.
Proof. reflexivity. Qed.

Example test_normalize4 : bin_to_nat (normalize (B2 (B2 (B21 B0)))) = 4.
Proof. reflexivity. Qed.

Theorem bin_to_nat_plus : forall b : bin,
  (bin_to_nat b) + (bin_to_nat b) = bin_to_nat (B2 b).
Proof.
  intros b.
  destruct b as [|b'|b'].
  - reflexivity.
  - reflexivity.
  - reflexivity.
Qed.

Lemma nat_to_bin_Sn_plus_n : forall n : nat,
  nat_to_bin (S n + n) = B21 (nat_to_bin n).
Proof.
  intros n.
  induction n.
  reflexivity.
  simpl.
  rewrite -> plus_comm.
  rewrite -> IHn.
  reflexivity.
Qed.

Lemma nat_to_bin_n_plus_Sn : forall n : nat,
  nat_to_bin (n + S n) = B21 (nat_to_bin n).
Proof.
  intros n.
  rewrite -> plus_comm.
  rewrite -> nat_to_bin_Sn_plus_n.
  reflexivity.
Qed.

Lemma nat_to_bin_Sn_Sn : forall n : nat,
  nat_to_bin (S n + S n) = B2 (incr (nat_to_bin n)).
Proof.
  intros n.
  simpl.
  rewrite -> nat_to_bin_n_plus_Sn.
  simpl.
  reflexivity.
Qed.

Theorem nat_to_bin_bin_to_nat : forall b : bin,
  nat_to_bin(bin_to_nat(b)) = normalize b.
Proof.
  intros b.
  induction b as [|b'|b'].
  - reflexivity.
  - simpl.
    rewrite <- IHb'.
    destruct (bin_to_nat b').
    + reflexivity.
    + rewrite -> nat_to_bin_Sn_Sn.
      simpl.
      destruct (nat_to_bin n) as [|b''|b''].
      * reflexivity.
      * simpl. reflexivity.
      * simpl. reflexivity.
  - simpl.
    rewrite <- IHb'.
    destruct (bin_to_nat b') as [|n].
    + reflexivity.
    + rewrite -> nat_to_bin_Sn_Sn.
      simpl. reflexivity.
Qed. *)

(* Do not modify the following line: *)
Definition manual_grade_for_binary_inverse : option (prod nat string) := None.
(** [] *)


Require Import Coq.Classes.Morphisms.
Require Import Coq.Relations.Relation_Definitions.

(* an equivalence for a relation on trivial things, like [unit] *)
Global Instance Equivalence_trivial {A} : Equivalence (fun _ _ : A => True).
Proof.
  repeat constructor.
Qed.


Load LFindLoad.
Load LFindLoad.
From adtind Require Import goal35.

From lfind Require Import LFind.
Lemma lfind_state  (x:natural) (y:natural) (z:natural) (a:natural):@eq natural (qexp x y (mult (mult z x) a)) (qexp x y (mult (mult z a) x)).
Admitted.
From QuickChick Require Import QuickChick.
QCInclude "/home/yousef/lemmafinder/benchmark/_lfind_clam_lf_goal35_mult_qexp_110_mult_assoc/".
QCInclude ".".
Extract Constant defNumTests => "50".
Derive Show for natural.

              Derive Arbitrary for natural.

              Instance Dec_Eq_natural : Dec_Eq natural.

              Proof. dec_eq. Qed.

Open Scope string_scope.

Parameter print : natural -> string -> natural.
Extract Constant print => "Extract.print".
Definition collect_data  (x:natural) (y:natural) (z:natural) (a:natural) :=
 let lfind_var := "x:" ++ "(" ++ show x ++ ")"++ "|" ++"y:" ++ "(" ++ show y ++ ")"++ "|" ++"z:" ++ "(" ++ show z ++ ")"++ "|" ++"a:" ++ "(" ++ show a ++ ")"
 in let lfind_v := print x lfind_var
 in lfind_state lfind_v  y z a.
QuickChick collect_data.
Success.


Require Import List.

Require Import StructTact.StructTactics.

Require Import Chord.Chord.
Require Import Chord.SystemReachable.

Require Import Chord.HandlerLemmas.

Definition timeout_means_active_invariant (gst : global_state) : Prop :=
  forall h t,
    In t (timeouts gst h) ->
    In h (nodes gst).
Hint Unfold timeout_means_active_invariant.

Theorem timeout_means_active_inductive :
  forall gst,
    reachable_st gst ->
    timeout_means_active_invariant gst.
Proof using.
  eapply chord_net_invariant; do 2 autounfold; intros;
    repeat find_rewrite;
    repeat handler_simpl.
  inv_prop initial_st; expand_def.
  destruct (In_dec addr_eq_dec h (nodes gst)); auto.
  assert (timeouts gst h = nil).
  auto.
  repeat find_rewrite.
  exfalso; eapply in_nil; eauto.
Qed.

Lemma timeout_means_active :
  forall gst,
    reachable_st gst ->
    forall t h,
      In t (timeouts gst h) ->
      In h (nodes gst).
Proof.
  intros.
  eapply timeout_means_active_inductive; eauto.
Qed.


Require Import Coq.ZArith.ZArith.
Require Import Crypto.Compilers.Syntax.
Require Import Crypto.Compilers.TypeInversion.
Require Import Crypto.Compilers.Equality.
Require Import Crypto.Compilers.Z.Syntax.
Require Import Crypto.Util.Decidable.
Require Import Crypto.Util.PartiallyReifiedProp.
Require Import Crypto.Util.HProp.
Require Import Crypto.Util.Tactics.BreakMatch.
Require Import Crypto.Util.Tactics.DestructHead.
Require Import Crypto.Util.FixedWordSizesEquality.
Require Import Crypto.Util.NatUtil.

Global Instance dec_eq_base_type : DecidableRel (@eq base_type)
  := base_type_eq_dec.
Global Instance dec_eq_flat_type : DecidableRel (@eq (flat_type base_type)) := _.
Global Instance dec_eq_type : DecidableRel (@eq (type base_type)) := _.

Notation base_type_dec_bl := internal_base_type_dec_bl.
Notation base_type_dec_lb := internal_base_type_dec_lb.
Notation flat_type_beq := (@flat_type_beq base_type base_type_beq).
Notation flat_type_dec_bl := (@flat_type_dec_bl base_type base_type_beq base_type_dec_bl).
Notation flat_type_dec_lb := (@flat_type_dec_lb base_type base_type_beq base_type_dec_lb).

Definition base_type_eq_semidec_transparent (t1 t2 : base_type)
  : option (t1 = t2)
  := match base_type_eq_dec t1 t2 with
     | left pf => Some pf
     | right _ => None
     end.
Lemma base_type_eq_semidec_is_dec t1 t2 : base_type_eq_semidec_transparent t1 t2 = None -> t1 <> t2.
Proof.
  unfold base_type_eq_semidec_transparent; break_match; congruence.
Qed.

Definition op_beq_hetero {t1 tR t1' tR'} (f : op t1 tR) (g : op t1' tR') : bool
  := match f, g return bool with
     | OpConst T1 v, OpConst T2 v'
       => base_type_beq T1 T2 && Z.eqb v v'
     | Add T1 T2 Tout, Add T1' T2' Tout'
     | Sub T1 T2 Tout, Sub T1' T2' Tout'
     | Mul T1 T2 Tout, Mul T1' T2' Tout'
     | Shl T1 T2 Tout, Shl T1' T2' Tout'
     | Shr T1 T2 Tout, Shr T1' T2' Tout'
     | Land T1 T2 Tout, Land T1' T2' Tout'
     | Lor T1 T2 Tout, Lor T1' T2' Tout'
       => base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq Tout Tout'
     | Opp Tin Tout, Opp Tin' Tout'
       => base_type_beq Tin Tin' && base_type_beq Tout Tout'
     | IdWithAlt T1 T2 Tout, IdWithAlt T1' T2' Tout'
       => base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq Tout Tout'
     | Zselect T1 T2 T3 Tout, Zselect T1' T2' T3' Tout'
       => base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq T3 T3' && base_type_beq Tout Tout'
     | MulSplit bitwidth T1 T2 Tout1 Tout2, MulSplit bitwidth' T1' T2' Tout1' Tout2'
       => Z.eqb bitwidth bitwidth' && base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq Tout1 Tout1' && base_type_beq Tout2 Tout2'
     | AddWithCarry T1 T2 T3 Tout, AddWithCarry T1' T2' T3' Tout'
       => base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq T3 T3' && base_type_beq Tout Tout'
     | AddWithGetCarry bitwidth T1 T2 T3 Tout1 Tout2, AddWithGetCarry bitwidth' T1' T2' T3' Tout1' Tout2'
       => Z.eqb bitwidth bitwidth' && base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq T3 T3' && base_type_beq Tout1 Tout1' && base_type_beq Tout2 Tout2'
     | SubWithBorrow T1 T2 T3 Tout, SubWithBorrow T1' T2' T3' Tout'
       => base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq T3 T3' && base_type_beq Tout Tout'
     | SubWithGetBorrow bitwidth T1 T2 T3 Tout1 Tout2, SubWithGetBorrow bitwidth' T1' T2' T3' Tout1' Tout2'
       => Z.eqb bitwidth bitwidth' && base_type_beq T1 T1' && base_type_beq T2 T2' && base_type_beq T3 T3' && base_type_beq Tout1 Tout1' && base_type_beq Tout2 Tout2'
     | OpConst _ _, _
     | Add _ _ _, _
     | Sub _ _ _, _
     | Mul _ _ _, _
     | Shl _ _ _, _
     | Shr _ _ _, _
     | Land _ _ _, _
     | Lor _ _ _, _
     | Opp _ _, _
     | IdWithAlt _ _ _, _
     | Zselect _ _ _ _, _
     | MulSplit _ _ _ _ _, _
     | AddWithCarry _ _ _ _, _
     | AddWithGetCarry _ _ _ _ _ _, _
     | SubWithBorrow _ _ _ _, _
     | SubWithGetBorrow _ _ _ _ _ _, _
       => false
     end%bool.

Definition op_beq t1 tR (f g : op t1 tR) : bool
  := Eval cbv [op_beq_hetero] in op_beq_hetero f g.

Definition op_beq_hetero_type_eq {t1 tR t1' tR'} f g : to_prop (@op_beq_hetero t1 tR t1' tR' f g) -> t1 = t1' /\ tR = tR'.
Proof.
  destruct f, g;
    repeat match goal with
           | _ => progress unfold op_beq_hetero in *
           | _ => simpl; intro; exfalso; assumption
           | _ => solve [ repeat constructor ]
           | [ |- context[reified_Prop_of_bool ?b] ]
             => let H := fresh in destruct (Sumbool.sumbool_of_bool b) as [H|H]; rewrite H
           | [ H : nat_beq _ _ = true |- _ ] => apply internal_nat_dec_bl in H; subst
           | [ H : base_type_beq _ _ = true |- _ ] => apply internal_base_type_dec_bl in H; subst
           | [ H : wordT_beq_hetero _ _ = true |- _ ] => apply wordT_beq_bl in H; subst
           | [ H : wordT_beq_hetero _ _ = true |- _ ] => apply wordT_beq_hetero_bl in H; destruct H; subst
           | [ H : andb ?x ?y = true |- _ ]
             => assert (x = true /\ y = true) by (destruct x, y; simpl in *; repeat constructor; exfalso; clear -H; abstract congruence);
                  clear H
           | [ H : and _ _ |- _ ] => destruct H
           | [ H : false = true |- _ ] => exfalso; clear -H; abstract congruence
           | [ H : true = false |- _ ] => exfalso; clear -H; abstract congruence
           | _ => progress break_match_hyps
           end.
Defined.

Definition op_beq_hetero_type_eqs {t1 tR t1' tR'} f g : to_prop (@op_beq_hetero t1 tR t1' tR' f g) -> t1 = t1'
  := fun H => let (p, q) := @op_beq_hetero_type_eq t1 tR t1' tR' f g H in p.
Definition op_beq_hetero_type_eqd {t1 tR t1' tR'} f g : to_prop (@op_beq_hetero t1 tR t1' tR' f g) -> tR = tR'
  := fun H => let (p, q) := @op_beq_hetero_type_eq t1 tR t1' tR' f g H in q.

Definition op_beq_hetero_eq {t1 tR t1' tR'} f g
  : forall pf : to_prop (@op_beq_hetero t1 tR t1' tR' f g),
    eq_rect
      _ (fun src => op src tR')
      (eq_rect _ (fun dst => op t1 dst) f _ (op_beq_hetero_type_eqd f g pf))
      _ (op_beq_hetero_type_eqs f g pf)
    = g.
Proof.
  destruct f, g;
    repeat match goal with
           | _ => solve [ intros [] ]
           | _ => reflexivity
           | [ H : False |- _ ] => exfalso; assumption
           | _ => intro
           | [ |- context[op_beq_hetero_type_eqd ?f ?g ?pf] ]
             => generalize (op_beq_hetero_type_eqd f g pf), (op_beq_hetero_type_eqs f g pf)
           | _ => intro
           | _ => progress eliminate_hprop_eq
           | _ => progress inversion_flat_type
           | _ => progress unfold op_beq_hetero in *
           | _ => progress simpl in *
           | [ H : context[andb ?x ?y] |- _ ]
             => destruct x eqn:?, y eqn:?; simpl in H
           | [ H : Z.eqb _ _ = true |- _ ] => apply Z.eqb_eq in H
           | [ H : to_prop (reified_Prop_of_bool ?b) |- _ ] => destruct b eqn:?; compute in H
           | _ => progress subst
           | _ => progress break_match_hyps
           | [ H : wordT_beq_hetero _ _ = true |- _ ] => apply wordT_beq_bl in H; subst
           | [ H : wordT_beq_hetero _ _ = true |- _ ] => apply wordT_beq_hetero_bl in H; destruct H; subst
           | _ => congruence
           end.
Qed.

Lemma op_beq_bl : forall t1 tR x y, to_prop (op_beq t1 tR x y) -> x = y.
Proof.
  intros ?? f g H.
  pose proof (op_beq_hetero_eq f g H) as H'.
  generalize dependent (op_beq_hetero_type_eqd f g H).
  generalize dependent (op_beq_hetero_type_eqs f g H).
  intros; eliminate_hprop_eq; simpl in *; assumption.
Qed.

Section encode_decode.
  Definition base_type_code (t1 t2 : base_type) : Prop
    := match t1, t2 with
       | TZ, TZ => True
       | TWord s1, TWord s2 => s1 = s2
       | TZ, _
       | TWord _, _
         => False
       end.

    Definition base_type_encode (x y : base_type) : x = y -> base_type_code x y.
    Proof. intro p; destruct p, x; repeat constructor. Defined.

    Definition base_type_decode (x y : base_type) : base_type_code x y -> x = y.
    Proof.
      destruct x, y; simpl in *; intro H;
        try first [ apply f_equal; assumption
                  | exfalso; assumption
                  | reflexivity
                  | apply f_equal2; destruct H; assumption ].
    Defined.
    Definition path_base_type_rect {x y : base_type} (Q : x = y -> Type)
               (f : forall p, Q (base_type_decode x y p))
      : forall p, Q p.
    Proof. intro p; specialize (f (base_type_encode x y p)); destruct x, p; exact f. Defined.
End encode_decode.

Ltac induction_type_in_using H rect :=
  induction H as [H] using (rect _ _);
  cbv [base_type_code] in H;
  let H1 := fresh H in
  let H2 := fresh H in
  try lazymatch type of H with
      | False => exfalso; exact H
      | True => destruct H
      end.
Ltac inversion_base_type_step :=
  lazymatch goal with
  | [ H : _ = TWord _ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : TWord _ = _ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : _ = TZ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : TZ = _ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  end.
Ltac inversion_base_type := repeat inversion_base_type_step.
Ltac inversion_base_type_constr_step :=
  lazymatch goal with
  | [ H : TWord _ = TWord _ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : TWord _ = TZ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : TZ = TWord _ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  | [ H : TZ = TZ |- _ ]
    => induction_type_in_using H @path_base_type_rect
  end.
Ltac inversion_base_type_constr := repeat inversion_base_type_constr_step.


Require Import TraceModel.
Require Import XPrefix.
Require Import Properties.
Require Import ClassicalExtras.
Require Import Events.

(** This file defines the notion of language and of compilation
    chain we use in the rest of the development *)

Set Implicit Arguments.

Record language :=
  {
    int  : Set;
    par  : int -> Set;  (* partial programs *)
    ctx  : int -> Set;  (* context *)
    prg  : Set;  (* whole programs *)
    plug : forall {i:int}, par i -> ctx i -> prg;
    sem  : prg -> prop;
    non_empty_sem : forall W, exists t, sem W t
  }.

Axiom src : language.
Axiom tgt : language.

Axiom cint : int src -> int tgt.

Axiom compile_par : forall {i}, (par src i) -> (par tgt (cint i)).
Axiom compile_ctx : forall {i}, (ctx src i) -> (ctx tgt (cint i)).

Axiom i : int src.

Notation "C [ P ]" := (plug _ P C) (at level 50).
Notation "P ↓" := (compile_par P) (at level 50).

Section Ki.

Context {K : language} {i : int K}.

Definition psem (P : prg K)
                (m : finpref) : Prop :=
  exists t, prefix m t /\ (sem K) P t.

Definition xsem (P : prg K)
                (x : xpref) : Prop :=
  exists t, xprefix x t /\ (sem K) P t.

Definition sat (P : prg K)
               (π : prop) : Prop :=
  forall t, sem K P t -> π t.

Definition rsat (P : par K i)
                (π : prop) : Prop :=
  forall C, sat (C [ P ] ) π.

Lemma neg_rsat :
    forall P π, (~ rsat P π <->
           (exists C t, sem K (C [ P ]) t /\ ~ π t)).
Proof.
   unfold rsat. unfold sat. split.
  - intros r. rewrite not_forall_ex_not in r.
    destruct r as [C r]. rewrite not_forall_ex_not in r.
    destruct r as [t r]. exists C,t. rewrite not_imp in r.
    assumption.
  - intros [C [t r]]. rewrite not_forall_ex_not.
    exists C. rewrite not_forall_ex_not. exists t.
    rewrite not_imp. assumption.
Qed.

Definition beh (P : prg K) : prop :=
  fun b => sem K P b.

Definition hsat (P : prg K)
                (H : hprop) : Prop :=
  H (beh P).

Definition rhsat (P : par K i)
                 (H : hprop) : Prop :=
  forall C, hsat ( C [ P ] ) H.

Lemma neg_rhsat : forall (P:par K i) H,
  (~ rhsat P H <-> ( exists (C : ctx K i), ~ H (beh ( C [ P ] )))).
Proof.
  intros P H. split; unfold rhsat; intro H0;
  [now rewrite <- not_forall_ex_not | now rewrite not_forall_ex_not].
Qed.

Definition sat2 (P1 P2 : @prg K) (r : rel_prop) : Prop :=
  forall t1 t2, sem K P1 t1 -> sem K P2 t2 -> r t1 t2.

Lemma neg_sat2 : forall P1 P2 r,
    ~ sat2 P1 P2 r <-> (exists t1 t2, sem K P1 t1 /\ sem K P2 t2 /\ ~ r t1 t2).
Proof.
  unfold sat2. intros P1 P2 r. split.
  +  intros H. rewrite not_forall_ex_not in H.
    destruct H as [t1 H]. rewrite not_forall_ex_not in H.
    destruct H as [t2 H]. rewrite not_imp in H. destruct H as [H1 H2].
    rewrite not_imp in H2. destruct H2 as [H2 H3].
    now exists t1, t2.
  + intros [t1 [t2  [H1 [H2 H3]]]]. rewrite not_forall_ex_not.
    exists t1. rewrite not_forall_ex_not. exists t2.
    rewrite not_imp. split.
    ++ assumption.
    ++ rewrite not_imp. now auto.
Qed.

Definition rsat2 (P1 P2 : par K i) (r : rel_prop) : Prop :=
  forall C, sat2 (C [ P1 ]) (C [ P2 ]) r.

Definition hsat2 (P1 P2 : prg K) (r : rel_hprop) : Prop :=
   r (sem K P1) (sem K P2).

Definition hrsat2 (P1 P2 : par K i) (r : rel_hprop) : Prop :=
  forall C, r (sem K (C [P1])) (sem K (C [P2])).

(**************************************************************************)

Definition input_totality : Prop :=
  forall (P : prg K) l e1 e2,
    is_input e1  -> is_input e2 -> psem P (ftbd (snoc l e1)) -> psem P (ftbd (snoc l e2)).

Definition traces_match (t1 t2 : trace) : Prop :=
 t1 = t2 \/
 (exists (l : list event) (e1 e2 : event),
   is_input e1 /\ is_input e2 /\  e1 <> e2 /\
   prefix (ftbd (snoc l e1)) t1 /\ prefix (ftbd (snoc l e2)) t2).

Definition determinacy : Prop :=
  forall (P : prg K) t1 t2,
    sem K P t1 -> sem K P t2 -> traces_match t1 t2.

Definition semantics_safety_like : Prop :=
  forall t P,
    ~ sem K P t -> inf t -> ~ diverges t ->
    (exists l ebad, psem P (ftbd l) /\ prefix (ftbd (snoc l ebad)) t /\ ~ psem P (ftbd (snoc l ebad))).

End Ki.


Require Import Coq.Classes.RelationClasses.
Require Import Coq.Relations.Relation_Definitions.
Require Import Logic.lib.Coqlib.
Require Import Logic.lib.Ensembles_ext.
Require Import Logic.GeneralLogic.Base.
Require Import Logic.GeneralLogic.KripkeModel.
Require Import Logic.GeneralLogic.Semantics.Kripke.
Require Import Logic.MinimunLogic.Syntax.
Require Import Logic.MinimunLogic.Semantics.Kripke.
Require Import Logic.PropositionalLogic.Syntax.
Require Import Logic.PropositionalLogic.Semantics.Kripke.
Require Import Logic.SeparationLogic.Syntax.
Require Import Logic.SeparationLogic.Model.SeparationAlgebra.
Require Import Logic.SeparationLogic.Model.OrderedSA.
Require Import Logic.SeparationLogic.Model.UpwardsClosure.
Require Import Logic.SeparationLogic.Semantics.WeakSemantics.
Require Import Logic.SeparationLogic.Semantics.StrongSemantics.
Require Import Logic.SeparationLogic.Semantics.DownwardsSemantics.
Require Import Logic.SeparationLogic.Semantics.FlatSemantics.

Local Open Scope logic_base.
Local Open Scope syntax.
Local Open Scope kripke_model.
Import PropositionalLanguageNotation.
Import SeparationLogicNotation.
Import KripkeModelFamilyNotation.
Import KripkeModelNotation_Intuitionistic.

Module Down2Flat.
Section Down2Flat.

Context {L: Language}
        {minL: MinimunLanguage L}
        {pL: PropositionalLanguage L}
        {sL: SeparationLanguage L}
        {MD: Model}
        {kMD: KripkeModel MD}
        {M: Kmodel}
        {R: Relation (Kworlds M)}
        {po_R: PreOrder Krelation}
        {J: Join (Kworlds M)}
        {SA: SeparationAlgebra (Kworlds M)}
        {dSA: DownwardsClosedSeparationAlgebra (Kworlds M)}
        {SM: Semantics L MD}
        {kiSM: KripkeIntuitionisticSemantics L MD M SM}
        {dsSM: DownwardsSemantics.SeparatingSemantics L MD M SM}.

Definition fsSM: @FlatSemantics.SeparatingSemantics L _ MD _ M _ UpwardsClosure_J SM.
Proof.
  constructor.
  + (* sat_sepcon *)
    intros.
    unfold WeakSemantics.sepcon.
    split; unfold Included, Ensembles.In; intros m ?.
    - rewrite (app_same_set (DownwardsSemantics.denote_sepcon _ _) m) in H.
      destruct H as [m0 [m1 [m2 [? [? [? ?]]]]]].
      exists m1, m2.
      split; [| split]; auto.
      exists m0; split; auto.
    - rewrite (app_same_set (DownwardsSemantics.denote_sepcon _ _) m).
      destruct H as [m1 [m2 [[n [? ?]] [? ?]]]].
      exists n, m1, m2.
      split; [| split; [| split]]; auto.
  + (* sat_wand *)
    intros.
    unfold WeakSemantics.wand.
    split; unfold Included, Ensembles.In; intros m ?.
    - rewrite (app_same_set (DownwardsSemantics.denote_wand _ _) m) in H.
      intros.
      destruct H0 as [m2' [? ?]].
      eapply sat_mono; eauto.
      eapply H; eauto.
    - rewrite (app_same_set (DownwardsSemantics.denote_wand _ _) m).
      hnf; intros.
      apply (H m1 m2); auto.
      exists m2; split; auto.
      reflexivity.
Qed.

Definition feSM {s'L: SeparationEmpLanguage L} {USA: UnitalSeparationAlgebra (Kworlds M)} {deSM: DownwardsSemantics.EmpSemantics L MD M SM}: @FlatSemantics.EmpSemantics L _ _ MD _ M _ UpwardsClosure_J SM.
Proof.
  split; intros m; unfold Ensembles.In; unfold WeakSemantics.emp;
  rewrite <- UpwardsClosure_increasing;
  apply DownwardsSemantics.denote_emp.
Qed.

End Down2Flat.
End Down2Flat.


Load LFindLoad.
From lfind Require Import LFind.
From QuickChick Require Import QuickChick.
From adtind Require Import goal33.

Derive Show for natural.

              Derive Arbitrary for natural.

              Instance Dec_Eq_natural : Dec_Eq natural.

              Proof. dec_eq. Qed.

Lemma conj8synthconj5 : forall  (lv0 : natural) (lv1 : natural), (@eq natural (Succ (plus lv0 lv1)) (plus (Succ lv1) lv0)).
Admitted.
QuickChick conj8synthconj5.


Require Import SpecDeps.
Require Import RData.
Require Import EventReplay.
Require Import MoverTypes.
Require Import Constants.
Require Import CommonLib.
Require Import AbsAccessor.Spec.
Require Import RVIC2.Spec.

Local Open Scope Z_scope.

Section SpecLow.

  Definition need_ns_notify_spec0 (rec: Pointer) (target_rec: Pointer) (intid: Z64) (adt: RData) : option Z :=
    match rec, target_rec, intid with
    | (_rec_base, _rec_ofst), (_target_rec_base, _target_rec_ofst), VZ64 _intid =>
      when' _idx1 == get_rec_rec_idx_spec (_target_rec_base, _target_rec_ofst) adt;
      rely is_int64 _idx1;
      when' _idx2 == get_rec_rec_idx_spec (_rec_base, _rec_ofst) adt;
      rely is_int64 _idx2;
      if (_idx1 =? _idx2) then
        Some 0
      else
        when _enabled == get_rec_rvic_enabled_spec (_target_rec_base, _target_rec_ofst) adt;
        rely is_int _enabled;
        if (_enabled =? 0) then
          Some 0
        else
          when'' _rvic_base, _rvic_ofst == get_rec_rvic_spec (_target_rec_base, _target_rec_ofst) adt;
          rely is_int _rvic_ofst;
          rely is_int64 _intid;
          when _pending == rvic_is_pending_spec (_rvic_base, _rvic_ofst) (VZ64 _intid) adt;
          rely is_int _pending;
          if (_pending =? 0) then
            Some 0
          else
            when _masked == rvic_is_masked_spec (_rvic_base, _rvic_ofst) (VZ64 _intid) adt;
            rely is_int _masked;
            if (_masked =? 1) then
              Some 0
            else
              Some 1
     end
    .

End SpecLow.


(**
CoLoR, a Coq library on rewriting and termination.
See the COPYRIGHTS and LICENSE files.

- Joerg Endrullis, 2008-07-28
- Frederic Blanqui, 2007-02-05

when the union of two wellfounded relations is wellfounded
*)

Set Implicit Arguments.

From Coq Require Import Lia Wellfounded.Union.
From CoLoR Require Import SN RelUtil LogicUtil AccUtil NotSN_IS ClassicUtil
     LeastNat DepChoice.

Section S.

  Variables (A : Type) (R S : relation A).

(***********************************************************************)
(** When the two relations commute. *)

  Lemma SN_union_commut t :
    (forall x, SN S x -> SN R x) -> SN S t -> R @ S << S @ R -> SN (R U S) t.

  Proof.
    intros h s c. apply Acc_transp_SN. rewrite transp_union. apply Acc_union.
    intros x y. unfold transp. intros xy z yz.
    assert (a : (R@S) x z). exists y. fo.
    apply c in a. fo.
    intros x hx. apply SN_Acc_transp. apply h. apply Acc_transp_SN. hyp.
    apply SN_Acc_transp. hyp.
  Qed.

  Lemma WF_union_commut : WF R -> WF S -> R @ S << S @ R -> WF (R U S).

  Proof.
    intros R_wf S_wf commut. apply wf_transp_WF. rewrite transp_union.
    apply wf_union. intros x y. unfold transp. intros xy z yz.
    assert (a : (R@S) x z). exists y. fo.
    apply commut in a. fo. apply WF_wf_transp. hyp. apply WF_wf_transp. hyp.
  Qed.

(***********************************************************************)
(** When a relation absorbs the other. *)

  Lemma wf_union_absorb : WF R -> WF S -> R @ S << R -> WF (R U S).

  Proof.
    intros R_wf S_wf RS x. apply notNT_SN. intros [f [f1 f2]].
    (* We prove that, for all [i], there is [j > i] such that
       [R (f j) (f (j+1))]. *)
    assert (a1 : forall i, exists j, i < j /\ R (f j) (f (1+j))).
    apply NNPP. rewrite not_forall_eq. intros [i0 hi0].
    rewrite not_exists_eq in hi0.
    assert (hi0' : forall i, i0 < i -> S (f i) (f (1+i))).
    intros i ii0. gen (hi0 i). rewrite not_and_eq. fo.
    absurd (NT S (f (i0+1))). apply SN_notNT. fo.
    exists (fun i => f (i+(i0+1))). split. refl. intro i. apply hi0'. lia.
    (* We prove that, for all [i], there is [j > i] such that
       [R (f j) (f (j+1))] and, for all [k] between [i] and [j],
       [S (f k) (f (k+1))]. *)
    assert (a2 : forall i, exists j, i < j /\ R (f j) (f (1+j))
        /\ forall k, i < k < j -> S (f k) (f (1+k))).
    intro i. gen (ch_min (a1 i)); intros [j [[[j1 j2] j3] j4]].
    ex j. split. hyp. split. hyp. intros k ikj. destruct (f2 k). 2: hyp.
    assert (j <= k). apply j3. fo. lia.
    (* Let [i0] be the first or second [R] step in [f]. *)
    destruct (a2 0) as [i0 [i0h [i0r i0s]]].
    (* Let [b] be the function mapping [j] to [i] and such that [b 0 = i0]. *)
    destruct (dep_choice i0 a2) as [b [b1 b2]].
    (* We prove that there is an infinite sequence of [R @ S#] steps
       starting from [f i0]. *)
    assert (a3 : NT (R @ S#) (f i0)). ex (fun i => f (b i)). split.
    rewrite b2. refl.
    intro i. ex (f (1 + b i)). split.
    destruct i. rewrite b2. hyp. gen (b1 i). fo.
    apply rtc_intro_seq. gen (b1 i). lia.
    intros k hk. gen (b1 i). intros [c1 [c2 c3]]. apply c3. lia.
    (* We therefore get a contradiction since [R @ S# << R] and [R] is WF. *)
    revert a3. apply SN_notNT. apply (SN_incl R).
    apply absorbs_right_rtc. hyp. fo.
  Qed.

(***********************************************************************)
(** Commutation and SN *)

  Lemma sn_comm_sntr :
    forall x, SN R x -> (forall y, R#1 x y -> SN S y) ->
      S@R << R!1@S#1 -> SN (R!1 U S!1) x.

  Proof.
    intros x snr sns comm.
    assert (snrt := SN_tc1 snr); clear snr.

    (* Induction on x R! ... *)
    induction snrt as [x _ IHr].
    apply SN_intro. intros y xRSy.
    destruct xRSy as [xRy | xSy]. apply IHr. hyp.
    assert (SN (S!1) y) as sny.
    apply SN_tc1. apply sns. apply tc1_incl_rtc1; hyp.

    (* x R! y *)
    intros y0 yRy0. apply sns.
    apply trans_comp_incl. class. exists y; split; trivial.
    apply tc1_incl_rtc1. hyp.

    (* x S! y *)
    assert (SN (S!1) y) as sny. apply SN_tc1.
    apply SN_inv_rtc1 with x. apply tc1_incl_rtc1. hyp.
    apply sns. refl.

    (* Induction on y S! ... *)
    induction sny as [y _ IHs].
    apply SN_intro. intros z yRSz.
    destruct yRSz as [yRz | ySz].

    assert ((R!1 @ (R U S)#1) x z) as xRz.
    apply comm_s_r; trivial.
    destruct yRz as [y z yRz | y m z yRm mRz].
    exists z. split. exists y. split; trivial.
    apply union_rel_rt1_right. apply tc1_incl_rtc1. hyp. refl.
    exists m. split. exists y. split; trivial.
    apply union_rel_rt1_right. apply tc1_incl_rtc1. hyp.
    apply union_rel_rt1_left. apply tc1_incl_rtc1. hyp.

    destruct xRz as [m [xRm mz]].
    assert (SN (R!1 U S!1) m) as SNm.
    apply IHr. hyp. intros y0 mRy0.
    apply sns. apply trans_comp_incl. class. exists m.
    split; trivial. apply tc1_incl_rtc1. hyp.

    apply SN_inv_rtc1 with m. apply incl_union_rt1_union. hyp. hyp.
    apply IHs; trivial. trans y; trivial.
  Qed.

  Lemma sn_comm_sn :
    forall x, SN R x -> (forall y, R#1 x y -> SN S y) ->
      S@R << R!1@S#1 -> SN (R U S) x.

  Proof.
    intros x snr sns comm.
    assert (SN (R!1 U S!1) x) as sntr.
    apply sn_comm_sntr; trivial.
    apply (SN_incl (R!1 U S!1)).
    apply union_incl.
    intros a b. apply t1_step.
    intros a b. apply t1_step.
    hyp.
  Qed.

End S.