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)
|}.


Require Import VerdiRaft.Raft.
Require Import VerdiRaft.RaftRefinementInterface.
Local Arguments update {_} {_} _ _ _ _ _ : simpl never.

Require Import VerdiRaft.SpecLemmas.
Require Import VerdiRaft.RefinementSpecLemmas.

Require Import VerdiRaft.RequestVoteMaxIndexMaxTermInterface.
Require Import VerdiRaft.RequestVoteTermSanityInterface.

Section RequestVoteMaxIndexMaxTerm.
  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 {rvrtsi : requestVote_term_sanity_interface}.

  Lemma requestVote_maxIndex_maxTerm_append_entries :
    refined_raft_net_invariant_append_entries requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    subst. repeat find_higher_order_rewrite.
    assert (In p0 (nwPackets net)) by
        (find_apply_hyp_hyp; repeat find_rewrite; intuition; [in_crush|];
         exfalso; subst; simpl in *; subst;
         unfold handleAppendEntries in *;
           repeat break_match; find_inversion).
    repeat find_rewrite.
    destruct_update; simpl in *; eauto;
    try rewrite votesWithLog_same_append_entries; eauto;
    find_apply_lem_hyp handleAppendEntries_log_term_type.
    break_or_hyp; try congruence.
    break_and; repeat find_rewrite; eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_append_entries_reply :
    refined_raft_net_invariant_append_entries_reply requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    assert (In p0 (nwPackets net)) by
        (repeat find_rewrite;
         find_apply_lem_hyp handleAppendEntriesReply_packets;
         subst; simpl in *; find_apply_hyp_hyp; intuition; in_crush).
    repeat find_rewrite.
    find_copy_apply_lem_hyp handleAppendEntriesReply_log_term_type.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    break_or_hyp; try congruence; break_and; repeat find_rewrite; eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_request_vote :
    refined_raft_net_invariant_request_vote requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    assert (In p0 (nwPackets net)) by
        (find_apply_hyp_hyp; repeat find_rewrite; intuition; [in_crush|];
         exfalso; subst; simpl in *; subst;
         unfold handleRequestVote in *;
           repeat break_match; find_inversion).
    find_copy_apply_lem_hyp handleRequestVote_log_term_type.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    break_or_hyp; try congruence; break_and; repeat find_rewrite; eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_request_vote_reply :
    refined_raft_net_invariant_request_vote_reply requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto.
    find_copy_eapply_lem_hyp handleRequestVoteReply_log_term_type; eauto.
    break_and; repeat find_rewrite; eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_timeout :
    refined_raft_net_invariant_timeout requestVote_maxIndex_maxTerm.
  Proof using rvrtsi.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto.
    - find_apply_hyp_hyp. break_or_hyp.
      + exfalso.
        find_eapply_lem_hyp update_elections_data_timeout_votesWithLog_votesReceived; eauto.
        intuition; try congruence.
        find_apply_lem_hyp requestVote_term_sanity_invariant.
        eapply_prop_hyp requestVote_term_sanity pBody; eauto.
        unfold raft_data in *; simpl in *; unfold raft_data in *; simpl in *.
        lia.
      + do_in_map. remember (pSrc p). subst p.
        simpl in *.
        intuition; eapply handleTimeout_messages; eauto.
    - find_apply_hyp_hyp. break_or_hyp; eauto.
      do_in_map. subst. simpl in *. intuition.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_client_request :
    refined_raft_net_invariant_client_request requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    find_copy_apply_lem_hyp handleClientRequest_packets.
    subst. simpl in *.
    find_apply_hyp_hyp. break_or_hyp.
    repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    try rewrite votesWithLog_same_client_request; eauto;
    find_apply_lem_hyp handleClientRequest_candidate; subst; eauto.
    intuition.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_do_leader :
    refined_raft_net_invariant_do_leader requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    assert (In p (nwPackets net)) by
        (find_apply_hyp_hyp; intuition;
         do_in_map; subst;
         unfold doLeader, replicaMessage in *;
           repeat break_match; find_inversion; subst; simpl in *; intuition;
         do_in_map; subst; simpl in *; congruence).
    match goal with
      | H : nwState ?net ?h = (?gd, ?d) |- _ =>
        replace gd with (fst (nwState net h)) in * by (rewrite H; reflexivity);
          replace d with (snd (nwState net h)) in * by (rewrite H; reflexivity);
          clear H
    end.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    find_apply_lem_hyp doLeader_candidate; subst; eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_do_generic_server :
    refined_raft_net_invariant_do_generic_server requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    find_copy_apply_lem_hyp doGenericServer_packets. subst. simpl in *.
    find_apply_hyp_hyp. break_or_hyp.
    match goal with
      | H : nwState ?net ?h = (?gd, ?d) |- _ =>
        replace gd with (fst (nwState net h)) in * by (rewrite H; reflexivity);
          replace d with (snd (nwState net h)) in * by (rewrite H; reflexivity);
          clear H
    end.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    find_apply_lem_hyp doGenericServer_log_type_term_votesReceived;
    break_and; repeat find_rewrite; eauto. intuition.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_reboot :
    refined_raft_net_invariant_reboot requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    match goal with
      | H : nwState ?net ?h = (?gd, ?d) |- _ =>
        replace gd with (fst (nwState net h)) in * by (rewrite H; reflexivity);
          replace d with (snd (nwState net h)) in * by (rewrite H; reflexivity);
          clear H
    end.
    subst. repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto; congruence.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_state_same_packet_subset :
    refined_raft_net_invariant_state_same_packet_subset requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    subst. repeat find_reverse_higher_order_rewrite.
    eauto.
  Qed.

  Lemma requestVote_maxIndex_maxTerm_init :
    refined_raft_net_invariant_init requestVote_maxIndex_maxTerm.
  Proof using.
    red. unfold requestVote_maxIndex_maxTerm. intros. simpl in *.
    intuition.
  Qed.

  Instance rvmimti : requestVote_maxIndex_maxTerm_interface.
  split.
  intros.
  apply refined_raft_net_invariant; auto.
  - apply requestVote_maxIndex_maxTerm_init.
  - apply requestVote_maxIndex_maxTerm_client_request.
  - apply requestVote_maxIndex_maxTerm_timeout.
  - apply requestVote_maxIndex_maxTerm_append_entries.
  - apply requestVote_maxIndex_maxTerm_append_entries_reply.
  - apply requestVote_maxIndex_maxTerm_request_vote.
  - apply requestVote_maxIndex_maxTerm_request_vote_reply.
  - apply requestVote_maxIndex_maxTerm_do_leader.
  - apply requestVote_maxIndex_maxTerm_do_generic_server.
  - apply requestVote_maxIndex_maxTerm_state_same_packet_subset.
  - apply requestVote_maxIndex_maxTerm_reboot.
  Qed.

