Require Import Coq.omega.Omega.
(* Structured programming (module construction) *)

Require Import Coq.Bool.Bool Coq.NArith.NArith Coq.Strings.String Coq.Lists.List.

Require Import Bedrock.Nomega Bedrock.PropX Bedrock.PropXTac Bedrock.Word Bedrock.LabelMap Bedrock.IL Bedrock.XCAP Bedrock.Structured.
Require Import Bedrock.StringSet.

Set Implicit Arguments.

Local Open Scope N_scope.

Import DefineStructured.

Section module.
  Definition import := (string * string * assert)%type.

  Variable imports : list import.
  (* Which functions from outside this module do we need? *)

  Variable modName : string.
  (* New module name *)

  Definition function := (string * assert * forall imports, importsGlobal imports -> cmd imports modName)%type.

  Variable functions : list function.
  (* All functions in this module. *)

  (* Build the full list of imports for the commands, including both external and internal functions.
   * First, we build a version for only the external functions. *)

  Definition importsMap : LabelMap.t assert :=
    List.fold_left (fun m p => let '(modl, f, pre) := p in
      LabelMap.add (modl, Global f) pre m) imports (LabelMap.empty _).

  Lemma importsMapGlobal' : forall (im : list import) m,
    importsGlobal m
    -> importsGlobal (List.fold_left (fun m p => let '(modl, f, pre) := p in
      LabelMap.add (modl, Global f) pre m) im m).
    unfold importsGlobal; induction im as [ | [ ] ]; simpl; intuition.
    apply IHim in H0; auto.
    intros.
    apply LabelFacts.add_mapsto_iff in H1; intuition; subst; simpl; eauto.
  Qed.

  Theorem importsMapGlobal : importsGlobal importsMap.
    apply importsMapGlobal'; red; intros.
    destruct (LabelMap.empty_1 H).
  Qed.

  Definition fullImports : LabelMap.t assert :=
    List.fold_left (fun m p => let '(f, pre, _) := p in
      LabelMap.add (modName, Global f) pre m) functions importsMap.

  Lemma fullImportsGlobal' : forall (fs : list function) m,
    importsGlobal m
    -> importsGlobal (List.fold_left (fun m p => let '(f, pre, _) := p in
      LabelMap.add (modName, Global f) pre m) fs m).
    induction fs as [ | [ ] ]; simpl; intuition.
    apply IHfs; red; intros.
    apply LabelFacts.add_mapsto_iff in H0; intuition; subst; simpl; eauto.
  Qed.

  Theorem fullImportsGlobal : importsGlobal fullImports.
    apply fullImportsGlobal'; apply importsMapGlobal.
  Qed.

  (* Now we are ready to generate a module out of the functions. *)

  Definition buildLocals (bls : list (assert * block)) Base := snd (List.fold_left (fun b_m p => let '(b, m) := b_m in
    (Nsucc b, LabelMap.add (modName, Local b) p m)) bls (Base, LabelMap.empty _)).

  Fixpoint blocks (fs : list function) (Base : N) : LabelMap.t (assert * block) :=
    match fs with
      | nil => LabelMap.empty _
      | (f, pre, c) :: fs' =>
        let cout := c fullImports fullImportsGlobal pre in
        let cg := Generate cout (Nsucc Base) Base in
        LabelMap.add (modName, Global f) (pre, (nil, Uncond (RvLabel (modName, Local (Nsucc Base + Entry cg)))))
          (LabelMap.add (modName, Local Base) (Postcondition cout, (nil, Uncond (RvLabel (modName, Local Base))))
            (union (buildLocals (Blocks cg) (Nsucc Base))
              (blocks fs' (Nsucc Base + N_of_nat (length (Blocks cg))))))
    end.

  Fixpoint exps (fs : list function) : LabelMap.t assert :=
    match fs with
      | nil => LabelMap.empty _
      | (f, pre, _) :: fs' => LabelMap.add (modName, Global f) pre (exps fs')
    end.

  Definition bmodule_ : module := {|
    Imports := importsMap;
    XCAP.Blocks := blocks functions 1;
    Exports := exps functions;
    Modules := StringSet.singleton modName
  |}.

  Lemma Forall_MapsTo : forall A (P : _ * A -> Prop) m,
    (forall k v, LabelMap.MapsTo k v m -> P (k, v))
    -> List.Forall P (LabelMap.elements m).
    intros.
    generalize (fun k v H' => H k v (LabelMap.elements_2 H')); clear H; intro H.
    induction (LabelMap.elements m); simpl in *; intuition.
    constructor; auto.
    destruct a.
    apply H.
    constructor; hnf; auto.
  Qed.

  Hypothesis NoSelfImport :
    List.fold_left (fun b p => let '(m, _, _) := p in
      b || if string_dec m modName then true else false) imports false = false.

  Theorem importsNotThis : forall l, LabelMap.In (elt:=assert) (modName, l) importsMap -> False.
    intros.
    assert (forall k v, LabelMap.MapsTo k v (LabelMap.empty assert) -> k <> (modName, l)).
    intros.
    apply LabelMap.empty_1 in H0; tauto.
    destruct H.
    unfold importsMap in *.
    generalize dependent (LabelMap.empty assert).
    generalize NoSelfImport; clear NoSelfImport.
    generalize false at 2.
    induction imports; simpl in *; intuition.
    apply H0 in H; auto.
    destruct a as [ [ ] ]; simpl in *.
    eapply IHl0 in NoSelfImport.
    auto.
    eauto.
    intros; subst.
    apply LabelFacts.add_mapsto_iff in H1; intuition; subst; [ | eauto ].
    destruct (string_dec s modName); subst; try congruence.
    replace (b || true) with true in NoSelfImport by (destruct b; auto).
    generalize NoSelfImport; clear.
    induction l0; simpl; intuition.
    destruct a as [ [ ] ]; intuition.
  Qed.

  Hint Immediate importsNotThis.

  Theorem importsNotThis' : forall l v, LabelMap.MapsTo (elt:=assert) (modName, l) v importsMap -> False.
    intros; eapply importsNotThis.
    hnf.
    eauto.
  Qed.

  Hint Resolve importsNotThis'.

  Lemma Forall_nth_error : forall A (P : A -> Prop) x ls,
    List.Forall P ls
    -> forall n, nth_error ls n = Some x
      -> P x.
    induction 1; destruct n; simpl; intuition; try discriminate.
    injection H1; congruence.
    eauto.
  Qed.

  Hypothesis NoDupFunc :
    match (List.fold_left (fun mOpt p => let '(modl, _, _) := p in
      match mOpt with
        | None => None
        | Some m => let k := (modl, Local 0) in
          if LabelMap.mem k m then None
          else Some (LabelMap.add k tt m)
      end) functions (Some (LabelMap.empty unit))) with
      | None => False
      | Some _ => True
    end.

  Fixpoint makeVcs (fs : list function) : list Prop :=
    match fs with
      | nil => nil
      | f :: fs' =>
        let '(_, pre, c) := f in
          let cout := c fullImports fullImportsGlobal pre in
            (forall stn_st specs, ~interp specs (Postcondition cout stn_st))
            :: VerifCond cout
            ++ makeVcs fs'
    end.

  Hypothesis BlocksGood : vcs (makeVcs functions).

  Lemma BlocksGood' : List.Forall (fun f : function => let '(_, pre, c) := f in
    let cout := c fullImports fullImportsGlobal pre in
    (forall stn_st specs, ~interp specs (Postcondition cout stn_st))
    /\ vcs (VerifCond cout)) functions.
    generalize BlocksGood; clear.
    induction functions; simpl; intuition.
    destruct a as [ [ ] ].
    inversion BlocksGood; subst; intuition.
    constructor; intuition.
    eapply vcs_app_bwd1; eauto.
    apply IHl; eapply vcs_app_bwd2; eauto.
  Qed.

  Theorem buildLocals_notImport' : forall k v Base bls Base',
    LabelMap.MapsTo k v (buildLocals bls Base')
    -> Base' >= Base
    -> exists l, k = (modName, Local l) /\ l >= Base /\ l < Base' + N_of_nat (length bls).
    unfold buildLocals; intros.
    assert (LabelMap.MapsTo k v (LabelMap.empty (assert * block)) -> exists l, k = (modName, Local l) /\ l >= Base /\ l < Base' + N_of_nat (length bls))
      by (intro uhoh; destruct (LabelMap.empty_1 uhoh)).
    generalize dependent (LabelMap.empty (assert * block)).
    generalize dependent Base'; clear; induction bls; simpl; intuition.
    assert (Nsucc Base' >= Base) by nomega.
    destruct (IHbls _ H2 _ H); clear IHbls; intuition.
    apply LabelFacts.add_mapsto_iff in H3; intuition; subst; eauto.
    repeat esplit.
    auto.
    nomega.
    destruct H4; intuition.
    repeat esplit; eauto.
    nomega.
    repeat esplit; eauto.
    nomega.
  Qed.

  Theorem buildLocals_notImport : forall k v Base bls,
    LabelMap.MapsTo k v (buildLocals bls Base)
    -> exists l, k = (modName, Local l) /\ l >= Base /\ l < Base + N_of_nat (length bls).
    intros; eapply buildLocals_notImport'; eauto; nomega.
  Qed.

  Lemma getLocal : forall v bls Base Entry,
    nth_error bls (nat_of_N Entry) = Some v
    -> LabelMap.MapsTo (modName, Local (Base + Entry)) v (buildLocals bls Base).
    unfold buildLocals; intros.
    generalize (LabelMap.empty (assert * block)).
    generalize dependent Base.
    generalize dependent Entry.
    induction bls; simpl; intuition.
    elimtype False.
    destruct (nat_of_N Entry); discriminate.
    destruct (N_eq_dec Entry 0); subst; simpl in *.
    injection H; clear H; intros; subst.
    replace (Base + 0) with Base by nomega.
    assert (LabelMap.MapsTo (modName, Local Base) (a0, b) (LabelMap.add (modName, Local Base) (a0, b) t))
      by (apply LabelMap.add_1; auto).
    generalize H; clear.
    generalize (LabelMap.add (modName, Local Base) (a0, b) t).
    assert (Base < Nsucc Base) by nomega.
    generalize dependent (Nsucc Base).
    induction bls; simpl; intuition; eauto.
    apply IHbls.
    nomega.
    apply LabelMap.add_2; eauto.

    replace (Base + Entry) with (Nsucc Base + (Entry - 1)) by nomega.
    apply IHbls.
    autorewrite with N; simpl.
    assert (nat_of_N Entry <> O).
    nomega.
    destruct (nat_of_N Entry); simpl in *.
    tauto.
    replace (n0 - 0)%nat with n0 by omega; auto.
  Qed.

  Lemma ungetLocal' : forall A k v bls Base (m : LabelMap.t A),
    LabelMap.MapsTo k v (snd (List.fold_left (fun b_m p => let '(b, m) := b_m in
      (Nsucc b, LabelMap.add (modName, Local b) p m)) bls (Base, m)))
    -> LabelMap.MapsTo k v m
    \/ exists n, nth_error bls n = Some v /\ k = (modName, Local (Base + N_of_nat n)).
    clear; induction bls; simpl; intuition.
    apply IHbls in H; clear IHbls; intuition.
    apply LabelFacts.add_mapsto_iff in H0; intuition; subst.
    right; exists O; intuition.
    do 2 f_equal; nomega.
    destruct H0; intuition; subst.
    right; exists (S x); intuition.
    do 2 f_equal; nomega.
  Qed.

  Lemma ungetLocal : forall k v bls Base,
    LabelMap.MapsTo k v (buildLocals bls Base)
    -> exists n, nth_error bls n = Some v /\ k = (modName, Local (Base + N_of_nat n)).
    unfold buildLocals; intros.
    apply ungetLocal' in H; intuition.
    destruct (LabelMap.empty_1 H0).
  Qed.

  Hint Extern 1 (_ >= _) => nomega.

  Lemma MapsTo_blocks : forall k v fs Base,
    LabelMap.MapsTo k v (blocks fs Base)
    -> exists f, exists pre, exists c, In (f, pre, c) fs
      /\ exists Base', Base' >= Base /\
        let cout := c fullImports fullImportsGlobal pre in
        let cg := Generate (c fullImports fullImportsGlobal pre) (Nsucc Base') Base' in
          (forall n v', nth_error (Blocks cg) n = Some v'
            -> LabelMap.MapsTo (modName, Local (Nsucc Base' + N_of_nat n)) v' (blocks fs Base))
          /\ (exists bl, LabelMap.MapsTo (modName, Local Base') (Postcondition cout, bl) (blocks fs Base))
          /\ ((k = (modName, Global f)
            /\ v = (pre, (nil, Uncond (RvLabel (modName, Local (Nsucc Base' + Entry cg))))))
          \/ (k = (modName, Local Base') /\ v = (Postcondition cout, (nil, Uncond (RvLabel (modName, Local Base')))))
          \/ exists n, k = (modName, Local (Nsucc Base' + N_of_nat n)) /\ nth_error (Blocks cg) n = Some v).
    clear; induction fs as [ | [ [ ] ] ]; simpl; intuition.
    destruct (LabelMap.empty_1 H).
    apply LabelFacts.add_mapsto_iff in H; intuition; subst.

    do 4 esplit.
    eauto.
    exists Base; intuition.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union1.
    apply getLocal; autorewrite with N; auto.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_1; auto.

    apply LabelFacts.add_mapsto_iff in H1; intuition; subst.

    do 4 esplit.
    eauto.
    exists Base; intuition.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union1.
    apply getLocal; autorewrite with N; auto.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_1; auto.

    apply MapsTo_union in H2; intuition.

    apply ungetLocal in H0; destruct H0; intuition; subst.
    do 4 esplit.
    eauto.
    exists Base; intuition.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union1.
    apply getLocal; autorewrite with N; auto.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_1; auto.
    eauto 10.

    apply IHfs in H0; clear IHfs; intuition.
    destruct H0 as [? [? [? [ ] ] ] ].
    destruct H2; intuition; subst.

    do 4 esplit.
    right; eauto.
    exists x2; intuition.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; intuition.
    apply ungetLocal in H6; destruct H6; intuition.
    elimtype False; injection H8; intros.
    apply nth_error_bound in H7.
    nomega.
    destruct H4.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; eauto.
    intros.
    apply ungetLocal in H5; destruct H5; intuition.
    elimtype False; injection H7; intros.
    apply nth_error_bound in H6.
    nomega.

    do 4 esplit.
    right; eauto.
    exists x2; intuition.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; intuition.
    apply ungetLocal in H6; destruct H6; intuition.
    elimtype False; injection H8; intros.
    apply nth_error_bound in H7.
    nomega.
    destruct H4.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; eauto.
    intros.
    apply ungetLocal in H5; destruct H5; intuition.
    elimtype False; injection H7; intros.
    apply nth_error_bound in H6.
    nomega.

    destruct H6; intuition; subst.
    do 4 esplit.
    right; eauto.
    exists x2; intuition eauto.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; intuition.
    apply ungetLocal in H6; destruct H6; intuition.
    elimtype False; injection H9; intros.
    apply nth_error_bound in H8.
    nomega.
    destruct H4.
    eexists.
    apply LabelMap.add_2; [ congruence | ].
    apply LabelMap.add_2; [ intro Ho; injection Ho; nomega | ].
    apply MapsTo_union2; eauto.
    intros.
    apply ungetLocal in H5; destruct H5; intuition.
    apply nth_error_bound in H6.
    elimtype False; injection H8; intros.
    nomega.
  Qed.

  Lemma skipImports : forall m l p bls,
    LabelMap.MapsTo (modName, Local l) (p, bls) m
    -> LabelMap.MapsTo (modName, Local l) p
    (LabelMap.fold
      (fun (l : LabelMap.key) (x : assert * block) (m : LabelMap.t assert) =>
        LabelMap.add l (fst x) m) m importsMap).
    clear NoDupFunc BlocksGood.
    unfold importsMap.
    generalize NoSelfImport; clear NoSelfImport.
    generalize false at 2.
    intros; assert (forall v, ~LabelMap.MapsTo (modName, Local l) v (LabelMap.empty assert)).
    do 2 intro.
    apply LabelMap.empty_1 in H0; tauto.
    apply LabelMap.elements_1 in H.
    generalize (LabelMap.elements_3w m).
    generalize dependent (LabelMap.empty assert).
    induction imports; simpl in *; intuition.
    rewrite LabelMap.fold_1.
    generalize dependent t.
    induction (LabelMap.elements m); simpl; intuition.

    inversion H.
    inversion H1; clear H1; subst.
    inversion H; clear H; subst.
    hnf in H2; simpl in H2; intuition.
    destruct a; simpl in *; subst; simpl in *.
    generalize H4; clear.
    assert (LabelMap.MapsTo (modName, Local l) p (LabelMap.add (modName, Local l) p t)).
    apply LabelMap.add_1; auto.
    generalize dependent (LabelMap.add (modName, Local l) p t).
    induction l0; simpl; intuition; simpl.
    apply IHl0; auto.
    apply LabelMap.add_2.
    intro; subst.
    apply H4.
    constructor; hnf; auto.
    auto.

    intuition.
    apply H1; clear H1.
    intros.
    apply LabelFacts.add_mapsto_iff in H; intuition.
    destruct a as [ [ ] ]; simpl in *; subst; simpl in *.
    injection H; clear H; intros; subst.
    generalize H2 H4; clear.
    induction 1; simpl; intuition.
    apply H4.
    constructor.
    hnf in H; hnf; simpl in *; tauto.
    eauto.

    destruct a as [ [ ] ]; simpl in *.
    apply IHl0; auto.

    generalize NoSelfImport; clear.
    match goal with
      | [ |- context[?E || ?F] ] =>
        assert (E || F = false -> E = false) by (destruct b; auto);
          generalize dependent (E || F); generalize dependent E
    end.
    induction l0; simpl; intuition.
    destruct a as [ [ ] ]; simpl in *.
    eapply IHl0; [ | eassumption ].
    destruct b0; destruct b; simpl in *; intuition congruence.

    intros.
    apply LabelFacts.add_mapsto_iff in H2; intuition; subst.
    congruence.
    eauto.
  Qed.

  Lemma imps_cases : forall k v ims exit post bls base,
    LabelMap.MapsTo k v (imps ims modName bls base exit post)
    -> (k = (modName, Local exit) /\ v = post)
    \/ LabelMap.MapsTo k v ims
    \/ exists n, exists bl, nth_error bls n = Some (v, bl) /\ k = (modName, Local (base + N_of_nat n)).
    induction bls; simpl; intuition.

    apply LabelFacts.add_mapsto_iff in H; intuition.

    apply LabelFacts.add_mapsto_iff in H; intuition; subst.

    do 2 right; exists O; exists b; intuition.
    do 2 f_equal; nomega.

    apply IHbls in H1; intuition.
    destruct H1 as [ ? [ ] ]; intuition.
    do 2 right; exists (S x); exists x0; intuition.
    rewrite H2; do 2 f_equal; nomega.
  Qed.

  Lemma MapsTo_fullImports : forall k v,
    LabelMap.MapsTo k v fullImports
    -> LabelMap.MapsTo k v importsMap
    \/ (exists f, k = (modName, Global f) /\ exists c, In (f, v, c) functions).
    clear; unfold fullImports; do 2 intro; generalize importsMap.
    induction functions as [ | [ [ ] ] ]; simpl; intuition.
    apply IHl in H; clear IHl; intuition.
    apply LabelFacts.add_mapsto_iff in H0; intuition; subst.
    eauto 10.
    destruct H0; intuition; subst.
    destruct H1; eauto 10.
  Qed.

  Lemma importsMap_global' : forall l pre imps (acc : LabelMap.t assert),
    (forall l' pre', LabelMap.MapsTo l' pre' acc
      -> exists g, snd l' = Global g)
    -> LabelMap.MapsTo l pre (fold_left (fun m p => let '(modl, f, pre) := p in
      LabelMap.add (modl, Global f) pre m) imps acc)
    -> exists g, snd l = Global g.
    clear; induction imps; simpl; intuition eauto.
    eapply IHimps; [ | eauto ].
    intros.
    destruct (LabelKey.eq_dec l' (a, Global b0)).
    hnf in e; subst; simpl; eauto.
    eapply LabelMap.add_3 in H1.
    eauto.
    auto.
  Qed.

  Lemma importsMap_global : forall l pre,
    LabelMap.MapsTo l pre importsMap -> exists g, snd l = Global g.
    intros; apply importsMap_global' with pre imports (LabelMap.empty _).
    intros.
    apply LabelMap.empty_1 in H0; tauto.
    assumption.
  Qed.

  Lemma MapsTo_func : forall A (m : LabelMap.t A) k v v',
    LabelMap.MapsTo k v m
    -> LabelMap.MapsTo k v' m
    -> v = v'.
    intros.
    apply LabelMap.find_1 in H.
    apply LabelMap.find_1 in H0.
    congruence.
  Qed.

  Lemma blocks_exps : forall (mn g : string) (pre : assert) (bl : block)
    funcs start,
    LabelMap.MapsTo (mn, Global g) (pre, bl) (blocks funcs start) ->
    LabelMap.MapsTo (mn, Global g) pre (exps funcs).
    clear; induction funcs; simpl; intuition.
    apply LabelMap.empty_1 in H; tauto.
    destruct a as [ [ ] ].
    destruct (LabelKey.eq_dec (modName, Global s) (mn, Global g)).
    generalize e; intro e'.
    eapply LabelMap.add_1 in e'.

    hnf in e.
    rewrite <- e in *.
    eapply MapsTo_func in e'.
    2: apply H.
    injection e'; clear e'; intros; subst.
    eauto.

    apply LabelMap.add_3 in H; [ | assumption ].
    apply LabelMap.add_3 in H; [ | lomega ].
    apply MapsTo_union in H; intuition.
    apply buildLocals_notImport in H0; destruct H0; intuition congruence.

    eauto.
  Qed.

  Lemma exps_blocks : forall (mn g : string) (pre : assert)
    funcs start,
    LabelMap.MapsTo (mn, Global g) pre (exps funcs)
    -> exists bl, LabelMap.MapsTo (mn, Global g) (pre, bl) (blocks funcs start).
    induction funcs; simpl; intuition.
    apply LabelMap.empty_1 in H; tauto.
    destruct a as [ [ ] ].
    destruct (LabelKey.eq_dec (modName, Global s) (mn, Global g)).
    generalize e; intro e'.
    eapply LabelMap.add_1 in e'.
    hnf in e.
    rewrite <- e in *.
    eapply MapsTo_func in e'.
    2: apply H.
    subst.
    eauto.

    apply LabelMap.add_3 in H; [ | assumption ].
    eapply IHfuncs in H; destruct H.
    exists x.
    apply LabelMap.add_2; auto.
    apply LabelMap.add_2; auto.
    apply MapsTo_union2.
    eauto.
    intros.
    apply buildLocals_notImport in H0; destruct H0; intuition congruence.
  Qed.

  Lemma blocks_modName : forall mn l pre_bl funcs start,
    LabelMap.MapsTo (mn, l) pre_bl (blocks funcs start)
    -> modName = mn.
    clear; induction funcs; simpl; intuition.
    apply LabelMap.empty_1 in H; tauto.
    destruct a as [ [ ] ].
    destruct (LabelKey.eq_dec (modName, Global s) (mn, l)).
    congruence.
    apply LabelMap.add_3 in H; [ | assumption ].
    destruct (LabelKey.eq_dec (modName, Local start) (mn, l)).
    congruence.
    apply LabelMap.add_3 in H; [ | assumption ].
    apply MapsTo_union in H; intuition.
    apply buildLocals_notImport in H0; destruct H0; intuition congruence.
    eauto.
  Qed.

  Theorem bmoduleOk : moduleOk bmodule_.
    constructor.

    clear NoDupFunc BlocksGood.
    red; simpl.
    apply Forall_MapsTo.
    intros.
    simpl.
    generalize dependent 1.
    induction functions; simpl; intuition.
    apply LabelMap.empty_1 in H; tauto.
    destruct a as [ [ ] ]; simpl in *.
    apply LabelFacts.add_mapsto_iff in H; intuition; subst; eauto.
    apply LabelFacts.add_mapsto_iff in H2; intuition; subst; eauto.
    apply MapsTo_union in H3; intuition.

    destruct (buildLocals_notImport _ _ H1); intuition; subst; eauto.
    eauto.

    red; simpl; unfold allPreconditions; simpl; intros.

    generalize (MapsTo_blocks _ _ H); intros.
    repeat match goal with
             | [ H : ex _ |- _ ] => destruct H; intuition; subst
           end.

    injection H8; clear H8; intros; subst; simpl.
    destruct (PreconditionOk (Generate (x1 fullImports fullImportsGlobal x0) (Nsucc x2) x2)).
    apply H2 in H6.
    autorewrite with N in H6.

    match type of H6 with
      | LabelMap.MapsTo ?k (?v, _) _ => destruct (H0 k v)
    end.
    eapply skipImports; eauto.
    intuition.
    rewrite H8.
    eauto.

    injection H8; clear H8; intros; subst; simpl.
    match type of H5 with
      | LabelMap.MapsTo ?k (?v, _) _ => destruct (H0 k v)
    end.
    eapply skipImports; eauto.
    intuition.
    rewrite H7.
    eauto.

    generalize (BlocksOk (Generate (x1 fullImports fullImportsGlobal x0) (Nsucc x2) x2)); intuition.
    match type of H6 with
      | ?P -> _ => assert P
    end.
    generalize BlocksGood' H3; clear.
    induction functions; simpl; intuition; subst.
    inversion H; clear H; subst.
    tauto.
    inversion H; clear H; subst.
    auto.

    intuition.
    match type of H9 with
      | ?P -> _ => assert P by nomega
    end; intuition.
    apply (Forall_nth_error H10) in H8; simpl in *.
    apply H8; intuition.
    apply H0.

    apply imps_cases in H9; intuition; subst.

    eapply skipImports; eauto.

    apply MapsTo_fullImports in H9; intuition.
    assert (~LabelMap.In l (blocks functions 1)).
    pose proof importsNotThis' as importsNotThis'.
    generalize NoSelfImport H11; clear -importsNotThis'.
    generalize false at 2.
    generalize 1.
    induction functions as [ | [ [ ] ] ]; simpl; intuition.
    destruct H.
    destruct (LabelMap.empty_1 H).
    destruct H.
    apply LabelFacts.add_mapsto_iff in H; intuition; subst.
    eapply importsNotThis'; eauto.
    apply LabelFacts.add_mapsto_iff in H1; intuition; subst.
    eapply importsNotThis'; eauto.
    apply MapsTo_union in H2; intuition.
    apply ungetLocal in H0; destruct H0; intuition; subst.
    eapply importsNotThis'; eauto.
    eapply IHl0 in NoSelfImport; eauto.
    eexists; eauto.

    assert (forall v, ~SetoidList.InA (@LabelMap.eq_key_elt _) (l, v) (LabelMap.elements (blocks functions 1))).
    generalize H9; clear.
    intros; intro.
    apply H9.
    eexists.
    apply LabelMap.elements_2; eauto.

    generalize H11 H12; clear.
    rewrite LabelMap.fold_1.
    generalize importsMap.
    induction (LabelMap.elements (blocks functions 1)) as [ | [ [ ] ] ]; simpl; intuition; simpl.
    apply IHl0.
    apply LabelMap.add_2; auto.
    intro; subst.
    eapply H12.
    constructor; hnf; eauto.
    eauto.

    destruct H11; intuition; subst.
    destruct H12.

    assert (SetoidList.NoDupA (fun p1 p2 => fst (fst p1) = fst (fst p2)) functions).
    generalize NoDupFunc; clear.
    generalize dependent (LabelMap.empty unit).
    induction functions as [ | [ [ ] ] ]; simpl; intuition.
    case_eq (LabelMap.mem (s, Local 0) t); intro Heq; rewrite Heq in *.
    elimtype False.
    generalize NoDupFunc; clear.
    induction l as [ | [ [ ] ] ]; simpl; intuition.
    specialize (IHl _ NoDupFunc).
    constructor; auto.
    generalize NoDupFunc; clear.
    assert (LabelMap.MapsTo (s, Local 0) tt (LabelMap.add (s, Local 0) tt t)) by (apply LabelMap.add_1; auto).
    generalize dependent (LabelMap.add (s, Local 0) tt t).
    induction l as [ | [ [ ] ] ]; simpl; intuition.
    inversion H0.
    inversion H0; clear H0; simpl in *; subst.
    assert (LabelMap.In (s0, Local 0) t0) by (hnf; eauto).
    apply LabelMap.mem_1 in H0.
    rewrite H0 in NoDupFunc.
    elimtype False.
    generalize NoDupFunc; clear.
    induction l as [ | [ [ ] ] ]; simpl; intuition.
    case_eq (LabelMap.mem (s0, Local 0) t0); intro Heq; rewrite Heq in *.
    elimtype False.
    generalize NoDupFunc; clear.
    induction l as [ | [ [ ] ] ]; simpl; intuition.
    specialize (fun H => IHl _ H NoDupFunc).
    apply IHl; auto.

    assert (exists bl, LabelMap.MapsTo (modName, Global x5) (pre0, bl) (blocks functions 1)).
    generalize H11 H9; clear.
    generalize 1.
    induction functions; simpl; intuition; subst.
    eexists; apply LabelMap.add_1; eauto.
    destruct a as [ [ ] ].
    inversion H11; clear H11; subst.
    match goal with
      | [ |- context[blocks _ ?n] ] => destruct (IHl n H3)
    end; auto.
    eexists.
    apply LabelMap.add_2.
    intro Ho; injection Ho; clear Ho; intros; subst.
    apply H2.
    generalize H; clear.
    induction l; simpl; intuition; subst; auto.
    apply LabelMap.add_2; [ congruence | ].
    apply MapsTo_union2; eauto.
    intros.
    destruct (ungetLocal _ _ H1); intuition congruence.

    rewrite LabelMap.fold_1.
    destruct H12.
    assert (SetoidList.InA (@LabelMap.eq_key_elt _) ((modName, Global x5), (pre0, x7)) (LabelMap.elements (blocks functions 1))).
    apply LabelMap.elements_1; auto.
    generalize H13; clear.
    generalize (LabelMap.elements_3w (blocks functions 1)).
    generalize importsMap.
    induction (LabelMap.elements (blocks functions 1)); simpl; intuition.
    inversion H13.
    inversion H; clear H; subst.
    inversion H13; clear H13; subst; simpl.
    hnf in H0; simpl in H0; intuition; subst.
    injection H1; clear H1; intros; subst.
    assert (LabelMap.MapsTo (modName, Global x5) a (LabelMap.add (modName, Global x5) a t)) by (apply LabelMap.add_1; auto).
    generalize H2 H; clear.
    generalize (LabelMap.add (modName, Global x5) a t).
    induction l; simpl; intuition; simpl.
    apply IHl; auto.
    apply LabelMap.add_2; auto.
    intro; subst.
    apply H2; constructor; hnf; auto.
    auto.

    destruct H9 as [ ? [ ] ]; intuition; subst.
    eapply skipImports; eauto.

    simpl.

    intros; eapply importsMap_global; eauto.

    simpl.

    intros; eapply blocks_exps; eauto.

    simpl.

    intros; eapply exps_blocks; eauto.

    simpl.
    intros.
    apply StringSet.singleton_2.

    eapply blocks_modName; eauto.
  Qed.

End module.


Require Import Verdi.GhostSimulations.
Require Import VerdiRaft.Raft.
Require Import VerdiRaft.CommonTheorems.
Require Import VerdiRaft.OneLeaderPerTermInterface.

Require Import VerdiRaft.CandidateEntriesInterface.
Require Import VerdiRaft.RaftRefinementInterface.
Require Import VerdiRaft.VotesCorrectInterface.
Require Import VerdiRaft.CroniesCorrectInterface.
Require Import VerdiRaft.RefinementCommonTheorems.

Require Import VerdiRaft.LeaderSublogInterface.

#[global]
Hint Extern 4 (@BaseParams) => apply base_params : typeclass_instances.
#[global]
Hint Extern 4 (@MultiParams _) => apply multi_params : typeclass_instances.
#[global]
Hint Extern 4 (@FailureParams _ _) => apply failure_params : typeclass_instances.

Section LeaderSublogProof.
  Context {orig_base_params : BaseParams}.
  Context {one_node_params : OneNodeParams orig_base_params}.
  Context {raft_params : RaftParams orig_base_params}.

  Context {rri : raft_refinement_interface}.
  Context {cei : candidate_entries_interface}.
  Context {vci : votes_correct_interface}.
  Context {cci : cronies_correct_interface}.
  Context {olpti : one_leader_per_term_interface}.

  Ltac prove_in :=
    match goal with
      | [ _ : nwPackets ?net = _,
              _ : In ?p _ |- _] =>
        assert (In p (nwPackets net)) by (repeat find_rewrite; do_in_app; intuition)
      | [ _ : nwPackets ?net = _,
              _ : pBody ?p = _ |- _] =>
        assert (In p (nwPackets net)) by (repeat find_rewrite; intuition)
    end.

  Notation is_append_entries m :=
    (exists t n prevT prevI entries c,
       m = AppendEntries t n prevT prevI entries c).

  Lemma leader_sublog_invariant_same_state :
    forall net net',
      leader_sublog_host_invariant net ->
      (forall h, log (nwState net h) = log (nwState net' h)) ->
      (forall h, type (nwState net' h) = Leader ->
            type (nwState net h) = Leader /\
            currentTerm (nwState net h) = currentTerm (nwState net' h)) ->
      leader_sublog_host_invariant net'.
  Proof using.
    unfold leader_sublog_host_invariant in *. intros.
    specialize (H leader e h).
    forward H; [apply H1; auto|].
    intuition.
    rewrite H0 in *. specialize (H1 leader). intuition.
    rewrite H7 in *; auto.
    rewrite H0 in *. auto.
  Qed.

  Lemma leader_sublog_invariant_subset :
    forall net net',
      leader_sublog_invariant net ->
      (forall p, is_append_entries (pBody p) -> In p (nwPackets net') -> In p (nwPackets net)) ->
      (forall h, log (nwState net h) = log (nwState net' h)) ->
      (forall h, type (nwState net' h) = Leader ->
            type (nwState net h) = Leader /\
            currentTerm (nwState net h) = currentTerm (nwState net' h)) ->
      leader_sublog_invariant net'.
  Proof using.
    unfold leader_sublog_invariant in *. intros; intuition.
    - eauto using leader_sublog_invariant_same_state.
    - unfold leader_sublog_nw_invariant in *. intros.
      pose proof H1 leader.
      pose proof H2 leader; concludes.
      pose proof H3 leader. intuition.
      symmetry in H13.
      repeat find_rewrite.
      eapply H11; simpl in *; repeat find_rewrite; eauto.
      assert (is_append_entries (pBody p)) by (repeat eexists; eauto).
      eauto.
  Qed.

  Theorem leader_sublog_do_leader :
    raft_net_invariant_do_leader leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_do_leader.
    intros.
    unfold doLeader in *.
    break_match; try solve [
                       find_inversion; simpl in *;
                       eapply leader_sublog_invariant_subset;
                       eauto; intuition; simpl in *;
                       repeat find_apply_hyp_hyp; intuition;
                       repeat find_higher_order_rewrite; repeat break_if; subst; intuition].
    break_if.
    - unfold replicaMessage in *. find_inversion. simpl in *.
      unfold leader_sublog_invariant in *; intuition.
      + unfold leader_sublog_host_invariant in *. intros.
        simpl in *. repeat find_higher_order_rewrite.
        repeat break_if; simpl in *; intuition eauto.
      + unfold leader_sublog_nw_invariant in *. intros.
        simpl in *. repeat find_higher_order_rewrite.
        find_apply_hyp_hyp.
        break_if; intuition idtac; simpl in *; subst; intuition eauto;
        simpl in *;
        repeat do_in_map; subst; simpl in *;
        find_inversion; eauto using findGtIndex_in.
    - find_inversion.
      unfold leader_sublog_invariant, leader_sublog_nw_invariant,
      leader_sublog_host_invariant, advanceCommitIndex in *.

      intuition; find_higher_order_rewrite; repeat break_if; simpl in *; subst;
      repeat break_if; simpl in *; eauto;
      find_apply_hyp_hyp; intuition; eauto.
  Qed.

  Lemma leader_sublog_client_request :
    raft_net_invariant_client_request leader_sublog_invariant.
  Proof using olpti.
    unfold raft_net_invariant_client_request.
    intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, handleClientRequest in *. intuition idtac.
    - break_match; find_inversion; simpl in *; repeat find_higher_order_rewrite;
      repeat break_if; subst; simpl in *;
      intuition eauto;
      simpl in *; in_crush_finish; intuition eauto. in_crush; eauto.
      exfalso.
      match goal with
        | H : raft_intermediate_reachable _ |- _ =>
          eapply one_leader_per_term_invariant in H
      end.
      assert (leader = h) by (eapply_prop one_leader_per_term; eauto).
      intuition.
    - break_match; find_inversion; simpl in *; repeat find_higher_order_rewrite;
      repeat break_if; find_apply_hyp_hyp; intuition eauto; subst.
      simpl in *.
      in_crush; eauto.
  Qed.

  Lemma leader_sublog_timeout :
    raft_net_invariant_timeout
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_timeout. intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, handleTimeout, tryToBecomeLeader in *.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto; solve_by_inversion.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto;
      find_apply_hyp_hyp; intuition eauto; try solve_by_inversion;
      in_crush; repeat find_inversion; discriminate.
  Qed.

  Lemma leader_sublog_append_entries :
    raft_net_invariant_append_entries
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_append_entries. intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, handleAppendEntries, advanceCurrentTerm in *.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto; try solve_by_inversion;
      do_in_app; intuition; eauto using removeAfterIndex_in.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto;
      find_apply_hyp_hyp; intuition eauto; subst; try discriminate.
  Qed.

  Lemma leader_sublog_append_entries_reply :
    raft_net_invariant_append_entries_reply
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_append_entries_reply. intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, handleAppendEntriesReply, advanceCurrentTerm in *.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto; try solve_by_inversion.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto;
      find_apply_hyp_hyp; intuition eauto; try discriminate.
  Qed.

  Lemma leader_sublog_request_vote :
    raft_net_invariant_request_vote
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_request_vote. intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, handleRequestVote, advanceCurrentTerm in *.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto; try solve_by_inversion.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *;
      intuition eauto;
      find_apply_hyp_hyp; intuition eauto; subst; try discriminate.
  Qed.

  Definition CandidateEntriesLowered net e h :=
      currentTerm (nwState net h) = eTerm e ->
      wonElection (dedup name_eq_dec (votesReceived (nwState net h))) = true ->
      type (nwState net h) <> Candidate.

  Lemma candidate_entries_lowered' :
    forall net,
      CandidateEntries net ->
      votes_correct net ->
      cronies_correct net ->
      forall h h' e,
        In e (log (snd (nwState net h'))) ->
        CandidateEntriesLowered (deghost net) e h.
  Proof using rri.
    unfold CandidateEntriesLowered, CandidateEntries, votes_correct, cronies_correct.
    intros. break_and.
    rewrite deghost_spec.

    apply_prop_hyp candidateEntries_host_invariant In.
    eapply candidateEntries_wonElection; auto; repeat find_rewrite_lem deghost_spec; eauto.
  Qed.

  Lemma candidate_entries_lowered :
    forall net,
      raft_intermediate_reachable net ->
      forall h h' e,
        In e (log (nwState net h')) ->
        CandidateEntriesLowered net e h.
  Proof using cci vci cei rri.
    intros net H.
    pattern net.
    apply lower_prop; auto.
    clear H net.
    intros.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.
    eapply candidate_entries_lowered';
      eauto using candidate_entries_invariant, votes_correct_invariant, cronies_correct_invariant.
  Qed.

  Definition CandidateEntriesLowered_rvr net e p :=
    In p (nwPackets net) ->
    pBody p = RequestVoteReply (eTerm e) true ->
    currentTerm (nwState net (pDst p)) = eTerm e ->
    wonElection (dedup name_eq_dec (pSrc p :: votesReceived (nwState net (pDst p)))) = true ->
    type (nwState net (pDst p)) <> Candidate.

  Lemma deghost_packet_exists :
    forall net p,
      In p (nwPackets (deghost net)) ->
      exists (q : packet (params := raft_refined_multi_params (raft_params := raft_params))),
        In q (nwPackets net) /\ p = deghost_packet q.
  Proof using.
    unfold deghost.
    simpl.
    intros.
    do_in_map.
    eauto.
  Qed.

  Lemma candidate_entries_lowered_rvr' :
    forall net,
      CandidateEntries net ->
      votes_correct net ->
      cronies_correct net ->
      forall p h e,
        In e (log (snd (nwState net h))) ->
        CandidateEntriesLowered_rvr (deghost net) e p.
  Proof using rri.
    unfold CandidateEntriesLowered_rvr, CandidateEntries.
    intros. break_and.
    rewrite deghost_spec.

    find_apply_lem_hyp deghost_packet_exists.
    break_exists.  break_and. subst.

    apply_prop_hyp candidateEntries_host_invariant In.
    eapply wonElection_candidateEntries_rvr; auto;
    repeat find_rewrite_lem deghost_spec; eauto.
  Qed.

  Lemma candidate_entries_lowered_rvr :
    forall net,
      raft_intermediate_reachable net ->
      forall p h e,
        In e (log (nwState net h)) ->
        CandidateEntriesLowered_rvr net e p.
  Proof using cci vci cei rri.
    intros net H.
    pattern net.
    apply lower_prop; auto.
    clear H net.
    intros.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.
    eapply candidate_entries_lowered_rvr';
      eauto using candidate_entries_invariant, votes_correct_invariant, cronies_correct_invariant.
  Qed.

  Lemma candidate_entries_lowered_nw' :
    forall net,
      CandidateEntries net ->
      votes_correct net ->
      cronies_correct net ->
      forall h p e t li pli plt es lc,
        pBody p = AppendEntries t li pli plt es lc ->
        In p (nwPackets (deghost net)) ->
        In e es ->
        CandidateEntriesLowered (deghost net) e h.
  Proof using rri.
    unfold CandidateEntriesLowered, CandidateEntries, votes_correct, cronies_correct.
    intros. break_and.
    rewrite deghost_spec.

    find_apply_lem_hyp deghost_packet_exists.
    break_exists. break_and. subst.

    eapply_prop_hyp candidateEntries_nw_invariant In; eauto.
    unfold candidateEntries in *. break_exists. break_and.
    match goal with
    | H : wonElection _ = _ |- _ =>
      eapply wonElection_one_in_common in H; [|clear H; eauto]
    end.
    break_exists. break_and.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.

    intro.
    assert (h = x0).
    {
      eapply_prop one_vote_per_term;
      eapply_prop cronies_votes.
      - eapply_prop votes_received_cronies; eauto.
      - find_reverse_rewrite. auto.
    }
    subst.
    concludes. contradiction.
  Qed.

  Lemma candidate_entries_lowered_nw :
    forall net,
      raft_intermediate_reachable net ->
      forall h p e t li pli plt es lc,
        pBody p = AppendEntries t li pli plt es lc ->
        In p (nwPackets net) ->
        In e es ->
        CandidateEntriesLowered net e h.
  Proof using cci vci cei rri.
    intros net H.
    pattern net.
    apply lower_prop; auto.
    clear H net.
    intros.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.
    eapply candidate_entries_lowered_nw';
      eauto using candidate_entries_invariant, votes_correct_invariant, cronies_correct_invariant.
  Qed.

  Lemma candidate_entries_lowered_nw_rvr' :
    forall net,
      CandidateEntries net ->
      votes_correct net ->
      cronies_correct net ->
      forall p' p e t li pli plt es lc,
        pBody p = AppendEntries t li pli plt es lc ->
        In p (nwPackets (deghost net)) ->
        In e es ->
        CandidateEntriesLowered_rvr (deghost net) e p'.
  Proof using rri.
    unfold CandidateEntriesLowered_rvr, CandidateEntries, votes_correct, cronies_correct.
    intros. break_and.
    rewrite deghost_spec.

    find_apply_lem_hyp deghost_packet_exists.
    find_apply_lem_hyp deghost_packet_exists.
    break_exists.  break_and. subst.
    eapply_prop_hyp votes_nw pBody. concludes.
    eapply_prop_hyp candidateEntries_nw_invariant pBody; auto.

    find_insterU. conclude_using eauto.
    unfold candidateEntries in *.
    break_exists. break_and.

    match goal with
    | H : wonElection _ = _ |- _ =>
      eapply wonElection_one_in_common in H; [|clear H; eauto]
    end.
    break_exists. break_and.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.
    simpl in *.
    break_or_hyp.
    - apply_prop_hyp cronies_votes In.
      assert (pDst x = x1) by (eapply_prop one_vote_per_term; eauto).
      subst. auto.
    - intro.
      assert (pDst x = x1).
      { eapply_prop one_vote_per_term;
        eapply_prop cronies_votes.
        - eapply_prop votes_received_cronies; eauto.
        - repeat find_reverse_rewrite. auto.
      }
      subst.
      concludes. contradiction.
  Qed.

  Lemma candidate_entries_lowered_nw_rvr :
    forall net,
      raft_intermediate_reachable net ->
      forall p' p e t li pli plt es lc,
        pBody p = AppendEntries t li pli plt es lc ->
        In p (nwPackets net) ->
        In e es ->
        CandidateEntriesLowered_rvr net e p'.
  Proof using cci vci cei rri.
    intros net H.
    pattern net.
    apply lower_prop; auto.
    clear H net.
    intros.
    repeat match goal with
           | [ H : _ |- _ ] => rewrite deghost_spec in H
           end.
    eapply candidate_entries_lowered_nw_rvr';
      eauto using candidate_entries_invariant, votes_correct_invariant, cronies_correct_invariant.
  Qed.

  Lemma leader_sublog_request_vote_reply :
    raft_net_invariant_request_vote_reply
      leader_sublog_invariant.
  Proof using cci vci cei rri.
    unfold raft_net_invariant_request_vote_reply.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
           leader_sublog_host_invariant, handleRequestVoteReply, advanceCurrentTerm.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *; intuition eauto; try discriminate.
      + exfalso. eapply candidate_entries_lowered; eauto.
      + rewrite dedup_not_in_cons in * by auto.
        exfalso. eapply candidate_entries_lowered_rvr; eauto.
        do_bool.
        find_rewrite.
        f_equal.
        lia.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      subst; simpl in *; intuition eauto;
      find_apply_hyp_hyp; intuition eauto; subst; try discriminate.
      + exfalso. eapply candidate_entries_lowered_nw; eauto.
      + rewrite dedup_not_in_cons in * by auto.
        exfalso. eapply candidate_entries_lowered_nw_rvr; eauto.
        do_bool.
        find_rewrite.
        f_equal.
        lia.
  Qed.

  Lemma leader_sublog_do_generic_server :
    raft_net_invariant_do_generic_server
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_do_generic_server.
    intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, doGenericServer in *.
    intuition idtac; simpl in *.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      use_applyEntries_spec;
      subst; simpl in *;
      intuition eauto;
      try solve_by_inversion.
    - repeat find_higher_order_rewrite; repeat break_match; repeat find_inversion;
      use_applyEntries_spec;
      subst; simpl in *;
      intuition eauto;
      find_apply_hyp_hyp; intuition eauto; subst; try discriminate.
  Qed.

  Lemma leader_sublog_state_same_packet_subset :
    raft_net_invariant_state_same_packet_subset
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_state_same_packet_subset.
    intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant in *. intuition idtac;
      repeat find_reverse_higher_order_rewrite; intuition eauto.
  Qed.

  Lemma leader_sublog_reboot :
    raft_net_invariant_reboot
      leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_reboot. intros.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant,
    leader_sublog_host_invariant, reboot in *. intuition idtac.
    - repeat find_higher_order_rewrite.
      simpl in *. repeat break_match; subst; simpl in *; intuition eauto; discriminate.
    - repeat find_higher_order_rewrite.
      simpl in *. repeat find_rewrite.
      repeat break_match; subst; simpl in *;
      intuition eauto; try discriminate.
  Qed.

  Theorem leader_sublog_init :
    raft_net_invariant_init leader_sublog_invariant.
  Proof using.
    unfold raft_net_invariant_init, leader_sublog_invariant,
    leader_sublog_host_invariant, leader_sublog_nw_invariant;
    intuition.
  Qed.

  Theorem leader_sublog_invariant_invariant :
    forall net,
      raft_intermediate_reachable net ->
      leader_sublog_invariant net.
  Proof using olpti cci vci cei rri.
    intros.
    eapply raft_net_invariant; eauto.
    - apply leader_sublog_init.
    - apply leader_sublog_client_request.
    - apply leader_sublog_timeout.
    - apply leader_sublog_append_entries.
    - apply leader_sublog_append_entries_reply.
    - apply leader_sublog_request_vote.
    - apply leader_sublog_request_vote_reply.
    - apply leader_sublog_do_leader.
    - apply leader_sublog_do_generic_server.
    - apply leader_sublog_state_same_packet_subset.
    - apply leader_sublog_reboot.
  Qed.

  Instance lsi : leader_sublog_interface.
  Proof.
    split.
    auto using leader_sublog_invariant_invariant.
  Qed.
End LeaderSublogProof.


Require Import VerdiRaft.Raft.
Require Import VerdiRaft.RaftRefinementInterface.

Require Import VerdiRaft.CommonTheorems.
Require Import VerdiRaft.RefinementCommonTheorems.
Require Import VerdiRaft.SpecLemmas.
Require Import VerdiRaft.RefinementSpecLemmas.

Local Arguments update {_} {_} _ _ _ _ _ : simpl never.

Require Import VerdiRaft.NoAppendEntriesToLeaderInterface.
Require Import VerdiRaft.NoAppendEntriesToSelfInterface.
Require Import VerdiRaft.TermsAndIndicesFromOneLogInterface.
Require Import VerdiRaft.RefinedLogMatchingLemmasInterface.
Require Import VerdiRaft.LogAllEntriesInterface.
Require Import VerdiRaft.AppendEntriesRequestLeaderLogsInterface.
Require Import VerdiRaft.LeaderSublogInterface.
Require Import VerdiRaft.LeadersHaveLeaderLogsStrongInterface.
Require Import VerdiRaft.OneLeaderLogPerTermInterface.
Require Import VerdiRaft.MatchIndexLeaderInterface.
Require Import VerdiRaft.MatchIndexSanityInterface.
Require Import VerdiRaft.AppendEntriesReplySublogInterface.
Require Import VerdiRaft.CandidateEntriesInterface.
Require Import VerdiRaft.VotesCorrectInterface.
Require Import VerdiRaft.CroniesCorrectInterface.

Require Import VerdiRaft.MatchIndexAllEntriesInterface.

Section MatchIndexAllEntries.
  Context {orig_base_params : BaseParams}.
  Context {one_node_params : OneNodeParams orig_base_params}.
  Context {raft_params : RaftParams orig_base_params}.

  Context {rri : raft_refinement_interface}.

  Context {naetli : no_append_entries_to_leader_interface}.
  Context {naetsi : no_append_entries_to_self_interface}.
  Context {taifoli : terms_and_indices_from_one_log_interface}.
  Context {rlmli : refined_log_matching_lemmas_interface}.
  Context {laei : log_all_entries_interface}.
  Context {aelli : append_entries_leaderLogs_interface}.
  Context {lsi : leader_sublog_interface}.
  Context {lhllsi : leaders_have_leaderLogs_strong_interface}.
  Context {ollpti : one_leaderLog_per_term_interface}.
  Context {mili : match_index_leader_interface}.
  Context {matchisi : match_index_sanity_interface}.
  Context {aersi : append_entries_reply_sublog_interface}.
  Context {cei : candidate_entries_interface}.
  Context {vci : votes_correct_interface}.
  Context {cci : cronies_correct_interface}.

  Definition match_index_all_entries_nw (net : network) : Prop :=
    forall p t es e,
      In p (nwPackets net) ->
      pBody p = AppendEntriesReply t es true ->
      currentTerm (snd (nwState net (pDst p))) = t ->
      In e (log (snd (nwState net (pDst p)))) ->
      eTerm e = t ->
      eIndex e <= maxIndex es ->
      type (snd (nwState net (pDst p))) = Leader ->
      In (t, e) (allEntries (fst (nwState net (pSrc p)))).

  Definition match_index_all_entries_inv (net : network) : Prop :=
    match_index_all_entries net /\ match_index_all_entries_nw net.

  Lemma match_index_all_entries_init :
    refined_raft_net_invariant_init match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_init,
           match_index_all_entries_inv,
           match_index_all_entries_nw,
           match_index_all_entries.
    simpl. intros.
    intuition.
  Qed.

  Theorem handleClientRequest_matchIndex_log :
    forall h st client id c out st' ps,
      handleClientRequest h st client id c = (out, st', ps) ->
      ps = nil /\
      (log st' = log st /\ matchIndex st' = matchIndex st \/
       exists e,
         log st' = e :: log st /\
         eIndex e = S (maxIndex (log st)) /\
         eTerm e = currentTerm st /\
         eClient e = client /\
         eInput e = c /\
         eId e = id /\
         type st = Leader /\
         matchIndex st' = assoc_set name_eq_dec (matchIndex st) h (S (maxIndex (log st)))).
  Proof using.
    intros. unfold handleClientRequest in *.
    break_match; find_inversion; subst; intuition.
    simpl in *. eauto 10.
  Qed.

  Lemma lifted_match_index_leader :
    forall net leader,
      refined_raft_intermediate_reachable net ->
      type (snd (nwState net leader)) = Leader ->
      assoc_default name_eq_dec (matchIndex (snd (nwState net leader))) leader 0 =
      maxIndex (log (snd (nwState net leader))).
  Proof using mili rri.
    intros.
    pose proof lift_prop _ match_index_leader_invariant _ ltac:(eauto) leader.
    find_rewrite_lem deghost_spec. concludes. auto.
  Qed.

  Lemma lifted_match_index_sanity :
    forall net leader h,
      refined_raft_intermediate_reachable net ->
      type (snd (nwState net leader)) = Leader ->
      assoc_default name_eq_dec (matchIndex (snd (nwState net leader))) h 0 <=
      maxIndex (log (snd (nwState net leader))).
  Proof using matchisi rri.
    intros.
    pose proof lift_prop _ match_index_sanity_invariant _ ltac:(eauto) leader h.
    find_rewrite_lem deghost_spec. concludes. auto.
  Qed.

  Lemma lifted_append_entries_reply_sublog :
    forall net p t es h e,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntriesReply t es true ->
      currentTerm (snd (nwState net h)) = t ->
      type (snd (nwState net h)) = Leader ->
      In e es ->
      In e (log (snd (nwState net h))).
  Proof using aersi rri.
    intros.
    pose proof lift_prop _ append_entries_reply_sublog_invariant _ ltac:(eauto).
    unfold append_entries_reply_sublog in *.
    find_apply_lem_hyp ghost_packet.
    eapply_prop_hyp In In; eauto; try rewrite deghost_spec; eauto.
    find_rewrite_lem deghost_spec. auto.
  Qed.

  Lemma match_index_all_entries_client_request :
    refined_raft_net_invariant_client_request match_index_all_entries_inv.
  Proof using aersi matchisi mili rlmli rri.
    unfold refined_raft_net_invariant_client_request, match_index_all_entries_inv.
    simpl. intros. break_and. split.
    - unfold match_index_all_entries in *. simpl in *. intros.
      repeat find_higher_order_rewrite. update_destruct_simplify_hyp.
      + find_copy_apply_lem_hyp handleClientRequest_type.
        find_copy_apply_lem_hyp handleClientRequest_matchIndex_log. intuition.
        * repeat find_rewrite.
          { update_destruct_simplify_hyp.
            - apply update_elections_data_clientRequest_allEntries_old'.
              find_apply_hyp_hyp. repeat find_rewrite. auto.
            - find_apply_hyp_hyp. repeat find_rewrite. auto.
          }
        * break_exists. break_and. repeat find_rewrite.
          { update_destruct_simplify_hyp.
            - unfold update_elections_data_client_request. find_rewrite.
              break_if.
              + repeat find_rewrite. simpl. break_or_hyp.
                * auto.
                * right.
                  find_copy_apply_lem_hyp maxIndex_is_max; [|solve[apply entries_sorted_invariant; auto]].
                  rewrite <- lifted_match_index_leader in * by auto.
                  eapply_prop_hyp In In; eauto. repeat find_rewrite. auto.
              + do_bool. find_rewrite. simpl length in *. lia.
            - find_erewrite_lem get_set_diff_default.
              pose proof lifted_match_index_sanity _ leader h0 ltac:(eauto) ltac:(auto).
              break_or_hyp.
              + simpl in *. lia.
              + find_apply_hyp_hyp. repeat find_rewrite. auto.
          }
      + find_apply_hyp_hyp. update_destruct_simplify_hyp.
        * apply update_elections_data_clientRequest_allEntries_old'.
          repeat find_rewrite. auto.
        * repeat find_rewrite. auto.
    - unfold match_index_all_entries_nw in *.
      simpl. intros.
      find_apply_hyp_hyp. break_or_hyp.
      + repeat find_higher_order_rewrite. update_destruct_simplify_hyp.
        * find_copy_apply_lem_hyp handleClientRequest_type. break_and. repeat find_rewrite.
          find_copy_apply_lem_hyp handleClientRequest_log.
          { intuition.
            - repeat find_rewrite.
              eapply_prop_hyp In In; eauto.
              update_destruct_simplify_hyp.
              + apply update_elections_data_clientRequest_allEntries_old'.
                repeat find_rewrite. auto.
              + repeat find_rewrite. auto.
            - break_exists. break_and. repeat find_rewrite.
              assert (es <> nil).
              {
                apply maxIndex_gt_0_nonempty.
                eapply Nat.lt_le_trans; [|eauto].
                simpl in *. break_or_hyp.
                - repeat find_rewrite. lia.
                - eapply entries_gt_0_invariant; eauto.
              }
              pose proof maxIndex_non_empty es. concludes.
              break_exists_name max_e. intuition.
              find_eapply_lem_hyp lifted_append_entries_reply_sublog; repeat find_rewrite; eauto.
              simpl In in *. break_or_hyp.
              + find_apply_lem_hyp maxIndex_is_max; [|solve[apply entries_sorted_invariant; auto]].
                lia.
              + eapply_prop_hyp In In; eauto; [|solve[repeat find_rewrite; auto]].
                update_destruct_simplify_hyp.
                * apply update_elections_data_clientRequest_allEntries_old'.
                  repeat find_rewrite. auto.
                * repeat find_rewrite. auto.
          }
        * eapply_prop_hyp In In; eauto.
          { update_destruct_simplify_hyp.
            - apply update_elections_data_clientRequest_allEntries_old'.
              repeat find_rewrite. auto.
            - repeat find_rewrite. auto.
          }
      + find_apply_lem_hyp handleClientRequest_packets. subst. simpl in *. intuition.
  Qed.

  Lemma handleTimeout_matchIndex :
    forall h st out st' l,
       handleTimeout h st = (out, st', l) ->
       matchIndex st' = matchIndex st.
  Proof using.
    unfold handleTimeout, tryToBecomeLeader.
    intros.
    repeat break_match; repeat find_inversion; simpl in *; auto.
  Qed.

  Lemma allEntries_update_timeout :
    forall x h h' net d,
      In x (allEntries (fst (nwState net h))) ->
      In x (allEntries (fst (update name_eq_dec (nwState net) h'
                                    (update_elections_data_timeout h' (nwState net h'), d) h))).
  Proof using.
    intros.
    update_destruct_simplify_hyp.
    - unfold update_elections_data_timeout. repeat break_match; auto.
    - auto.
  Qed.

  Lemma handleTimeout_sends_RV :
    forall h st out st' l m,
      handleTimeout h st = (out, st', l) ->
      In m l ->
      exists node t h mi mt,
        m = (node, RequestVote t h mi mt).
  Proof using.
    unfold handleTimeout, tryToBecomeLeader.
    intros.
    repeat break_match; repeat find_inversion.
    - do_in_map. subst. eauto 10.
    - do_in_map. subst. eauto 10.
    - simpl in *. intuition.
  Qed.

  Lemma match_index_all_entries_timeout :
    refined_raft_net_invariant_timeout match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_timeout, match_index_all_entries_inv.
    simpl. intros. break_and. split.
    - unfold match_index_all_entries in *. simpl. intros.
      repeat find_higher_order_rewrite.
      apply allEntries_update_timeout.
      update_destruct_simplify_hyp.
      + find_erewrite_lem handleTimeout_log_same.
        find_copy_apply_lem_hyp handleTimeout_type. intuition; try congruence.
        find_erewrite_lem handleTimeout_matchIndex.
        repeat find_rewrite.
        eapply_prop_hyp In In; eauto. congruence.
      + eapply_prop_hyp In In; eauto. congruence.
    - unfold match_index_all_entries_nw in *.
      simpl. intros.
      find_apply_hyp_hyp. break_or_hyp.
      + repeat find_higher_order_rewrite.
        apply allEntries_update_timeout.
        update_destruct_simplify_hyp.
        * find_erewrite_lem handleTimeout_log_same.
          find_copy_apply_lem_hyp handleTimeout_type.
          intuition; try congruence.
          eapply_prop_hyp In In; eauto; try congruence.
        * eapply_prop_hyp In In; eauto; congruence.
      + do_in_map. find_eapply_lem_hyp handleTimeout_sends_RV; eauto.
        break_exists. subst. simpl in *. discriminate.
  Qed.

  Lemma handleAppendEntries_post_leader_nop :
    forall h st t n pli plt es ci st' m,
      currentTerm st <> t ->
      handleAppendEntries h st t n pli plt es ci = (st', m) ->
      type st' = Leader ->
      st' = st.
  Proof using.
    unfold handleAppendEntries.
    intros.
    repeat break_match; repeat find_inversion; auto; try discriminate.
  Qed.

  Lemma handleAppendEntries_leader_was_leader :
    forall h st t n pli plt es ci st' m,
      handleAppendEntries h st t n pli plt es ci = (st', m) ->
      type st' = Leader ->
      type st = Leader.
  Proof using.
    unfold handleAppendEntries.
    intros.
    repeat break_match; repeat find_inversion; auto; try discriminate.
  Qed.

  Lemma lifted_no_AE_to_leader :
    forall net p t n pli plt es ci,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t n pli plt es ci ->
      type (snd (nwState net (pDst p))) = Leader ->
      currentTerm (snd (nwState net (pDst p))) = t ->
      False.
  Proof using naetli rri.
    intros.
    pose proof (lift_prop _ no_append_entries_to_leader_invariant _ ltac:(eauto)).
    unfold no_append_entries_to_leader in *.
    find_apply_lem_hyp ghost_packet.
    match goal with
    | [ H : forall _ _ _ , _, H' : In _ _ |- _ ] => eapply H in H'; eauto
    end;
    rewrite deghost_spec;
    rewrite pDst_deghost_packet; auto.
  Qed.

  Lemma lifted_no_AE_to_self :
    forall net p t n pli plt es ci,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t n pli plt es ci ->
      pDst p = pSrc p ->
      False.
  Proof using naetsi rri.
    intros.
    pose proof (lift_prop _ no_append_entries_to_self_invariant _ ltac:(eauto)).
    unfold no_append_entries_to_self in *.
    find_apply_lem_hyp ghost_packet.
    match goal with
    | [ H : forall _ _ _ , _, H' : In _ _ |- _ ] => eapply H in H'; eauto
    end.
  Qed.

  Lemma handleAppendEntries_message :
    forall h st t n pli plt es ci st' m,
      handleAppendEntries h st t n pli plt es ci = (st', m) ->
      exists res, m = AppendEntriesReply (currentTerm st') es res.
  Proof using.
    unfold handleAppendEntries, advanceCurrentTerm.
    intros. repeat break_match; repeat find_inversion; simpl in *; repeat do_bool; eauto;
            eexists; f_equal; eauto using NPeano.Nat.le_antisymm.
  Qed.

  Lemma not_empty_true_elim :
    forall A (l : list A),
      not_empty l = true -> l <> nil.
  Proof using.
    unfold not_empty.
    intros. break_match; congruence.
  Qed.

  Lemma not_empty_false_elim :
    forall A (l : list A),
      not_empty l = false -> l = nil.
  Proof using.
    unfold not_empty.
    intros. break_match; congruence.
  Qed.

  Lemma handleAppendEntries_success_allEntries :
    forall h st t n pli plt es ci st' t',
      handleAppendEntries h st t n pli plt es ci = (st', AppendEntriesReply t' es true) ->
      es <> nil ->
      (forall e e' e'',
          In e es ->
          In e' (log st) ->
          eIndex e = eIndex e' ->
          eTerm e = eTerm e' ->
          In e'' es ->
          eIndex e'' <= eIndex e ->
          In e'' (log st)) ->
      sorted (log st) ->
      exists e, In e (log st') /\ In e es /\
                eIndex e = maxIndex es /\
                eTerm e = maxTerm es.
  Proof using.
    unfold handleAppendEntries, haveNewEntries.
    intros.
    break_if; try find_inversion.
    break_if.
    - break_if; find_inversion; simpl;
      repeat (do_bool; repeat break_and).
      + find_apply_lem_hyp not_empty_true_elim.
        pose proof maxIndex_non_empty es ltac:(auto).
        break_exists_exists. intuition.
      + break_or_hyp.
        * find_apply_lem_hyp not_empty_false_elim. congruence.
        * break_match; try discriminate.
          do_bool. rewrite advanceCurrentTerm_log.
          find_apply_lem_hyp findAtIndex_elim. break_and.
          pose proof maxIndex_non_empty es ltac:(auto).
          break_exists_name e'. break_and.
          match goal with
          | [ H : forall _ _ _, In _ _ -> _ |- _ ] =>
            specialize (H e' e e')
          end.
          repeat find_rewrite. repeat concludes. intuition.
          assert (e = e').
          { eapply uniqueIndices_elim_eq; eauto.
            auto using sorted_uniqueIndices.
          }
          subst. eauto.
    - break_match; try find_inversion.
      break_if; try find_inversion.
      break_if; find_inversion; simpl;
      repeat (do_bool; repeat break_and).
      + find_apply_lem_hyp not_empty_true_elim.
        pose proof maxIndex_non_empty es ltac:(auto).
        break_exists_exists. intuition.
      + break_or_hyp.
        * find_apply_lem_hyp not_empty_false_elim. congruence.
        * break_match; try discriminate.
          do_bool. rewrite advanceCurrentTerm_log.
          find_apply_lem_hyp findAtIndex_elim. break_and.
          pose proof maxIndex_non_empty es ltac:(auto).
          break_exists_name e'. break_and.
          match goal with
          | [ H : forall _ _ _, In _ _ -> _ |- _ ] =>
            specialize (H e' e0 e')
          end.
          repeat find_rewrite. repeat concludes. intuition.
          assert (e0 = e').
          { eapply uniqueIndices_elim_eq; eauto.
            auto using sorted_uniqueIndices.
          }
          subst. eauto.
  Qed.

  Lemma handleAppendEntries_success_term :
    forall h st t n pli plt es ci st' t',
      handleAppendEntries h st t n pli plt es ci = (st', AppendEntriesReply t' es true) ->
      currentTerm st' = t.
  Proof using.
    unfold handleAppendEntries, advanceCurrentTerm.
    intros. repeat break_match; repeat find_inversion; simpl; auto; repeat do_bool;
    eauto using Nat.le_antisymm.
  Qed.

  Lemma lifted_terms_and_indices_from_one_log : forall net h,
    refined_raft_intermediate_reachable net ->
    terms_and_indices_from_one (log (snd (nwState net h))).
  Proof using taifoli rri.
    intros net0;intros.
    pose proof (lift_prop _ terms_and_indices_from_one_log_invariant).
    unfold terms_and_indices_from_one_log in *.
    rewrite <- deghost_spec with (net := net0). auto.
  Qed.

  Lemma lifted_leader_sublog_nw :
    forall net p t n pli plt es ci h e,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t n pli plt es ci ->
      type (snd (nwState net h)) = Leader ->
      eTerm e = currentTerm (snd (nwState net h)) ->
      In e es ->
      In e (log (snd (nwState net h))).
  Proof using lsi rri.
    intros.
    pose proof (lift_prop _ leader_sublog_invariant_invariant _ ltac:(eauto)) as Hinv.
    unfold leader_sublog_invariant, leader_sublog_nw_invariant in *.
    destruct Hinv as [Hhost Hnw].
    find_apply_lem_hyp ghost_packet.
    eapply_prop_hyp In In; eauto; try find_rewrite_lem deghost_spec; try rewrite deghost_spec; eauto.
  Qed.

  Lemma appendEntries_sublog :
    forall net p t n pli plt es ci h e,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t n pli plt es ci ->
      currentTerm (snd (nwState net h)) = t ->
      type (snd (nwState net h)) = Leader ->
      In e es ->
      In e (log (snd (nwState net h))).
  Proof using ollpti lhllsi lsi aelli rri.
    intros.
    find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
    break_exists. break_and.
    subst es.
    find_apply_lem_hyp in_app_or. destruct H4.
    - find_copy_apply_hyp_hyp.
      eapply lifted_leader_sublog_nw; eauto; [subst; auto|auto with datatypes].
    - find_eapply_lem_hyp leaders_have_leaderLogs_strong_invariant; auto.
      break_exists.  break_and.
      pose proof one_leaderLog_per_term_invariant _ ltac:(eauto) h x (currentTerm (snd (nwState net h))) x3 x0.
      concludes.
      subst_max.
      concludes.
      break_and. subst.
      find_rewrite. eauto using Prefix_In with *.
  Qed.

  Lemma match_index_all_entries_append_entries :
    refined_raft_net_invariant_append_entries' match_index_all_entries_inv.
  Proof using ollpti lhllsi lsi aelli laei rlmli taifoli naetsi naetli rri.
    unfold refined_raft_net_invariant_append_entries', match_index_all_entries_inv.
    simpl. intros. break_and.
    split.
    - unfold match_index_all_entries in *. simpl. intros.
      repeat find_higher_order_rewrite.
      update_destruct_simplify_hyp.
      + assert (currentTerm (snd (nwState net (pDst p))) <> t).
        { intro.
          match goal with
          | [ H : pBody _ = AppendEntries _ _ _ _ _ _ |- _ ] =>
            eapply lifted_no_AE_to_leader with (net := net) in H; eauto
          end.
          eapply handleAppendEntries_leader_was_leader; eauto.
        }
        find_apply_lem_hyp handleAppendEntries_post_leader_nop; auto.
        subst. eapply_prop_hyp In In; eauto.
        repeat find_rewrite.
        update_destruct_simplify_hyp; auto using update_elections_data_appendEntries_preserves_allEntries.
      + eapply_prop_hyp In In; eauto. repeat find_rewrite.
        update_destruct_simplify_hyp; auto using update_elections_data_appendEntries_preserves_allEntries.
    - unfold match_index_all_entries_nw. simpl.  intros.
      find_apply_hyp_hyp. break_or_hyp.
      + unfold match_index_all_entries_nw in *.
        repeat find_higher_order_rewrite.
        update_destruct_simplify_hyp.
        * assert (currentTerm (snd (nwState net (pDst p))) <> t).
          { intro.
            match goal with
            | [ H : pBody _ = AppendEntries _ _ _ _ _ _ |- _ ] =>
              eapply lifted_no_AE_to_leader with (net := net) in H; eauto
            end.
            eapply handleAppendEntries_leader_was_leader; eauto.
          }
          match goal with
          | [ H : context [handleAppendEntries] |- _ ] =>
            apply handleAppendEntries_post_leader_nop in H; auto
          end.
          subst.
          match goal with
          | [ H : In _ (_ ++ _), H' : forall _ _ _ _, In _ _ -> _ |- _ ] =>
            eapply in_middle_insert in H; eapply H' in H; eauto; try congruence
          end.
          { update_destruct_simplify_hyp.
            - apply update_elections_data_appendEntries_preserves_allEntries.
              repeat find_rewrite. auto.
            - auto.
          }
        * match goal with
          | [ H : forall _ _ _ _, In _ _ -> _, H' : pBody _ = AppendEntriesReply _ _ _ |- _ ] =>
            eapply H in H'; eauto
          end.
          { update_destruct_simplify_hyp.
            - apply update_elections_data_appendEntries_preserves_allEntries.
              repeat find_rewrite. auto.
            - auto.
          }
      + simpl in *.
        find_copy_apply_lem_hyp handleAppendEntries_message. break_exists.
        subst. find_inversion.
        repeat find_higher_order_rewrite.
        update_destruct_simplify_hyp.
        * exfalso. eapply lifted_no_AE_to_self with (net := net); eauto.
        * unfold update_elections_data_appendEntries. repeat find_rewrite. simpl.
          { find_copy_apply_lem_hyp handleAppendEntries_success_allEntries.
            - break_exists. break_and.
              find_copy_apply_lem_hyp handleAppendEntries_success_term.
              assert (In x (log (snd (nwState net (pSrc p))))).
              { eapply appendEntries_sublog; eauto. subst. repeat find_rewrite. auto. }
              assert (entries_match (log d) (log (snd (nwState net (pSrc p))))).
              { match goal with
                | [ H : refined_raft_intermediate_reachable (mkNetwork ?a ?b) |- _ ] =>
                  let H' := fresh "H" in
                  pose proof entries_match_invariant _ (pDst p) (pSrc p) H as H';
                    simpl in H'; repeat find_higher_order_rewrite; rewrite_update;
                    simpl in H'; auto
                end.
              }
              assert (In e (log d)) as Helogd.
              { match goal with
                | [ H : entries_match _ _ |- _ ] =>
                  specialize (H x x e)
                end.
                assert (eIndex e <= eIndex x) by lia.
                repeat concludes. intuition.
              }

              match goal with
              | [ H : refined_raft_intermediate_reachable (mkNetwork ?a ?b) |- _ ] =>
                let H' := fresh "H" in
                pose proof log_all_entries_invariant _ H (pDst p) e as H';
                  simpl in H'; repeat find_higher_order_rewrite; rewrite_update;
                  simpl in H'; unfold update_elections_data_appendEntries in H';
                  repeat find_rewrite; simpl in H'
              end.
              auto.
            - find_apply_lem_hyp lifted_terms_and_indices_from_one_log; auto. break_and.
              apply maxIndex_gt_0_nonempty. lia.
            - intros.
              match goal with
              | [ H : refined_raft_intermediate_reachable (mkNetwork _ _) |- _ ] => clear H
              end.
              pose proof entries_match_nw_host_invariant _ ltac:(eauto) _ _ _ _ _ _ _ (pDst p)
                   e0 e' e'' ltac:(eauto) ltac:(eauto).
              repeat find_rewrite. auto.
            - apply entries_sorted_invariant. auto.
          }
  Qed.

  Lemma handleAppendEntriesReply_spec :
    forall n st src t es b st' l,
      handleAppendEntriesReply n st src t es b = (st', l) ->
      (type st' = type st /\
       matchIndex st' = matchIndex st /\
       currentTerm st' = currentTerm st) \/
      (currentTerm st' = t /\ type st' = Follower) \/
      (b = true /\
       t = currentTerm st' /\
       type st' = type st /\
       matchIndex st' = assoc_set name_eq_dec (matchIndex st) src
                                  (PeanoNat.Nat.max
                                     (assoc_default name_eq_dec (matchIndex st) src 0) (maxIndex es)) /\
       currentTerm st' = currentTerm st).
  Proof using.
    unfold handleAppendEntriesReply.
    intros.
    repeat break_match; repeat find_inversion; simpl in *; auto.
    - do_bool. intuition.
    - unfold advanceCurrentTerm. break_match; auto.
  Qed.

  Lemma update_nop_fst :
    forall A B f x (v2 : B) y,
      fst (update name_eq_dec f x (fst (f x), v2) y) = fst (A := A) (f y).
  Proof using.
    intros.
    update_destruct_simplify_hyp; auto.
  Qed.

  Lemma match_index_all_entries_append_entries_reply :
    refined_raft_net_invariant_append_entries_reply match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_append_entries_reply, match_index_all_entries_inv.
    simpl. intros. split.
    - { unfold match_index_all_entries in *. simpl. intros. break_and.
        repeat find_higher_order_rewrite.
        rewrite update_nop_fst.
        update_destruct_simplify_hyp.
        - find_copy_apply_lem_hyp handleAppendEntriesReply_spec.
          intuition.
          + match goal with
            | [ H : forall _ _ _, type _ = _ -> _ |- _ ] => specialize (H e (pDst p) h)
            end.
            repeat find_rewrite. repeat concludes.
            find_erewrite_lem handleAppendEntriesReply_log.
            auto.
          + congruence.
          + repeat find_rewrite.
            match goal with
            | [ H : context [ assoc_default _ (assoc_set _ _ ?x _) ?y _ ]  |- _ ] =>
              destruct (name_eq_dec x y)
            end.
            * subst. rewrite get_set_same_default in *.
              find_apply_lem_hyp app_cons_in.
              find_erewrite_lem handleAppendEntriesReply_log.
              { find_apply_lem_hyp PeanoNat.Nat.max_le. break_or_hyp.
                - match goal with
                  | [ H : forall _ _ _, type _ = _ -> _ |- _ ] => specialize (H e (pDst p) (pSrc p))
                  end. repeat find_rewrite. auto.
                - unfold match_index_all_entries_nw in *.
                  match goal with
                  | [ H : pBody _ = _, H' : _  |- _ ] => eapply H' with (e := e) in H; auto
                  end.
              }
            * rewrite get_set_diff_default in * by auto.
              match goal with
              | [ H : forall _ _ _, type _ = _ -> _ |- _ ] => specialize (H e (pDst p) h)
              end.
              repeat find_rewrite. repeat concludes.
              find_erewrite_lem handleAppendEntriesReply_log.
              auto.
        - find_apply_hyp_hyp. congruence.
      }
    - break_and. unfold match_index_all_entries_nw in *. simpl. intros.
      repeat find_higher_order_rewrite. rewrite update_nop_fst.
      find_apply_hyp_hyp.
      break_or_hyp.
      + update_destruct_simplify_hyp.
        * find_erewrite_lem handleAppendEntriesReply_log.
          find_copy_apply_lem_hyp handleAppendEntriesReply_spec.
          { repeat break_or_hyp; break_and.
            - repeat find_rewrite. eauto using in_middle_insert.
            - congruence.
            - repeat find_rewrite. eauto using in_middle_insert.
          }
        * eauto using in_middle_insert.
      + do_in_map. find_apply_lem_hyp handleAppendEntriesReply_packets. subst.
        simpl in *. intuition.
  Qed.

  Lemma handleRequestVote_sends_RVR :
    forall st h h' t lli llt st' m,
      handleRequestVote h st t h' lli llt = (st', m) ->
      exists t b, m = RequestVoteReply t b.
  Proof using.
    unfold handleRequestVote.
    intros.
    repeat break_match; repeat find_inversion; eauto.
  Qed.

  Lemma match_index_all_entries_request_vote :
    refined_raft_net_invariant_request_vote match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_request_vote, match_index_all_entries_inv.
    simpl. intros. split.
    - unfold match_index_all_entries in *. simpl. intros. break_and.
      repeat find_higher_order_rewrite.
      update_destruct_simplify_hyp.
      + find_copy_apply_lem_hyp handleRequestVote_type.
        intuition; try congruence.
        find_copy_apply_lem_hyp handleRequestVote_matchIndex_preserved.
        unfold matchIndex_preserved in *. intuition.
        update_destruct_simplify_hyp.
        * rewrite update_elections_data_requestVote_allEntries.
          repeat find_reverse_rewrite.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            apply H with (leader := pDst p)
          end; auto;
            repeat find_rewrite; auto.
        * repeat find_reverse_rewrite.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            apply H with (leader := pDst p)
          end; auto;
            repeat find_rewrite; auto.
      + update_destruct_simplify_hyp.
        * rewrite update_elections_data_requestVote_allEntries.
          repeat find_reverse_rewrite.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto
          end.
        * repeat find_reverse_rewrite.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto
          end.
    - break_and.
      unfold match_index_all_entries_nw in *.
      simpl. intros.
      find_apply_hyp_hyp. break_or_hyp.
      + repeat find_higher_order_rewrite.
        update_destruct_simplify_hyp.
        * { find_copy_apply_lem_hyp handleRequestVote_type.
            intuition; try congruence.
            find_copy_apply_lem_hyp handleRequestVote_matchIndex_preserved.
            unfold matchIndex_preserved in *. intuition.
            update_destruct_simplify_hyp.
            - rewrite update_elections_data_requestVote_allEntries.
              repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                apply H with (es := es); auto using in_middle_insert; try congruence
              end.
            - match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                apply H with (es := es); auto using in_middle_insert; try congruence
              end.
          }
        * { update_destruct_simplify_hyp.
            - rewrite update_elections_data_requestVote_allEntries.
              repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                apply H with (es := es); auto using in_middle_insert; try congruence
              end.
            - match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                apply H with (es := es); auto using in_middle_insert; try congruence
              end.
          }
      + simpl in *.
        find_apply_lem_hyp handleRequestVote_sends_RVR.
        break_exists.
        congruence.
  Qed.

  Lemma handleRequestVoteReply_spec :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      type st' = Follower \/
      st' = st \/
      (log st' = log st /\
       currentTerm st' = currentTerm st /\
       matchIndex st' = assoc_set name_eq_dec nil h (maxIndex (log st))).
  Proof using.
    unfold handleRequestVoteReply.
    intros.
    repeat break_match; repeat find_inversion; subst; simpl; intuition.
  Qed.

  Lemma handleRequestVoteReply_spec' :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      type st' = Follower \/
      st' = st \/
      type st' = Candidate \/
      (type st' = Leader /\
       type st = Candidate /\
       log st' = log st /\
       r = true /\
       t = currentTerm st /\
       wonElection (dedup name_eq_dec (h' :: votesReceived st)) = true /\
       currentTerm st' = currentTerm st).
  Proof using.
    unfold handleRequestVoteReply.
    intros.
    repeat break_match; repeat find_inversion; do_bool; subst; simpl; intuition.
  Qed.

  Lemma match_index_all_entries_request_vote_reply :
    refined_raft_net_invariant_request_vote_reply match_index_all_entries_inv.
  Proof using cci vci cei laei taifoli rri.
    unfold refined_raft_net_invariant_request_vote_reply, match_index_all_entries_inv.
    simpl. intros. split.
    - unfold match_index_all_entries in *. simpl. intros. break_and.
      find_apply_lem_hyp handleRequestVoteReply_spec.
      repeat find_higher_order_rewrite.
      update_destruct_simplify_hyp.
      + intuition; try congruence.
        * subst.
          { update_destruct_simplify_hyp.
            - rewrite update_elections_data_requestVoteReply_allEntries.
              repeat find_reverse_rewrite. eauto.
            - repeat find_reverse_rewrite. eauto.
          }
        * repeat find_rewrite.
          unfold assoc_default in *.
          { break_match.
            - simpl in *. break_if; try discriminate.
              match goal with
              | [ H : Some _ = Some _ |- _ ] => invc H
              end.
              rewrite_update.
              simpl.
              rewrite update_elections_data_requestVoteReply_allEntries.
              match goal with
              | [ |- In (?t, _) _ ] => replace t with (eTerm e) by congruence
              end.
              eapply log_all_entries_invariant; auto.
            - find_apply_lem_hyp lifted_terms_and_indices_from_one_log; auto.
              intuition. lia.
          }
      + update_destruct_simplify_hyp.
        * rewrite update_elections_data_requestVoteReply_allEntries.
          repeat find_reverse_rewrite. eauto.
        * repeat find_reverse_rewrite. eauto.
    - unfold match_index_all_entries_nw in *.
      simpl in *.
      intros.
      find_apply_lem_hyp handleRequestVoteReply_spec'.
      repeat find_higher_order_rewrite.
      find_apply_hyp_hyp.
      update_destruct_simplify_hyp.
      + intuition; try congruence.
        * subst.
          { update_destruct_simplify_hyp.
            - rewrite update_elections_data_requestVoteReply_allEntries.
              repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                eapply H; eauto; congruence
              end.
            - repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                eapply H; eauto; congruence
              end.
          }
        * subst.
          intro_refined_invariant candidate_entries_invariant.
          match goal with
          | [ H : candidateEntries_host_invariant _ |- _ ] =>
            pose proof H (pDst p) e
          end.
          unfold raft_data in *.
          conclude_using congruence.
          { find_eapply_lem_hyp wonElection_candidateEntries_rvr; eauto.
            - intuition.
            - eauto using votes_correct_invariant.
            - eauto using cronies_correct_invariant.
            - unfold raft_refined_base_params, raft_refined_multi_params in *. congruence.
            - unfold raft_refined_multi_params, raft_refined_base_params in *.
              simpl in *.
              unfold raft_data in *.
              congruence.
          }
      + { update_destruct_simplify_hyp.
            - rewrite update_elections_data_requestVoteReply_allEntries.
              repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                eapply H; eauto; congruence
              end.
            - repeat find_rewrite.
              match goal with
              | [ H : context [ In _ (allEntries _) ] |- _ ] =>
                eapply H; eauto; congruence
              end.
          }
  Qed.

  Lemma doLeader_sends_AE :
    forall st h os st' ms m,
      doLeader st h = (os, st', ms) ->
      In m ms ->
      is_append_entries (snd m).
  Proof using.
    unfold doLeader.
    intros.
    repeat break_match; repeat find_inversion; simpl in *; intuition.
    do_in_map.
    subst.
    unfold replicaMessage.
    repeat break_match; simpl; eauto 10.
  Qed.

  Lemma match_index_all_entries_do_leader :
    refined_raft_net_invariant_do_leader match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_do_leader, match_index_all_entries_inv.
    intros. break_and. split.
    - unfold match_index_all_entries in *.
      simpl in *. intros.
      repeat find_higher_order_rewrite.
      match goal with
      | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
        replace x with (fst (nwState net h)) in * by (rewrite H; auto);
        replace y with (snd (nwState net h)) in * by (rewrite H; auto);
        clear H
      end.
      rewrite update_nop_fst.
      update_destruct_simplify_hyp.
      + repeat find_reverse_rewrite.
        find_copy_apply_lem_hyp doLeader_type. intuition.
        find_copy_apply_lem_hyp doLeader_matchIndex_preserved.
        unfold matchIndex_preserved in *. intuition.
        match goal with
        | [ H : context [ In _ (allEntries _) ] |- _ ] =>
          eapply H; eauto; try congruence
        end.
      + repeat find_reverse_rewrite.
        match goal with
        | [ H : context [ In _ (allEntries _) ] |- _ ] =>
          eapply H; eauto; try congruence
        end.
    - unfold match_index_all_entries_nw in *.
      simpl in *.
      intros.
      find_apply_hyp_hyp.
      break_or_hyp.
      + repeat find_higher_order_rewrite.
        match goal with
        | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
          replace x with (fst (nwState net h)) in * by (rewrite H; auto);
            replace y with (snd (nwState net h)) in * by (rewrite H; auto);
            clear H
        end.
        rewrite update_nop_fst in *.
        find_copy_apply_lem_hyp doLeader_type. intuition.
        update_destruct_simplify_hyp.
        * find_copy_apply_lem_hyp doLeader_matchIndex_preserved.
          unfold matchIndex_preserved in *. intuition.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto; try congruence
          end.
        * match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto; try congruence
          end.
      + do_in_map.
        find_eapply_lem_hyp doLeader_sends_AE; [|eauto].
        break_exists. subst.
        simpl in *. congruence.
  Qed.

  Lemma match_index_all_entries_do_generic_server :
    refined_raft_net_invariant_do_generic_server match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_do_generic_server, match_index_all_entries_inv.
    simpl. intros. break_and. split.
    - unfold match_index_all_entries in *.
      simpl in *. intros.
      repeat find_higher_order_rewrite.
      match goal with
      | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
        replace x with (fst (nwState net h)) in * by (rewrite H; auto);
        replace y with (snd (nwState net h)) in * by (rewrite H; auto);
        clear H
      end.
      rewrite update_nop_fst.
      update_destruct_simplify_hyp.
      + repeat find_reverse_rewrite.
        find_copy_apply_lem_hyp doGenericServer_type. intuition.
        find_copy_apply_lem_hyp doGenericServer_matchIndex_preserved.
        unfold matchIndex_preserved in *. intuition.
        match goal with
        | [ H : context [ In _ (allEntries _) ] |- _ ] =>
          eapply H; eauto; try congruence
        end.
      + repeat find_reverse_rewrite.
        match goal with
        | [ H : context [ In _ (allEntries _) ] |- _ ] =>
          eapply H; eauto; try congruence
        end.
    - unfold match_index_all_entries_nw in *.
      simpl in *.
      intros.
      find_apply_hyp_hyp.
      break_or_hyp.
      + repeat find_higher_order_rewrite.
        match goal with
        | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
          replace x with (fst (nwState net h)) in * by (rewrite H; auto);
            replace y with (snd (nwState net h)) in * by (rewrite H; auto);
            clear H
        end.
        rewrite update_nop_fst in *.
        find_copy_apply_lem_hyp doGenericServer_type. intuition.
        update_destruct_simplify_hyp.
        * find_copy_apply_lem_hyp doGenericServer_matchIndex_preserved.
          unfold matchIndex_preserved in *. intuition.
          match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto; try congruence
          end.
        * match goal with
          | [ H : context [ In _ (allEntries _) ] |- _ ] =>
            eapply H; eauto; try congruence
          end.
      + do_in_map.
        find_apply_lem_hyp doGenericServer_packets.
        subst. simpl in *. intuition.
  Qed.

  Lemma match_index_all_entries_state_same_packet_subset :
    refined_raft_net_invariant_state_same_packet_subset match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_state_same_packet_subset, match_index_all_entries_inv.
    simpl. intros. break_and. split.
    - unfold match_index_all_entries in *.
      intros.
      repeat find_reverse_higher_order_rewrite.
      eauto.
    - unfold match_index_all_entries_nw in *.
      intros.
      find_apply_hyp_hyp.
      repeat find_reverse_higher_order_rewrite.
      eauto.
  Qed.

  Lemma match_index_all_entries_reboot :
    refined_raft_net_invariant_reboot match_index_all_entries_inv.
  Proof using.
    unfold refined_raft_net_invariant_reboot, match_index_all_entries_inv.
    intros. break_and. subst. split.
    - unfold match_index_all_entries in *.
      intros.
      repeat find_higher_order_rewrite.
      match goal with
      | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
        replace x with (fst (nwState net h)) in * by (rewrite H; auto);
          replace y with (snd (nwState net h)) in * by (rewrite H; auto);
          clear H
      end.
      rewrite update_nop_fst.
      update_destruct_simplify_hyp.
      + discriminate.
      + repeat find_reverse_rewrite.
        eauto.
    - unfold match_index_all_entries_nw in *.
      intros.
      repeat find_higher_order_rewrite.
      match goal with
      | [ H : nwState ?net ?h = (?x, ?y) |- _ ] =>
        replace x with (fst (nwState net h)) in * by (rewrite H; auto);
          replace y with (snd (nwState net h)) in * by (rewrite H; auto);
          clear H
      end.
      rewrite update_nop_fst.
      update_destruct_simplify_hyp.
      + discriminate.
      + repeat find_reverse_rewrite.
        eauto.
  Qed.

  Lemma match_index_all_entries_invariant :
    forall net,
      refined_raft_intermediate_reachable net ->
      match_index_all_entries_inv net.
  Proof using cci vci cei aersi matchisi mili ollpti lhllsi lsi aelli laei rlmli taifoli naetsi naetli rri.
    intros.
    apply refined_raft_net_invariant'; auto.
    - apply match_index_all_entries_init.
    - apply refined_raft_net_invariant_client_request'_weak.
      apply match_index_all_entries_client_request.
    - apply refined_raft_net_invariant_timeout'_weak.
      apply match_index_all_entries_timeout.
    - apply match_index_all_entries_append_entries.
    - apply refined_raft_net_invariant_append_entries_reply'_weak.
      apply match_index_all_entries_append_entries_reply.
    - apply refined_raft_net_invariant_request_vote'_weak.
      apply match_index_all_entries_request_vote.
    - apply refined_raft_net_invariant_request_vote_reply'_weak.
      apply match_index_all_entries_request_vote_reply.
    - apply refined_raft_net_invariant_do_leader'_weak.
      apply match_index_all_entries_do_leader.
    - apply refined_raft_net_invariant_do_generic_server'_weak.
      apply match_index_all_entries_do_generic_server.
    - apply match_index_all_entries_state_same_packet_subset.
    - apply refined_raft_net_invariant_reboot'_weak.
      apply match_index_all_entries_reboot.
  Qed.

  Instance miaei : match_index_all_entries_interface.
  Proof.
    constructor.
    apply match_index_all_entries_invariant.
  Qed.
End MatchIndexAllEntries.


From Coq Require Import Utf8 RelationClasses.
From PDM Require Import util structures.

(* Guarded monad *)

Definition G (A : Type) :=
  ∑ (P : Prop), P → A.

#[export] Instance Monad_G : Monad G.
Proof.
  simple refine {|
    ret A x := (True ; λ _, x)
  |}.
  (* bind *)
  intros A B x f. simpl.
  exists (∃ (h : x.π1), (f (x.π2 h)).π1).
  simple refine (λ h, (f (x.π2 _)).π2 _).
  - destruct h. assumption.
  - destruct h as [h hf]. assumption.
Defined.

#[export] Instance ReqMonad_G : ReqMonad G := {|
  req p := (p ; λ h, h)
|}.