End RequestVoteMaxIndexMaxTerm.


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

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

Axiom qtmark : Type.
Parameter qtmark_WhyType : WhyType qtmark.
Existing Instance qtmark_WhyType.

(* Why3 assumption *)
Inductive ref (a:Type) {a_WT:WhyType a} :=
  | mk_ref : a -> ref a.
Axiom ref_WhyType : forall (a:Type) {a_WT:WhyType a}, WhyType (ref a).
Existing Instance ref_WhyType.
Implicit Arguments mk_ref [[a] [a_WT]].

(* Why3 assumption *)
Definition contents {a:Type} {a_WT:WhyType a} (v:(@ref a a_WT)): a :=
  match v with
  | (mk_ref x) => x
  end.

Import int.ComputerDivision.
Import Power.

(* Why3 goal *)
Theorem WP_parameter_fast_exp_imperative : forall (x:Z) (n:Z),
  (0%Z <= n)%Z -> forall (e:Z) (p:Z) (r:Z), ((0%Z <= e)%Z /\
  ((r * (int.Power.power p e))%Z = (int.Power.power x n))) -> ((0%Z < e)%Z ->
  (((ZArith.BinInt.Z.rem e 2%Z) = 1%Z) -> forall (r1:Z), (r1 = (r * p)%Z) ->
  forall (p1:Z), (p1 = (p * p)%Z) -> forall (e1:Z),
  (e1 = (ZArith.BinInt.Z.quot e 2%Z)) -> ((r1 * (int.Power.power p1
  e1))%Z = (int.Power.power x n)))).
(* Why3 intros x n h1 e p r (h2,h3) h4 h5 r1 h6 p1 h7 e1 h8. *)
intros x n h1 e p r (h2,h3) h4 h5 r1 h6 p1 h7 e1 h8.
subst.
assert (h: (2 <> 0)%Z) by omega.
generalize (Div_mod e 2 h). clear h.
assert (h: (0 < 2)%Z) by omega.
generalize (Div_bound e 2 (conj h2 h)). clear h.
rewrite h5; clear h5.
intros.
rewrite <- h3; clear h3.
rewrite H0 at 2. clear H0.
rewrite Power_sum by omega.
replace (2 * (Z.quot e 2))%Z with (Z.quot e 2 + Z.quot e 2)%Z by ring.
rewrite Power_sum by apply H.
rewrite Power_mult2 by apply H.
rewrite Power_1.
ring.
Qed.


Require Import compcert.lib.Coqlib.
Require Import compcert.lib.Maps.
Require Import compcert.lib.Integers.
Require Import compcert.lib.Axioms.

Require Import compcert.common.Values.
Require Import compcert.common.Memory.
Require Import compcert.common.Events.
Require Import compcert.common.AST.
Require Import compcert.common.Globalenvs.

Require Import VST.sepcomp.mem_lemmas.

(** * Interaction Semantics *)

(** NOTE: In the code, we call interaction semantics [CoreSemantics]. *)

(** The [G] type parameter is the type of global environments, the type
   [C] is the type of core states, and the type [E] is the type of
   extension requests. *)

(** [at_external] gives a way to determine when the sequential
   execution is blocked on an extension call, and to extract the
   data necessary to execute the call. *)

(** [after_external] give a way to inject the extension call results
   back into the sequential state so execution can continue. *)

(** [initial_core] produces the core state corresponding to an entry
   point of a module.  The arguments are the genv, a pointer to the
   function to run, and the arguments for that function. *)

(** [halted] indicates when a program state has reached a halted state,
   and what it's exit code/return value is when it has reached such
   a state. *)

(** [corestep] is the fundamental small-step relation for the
   sequential semantics. *)

(** The remaining properties give basic sanity properties which constrain
   the behavior of programs. *)
(** -1 a state cannot be both blocked on an extension call and also step, *)
(** -2 a state cannot both step and be halted, and *)
(** -3 a state cannot both be halted and blocked on an external call. *)

Record CoreSemantics {G C M : Type} : Type :=
  { initial_core : nat -> G -> val -> list val -> option C
  ; at_external : C -> option (external_function * list val)
  ; after_external : option val -> C -> option C
  ; halted : C -> option val
  ; corestep : G -> C -> M -> C -> M -> Prop

  ; corestep_not_at_external:
      forall ge m q m' q', corestep ge q m q' m' -> at_external q = None
  ; corestep_not_halted:
      forall ge m q m' q', corestep ge q m q' m' -> halted q = None
  ; at_external_halted_excl:
      forall q, at_external q = None \/ halted q = None }.

Arguments CoreSemantics : clear implicits.

Inductive mem_step m m' : Prop :=
    mem_step_storebytes: forall b ofs bytes,
       Mem.storebytes m b ofs bytes = Some m' -> mem_step m m'
  | mem_step_alloc: forall lo hi b',
       Mem.alloc m lo hi = (m',b') -> mem_step m m'
  | mem_step_freelist: forall l,
       Mem.free_list m l = Some m' -> mem_step m m'
  (*Some non-observable external calls are not a single alloc/free/store-step*)
  | mem_step_trans: forall m'',
       mem_step m m'' -> mem_step m'' m' -> mem_step m m'.

Local Notation "a # b" := (PMap.get b a) (at level 1).
Record perm_lesseq (m m': mem):= {
  perm_le_Cur:
    forall b ofs, Mem.perm_order'' ((Mem.mem_access m')#b ofs Cur) ((Mem.mem_access m)#b ofs Cur)
; perm_le_Max:
    forall b ofs, Mem.perm_order'' ((Mem.mem_access m')#b ofs Max) ((Mem.mem_access m)#b ofs Max)
; perm_le_cont:
    forall b ofs, Mem.perm m b ofs Cur Readable ->
     ZMap.get ofs (Mem.mem_contents m') !! b= ZMap.get ofs (Mem.mem_contents m) !! b
; perm_le_nb: Mem.nextblock m = Mem.nextblock m'
}.

(* Memory semantics are CoreSemantics that are specialized to CompCert memories
   and evolve memory according to mem_step. Previous notion CoopCoreSem is deprecated,
   but for now retained in file CoopCoreSem.v *)
Record MemSem {G C} :=
  { csem :> @CoreSemantics G C mem

  ; corestep_mem : forall g c m c' m' (CS: corestep csem g c m c' m'), mem_step m m'
  (*later, we'll want to add the following constraint
  ; corestep_incr_perm: forall g c m c' m' (CS: corestep csem g c m c' m')  m1 (PLE: perm_lesseq m m1),
         exists m1', corestep csem g c m1 c' m1' /\ perm_lesseq m' m1'*)
  }.

Arguments MemSem : clear implicits.


From iris.heap_lang Require Import notation lang.

Definition addConditionally: val :=
  rec: "addConditionally" "loc" "delta" "condition" :=
    let: "cur" := !"loc" in
    if: "condition" "cur"
    then if: CAS "loc" "cur" ("cur" + "delta")
         then #true
         else "addConditionally" "loc" "delta" "condition"
    else #false.

From iris.program_logic Require Import atomic.
From iris.heap_lang Require Import proofmode.

Section proof.

Context `{heapG}.

Theorem addConditionally_spec (pred: Z → bool) (ℓ: loc) (Δ: Z) (condition: val):
  ⊢ (∀ (k: Z), {{{ True }}}
                 condition #k
               {{{ RET #(pred k); True }}}) →
    <<< ∀ (k: Z), ▷ ℓ ↦ #k >>>
      addConditionally #ℓ #Δ condition @ ⊤
    <<< if pred k then ℓ ↦ #(k + Δ) else ℓ ↦ #k, RET #(pred k) >>>.
Proof.
  iIntros "#HCond" (Φ) "AU". iLöb as "IH". wp_lam. wp_pures.
  wp_bind (!_)%E. iMod "AU" as (k) "[Hℓ HClose]".
  wp_load.
  destruct (pred k) eqn:E; wp_pures.
  - iDestruct "HClose" as "[HClose _]".
    iMod ("HClose" with "Hℓ") as "AU". iModIntro. wp_pures.
    wp_apply ("HCond" with "[$]"). iIntros (_).
    rewrite E. wp_pures.
    wp_bind (CmpXchg _ _ _). iMod "AU" as (k') "[Hℓ HClose]".
    destruct (decide (k = k')) as [<-|HContra].
    + wp_cmpxchg_suc. iDestruct "HClose" as "[_ HClose]".
      iMod ("HClose" with "[Hℓ]") as "HΦ".
      { rewrite E. iFrame. }
      iModIntro. wp_pures. rewrite E. iAssumption.
    + wp_cmpxchg_fail. iDestruct "HClose" as "[HClose _]".
      iMod ("HClose" with "Hℓ") as "AU".
      iModIntro. wp_pures. iApply ("IH" with "AU").
  - iDestruct "HClose" as "[_ HClose]".
    iMod ("HClose" with "[Hℓ]") as "AU"; first by iFrame.
    iModIntro. wp_pures.
    wp_apply ("HCond" with "[$]"). iIntros (_).
    rewrite E. wp_pures.
    iAssumption.
Qed.

End proof.


Set Implicit Arguments.

Require Import Coq.omega.Omega.
Require Import Coq.Lists.List.
Require Import Coq.ZArith.BinInt.
Require Import Coq.Lists.SetoidList.

Require Import HJ.Tid.
Require Import HJ.Vars.
Require Import HJ.Phasers.Lang.
Require Import HJ.Phasers.PhaseDiff.
Require Import HJ.Phasers.LEDec.

Require Import HJ.Phasers.TransDiff.
Require Import HJ.Phasers.Typesystem.

Require Coq.FSets.FMapFacts.

Require HJ.Phasers.PhaseDiff.
Require HJ.Phasers.Rel.
Require HJ.Phasers.SubjectReduction.
Require HJ.Phasers.Typesystem.
Require HJ.Phasers.WellFormed.

Require Import Phasers.SubjectReduction.

Module S := HJ.Phasers.Lang.

Open Scope Z.

Section SIMPLE.

Lemma progress_unblocking_simple:
  forall pm t i,
  Valid pm ->
  Op.Valid pm t i ->
  i <> WAIT_ALL ->
  exists m, Reduces pm t i m.
Proof.
  intros.
  exists (run (get_impl i) t pm).
  apply reduces.
  destruct i; simpl; inversion H0; auto.
  contradiction H1.
  trivial.
Qed.

End SIMPLE.

Section HAS_SMALLEST.
Variable pm: phasermap.
Let IsA t := In t pm.

Let wtid_le (t1:tid) (t2:tid) := LE pm t1 t2.

Let wtid_le_inv := LE_inv pm.

Let wtid_le_trans:
  forall t1 t2 t3,
  wtid_le t1 t2 ->
  wtid_le t2 t3 ->
  wtid_le t1 t3.
Proof.
  unfold wtid_le in *.
  apply LE_trans.
Qed.

Let wtid_has_smallest :=
  Rel.has_smallest tid IsA wtid_le (LE_inv pm) (LE_dec pm)
  (LE_refl pm) wtid_le_trans.

Definition Smallest (t:tid) (ts:list tid)  :=
  List.In t ts /\
  forall t', List.In t' ts -> (~ LE pm t t' /\ ~ LE pm t' t) \/ LE pm t t'.

Let has_smallest:
  forall ts,
  ts <> nil ->
  Forall IsA ts ->
  exists t,
  Smallest t ts.
Proof.
  intros.
  destruct (wtid_has_smallest H H0) as (x, Hx).
  unfold Rel.Smallest in *.
  unfold Rel.Unrelated in *.
  unfold wtid_le in *.
  exists x.
  auto.
Qed.

Let tids := pm_tids pm.

Let smallest_inv:
  forall t,
  Smallest t tids ->
  List.In t tids.
Proof.
  intros.
  unfold Smallest in *.
  intuition.
Qed.

Let in_tids:
  forall p ph t,
  Map_PHID.MapsTo p ph pm ->
  Map_TID.In t ph ->
  List.In t tids.
Proof.
  intros.
  unfold tids.
  rewrite pm_tids_spec.
  eauto using in_def.
Qed.

Let Smallest_to_LE :
  forall t t' p ph,
  Smallest t tids ->
  Map_PHID.MapsTo p ph pm ->
  Map_TID.In t ph ->
  Map_TID.In t' ph ->
  LE pm t t'.
Proof.
  intros.
  unfold Smallest in *.
  destruct H as (Hin, H).
  assert (Hx := H t'); clear H.
  assert (Hin' : List.In t' tids) by eauto using in_tids.
  apply Hx in Hin'; clear Hx.
  destruct Hin' as [(?,?)|?].
  - destruct (LE_total _ _ _ _ _ H0 H1 H2); repeat contradiction. (* absurd *)
  - assumption.
Qed.

Let diff := pm_diff pm.

Variable check : Valid pm.

Let diff_det : TransDiffFun diff.
Proof.
  unfold Valid in *.
  intuition.
Qed.

Lemma Smallest_to_WaitPhase :
  forall t t' v v' p ph,
  Smallest t tids ->
  Map_PHID.MapsTo p ph pm ->
  Map_TID.MapsTo t v ph ->
  Map_TID.MapsTo t' v' ph ->
  (wait_phase v <= wait_phase v') % nat.
Proof.
  intros.
  assert (Hin: Map_TID.In t ph) by eauto using Map_TID_Extra.mapsto_to_in.
  assert (Hin': Map_TID.In t' ph) by eauto using Map_TID_Extra.mapsto_to_in.
  assert (Hle: LE pm t t') by eauto using Smallest_to_LE.
  remember ((Z.of_nat (wait_phase v)) - (Z.of_nat (wait_phase v'))) as z.
  assert (Hdiff : ph_diff ph (t, t') z). {
    subst.
    auto using ph_diff_def.
  }
  assert (Hz: (z <= 0 \/ -z <= 0) % Z) by omega.
  destruct Hz.
  - omega.
  - subst.
    remember (Z.of_nat (wait_phase v) - Z.of_nat (wait_phase v')) as z.
    assert (Hd: pm_diff pm (t, t') z) by eauto using pm_diff_def.
    assert ((z <= 0) % Z) by eauto using LE_to_pm_diff.
    intuition.
Qed.

Open Scope nat.

(**
  A crucial precondition to the wait-all working is that all tasks must have
  performed a signal prior to waiting.
*)

Import HJ.Phasers.WellFormed.

Import Phasermap.

Variable WF : WellFormed pm.

  Section Unblocked.
  Variable check_def:
    forall t,
    List.In t tids ->
    Op.Valid pm t WAIT_ALL.

  Theorem has_unblocked:
    Nonempty pm ->
    exists t,
    List.In t tids /\ exists m, Reduces pm t WAIT_ALL m.
  Proof.
    intros.
    apply pm_tids_nonempty in H.
    assert (Hisa : Forall IsA tids). {
      apply Forall_forall.
      intros.
      unfold IsA, tids in *.
      auto using pm_tids_spec_1.
    }
    assert (Hsmall := has_smallest H Hisa).
    destruct Hsmall as (t, Hsmall).
    exists t.
    split; auto.
    exists (Phasermap.wait_all t pm).
    apply reduces.
    simpl.
    apply wait_all_pre.
    intros.
    assert (i := H1).
    apply Map_TID_Extra.in_to_mapsto in H1.
    destruct H1 as (v, mt).
    assert (v_wf:Taskview.WellFormed v). {
      inversion WF.
      assert (Phaser.WellFormed ph) by eauto.
      inversion H2.
      eauto.
    }
    destruct (can_wait_so (mode v)). {
      apply try_wait_pre_can_wait.
      apply wait_pre_def with (v:=v); auto. {
        inversion c;
        symmetry in H2.
        - apply Taskview.wait_pre_sw; auto.
          apply Taskview.tv_well_formed_inv_sw in H2; auto.
          destruct H2; auto.
          assert (wait_phase v <> signal_phase v). {
            assert (Hk : Op.Valid pm t WAIT_ALL) by eauto.
            inversion Hk.
            assert (wait_phase v < signal_phase v) by eauto.
            intuition.
          }
          contradiction.
        - apply Taskview.wait_pre_wo; auto.
      }
      assert (Hs := mt).
      (* -- *)
      apply phase_def.
      intros x w; intros.
      assert (wait_phase w < signal_phase w). {
        assert (Hk : Op.Valid pm x WAIT_ALL). {
          eauto using in_tids, Map_TID_Extra.mapsto_to_in.
        }
        inversion Hk.
        eauto.
      }
      assert (wait_phase v <= wait_phase w). {
        eauto using Smallest_to_WaitPhase.
      }
      auto with *.
    }
    eauto using try_wait_pre_so.
  Qed.
  End Unblocked.

  Let both_cases:
    forall l,
    (forall x, List.In x l -> In x pm) ->
    (exists x, List.In x l /\ ~ Op.Valid pm x WAIT_ALL) \/
    (forall x, List.In x l -> Op.Valid pm x WAIT_ALL).
  Proof.
    induction l; intros. {
      right.
      intros.
      inversion H0.
    }
    assert (i: In a pm) by eauto using in_eq.
    destruct (Op.valid_dec pm a WAIT_ALL). {
      assert (j: forall x, List.In x l -> In x pm) by eauto using in_cons.
      apply IHl in j.
      destruct j as [(?,(?,?))|?]. {
        eauto using in_cons.
      }
      right.
      intros.
      destruct H1; subst; auto.
    }
    eauto using in_eq.
  Qed.

  Theorem progress:
    Nonempty pm ->
    exists t,
    In t pm /\
    forall o,
    (Op.Valid pm t o ->
    exists m, Reduces pm t o m).
  Proof.
    intros.
    edestruct (both_cases) as [(?,(?,?))|?]; eauto using pm_tids_spec_1. {
      exists x.
      split; auto using pm_tids_spec_1.
      intros.
      assert (o <> WAIT_ALL). {
        unfold not; intros; subst.
        contradiction.
      }
      eauto using progress_unblocking_simple.
    }
    destruct (has_unblocked) as (x,(?,?)); auto.
    apply pm_tids_nonempty in H.
    exists x.
    split; auto using pm_tids_spec_1.
    intros.
    assert (Ho: o = WAIT_ALL \/ o <> WAIT_ALL). {
      destruct o; auto; right; unfold not; intros N; inversion N.
    }
    destruct Ho. {
      subst.
      auto.
    }
    auto using progress_unblocking_simple.
  Qed.

End HAS_SMALLEST.

Section ProgressEmpty.
  Lemma progress_empty:
    forall t o pm l,
    Trace.ReducesN pm l ->
    pm_tids pm = nil ->
    Op.Valid pm t o ->
    exists pm', Reduces pm t o pm'.
  Proof.
    intros.
    assert (O: o = WAIT_ALL \/ o <> WAIT_ALL). {
      destruct o; auto; right; unfold not; intros N; inversion N.
    }
    destruct O as [?|?]. {
      exists (run (get_impl o) t pm).
      apply reduces.
      subst; simpl.
      apply wait_all_pre.
      intros.
      assert (i: In t pm) by eauto using in_def.
      apply pm_tids_spec_2 in i.
      rewrite H0 in *.
      inversion i.
    }
    eauto using progress_unblocking_simple, reduces_n_to_valid.
  Qed.

End ProgressEmpty.

Section ProgressEx.
  Corollary progress_ex:
    forall pm l,
    Trace.ReducesN pm l ->
    Nonempty pm ->
    exists t,
    In t pm /\
    forall o,
    (Op.Valid pm t o ->
    exists m, Reduces pm t o m).
  Proof.
    intros.
    eauto using progress, reduces_n_to_valid, WellFormed.Phasermap.well_formed_to_reduces_n.
  Qed.

End ProgressEx.