Require Import Verdi.GhostSimulations.
Require Import VerdiRaft.Raft.
Require Import VerdiRaft.RaftRefinementInterface.

Require Import VerdiRaft.CommonTheorems.

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

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

Require Import VerdiRaft.LogMatchingInterface.
Require Import VerdiRaft.LeaderLogsTermSanityInterface.
Require Import VerdiRaft.LeaderLogsSortedInterface.
Require Import VerdiRaft.SortedInterface.
Require Import VerdiRaft.LeaderLogsSublogInterface.
Require Import VerdiRaft.LeaderLogsContiguousInterface.
Require Import VerdiRaft.TermsAndIndicesFromOneInterface.

Require Import VerdiRaft.LeaderLogsLogMatchingInterface.

Section LeaderLogsLogMatching.

  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 {lmi : log_matching_interface}.
  Context {lltsi : leaderLogs_term_sanity_interface}.
  Context {llsi : leaderLogs_sorted_interface}.
  Context {si : sorted_interface}.
  Context {llsli : leaderLogs_sublog_interface}.
  Context {llci : leaderLogs_contiguous_interface}.
  Context {taifoi : terms_and_indices_from_one_interface}.

  Definition leaderLogs_entries_match_nw (net : network) : Prop :=
    forall h llt ll p t src pli plt es ci,
      In (llt, ll) (leaderLogs (fst (nwState net h))) ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t src pli plt es ci ->
      (forall e1 e2,
         eIndex e1 = eIndex e2 ->
         eTerm e1 = eTerm e2 ->
         In e1 es ->
         In e2 ll ->
         (forall e3,
            eIndex e3 <= eIndex e1 ->
            In e3 es ->
            In e3 ll) /\
         (pli <> 0 ->
          exists e4,
            eIndex e4 = pli /\
            eTerm e4 = plt /\
            In e4 ll)).

  Definition leaderLogs_entries_match (net : network) : Prop :=
    leaderLogs_entries_match_host net /\
    leaderLogs_entries_match_nw net.

  Lemma leaderLogs_entries_match_init :
    refined_raft_net_invariant_init leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_init, leaderLogs_entries_match,
           leaderLogs_entries_match_host, leaderLogs_entries_match_nw.
    simpl.
    intuition.
  Qed.

  Lemma entries_match_cons_gt_maxTerm :
    forall x xs ys,
      sorted xs ->
      sorted ys ->
      eIndex x > maxIndex xs ->
      eTerm x > maxTerm ys ->
      entries_match xs ys ->
      entries_match (x :: xs) ys.
  Proof using.
    unfold entries_match.
    intuition; simpl in *; intuition; subst; subst;
    try match goal with
        | [ H : In _ _ |- _ ] => apply maxTerm_is_max in H; [| solve[auto]]; lia
        | [ H : In _ _ |- _ ] => apply maxIndex_is_max in H; [| solve[auto]]; lia
      end.
    - match goal with
        | [ H : _ |- _ ] => solve [eapply H; eauto]
      end.
    - right. match goal with
        | [ H : _ |- _ ] => solve [eapply H; eauto]
      end.
  Qed.

  Lemma entries_match_cons_sublog :
    forall x xs ys,
      sorted xs ->
      sorted ys ->
      eIndex x > maxIndex xs ->
      entries_match xs ys ->
      (forall y, In y ys -> eTerm x = eTerm y -> In y xs) ->
      entries_match (x :: xs) ys.
  Proof using.
    unfold entries_match.
    intuition; simpl in *; intuition; subst; subst;
    try solve [
         exfalso; try find_apply_hyp_hyp;
          match goal with
            | [ H : In _ _ |- _ ] => apply maxIndex_is_max in H; [| solve[auto]]; lia
          end].
    - match goal with
        | [ H : _ |- _ ] => solve [eapply H; eauto]
      end.
    - right. match goal with
        | [ H : _ |- _ ] => solve [eapply H; eauto]
      end.
  Qed.

  Lemma entries_match_nil :
    forall l,
      entries_match l [].
  Proof using.
    red.
    simpl.
    intuition.
  Qed.

  Lemma lifted_logs_sorted_nw :
    forall net p t n plt plti es ci,
      refined_raft_intermediate_reachable net ->
      In p (nwPackets net) ->
      pBody p = AppendEntries t n plt plti es ci ->
      sorted es.
  Proof using si rri.
    intros.
    pose proof (lift_prop _ logs_sorted_invariant).
    find_insterU. conclude_using eauto.
    unfold logs_sorted in *. break_and.
    unfold logs_sorted_nw in *.
    eapply H3.
    - unfold deghost. simpl.
      apply in_map_iff. eauto.
    - simpl. eauto.
  Qed.

  Lemma lifted_logs_sorted_host :
    forall net h ,
      refined_raft_intermediate_reachable net ->
      sorted (log (snd (nwState net h))).
  Proof using si rri.
    intros.
    pose proof (lift_prop _ logs_sorted_invariant).
    find_insterU. conclude_using eauto.
    unfold logs_sorted in *. break_and.
    unfold logs_sorted_host in *.
    find_insterU.
    find_rewrite_lem deghost_spec.
    eauto.
  Qed.

  Lemma leaderLogs_entries_match_nw_packet_set :
    forall net net',
      leaderLogs_entries_match_nw net ->
      (forall p, In p (nwPackets net') ->
                 is_append_entries (pBody p) ->
                 In p (nwPackets net)) ->
      (forall h, leaderLogs (fst (nwState net' h)) = leaderLogs (fst (nwState net h))) ->
      leaderLogs_entries_match_nw net'.
  Proof using.
    unfold leaderLogs_entries_match_nw.
    intros.
    eapply_prop_hyp In nwPackets; [|eauto 10].
    match goal with
      | [ H : _ |- _ ] =>
        solve [eapply H; eauto;
               repeat find_higher_order_rewrite;
               eauto]
    end.
  Qed.

  Lemma leaderLogs_entries_match_host_state_same :
    forall net net',
      leaderLogs_entries_match_host net ->
      (forall h, leaderLogs (fst (nwState net' h)) = leaderLogs (fst (nwState net h))) ->
      (forall h, log (snd (nwState net' h)) = log (snd (nwState net h))) ->
      leaderLogs_entries_match_host net'.
  Proof using.
    unfold leaderLogs_entries_match_host.
    intuition.
    repeat find_higher_order_rewrite. eauto.
  Qed.

  Lemma handleClientRequest_no_send :
    forall h st client id c out st' ms,
      handleClientRequest h st client id c = (out, st', ms) ->
      ms = [].
  Proof using.
    unfold handleClientRequest.
    intros.
    repeat break_match; repeat find_inversion; auto.
  Qed.

  Lemma leaderLogs_entries_match_client_request :
    refined_raft_net_invariant_client_request leaderLogs_entries_match.
  Proof using llsli si llsi lltsi rri.
    unfold refined_raft_net_invariant_client_request, leaderLogs_entries_match.
    intros.
    split.
    - { unfold leaderLogs_entries_match_host.
        simpl. intuition. subst. repeat find_higher_order_rewrite.
        repeat update_destruct_simplify.
        - find_rewrite_lem update_elections_data_client_request_leaderLogs.
          destruct (log d) using (handleClientRequest_log_ind ltac:(eauto)).
          + eauto.
          + destruct ll.
            * apply entries_match_nil.
            * { apply entries_match_cons_gt_maxTerm; eauto.
                - eauto using lifted_logs_sorted_host.
                - eapply leaderLogs_sorted_invariant; eauto.
                - lia.
                - find_copy_apply_lem_hyp leaderLogs_currentTerm_invariant; auto.
                  find_copy_apply_lem_hyp leaderLogs_term_sanity_invariant.
                  unfold leaderLogs_term_sanity in *.
                  eapply_prop_hyp In In; simpl; eauto. repeat find_rewrite.
                  simpl in *. lia.
              }
        - destruct (log d) using (handleClientRequest_log_ind ltac:(eauto)).
          + eauto.
          + apply entries_match_cons_sublog; eauto.
            * eauto using lifted_logs_sorted_host.
            * eapply leaderLogs_sorted_invariant; eauto.
            * lia.
            * intros.
              eapply leaderLogs_sublog_invariant; eauto.
              simpl in *. congruence.
        - find_rewrite_lem update_elections_data_client_request_leaderLogs.
          eauto.
        - eauto.
      }
    - eapply leaderLogs_entries_match_nw_packet_set with (net:=net); intuition.
      + find_apply_hyp_hyp. intuition eauto.
        erewrite handleClientRequest_no_send with (ms := l) in * by eauto.
        simpl in *. intuition.
      + simpl. subst. find_higher_order_rewrite.
        rewrite update_fun_comm. simpl.
        rewrite update_fun_comm. simpl.
        rewrite update_elections_data_client_request_leaderLogs.
        now rewrite update_nop_ext' by auto.
  Qed.

  Lemma leaderLogs_entries_match_timeout :
    refined_raft_net_invariant_timeout leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_timeout, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto;
      simpl; intros; subst; find_higher_order_rewrite;
      repeat update_destruct_simplify; rewrite_update; auto;
      try rewrite update_elections_data_timeout_leaderLogs;
      try erewrite handleTimeout_log_same by eauto; eauto.
    - eapply leaderLogs_entries_match_nw_packet_set with (net:=net); intuition.
      + simpl in *. find_apply_hyp_hyp.  intuition.
        do_in_map. subst. simpl in *.
        exfalso. eapply handleTimeout_not_is_append_entries; eauto 10.
      + simpl. repeat find_higher_order_rewrite.
        rewrite update_fun_comm. simpl.
        rewrite update_fun_comm. simpl.
        rewrite update_elections_data_timeout_leaderLogs.
        rewrite update_nop_ext'; auto.
  Qed.

  Lemma lifted_log_matching :
    forall net,
      refined_raft_intermediate_reachable net ->
      log_matching (deghost net).
  Proof using lmi rri.
    intros.
    pose proof (lift_prop _ log_matching_invariant).
    find_insterU. conclude_using eauto.
    auto.
  Qed.

  Lemma lifted_log_matching_host :
    forall net,
      refined_raft_intermediate_reachable net ->
      (forall h h',
         entries_match (log (snd (nwState net h))) (log (snd (nwState net h')))) /\
      (forall h i,
         1 <= i <= maxIndex (log (snd (nwState net h))) ->
         exists e, eIndex e = i /\ In e (log (snd (nwState net h)))) /\
      (forall h e,
         In e (log (snd (nwState net h))) -> eIndex e > 0).
  Proof using lmi rri.
    intros.
    find_apply_lem_hyp lifted_log_matching.
    unfold log_matching, log_matching_hosts in *.
    rename net into net0.
    intuition; repeat rewrite <- deghost_spec with (net := net0).
    - auto.
    - match goal with
        | [ H : _ |- _ ] => solve [apply H; rewrite deghost_spec; auto]
      end.
    - match goal with
        | [ H : _ |- _ ] => solve [eapply H; rewrite deghost_spec; eauto]
      end.
  Qed.

  Lemma lifted_log_matching_nw :
    forall net,
      refined_raft_intermediate_reachable net ->
      forall p t leaderId prevLogIndex prevLogTerm entries leaderCommit,
        In p (nwPackets net) ->
        pBody p = AppendEntries t leaderId prevLogIndex prevLogTerm entries leaderCommit ->
        (forall h e1 e2,
           In e1 entries ->
           In e2 (log (snd (nwState net h))) ->
           eIndex e1 = eIndex e2 ->
           eTerm e1 = eTerm e2 ->
           (forall e3,
              eIndex e3 <= eIndex e1 ->
              In e3 entries ->
              In e3 (log (snd (nwState net h)))) /\
           (prevLogIndex <> 0 ->
            exists e4,
              eIndex e4 = prevLogIndex /\
              eTerm e4 = prevLogTerm /\
              In e4 (log (snd (nwState net h))))) /\
        (forall i,
           prevLogIndex < i <= maxIndex entries ->
           exists e,
             eIndex e = i /\
             In e entries) /\
        (forall e,
           In e entries ->
           prevLogIndex < eIndex e).
  Proof using lmi rri.
    intros.
    find_apply_lem_hyp lifted_log_matching.
    unfold log_matching, log_matching_nw in *.
    break_and.
    match goal with
      | [ H : forall _ : packet , _ |- _ ] =>
        do 7 insterU H;
          conclude H ltac:(unfold deghost; simpl; eapply in_map_iff; eexists; eauto);
          conclude H ltac:(simpl; eauto)
    end.
    rename net into net0.
    intuition.
    - rewrite <- deghost_spec with (net := net0).
      eapply H3 with (e1:=e1)(e2:=e2); eauto.
      rewrite deghost_spec.  auto.
    - rewrite <- deghost_spec with (net := net0).
      eapply H3 with (e1:=e1)(e2:=e2); eauto.
      rewrite deghost_spec.  auto.
  Qed.

  Ltac use_log_matching_nw :=
    pose proof (lifted_log_matching_nw _ ltac:(eauto));
    match goal with
      | [ H : _  |- _ ] =>
        eapply H; [|eauto];
        repeat find_rewrite; intuition
    end.

  Lemma handleAppendEntries_doesn't_send_AE :
    forall n st t i l t' l' l'' st' m,
      handleAppendEntries n st t i l t' l' l'' = (st', m) ->
      ~ is_append_entries m.
  Proof using.
    unfold handleAppendEntries.
    intros.
    repeat break_match; find_inversion; intro; break_exists; discriminate.
  Qed.

  Lemma leaderLogs_entries_match_append_entries :
    refined_raft_net_invariant_append_entries leaderLogs_entries_match.
  Proof using taifoi si llsi lmi rri.
    unfold refined_raft_net_invariant_append_entries, leaderLogs_entries_match.
    intuition.
    - unfold leaderLogs_entries_match_host in *. intros.
      {
        intros. simpl in *. repeat find_higher_order_rewrite.
        find_rewrite_lem update_fun_comm. simpl in *.
        find_rewrite_lem update_fun_comm.
        find_rewrite_lem update_elections_data_appendEntries_leaderLogs.
        find_erewrite_lem update_nop_ext'.
        update_destruct_simplify; rewrite_update;
        try rewrite update_elections_data_appendEntries_leaderLogs in *; eauto.
        destruct (log d) using (handleAppendEntries_log_ind ltac:(eauto)); eauto.
        + subst. eapply @entries_match_scratch with (plt := plt).
          * eauto using lifted_logs_sorted_nw.
          * apply sorted_uniqueIndices.
            eapply leaderLogs_sorted_invariant; eauto.
          * eapply_prop leaderLogs_entries_match_nw; eauto.
          * use_log_matching_nw.
          * use_log_matching_nw.
          * match goal with
              | [ H : In _ (leaderLogs _) |- _ ] =>
                eapply terms_and_indices_from_one_invariant in H; [|solve[auto]]
            end.
            unfold terms_and_indices_from_one in *. intros.
            find_apply_hyp_hyp. intuition.
        + eapply entries_match_append; eauto.
          * eauto using lifted_logs_sorted_host.
          * eapply leaderLogs_sorted_invariant; eauto.
          * eauto using lifted_logs_sorted_nw.
          * use_log_matching_nw.
          * use_log_matching_nw.
          * eapply findAtIndex_intro; eauto using lifted_logs_sorted_host, sorted_uniqueIndices.
      }
    - (* nw *)
      unfold leaderLogs_entries_match_nw in *.
      intros. simpl in *. repeat find_higher_order_rewrite.
      find_rewrite_lem update_fun_comm. simpl in *.
      find_rewrite_lem update_fun_comm.
      find_rewrite_lem update_elections_data_appendEntries_leaderLogs.
      find_erewrite_lem update_nop_ext'.
      find_apply_hyp_hyp. break_or_hyp.
      + intuition; match goal with
            | [ H : _ |- _ ] => solve [eapply (H _ _ _ p0); eauto with *]
          end.
      + simpl in *.
        find_copy_apply_lem_hyp handleAppendEntries_doesn't_send_AE.
        exfalso. eauto 10.
  Qed.

  Lemma leaderLogs_entries_match_append_entries_reply :
    refined_raft_net_invariant_append_entries_reply leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_append_entries_reply, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; simpl; intros;
      repeat find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto.
      erewrite handleAppendEntriesReply_same_log by eauto. auto.
    - eapply leaderLogs_entries_match_nw_packet_set; eauto; simpl.
      + intros. find_apply_hyp_hyp. repeat find_rewrite. intuition; [eauto with *|].
        find_apply_lem_hyp handleAppendEntriesReply_packets. subst. simpl in *. intuition.
      + intros. repeat find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto; find_rewrite; auto.
  Qed.

  Lemma handleRequestVote_packets :
    forall h st t candidate lli llt st' m,
      handleRequestVote h st t candidate lli llt = (st', m) ->
      ~ is_append_entries m.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion;
    subst; intuition; break_exists; congruence.
  Qed.

  Lemma leaderLogs_entries_match_request_vote :
    refined_raft_net_invariant_request_vote leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_request_vote, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; simpl; intros;
      repeat find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto.
      + now rewrite leaderLogs_update_elections_data_requestVote.
      + erewrite handleRequestVote_log; eauto.
    - eapply leaderLogs_entries_match_nw_packet_set; eauto; simpl.
      + intros. find_apply_hyp_hyp. repeat find_rewrite. intuition; [eauto with *|].
        find_apply_lem_hyp handleRequestVote_packets. subst. simpl in *. intuition.
      + intros. repeat find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto.
        now rewrite leaderLogs_update_elections_data_requestVote.
  Qed.

  Lemma leaderLogs_entries_match_request_vote_reply :
    refined_raft_net_invariant_request_vote_reply leaderLogs_entries_match.
  Proof using lmi rri.
    unfold refined_raft_net_invariant_request_vote_reply, leaderLogs_entries_match.
    intuition.
    - unfold leaderLogs_entries_match_host in *.
      intros. simpl in *. subst.
      repeat find_higher_order_rewrite.
      rewrite update_fun_comm. simpl.
      rewrite update_fun_comm. simpl.
      rewrite update_nop_ext' by now rewrite handleRequestVoteReply_same_log.
      find_rewrite_lem update_fun_comm. simpl in *.
      update_destruct_simplify; rewrite_update; eauto.
      find_eapply_lem_hyp leaderLogs_update_elections_data_RVR; eauto.
      intuition eauto.
      subst.
      rewrite handleRequestVoteReply_same_log.
      apply lifted_log_matching_host. auto.
    - unfold leaderLogs_entries_match_nw in *.
      intros. simpl in *. subst.
      repeat find_higher_order_rewrite.
      find_rewrite_lem update_fun_comm. simpl in *.
      find_rewrite_lem update_fun_comm.
      update_destruct_simplify; rewrite_update.
      + find_eapply_lem_hyp leaderLogs_update_elections_data_RVR; eauto.
        break_or_hyp.
        * repeat find_reverse_rewrite. eauto.
        * break_and. subst.
          rewrite handleRequestVoteReply_same_log.
          find_rewrite_lem handleRequestVoteReply_same_log.
          pose proof (lifted_log_matching_nw _ ltac:(eauto)).
          repeat find_reverse_rewrite.
          match goal with
            | [ H : _, pkt : packet  |- _ ] =>
              solve [eapply H with (p := pkt); eauto]
          end.
      + repeat find_reverse_rewrite. eauto.
  Qed.

  Lemma doLeader_messages :
    forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      ms = [] \/
      ms = map (replicaMessage st' h)
               (filter (fun h' : name => if name_eq_dec h h' then false else true)
                       nodes).
  Proof using.
    unfold doLeader.
    intros.
    repeat break_match; repeat find_inversion; auto.
  Qed.

  Lemma leaderLogs_entries_match_do_leader :
    refined_raft_net_invariant_do_leader leaderLogs_entries_match.
  Proof using llci si llsi rri.
    unfold refined_raft_net_invariant_do_leader, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; simpl; intros;
      find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto.
      + find_rewrite. auto.
      + erewrite doLeader_same_log by eauto. find_rewrite. auto.
    - unfold leaderLogs_entries_match_nw in *. intros. simpl in *.
      repeat find_higher_order_rewrite.
      find_rewrite_lem update_fun_comm. simpl in *.
      match goal with
        | [ H : _ |- _ ] =>
          rewrite update_nop_ext' in H by (find_rewrite; auto)
      end.
      find_apply_hyp_hyp. break_or_hyp.
      + find_reverse_rewrite. eauto.
      + do_in_map. subst. simpl in *.
        find_copy_apply_lem_hyp doLeader_messages.
        break_or_hyp.
        * simpl in *. intuition.
        * do_in_map. find_apply_lem_hyp filter_In.
          break_and. break_if; try discriminate.
          unfold replicaMessage in *. subst. simpl in *. find_inversion.
          { intuition.
            - repeat find_erewrite_lem doLeader_same_log.
              eapply_prop leaderLogs_entries_match_host; eauto.
              + match goal with
                  | [ H : _ |- _ ] => rewrite H
                end. simpl.
                eauto using findGtIndex_in.
              + auto with *.
              + find_rewrite. simpl. eauto using findGtIndex_in.
            - find_copy_apply_lem_hyp leaderLogs_contiguous_invariant; auto.
              unfold contiguous_range_exact_lo in *. break_and.
              match goal with
                | [ H : forall _, _ < _ <= _ -> _ |- context [eIndex _ = ?x]] =>
                  remember (x) as index;
                  specialize (H index); forward H
              end.
              + intuition; auto using Nat.neq_0_lt_0.
                find_apply_lem_hyp findGtIndex_necessary. break_and.
                eapply Nat.le_trans.
                * apply Nat.lt_le_incl. eauto.
                * repeat find_rewrite. eapply maxIndex_is_max; auto.
                  eapply leaderLogs_sorted_invariant; eauto.
              + concludes. break_exists_exists. intuition.
                match goal with
                  | [ H : context [leaderLogs_entries_match_host],
                     H' : context [leaderLogs] |- _ ] =>
                    eapply H with (h := src)(e := e1)(e' := e2)(e'' := x) in H'; auto
                end.
                * pose proof lifted_logs_sorted_host net src ltac:(auto).
                  repeat find_rewrite. simpl in *.
                  repeat find_erewrite_lem doLeader_same_log.
                  erewrite doLeader_same_log by eauto.
                  erewrite findAtIndex_intro; eauto using sorted_uniqueIndices.
                * find_apply_lem_hyp findGtIndex_necessary. break_and.
                  repeat find_erewrite_lem doLeader_same_log.
                  repeat find_rewrite. simpl in *.
                  auto.
                * find_apply_lem_hyp findGtIndex_necessary. break_and.
                  lia.
          }
  Qed.

  Lemma doGenericServer_packets :
    forall h st os st' ps,
      doGenericServer h st = (os, st', ps) ->
      ps = [].
  Proof using.
    intros. unfold doGenericServer in *.
    repeat break_match; find_inversion; subst; auto.
  Qed.

  Lemma leaderLogs_entries_match_do_generic_server :
    refined_raft_net_invariant_do_generic_server leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_do_generic_server, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; simpl; intros;
      find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto.
      + find_rewrite. auto.
      + erewrite doGenericServer_log by eauto. find_rewrite. auto.
    - eapply leaderLogs_entries_match_nw_packet_set; eauto; simpl.
      + intros. find_apply_hyp_hyp. intuition.
        find_apply_lem_hyp doGenericServer_packets. subst. simpl in *. intuition.
      + intros. find_higher_order_rewrite; update_destruct_simplify; rewrite_update; auto; find_rewrite; auto.
  Qed.

  Lemma leaderLogs_entries_match_state_same_packet_subset :
    refined_raft_net_invariant_state_same_packet_subset leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_state_same_packet_subset, leaderLogs_entries_match.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; intros; find_higher_order_rewrite; auto.
    - eapply leaderLogs_entries_match_nw_packet_set; eauto; intros; find_higher_order_rewrite; auto.
  Qed.

  Lemma leaderLogs_entries_match_reboot :
    refined_raft_net_invariant_reboot leaderLogs_entries_match.
  Proof using.
    unfold refined_raft_net_invariant_reboot, leaderLogs_entries_match, reboot.
    intuition.
    - eapply leaderLogs_entries_match_host_state_same; eauto; intros; find_higher_order_rewrite;
      update_destruct_simplify; rewrite_update; auto; find_rewrite; auto.
    - eapply leaderLogs_entries_match_nw_packet_set; eauto; try find_rewrite; intuition.
      find_higher_order_rewrite; update_destruct_simplify; rewrite_update; try find_rewrite; auto.
  Qed.

  Lemma leaderLogs_entries_match_invariant :
    forall net,
      refined_raft_intermediate_reachable net ->
      leaderLogs_entries_match net.
  Proof using taifoi llci llsli si llsi lltsi lmi rri.
    intros.
    apply refined_raft_net_invariant; auto.
    - apply leaderLogs_entries_match_init.
    - apply leaderLogs_entries_match_client_request.
    - apply leaderLogs_entries_match_timeout.
    - apply leaderLogs_entries_match_append_entries.
    - apply leaderLogs_entries_match_append_entries_reply.
    - apply leaderLogs_entries_match_request_vote.
    - apply leaderLogs_entries_match_request_vote_reply.
    - apply leaderLogs_entries_match_do_leader.
    - apply leaderLogs_entries_match_do_generic_server.
    - apply leaderLogs_entries_match_state_same_packet_subset.
    - apply leaderLogs_entries_match_reboot.
  Qed.

  Instance lllmi : leaderLogs_entries_match_interface : Prop.
  Proof.
    split.
    apply leaderLogs_entries_match_invariant.
  Qed.
End LeaderLogsLogMatching.


Require Import VST.veric.rmaps.
Require Import VST.concurrency.conclib.
Require Import VST.floyd.library.
Import FashNotation.

(* lock invariants should be exclusive *)
Class lock_impl := { t_lock : type; lock_handle : Type; ptr_of : lock_handle -> val;
  lock_inv : share -> lock_handle -> mpred -> mpred;
  lock_inv_nonexpansive : forall sh h, nonexpansive (lock_inv sh h);
  lock_inv_share_join : forall sh1 sh2 sh3 h R, sh1 <> Share.bot -> sh2 <> Share.bot ->
    sepalg.join sh1 sh2 sh3 -> lock_inv sh1 h R * lock_inv sh2 h R = lock_inv sh3 h R;
  lock_inv_exclusive : forall sh h R, exclusive_mpred (lock_inv sh h R);
  lock_inv_isptr : forall sh h R, lock_inv sh h R |-- !! isptr (ptr_of h) }.

Section lock_specs.

  Context {LI : lock_impl}.

  Lemma lock_inv_nonexpansive2 : forall {A} (P Q : A -> mpred) sh p x, (ALL x : _, |> (P x <=> Q x) |--
    |> lock_inv sh p (P x) <=> |> lock_inv sh p (Q x))%logic.
  Proof.
    intros.
    apply allp_left with x.
    eapply derives_trans, eqp_later1; apply later_derives.
    apply nonexpansive_entail; apply lock_inv_nonexpansive.
  Qed.

  Lemma lock_inv_super_non_expansive : forall sh h R n,
    compcert_rmaps.RML.R.approx n (lock_inv sh h R) = compcert_rmaps.RML.R.approx n (lock_inv sh h (compcert_rmaps.RML.R.approx n R)).
  Proof.
    intros; apply nonexpansive_super_non_expansive, lock_inv_nonexpansive.
  Qed.

  Notation InvType := Mpred.

  (* R should be able to take the lock_handle as an argument, with subspecs for plain and selflock *)
  Program Definition makelock_spec :=
    TYPE (ProdType (ConstType globals) (ArrowType (ConstType lock_handle) InvType)) WITH gv: _, R : _
    PRE [ ]
       PROP ()
       PARAMS () GLOBALS (gv)
       SEP (mem_mgr gv)
    POST [ tptr t_lock ] EX h,
       PROP ()
       RETURN (ptr_of h)
       SEP (mem_mgr gv; lock_inv Tsh h (R h)).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x; simpl.
    reflexivity.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x; simpl.
    rewrite !approx_exp; f_equal; extensionality.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal; apply lock_inv_super_non_expansive.
  Qed.

  Program Definition freelock_spec :=
    TYPE (ProdType (ProdType (ConstType _) InvType) Mpred)
    WITH h : _, R : _, P : _
    PRE [ tptr t_lock ]
     PROP ()
     PARAMS (ptr_of h)
     SEP (lock_inv Tsh h R; P; (P * lock_inv Tsh h R * R -* FF) && emp)
   POST[ tvoid ]
     PROP ()
     LOCAL ()
     SEP (P).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 3 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal.
    { apply lock_inv_super_non_expansive. }
    f_equal.
    rewrite !approx_andp; f_equal.
    setoid_rewrite wand_nonexpansive; rewrite !approx_sepcon; do 2 f_equal; rewrite !approx_idem; auto.
    do 2 f_equal; apply lock_inv_super_non_expansive.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, LOCALx, SEPx; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    reflexivity.
  Qed.

  Program Definition freelock_spec_simple :=
    TYPE (ProdType (ConstType _) InvType)
    WITH h : _, R : _
    PRE [ tptr t_lock ]
     PROP ()
     PARAMS (ptr_of h)
     SEP (weak_exclusive_mpred R && emp; lock_inv Tsh h R; R)
   POST[ tvoid ]
     PROP ()
     LOCAL ()
     SEP (R).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x; simpl.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 3 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal.
    { rewrite !approx_andp; f_equal.
      apply exclusive_mpred_super_non_expansive. }
    f_equal. apply lock_inv_super_non_expansive.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x; simpl.
    unfold PROPx, LOCALx, SEPx; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    reflexivity.
  Qed.

  Lemma freelock_simple : funspec_sub freelock_spec freelock_spec_simple.
  Proof.
    unfold funspec_sub; simpl.
    split; auto; intros ? (h, R) ?; Intros.
    eapply derives_trans, fupd_intro.
    Exists (nil : list Type) (h, R, R) emp; entailer!.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; entailer!.
    apply andp_right, andp_left2; auto.
    rewrite <- wand_sepcon_adjoint; sep_apply weak_exclusive_conflict; auto.
    rewrite FF_sepcon; auto.
  Qed.

  Program Definition acquire_spec :=
    TYPE (ProdType (ConstType _) InvType)
    WITH sh : _, h : _, R : _
    PRE [ tptr t_lock ]
       PROP (sh <> Share.bot)
       PARAMS (ptr_of h)
       SEP (lock_inv sh h R)
    POST [ tvoid ]
       PROP ()
       LOCAL ()
       SEP (lock_inv sh h R; R).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 3 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    apply lock_inv_super_non_expansive.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, LOCALx, SEPx; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal. apply lock_inv_super_non_expansive.
  Qed.

  Program Definition release_spec :=
    TYPE (ProdType (ProdType (ProdType (ConstType _) InvType) Mpred) Mpred)
    WITH sh : _, h : _, R : _, P : _, Q : _
    PRE [ tptr t_lock ]
       PROP (sh <> Share.bot)
       PARAMS (ptr_of h)
       SEP (weak_exclusive_mpred R && emp; |> lock_inv sh h R; P; lock_inv sh h R * P -* Q * R)
    POST [ tvoid ]
       PROP ()
       LOCAL ()
       SEP (Q).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((((?, ?), ?), ?), ?); simpl.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 3 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal.
    { rewrite !approx_andp; f_equal.
      apply exclusive_mpred_super_non_expansive. }
    f_equal.
    { setoid_rewrite later_nonexpansive; do 2 f_equal.
      apply lock_inv_super_non_expansive. }
    f_equal.
    setoid_rewrite wand_nonexpansive; rewrite !approx_sepcon; do 2 f_equal; rewrite !approx_idem; f_equal.
    apply lock_inv_super_non_expansive.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((((?, ?), ?), ?), ?); simpl.
    unfold PROPx, LOCALx, SEPx; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    reflexivity.
  Qed.

  Program Definition release_spec_simple :=
    TYPE (ProdType (ConstType _) InvType)
    WITH sh : _, h : _, R : _
    PRE [ tptr t_lock ]
       PROP (sh <> Share.bot)
       PARAMS (ptr_of h)
       SEP (weak_exclusive_mpred R && emp; lock_inv sh h R; R)
    POST [ tvoid ]
       PROP ()
       LOCAL ()
       SEP (lock_inv sh h R).
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; rewrite !approx_andp; do 3 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    f_equal.
    { rewrite !approx_andp; f_equal.
      apply exclusive_mpred_super_non_expansive. }
    f_equal.
    apply lock_inv_super_non_expansive.
  Qed.
  Next Obligation.
  Proof.
    repeat intro.
    destruct x as ((?, ?), ?); simpl.
    unfold PROPx, LOCALx, SEPx; simpl; rewrite !approx_andp; do 2 f_equal;
      rewrite -> !sepcon_emp, ?approx_sepcon, ?approx_idem.
    apply lock_inv_super_non_expansive.
  Qed.

  Lemma release_simple : funspec_sub release_spec release_spec_simple.
  Proof.
    unfold funspec_sub; simpl.
    split; auto; intros ? ((sh, h), R) ?; Intros.
    eapply derives_trans, fupd_intro.
    Exists (nil : list Type) (sh, h, R, R, lock_inv sh h R) emp; entailer!.
    unfold PROPx, PARAMSx, GLOBALSx, LOCALx, SEPx, argsassert2assert; simpl; entailer!.
    apply wand_refl_cancel_right.
  Qed.

End lock_specs.

#[export] Hint Resolve lock_inv_isptr : saturate_local.
#[export] Hint Resolve lock_inv_exclusive data_at_exclusive data_at__exclusive field_at_exclusive field_at__exclusive : core.

Ltac lock_props := match goal with |-context[weak_exclusive_mpred ?P && emp] => sep_apply (exclusive_weak_exclusive P); [auto with share | try timeout 20 cancel] end.


(* *********************************************************************)
(*                                                                     *)
(*            The CertiKOS Certified Kit Operating System              *)
(*                                                                     *)
(*                   The FLINT Group, Yale University                  *)
(*                                                                     *)
(*  Copyright The FLINT Group, Yale University.  All rights reserved.  *)
(*  This file is distributed under the terms of the Yale University    *)
(*  Non-Commercial License Agreement.                                  *)
(*                                                                     *)
(* *********************************************************************)
Require Import compcert.lib.Coqlib.
Require Import compcert.lib.Maps.
Require Import compcert.common.AST.
Require Import compcert.common.Globalenvs.
Require Import compcert.cfrontend.Ctypes.
Require Import liblayers.lib.Decision.
Require Import liblayers.logic.PTrees.
Require Import liblayers.logic.PTreeModules.
Require Import liblayers.logic.PTreeLayers.
Require Import liblayers.logic.PTreeSemantics.
Require Import liblayers.logic.LayerLogicImpl.
Require Export liblayers.compat.CompatLayerDef.
Require Import liblayers.compcertx.Observation.

(** * Semantics of languages *)

Section COMPAT_SEMANTICS.
  Context `{Hobs: Observation}.
  Context `{Hstencil: Stencil}.
  Context `{Hmem: Mem.MemoryModel} `{Hmem': !UseMemWithData mem}.
  Context {F: Type}.
  Local Existing Instance ptree_module_ops.
  Local Existing Instance ptree_module_prf.
  Context `{fsem_ops: !FunctionSemanticsOps _ _ _ _ (ptree_module F _) compatlayer}.
  Context `{Hfsem: !FunctionSemantics _ _ _ _ (ptree_module F _) compatlayer}.

  Section COMPAT_SEMANTICS_DEF.
  Local Existing Instance ptree_layer_sim_op.
  Local Existing Instance ptree_layer_ops.
  Local Existing Instance ptree_layer_prf.
  Local Existing Instance ptree_semof.
  Local Existing Instance ptree_semantics_ops.
  Local Existing Instance ptree_semantics_prf.

  (** In order to use the theorems about [ptree_layer]s, we need to
    come up with a corresponding version of the [FunctionSemantics]
    instance. *)

  Local Instance compat_ptree_fsem_ops:
    FunctionSemanticsOps _ F compatsem (globvar type)
      (ptree_module _ _)
      (ptree_layer _ _) :=
    {
      semof_fundef D M L i κ :=
        semof_fundef D M (cl_inj L) i κ
    }.

  Local Instance compat_ptree_fsem_prf:
    FunctionSemantics _ F compatsem (globvar type)
      (ptree_module _ _)
      (ptree_layer _ _).
  Proof.
    split.
    * intros D1 D2 R M1 M2 HM L1 L2 HL i κ.
      simpl semof_fundef.
      apply semof_fundef_sim_monotonic; eauto.
      split; simpl.
      - assumption.
      - repeat constructor.
      - repeat constructor.
    (*
    * intros.
      destruct Hfsem.
      specialize (semof_fundef_vcomp D M (cl_inj L) i κi σi j κj).
      apply semof_fundef_vcomp; simpl; assumption.
    *)
  Qed.

  (** ** [Semantics] *)

  Local Instance compat_semof: Semof (ptree_module _ _) compatlayer compatlayer :=
    {
      semof D M L := cl_inj (〚M〛 L)
    }.

  Local Instance compat_semantics_ops:
    SemanticsOps ident F compatsem _ (ptree_module _ _) compatlayer := {}.

  (** FIXME: the following is heavily copy-and-pasted from
    PTreeSemantics.v, we should figure out a way to avoid this. *)

  Local Existing Instance ptree_module_prf.
  Local Existing Instance ptree_layer_prf.
  Local Existing Instance ptree_semof.
  Local Transparent ptree_layer_ops.
  Local Transparent ptree_module ptree_module_ops.
  Local Transparent ptree_layer ptree_layer_ops.
  Local Opaque PTree.combine.

  Lemma compat_semantics_monotonic:
    Proper (∀ -, (≤) ++> (≤) ++> - ==> - ==> res_le (≤)) semof_fundef ->
    Proper (∀ -, (≤) ++> (≤) ++> (≤)) semof.
  Proof.
    intros H D M1 M2 HM L1 L2 HL.
    simpl.
    solve_monotonic.
  Qed.

  Lemma compat_semantics_sim_monotonic:
    Proper (∀ R, (≤) ++> sim R ++> - ==> - ==> res_le (sim R)) semof_fundef ->
    Proper (∀ R, (≤) ++> sim R ++> sim R) semof.
  Proof.
    intros H D1 D2 R M1 M2 HM L1 L2 HL.
    simpl.
    apply cl_inj_sim_monotonic.
    apply ptree_semantics_mapdef_sim_monotonic; eauto.
  Qed.

  (*
  Lemma compat_semantics_vcomp D M N (L: compatlayer D):
    (forall i (κi : F) (σi : compatsem D) j (κj : F),
       get_layer_primitive i L = OK None ->
       get_layer_globalvar i L = OK None ->
       semof_fundef D M L i κi = OK σi ->
       (res_le (≤))
         (semof_fundef D M (L ⊕ i ↦ σi) j κj)
         (semof_fundef D (M ⊕ i ↦ κi) L j κj)) ->
    〚M〛 (〚N〛 L ⊕ L) ⊕ 〚N〛 L ≤ 〚M ⊕ N〛 L.
  *)

  Lemma compat_semantics_hcomp D M N (L : compatlayer D):
    〚M 〛 L ⊕ 〚N 〛 L ≤ 〚M ⊕ N 〛 L.
  Proof.
    destruct M as [Mf Mv], N as [Nf Nv].
    simpl.
    constructor; simpl.
    * constructor; simpl.
      + intros i.
        rewrite !PTree.gcombine by reflexivity.
        rewrite !PTree.gmap.
        rewrite !PTree.gcombine by reflexivity.
        simpl.
        destruct (Mf!i) as [[|]|], (Nf!i) as [[|]|];
        simpl; monad_norm; simpl; repeat constructor.
        - apply upper_bound.
        - apply upper_bound.
        - transitivity (Some (semof_fundef D (Mf, Mv) L i f)).
          destruct (semof_fundef _ _ _ _ _); reflexivity.
          monotonicity.
          pose proof (semof_fundef_sim_monotonic D D id) as H;
            simpl in H; apply H; clear H.
          transitivity ((Mf, Mv) ⊕ (Nf, Nv)).
          pose proof (left_upper_bound (Mf, Mv) (Nf, Nv)) as H;
            simpl in H; apply H; clear H.
          reflexivity.
          change (sim id L L).
          reflexivity.
        - pose proof (semof_fundef_sim_monotonic D D id) as H;
            simpl in H; apply H; clear H.
          transitivity ((Mf, Mv) ⊕ (Nf, Nv)).
          pose proof (right_upper_bound (Mf, Mv) (Nf, Nv)) as H;
            simpl in H; apply H; clear H.
          reflexivity.
          change (sim id L L).
          reflexivity.
      + reflexivity.
    * reflexivity.
    * reflexivity.
  Qed.

  Lemma compat_get_semof_primitive {D} i (M: ptree_module F (globvar type)) (L: compatlayer D):
    get_layer_primitive (layer := compatlayer) i (〚M〛 L) =
    semof_function M L i (get_module_function i M).
  Proof.
    apply ptree_get_semof_primitive.
  Qed.

  Local Instance compat_semantics_prf:
    Semantics ident F compatsem _ (ptree_module _ _) compatlayer.
  Proof.
    split.
    * apply compat_semantics_sim_monotonic.
      apply semof_fundef_sim_monotonic.
    * reflexivity.
    * intros; apply compat_get_semof_primitive.
    (*
    * intros.
      apply compat_semantics_vcomp.
      (** For some reason that does not unify on its own *)
      apply (semof_fundef_vcomp (FunctionSemantics := Hfsem)).
    *)
    * apply compat_semantics_hcomp.
  Qed.

  End COMPAT_SEMANTICS_DEF.

  Local Existing Instance compat_semof.
  Local Existing Instance compat_semantics_ops.
  Local Existing Instance compat_semantics_prf.
  Local Existing Instance compat_ptree_fsem_ops.
  Local Existing Instance compat_ptree_fsem_prf.

  (** Quick test of the decision procedure. *)
  Goal
    forall D,
      module_layer_disjoint (D:=D) (F:=F)
        (1%positive ↦ {| gvar_info := Tvoid;
                          gvar_init := nil;
                          gvar_readonly := false;
                          gvar_volatile := false |})
        (2%positive ↦ {| gvar_info := Tvoid;
                         gvar_init := nil;
                         gvar_readonly := false;
                         gvar_volatile := false |}).
  Proof.
    intros D.
    decision.
  Qed.

  (** Versions of [compat_semantics_spec_xxx] with [module_layer_disjoint]. *)

  Lemma compat_semantics_spec_some_disj {D} M L i f:
    get_module_function i M = OK (Some f) ->
    get_layer_primitive i (〚M〛 L) = fmap Some (semof_fundef D M L i f).
  Proof.
    intros HMi.
    rewrite compat_get_semof_primitive.
    rewrite HMi.
    reflexivity.
  Qed.

  (** ** Instantiate the module system *)

  Global Instance compat_ll_ops:
    LayerLogicOps ident _ compatsem _ (ptree_module _ _) compatlayer :=
      logic_impl_ops.

  Global Instance compat_ll_prf:
    LayerLogic ident _ compatsem _ (ptree_module _ _) compatlayer :=
      logic_impl.

End COMPAT_SEMANTICS.

(** Fake [SimulationPaths] *)

Notation path_inj R := R (only parsing).


Require Import GhostSimulations.
Require Import Raft.

Require Import UpdateLemmas.
Local Arguments update {_} {_} {_} _ _ _ _ : simpl never.

Require Import RaftRefinementInterface.
Require Import CommonTheorems.
Require Import SpecLemmas.
Require Import RefinementSpecLemmas.

Require Import LogsLeaderLogsInterface.
Require Import AppendEntriesRequestLeaderLogsInterface.
Require Import RefinedLogMatchingLemmasInterface.
Require Import AllEntriesLeaderLogsTermInterface.
Require Import LeaderLogsContiguousInterface.
Require Import OneLeaderLogPerTermInterface.
Require Import LeaderLogsSortedInterface.
Require Import TermSanityInterface.
Require Import AllEntriesTermSanityInterface.

Require Import AllEntriesLogInterface.

Section AllEntriesLog.

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

  Context {llli : logs_leaderLogs_interface}.
  Context {aerlli : append_entries_leaderLogs_interface}.
  Context {rlmli : refined_log_matching_lemmas_interface}.
  Context {aellti : allEntries_leaderLogs_term_interface}.
  Context {llci : leaderLogs_contiguous_interface}.
  Context {ollpti : one_leaderLog_per_term_interface}.
  Context {llsi : leaderLogs_sorted_interface}.
  Context {tsi : term_sanity_interface}.
  Context {rri : raft_refinement_interface}.
  Context {aetsi : allEntries_term_sanity_interface}.

  (* strategy : prove allEntries_log as inductive invariant, then
     prove allEntries_leaderLogs inductive from that *)

  Ltac update_destruct :=
    match goal with
      | [ |- context [ update _ ?y _ ?x ] ] => destruct (name_eq_dec y x)
    end.

  Ltac update_destruct_hyp :=
    match goal with
      | [ _ : context [ update _ ?y _ ?x ] |- _ ] => destruct (name_eq_dec y x)
    end.

  Ltac destruct_update :=
    repeat (first [update_destruct_hyp|update_destruct]; subst; rewrite_update).

  Definition no_entries_past_current_term_host_lifted net :=
    forall (h : name) e,
      In e (log (snd (nwState net h))) ->
      eTerm e <= currentTerm (snd (nwState net h)).

  Lemma no_entries_past_current_term_host_lifted_invariant :
    forall net,
      refined_raft_intermediate_reachable net ->
      no_entries_past_current_term_host_lifted net.
  Proof using rri tsi.
    unfold no_entries_past_current_term_host_lifted.
    pose proof deghost_spec.
    do 4 intro.
    repeat find_reverse_higher_order_rewrite.
    eapply lift_prop; eauto.
    intros.
    find_apply_lem_hyp no_entries_past_current_term_invariant; eauto.
  Qed.

(** Succeed iff [x] is in the list [ls], represented with left-associated nested tuples. *)
Ltac inList x ls :=
  match ls with
    | x => idtac
    | (_, x) => idtac
    | (?LS, _) => inList x LS
  end.

(** Try calling tactic function [f] on every element of tupled list [ls], keeping the first call not to fail. *)
Ltac app f ls :=
  match ls with
    | (?LS, ?X) => f X || app f LS || fail 1
    | _ => f ls
  end.

(** Run [f] on every element of [ls], not just the first that doesn't fail. *)
Ltac all f ls :=
  match ls with
    | (?LS, ?X) => f X; all f LS
    | (_, _) => fail 1
    | _ => f ls
  end.

  Lemma appendEntries_haveNewEntries_false :
    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 ->
      haveNewEntries (snd (nwState net h)) es = false ->
      In e es ->
      In e (log (snd (nwState net h))).
  Proof using rlmli.
    intros.
    unfold haveNewEntries in *. do_bool. intuition;
      [unfold not_empty in *; break_match; subst; simpl in *; intuition; congruence|].
    break_match; try congruence.
    do_bool. find_apply_lem_hyp findAtIndex_elim. intuition.
    assert (es <> nil) by (destruct es; subst; simpl in *; intuition; congruence).
    find_eapply_lem_hyp maxIndex_non_empty.
    break_exists. intuition.
    find_copy_eapply_lem_hyp entries_sorted_nw_invariant; eauto.
    match goal with
      | H : In e es |- _ => copy_eapply maxIndex_is_max H; eauto
    end.
    repeat find_rewrite.
    find_eapply_lem_hyp entries_match_nw_host_invariant; eauto.
  Qed.

  Lemma maxIndex_le :
    forall l1 l2,
      sorted l1 ->
      contiguous_range_exact_lo l1 0 ->
      findAtIndex l1 (maxIndex l2) = None ->
      l2 = nil
      \/ (exists e, In e l2 /\ eIndex e = 0)
      \/ maxIndex l1 <= maxIndex l2.
  Proof using.
    intros. destruct l2; intuition.
    simpl in *. right.
    destruct l1; intuition.
    find_copy_eapply_lem_hyp findAtIndex_None; simpl in *; eauto.
    unfold contiguous_range_exact_lo in *.
    simpl in *. intuition.
    destruct (lt_eq_lt_dec 0 (eIndex e)); intuition; eauto.
    destruct (lt_eq_lt_dec (eIndex e0) (eIndex e)); intuition.
    exfalso. repeat break_if; do_bool; intuition.
    match goal with
      | H : forall _, _ < _ <= _ -> _ |- _ =>
        specialize (H (eIndex e))
    end; conclude_using omega.
    simpl in *. break_exists. intuition; subst; intuition.
    eapply findAtIndex_None; eauto.
  Qed.

  Lemma maxIndex_le' :
    forall l1 l2 i,
      sorted l1 ->
      contiguous_range_exact_lo l1 0 ->
      l2 <> nil ->
      contiguous_range_exact_lo l2 i ->
      findAtIndex l1 (maxIndex l2) = None ->
      maxIndex l1 <= maxIndex l2.
  Proof using.
    intros. find_eapply_lem_hyp maxIndex_le; intuition; eauto.
    break_exists. intuition.
    unfold contiguous_range_exact_lo in *.
    intuition.
    find_insterU. conclude_using eauto. omega.
  Qed.

  Lemma sorted_app_in_in :
    forall l1 l2 e e',
      sorted (l1 ++ l2) ->
      In e l1 ->
      In e' l2 ->
      eIndex e' < eIndex e.
  Proof using.
    induction l1; intros; simpl in *; intuition; eauto.
    subst. find_insterU. conclude_using ltac:(apply in_app_iff; intuition eauto).
    intuition.
  Qed.

  Lemma sorted_app_sorted_app_in1_in2 :
    forall l1 l2 l3 e e',
      sorted (l1 ++ l3) ->
      sorted (l2 ++ l3) ->
      In e l1 ->
      In e' (l2 ++ l3) ->
      eIndex e' = eIndex e ->
      In e' l2.
  Proof using.
    intros. do_in_app. intuition.
    match goal with
      | H : sorted (?l ++ ?l'), _ : In _ ?l, _ : In _ ?l' |- _ =>
        eapply sorted_app_in_in in H
    end; eauto.  omega.
  Qed.

  Lemma sorted_app_sorted_app_in1_in2_prefix :
    forall l1 l2 l3 l4 e e',
      sorted (l1 ++ l3) ->
      sorted (l2 ++ l4) ->
      Prefix l4 l3 ->
      In e l1 ->
      In e' (l2 ++ l4) ->
      eIndex e' = eIndex e ->
      In e' l2.
  Proof using.
    intros. do_in_app. intuition.
    find_eapply_lem_hyp Prefix_In; [|eauto].
    match goal with
      | H : sorted (?l ++ ?l'), _ : In _ ?l, _ : In _ ?l' |- _ =>
        eapply sorted_app_in_in in H
    end; eauto. omega.
  Qed.

  Lemma sorted_app_in2_in2 :
    forall l1 l2 e e',
      sorted (l1 ++ l2) ->
      In e' (l1 ++ l2) ->
      In e l2 ->
      eIndex e' = eIndex e ->
      In e' l2.
  Proof using.
    intros. do_in_app. intuition.
    match goal with
      | H : sorted (?l ++ ?l'), _ : In _ ?l, _ : In _ ?l' |- _ =>
        eapply sorted_app_in_in in H
    end; eauto.  omega.
  Qed.

(*  Lemma sorted_app_in3_in4_prefix :
    forall l1 l2 l3 l4 e e',
      sorted (l1 ++ l3) ->
      sorted (l2 ++ l4) ->
      Prefix l4 l3 ->
      In e l3 ->
      In e' (l2 ++ l4) ->
      eIndex e' = eIndex e ->
      In e' l4.
  Proof.
    intros. do_in_app. intuition.
    match goal with
      | H : sorted (?l ++ ?l'), _ : In _ ?l, _ : In _ ?l' |- _ =>
        eapply sorted_app_in_in in H
    end; eauto.  omega.
  Qed.
  *)
  Lemma sorted_term_index_le :
    forall l e e',
      sorted l ->
      In e l ->
      In e' l ->
      eTerm e' < eTerm e ->
      eIndex e' <= eIndex e.
  Proof using.
    induction l; intros; simpl in *; intuition; subst_max; intuition.
    - find_apply_hyp_hyp. intuition.
    - find_apply_hyp_hyp. intuition.
  Qed.
  Lemma term_ne_in_l2 :
    forall l e e' l1 l2,
      sorted l ->
      In e l ->
      (forall e', In e' l -> eTerm e' <= eTerm e) ->
      removeAfterIndex l (eIndex e) = l1 ++ l2 ->
      (forall e', In e' l1 -> eTerm e' = eTerm e) ->
      In e' l ->
      eTerm e' <> eTerm e ->
      In e' l2.
  Proof using.
    intros.
    assert (eIndex e' <= eIndex e) by
        (eapply sorted_term_index_le; eauto;
         find_apply_hyp_hyp;
         destruct (lt_eq_lt_dec (eTerm e') (eTerm e)); intuition).
    find_eapply_lem_hyp removeAfterIndex_le_In; eauto.
    repeat find_rewrite.
    do_in_app. intuition.
    find_apply_hyp_hyp. intuition.
  Qed.

  Lemma Prefix_maxIndex_eq :
    forall l l',
      Prefix l l' ->
      l <> nil ->
      maxIndex l = maxIndex l'.
  Proof using.
    intros.
    induction l; simpl in *; intuition.
    break_match; intuition. subst. simpl. auto.
  Qed.

  Lemma sorted_gt_maxIndex :
    forall e l1 l2,
      sorted (e :: l1 ++ l2) ->
      l2 <> nil ->
      maxIndex l2 < eIndex e.
  Proof using.
    intros; induction l1; simpl in *; intuition.
    - destruct l2; simpl in *; intuition.
      match goal with
        | H : forall _, ?e = _ \/ _ -> _ |- _ =>
          specialize (H e)
      end; intuition.
  Qed.

  Lemma allEntries_log_append_entries :
    refined_raft_net_invariant_append_entries allEntries_log.
  Proof using aetsi rri tsi llsi ollpti llci aellti rlmli aerlli llli.
    red. unfold allEntries_log in *. simpl in *. intros.
    repeat find_higher_order_rewrite.
    destruct_update; simpl in *;
    [|find_apply_hyp_hyp; intuition;
      right; break_exists_exists; intuition;
      repeat find_higher_order_rewrite;
      destruct_update; simpl in *;
      eauto; rewrite update_elections_data_appendEntries_leaderLogs; eauto].
    find_eapply_lem_hyp update_elections_data_appendEntries_allEntries_detailed; eauto.
    intuition;
      [|repeat find_rewrite;
         find_eapply_lem_hyp appendEntries_haveNewEntries_false; eauto].
    find_copy_apply_hyp_hyp. intuition;
      [|right; break_exists_exists; intuition;
        repeat find_higher_order_rewrite; destruct_update; simpl in *; eauto;
        try rewrite update_elections_data_appendEntries_leaderLogs; eauto; subst;
        find_apply_lem_hyp handleAppendEntries_currentTerm_leaderId; intuition;
        repeat find_rewrite; auto].
    destruct (in_dec entry_eq_dec e (log d)); intuition.
    right.
    find_copy_apply_lem_hyp handleAppendEntries_log_detailed.
    intuition; repeat find_rewrite; intuition.
    - subst.
      find_copy_eapply_lem_hyp allEntries_term_sanity_invariant; eauto.
      destruct (lt_eq_lt_dec t0 (currentTerm d)); intuition; unfold ghost_data in *; simpl in *; try omega.
      + find_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
        break_exists. break_and.
        match goal with
          | H : In (?t, ?ll) (leaderLogs (fst (nwState _ ?leader))) |- _ =>
            (exists t, leader, ll)
        end.
        split;
          [repeat find_higher_order_rewrite;
            destruct_update; simpl in *;
            eauto; rewrite update_elections_data_appendEntries_leaderLogs; eauto|];
          split; auto; intuition;
          [break_exists; intuition;
           find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
          subst. repeat find_rewrite. intuition.
      + subst.
        find_eapply_lem_hyp allEntries_leaderLogs_term_invariant; eauto. intuition.
        * subst. exfalso.
          find_copy_eapply_lem_hyp logs_leaderLogs_invariant; eauto.
          find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
          break_exists; intuition;
          [break_exists; intuition;
           find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
          subst. clean.
          repeat find_rewrite.
          find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
          conclude_using eauto. subst.
          match goal with
            | H : In _ _ -> False |- _ =>
              apply H
          end.
          find_copy_eapply_lem_hyp entries_sorted_invariant; eauto.
          unfold entries_sorted in *.
          repeat find_rewrite.
          match goal with
            | _ : removeAfterIndex ?l (eIndex ?e) = _ |- _ =>
              assert (In e (removeAfterIndex l (eIndex e))) by
                  (eapply removeAfterIndex_le_In; eauto)
          end.
          repeat find_rewrite.
          do_in_app; intuition.
          assert (exists e', eIndex e' = eIndex e /\ In e' (x1 ++ x4)) by
              (eapply entries_contiguous_nw_invariant; eauto;
               intuition; [eapply entries_contiguous_invariant; eauto|];
               eapply le_trans; [eapply maxIndex_is_max; eauto|];
               eapply maxIndex_le'; eauto;
               [eapply entries_contiguous_invariant; eauto|
                eapply entries_contiguous_nw_invariant; eauto]).
          break_exists. intuition.
          find_copy_eapply_lem_hyp sorted_app_sorted_app_in1_in2. Focus 5. eauto. Focus 4. eauto.
          all:(try solve [eapply entries_sorted_nw_invariant; eauto]).
          all:(try solve [repeat find_reverse_rewrite; eauto using removeAfterIndex_sorted]).
          find_apply_hyp_hyp.
          find_eapply_lem_hyp entries_match_nw_host_invariant; eauto; repeat conclude_using eauto.
          match goal with
            | H : eIndex _ = eIndex _ |- _ =>
              eapply uniqueIndices_elim_eq in H
          end; eauto using sorted_uniqueIndices.
          subst. auto.
        * exfalso.
          find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
          break_exists; intuition;
          [break_exists; intuition;
           find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
          subst. clean.
          find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
          conclude_using eauto. subst.
          match goal with
            | H : In _ _ -> False |- _ =>
              apply H
          end.
          repeat find_rewrite. apply in_app_iff; intuition.
    - subst.
      find_copy_eapply_lem_hyp allEntries_term_sanity_invariant; eauto.
      destruct (lt_eq_lt_dec t0 (currentTerm d)); intuition; unfold ghost_data in *; simpl in *; try omega.
      + find_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
        break_exists. break_and.
        match goal with
          | H : In (?t, ?ll) (leaderLogs (fst (nwState _ ?leader))) |- _ =>
            (exists t, leader, ll)
        end. find_higher_order_rewrite.
        split;
          [subst; find_higher_order_rewrite;
            destruct_update; simpl in *;
            eauto; rewrite update_elections_data_appendEntries_leaderLogs; eauto|];
          split; auto.
        intuition;
          [break_exists; intuition;
           find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
        subst. repeat find_rewrite. intuition.
      + subst.
        find_eapply_lem_hyp allEntries_leaderLogs_term_invariant; eauto. intuition.
        * { subst. exfalso.
            find_copy_eapply_lem_hyp logs_leaderLogs_invariant; eauto.
            find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
            break_exists; intuition;
            [break_exists; intuition;
             find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
            subst. clean.
            repeat find_rewrite.
            find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
            conclude_using eauto. subst.
            match goal with
              | H : In _ _ -> False |- _ =>
                apply H
            end.
            find_copy_eapply_lem_hyp entries_sorted_invariant; eauto.
            unfold entries_sorted in *.
            repeat find_rewrite.
            match goal with
              | _ : removeAfterIndex ?l (eIndex ?e) = _ |- _ =>
                assert (In e (removeAfterIndex l (eIndex e))) by
                    (eapply removeAfterIndex_le_In; eauto)
            end.
            repeat find_rewrite.
            do_in_app; intuition.
            find_apply_lem_hyp findAtIndex_elim. intuition.
            find_copy_apply_lem_hyp maxIndex_non_empty.
            break_exists.
            intuition.
            match goal with
              | _ : In ?e' (log _), _ : maxIndex ?l = eIndex ?e' |- _ =>
                destruct (le_lt_dec (eIndex e) (maxIndex l))
            end.
            - assert (exists e', eIndex e' = eIndex e /\ In e' (x1 ++ x4)) by
                  (eapply entries_contiguous_nw_invariant; eauto; intuition;
                   eapply entries_gt_0_invariant; eauto).
              break_exists. intuition.
              find_copy_eapply_lem_hyp sorted_app_sorted_app_in1_in2. Focus 5. eauto. Focus 4. eauto.
              all:(try solve [eapply entries_sorted_nw_invariant; eauto]).
              all:(try solve [repeat find_reverse_rewrite; eauto using removeAfterIndex_sorted]).
              find_apply_hyp_hyp.
              find_eapply_lem_hyp entries_match_nw_host_invariant; eauto; repeat conclude_using eauto.
              match goal with
                | H : eIndex _ = eIndex _ |- _ =>
                  eapply uniqueIndices_elim_eq in H
              end; eauto using sorted_uniqueIndices.
              subst. auto.
            - exfalso.
              repeat find_rewrite.
              match goal with
                | _ : eIndex ?e' = eIndex ?x,
                  _ : eIndex ?x < ?i,
                  _ : context [removeAfterIndex ?l ?i] |- _ =>
                  assert (In e' (removeAfterIndex l i)) by
                      (eapply removeAfterIndex_le_In; auto; omega)
              end.
              repeat find_rewrite.
              do_in_app. intuition.
              + find_copy_eapply_lem_hyp sorted_app_sorted_app_in1_in2. Focus 4. eauto.
                all:eauto.
                all:(try solve [eapply entries_sorted_nw_invariant; eauto]).
                all:(try solve [repeat find_reverse_rewrite; eauto using removeAfterIndex_sorted]).
                repeat find_apply_hyp_hyp. repeat find_rewrite. intuition.
              + find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
                find_copy_eapply_lem_hyp sorted_app_in2_in2. Focus 3. eauto.
                all:eauto.
                all:(try solve [eapply entries_sorted_nw_invariant; eauto]).
                match goal with
                  | H : eIndex ?e1 = eIndex ?e2, _ : In ?e1 ?ll, _ : In ?e2 ?ll |- _ =>
                    eapply uniqueIndices_elim_eq with (xs0 := ll) in H
                end; eauto using sorted_uniqueIndices.
                subst. intuition.
          }
        * exfalso.
          find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
          break_exists; intuition;
          [break_exists; intuition;
           find_eapply_lem_hyp leaderLogs_contiguous_invariant; eauto; omega|].
          subst. clean.
          find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
          conclude_using eauto. subst.
          match goal with
            | H : In _ _ -> False |- _ =>
              apply H
          end.
          repeat find_rewrite. apply in_app_iff; intuition.
    - find_copy_eapply_lem_hyp allEntries_term_sanity_invariant; eauto.
      destruct (lt_eq_lt_dec t0 t); intuition; unfold ghost_data in *; simpl in *; try omega.
      + match goal with
          | H : context [pBody] |- _ =>
            copy_eapply append_entries_leaderLogs_invariant H
        end; eauto.
        break_exists. break_and. subst.
        match goal with
          | H : In (?t, ?ll) (leaderLogs (fst (nwState _ ?leader))) |- _ =>
            (exists t, leader, ll)
        end.
        split;
          [find_higher_order_rewrite;
            destruct_update; simpl in *;
            eauto; rewrite update_elections_data_appendEntries_leaderLogs; eauto|];
          split; auto. intuition; subst.
        * find_false.
          apply in_app_iff. right. eapply removeAfterIndex_le_In; eauto.
          find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
          eapply le_trans; [eapply maxIndex_is_max; eauto|]. omega.
        * {
            break_exists. intuition. unfold Prefix_sane in *. intuition.
            - destruct (le_lt_dec (eIndex e) (eIndex x3)).
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. right. apply removeAfterIndex_le_In; auto.
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. left.
                apply in_app_iff. right.
                find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
                eapply prefix_contiguous; eauto.
                find_copy_eapply_lem_hyp entries_sorted_nw_invariant; eauto.
                eapply contiguous_app; eauto.
                eapply entries_contiguous_nw_invariant; eauto.
            - find_false.
              repeat find_rewrite.
              apply in_app_iff. right.
              find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
              apply removeAfterIndex_le_In; auto.
              eapply maxIndex_is_max; eauto.
          }
        * find_false. intuition.
      + subst.
        find_eapply_lem_hyp allEntries_leaderLogs_term_invariant; eauto. intuition.
        * { subst. exfalso.
            find_copy_eapply_lem_hyp logs_leaderLogs_invariant; eauto.
            find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
            break_exists. break_and.
            repeat find_rewrite.
            find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto.
            conclude_using eauto. subst. intuition.
            - repeat find_rewrite.
              destruct (le_lt_dec (eIndex e) (eIndex x6)).
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. right. apply removeAfterIndex_le_In; auto.
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. left.
                find_copy_eapply_lem_hyp entries_sorted_invariant.
                find_eapply_lem_hyp maxIndex_le'; eauto;
                [|eapply entries_contiguous_invariant; eauto|eapply entries_contiguous_nw_invariant; eauto].
                find_copy_eapply_lem_hyp entries_contiguous_nw_invariant; eauto.
                unfold contiguous_range_exact_lo in *.
                break_and.
                find_copy_eapply_lem_hyp entries_sorted_invariant.
                match goal with
                  | H : forall _, _ < _ <= _ -> _ |- _ =>
                    specialize (H (eIndex e));
                      conclude_using ltac:(intuition; eapply le_trans; [eapply maxIndex_is_max; eauto|]; eauto)
                end.
                break_exists. break_and.
                match goal with
                  | H : eIndex ?x = eIndex e |- _ =>
                    copy_eapply entries_match_nw_host_invariant H
                end; eauto.
                find_copy_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
                conclude_using
                  ltac:(match goal with
                          | H : _ |- _ => apply H
                        end; do_in_app; intuition;
                        match goal with
                          | H : In ?x _ |- In ?x _ =>
                            copy_eapply Prefix_maxIndex H
                        end; [|idtac|eauto]; eauto; omega).
                conclude_using eauto. conclude_using auto.
                match goal with
                  | H : _ = _ |- _ =>
                    eapply uniqueIndices_elim_eq in H
                end; eauto; eauto using sorted_uniqueIndices.
                subst. auto.
            - break_exists. break_and.
              destruct (le_lt_dec (eIndex e) (eIndex x6)).
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. right. apply removeAfterIndex_le_In; auto.
              + match goal with
                  | _ : removeAfterIndex _ (eIndex ?e) = ?l |- _ =>
                    assert (In e l) by (repeat find_reverse_rewrite;
                                        eapply removeAfterIndex_le_In; auto)
                end.
                find_copy_eapply_lem_hyp entries_sorted_invariant.
                assert (exists e', eIndex e' = eIndex e /\ In e' (x1 ++ x2)) by
                      (eapply entries_contiguous_nw_invariant; eauto;
                       intuition;
                       eapply le_trans; [eapply maxIndex_is_max; eauto|];
                       eapply maxIndex_le'; eauto;
                       [eapply entries_contiguous_invariant; eauto|
                        eapply entries_contiguous_nw_invariant; eauto]).
                do_in_app. intuition.
                * break_exists. break_and.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply sorted_app_sorted_app_in1_in2_prefix H
                  end; eauto.
                  all:try solve [repeat find_reverse_rewrite; eauto using removeAfterIndex_sorted].
                  all:try solve [eapply entries_sorted_nw_invariant; eauto].
                  find_apply_hyp_hyp.
                  match goal with
                    | H : In e _ -> False |- _ => apply H
                  end.
                  apply in_app_iff. left.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply entries_match_nw_host_invariant H
                  end; eauto. concludes. repeat conclude_using eauto.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply uniqueIndices_elim_eq H
                  end; eauto using sorted_uniqueIndices. subst. auto.
                * find_copy_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
                  unfold Prefix_sane in *.
                  intuition; [|find_eapply_lem_hyp maxIndex_is_max; eauto; omega].
                  find_eapply_lem_hyp prefix_contiguous. Focus 2. eauto.
                  all:eauto.
                  all:try solve [eapply contiguous_app; [|eapply entries_contiguous_nw_invariant; eauto];
                                 eapply entries_sorted_nw_invariant; eauto].
                  match goal with
                    | H : In e _ -> False |- _ => apply H
                  end. intuition.
            - subst.
              destruct (le_lt_dec (eIndex e) (eIndex x6)).
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. right. apply removeAfterIndex_le_In; auto.
              + match goal with
                  | _ : removeAfterIndex _ (eIndex ?e) = ?l |- _ =>
                    assert (In e l) by (repeat find_reverse_rewrite;
                                        eapply removeAfterIndex_le_In; auto)
                end.
                find_copy_eapply_lem_hyp entries_sorted_invariant.
                assert (exists e', eIndex e' = eIndex e /\ In e' (x1 ++ x4)) by
                      (eapply entries_contiguous_nw_invariant; eauto;
                       intuition;
                       eapply le_trans; [eapply maxIndex_is_max; eauto|];
                       eapply maxIndex_le'; eauto;
                       [eapply entries_contiguous_invariant; eauto|
                        eapply entries_contiguous_nw_invariant; eauto]).
                do_in_app. intuition.
                * break_exists. break_and.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply sorted_app_sorted_app_in1_in2 H
                  end; eauto.
                  all:try solve [repeat find_reverse_rewrite; eauto using removeAfterIndex_sorted].
                  all:try solve [eapply entries_sorted_nw_invariant; eauto].
                  find_apply_hyp_hyp.
                  match goal with
                    | H : In e _ -> False |- _ => apply H
                  end.
                  apply in_app_iff. left.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply entries_match_nw_host_invariant H
                  end; eauto. concludes. repeat conclude_using eauto.
                  match goal with
                    | H : eIndex _ = eIndex _ |- _ =>
                      copy_eapply uniqueIndices_elim_eq H
                  end; eauto using sorted_uniqueIndices. subst. auto.
                * match goal with
                    | H : In e _ -> False |- _ => apply H
                  end. intuition.
          }
        * { exfalso.
            find_copy_apply_lem_hyp append_entries_leaderLogs_invariant; eauto.
            break_exists; intuition.
            copy_eapply_prop_hyp append_entries_leaderLogs pBody; eauto.
            break_exists; break_and.
            subst.
            find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
            conclude_using eauto. subst.
            match goal with
              | H : In _ _ -> False |- _ =>
                apply H
            end.
            find_copy_apply_lem_hyp leaderLogs_sorted_invariant; auto.
            find_copy_apply_lem_hyp maxIndex_is_max; auto.
            destruct (le_lt_dec (eIndex e) (eIndex x1));
              [apply in_app_iff; right; eapply removeAfterIndex_le_In; eauto|].
            repeat find_rewrite. apply in_app_iff; intuition.
            - omega.
            - break_exists; break_and.
              unfold Prefix_sane in *. break_or_hyp; try omega.
              left; apply in_app_iff; right.
              eapply prefix_contiguous; eauto.
              eapply contiguous_app; [|eapply entries_contiguous_nw_invariant; eauto];
              eapply entries_sorted_nw_invariant; eauto.
            - subst. intuition.
          }
    - find_copy_eapply_lem_hyp allEntries_term_sanity_invariant; eauto.
      destruct (lt_eq_lt_dec t0 t); intuition; unfold ghost_data in *; simpl in *; try omega.
      + match goal with
          | H : context [pBody] |- _ =>
            copy_eapply append_entries_leaderLogs_invariant H
        end; eauto.
        break_exists. break_and. subst.
        match goal with
          | H : In (?t, ?ll) (leaderLogs (fst (nwState _ ?leader))) |- _ =>
            (exists t, leader, ll)
        end.
        split;
          [find_higher_order_rewrite;
            destruct_update; simpl in *;
            eauto; rewrite update_elections_data_appendEntries_leaderLogs; eauto|];
          split; auto. intuition; subst.
        * find_false.
          apply in_app_iff. right. eapply removeAfterIndex_le_In; eauto.
          find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
          eapply le_trans; [eapply maxIndex_is_max; eauto|]. omega.
        * {
            break_exists. intuition. unfold Prefix_sane in *. intuition.
            - destruct (le_lt_dec (eIndex e) (eIndex x4)).
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. right. apply removeAfterIndex_le_In; auto.
              + match goal with
                  | H : In e _ -> False |- _ => apply H
                end.
                apply in_app_iff. left.
                apply in_app_iff. right.
                find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
                eapply prefix_contiguous; eauto.
                find_copy_eapply_lem_hyp entries_sorted_nw_invariant; eauto.
                eapply contiguous_app; eauto.
                eapply entries_contiguous_nw_invariant; eauto.
            - find_false.
              repeat find_rewrite.
              apply in_app_iff. right.
              find_eapply_lem_hyp leaderLogs_sorted_invariant; eauto.
              apply removeAfterIndex_le_In; auto.
              eapply maxIndex_is_max; eauto.
          }
        * find_false. intuition.
      + subst.
        find_eapply_lem_hyp allEntries_leaderLogs_term_invariant; eauto. intuition.
        * { subst. exfalso.
            find_copy_eapply_lem_hyp logs_leaderLogs_invariant; eauto.
            find_copy_eapply_lem_hyp append_entries_leaderLogs_invariant; eauto.
            break_exists. break_and.
            find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto.
            repeat find_rewrite.
            conclude_using eauto. subst.
            find_eapply_lem_hyp le_antisym; eauto.
            destruct x1.
            - simpl in *. destruct x2; simpl in *; auto.
              break_match; auto.
              match goal with
                | H : _ \/ (exists _, _) \/ _ |- _ =>
                  clear H
              end.
              break_and. subst.
              cut (e1 = x6); intros; subst; auto.
              find_apply_lem_hyp findAtIndex_elim.
              break_and.
              find_copy_apply_lem_hyp entries_sorted_invariant.
              eapply uniqueIndices_elim_eq; eauto using sorted_uniqueIndices.
              eapply removeAfterIndex_in with (i := (eIndex e)).
              unfold raft_data, ghost_data in *; simpl in *.
              unfold raft_data, ghost_data in *; simpl in *.
              repeat find_rewrite. intuition.
            - simpl in *.
              match goal with
                | H : forall _, ?e = _ \/ _ -> _ |- _ =>
                  specialize (H e)
              end. conclude_using auto.
              repeat find_rewrite.
              find_apply_lem_hyp findAtIndex_elim. break_and.
              find_eapply_lem_hyp term_ne_in_l2. Focus 7. eauto. all:eauto.
              all:try solve [eapply entries_sorted_invariant; eauto].
              all:try solve [intros; find_eapply_lem_hyp no_entries_past_current_term_host_lifted_invariant; unfold ghost_data, raft_data in *; simpl in *;
                             unfold ghost_data, raft_data in *; simpl in *;
                             repeat find_rewrite; eauto].
              assert (eIndex e0 <= maxIndex x4) by
                  (repeat find_rewrite;
                   eapply maxIndex_is_max; eauto;
                   eapply leaderLogs_sorted_invariant; eauto).
              assert (eIndex x7 < eIndex e0) by
                  (eapply entries_contiguous_nw_invariant; eauto; intuition).
              intuition.
              + break_exists. break_and.
                unfold Prefix_sane in *. intuition.
                find_copy_eapply_lem_hyp Prefix_maxIndex_eq; eauto.
                find_eapply_lem_hyp entries_sorted_nw_invariant; eauto.
                find_eapply_lem_hyp sorted_gt_maxIndex; eauto; omega.
              + subst.
                find_eapply_lem_hyp entries_sorted_nw_invariant; eauto.
                find_eapply_lem_hyp sorted_gt_maxIndex; eauto; try omega.
                destruct x4; simpl in *; congruence.
          }
        * { exfalso.
            find_copy_apply_lem_hyp append_entries_leaderLogs_invariant; eauto.
            break_exists; intuition. subst.
            copy_eapply_prop_hyp append_entries_leaderLogs pBody; eauto.
            break_exists; break_and.
            subst.
            find_eapply_lem_hyp one_leaderLog_per_term_log_invariant; eauto;
            conclude_using eauto. subst.
            match goal with
              | H : In _ _ -> False |- _ =>
                apply H
            end.
            find_copy_apply_lem_hyp leaderLogs_sorted_invariant; auto.
            find_copy_apply_lem_hyp maxIndex_is_max; auto.
            destruct (le_lt_dec (eIndex e) (eIndex x2));
              [apply in_app_iff; right; eapply removeAfterIndex_le_In; eauto|].
            repeat find_rewrite. apply in_app_iff; intuition.
            - omega.
            - break_exists; break_and.
              unfold Prefix_sane in *. break_or_hyp; try omega.
              left; apply in_app_iff; right.
              eapply prefix_contiguous; eauto.
              eapply contiguous_app; [|eapply entries_contiguous_nw_invariant; eauto];
              eapply entries_sorted_nw_invariant; eauto.
            - subst. intuition.
          }
  Qed.

  Lemma handleAppendEntriesReply_currentTerm_leaderId :
    forall h st h' t es res st' m,
      handleAppendEntriesReply h st h' t es res = (st', m) ->
      currentTerm st < currentTerm st' \/
      (currentTerm st = currentTerm st' /\ leaderId st' = leaderId st).
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat (break_match; try find_inversion; simpl in *; auto).
  Qed.

  Lemma allEntries_log_append_entries_reply :
    refined_raft_net_invariant_append_entries_reply allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros. simpl in *.
    repeat find_higher_order_rewrite.
    find_copy_apply_lem_hyp handleAppendEntriesReply_log.
    find_apply_lem_hyp handleAppendEntriesReply_currentTerm_leaderId.
    destruct_update; simpl in *; eauto; find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; repeat find_rewrite; intuition;
    find_higher_order_rewrite;
    destruct_update; simpl in *; auto.
  Qed.

  Lemma update_elections_data_requestVote_leaderLogs :
    forall h h' t lli llt st,
      leaderLogs (update_elections_data_requestVote h h' t h' lli llt st) =
      leaderLogs (fst st).
  Proof using.
    unfold update_elections_data_requestVote.
    intros.
    repeat break_match; auto.
  Qed.

  Lemma update_elections_data_requestVoteReply_leaderLogs :
    forall h h' t  st t' ll' r,
      In (t', ll') (leaderLogs (fst st)) ->
      In (t', ll') (leaderLogs (update_elections_data_requestVoteReply h h' t r st)).
  Proof using.
    unfold update_elections_data_requestVoteReply.
    intros.
    repeat break_match; auto.
    simpl in *. intuition.
  Qed.

  Lemma allEntries_log_request_vote :
    refined_raft_net_invariant_request_vote allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros. simpl in *.
    repeat find_higher_order_rewrite.
    find_copy_apply_lem_hyp handleRequestVote_log.
    find_apply_lem_hyp handleRequestVote_currentTerm_leaderId.
    destruct_update; simpl in *; eauto;
    try find_rewrite_lem update_elections_data_requestVote_allEntries;
    find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; intuition; repeat find_higher_order_rewrite;
    destruct_update; simpl in *; auto;
    rewrite update_elections_data_requestVote_leaderLogs; auto.
  Qed.

  Lemma handleRequestVoteReply_log' :
    forall h st h' t r,
      log (handleRequestVoteReply h st h' t r) = log st.
  Proof using.
    eauto using handleRequestVoteReply_log.
  Qed.

  Lemma allEntries_log_request_vote_reply :
    refined_raft_net_invariant_request_vote_reply allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros. simpl in *.
    find_copy_apply_lem_hyp handleRequestVoteReply_currentTerm_leaderId.
    repeat find_higher_order_rewrite.
    destruct_update; simpl in *; eauto;
    try rewrite handleRequestVoteReply_log';
    try find_rewrite_lem update_elections_data_requestVoteReply_allEntries;
    find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; repeat find_rewrite; intuition;
    find_higher_order_rewrite;
    destruct_update; simpl in *; auto;
    apply update_elections_data_requestVoteReply_leaderLogs; auto.
  Qed.

  Lemma update_elections_data_client_request_allEntries' :
    forall h st client id c out st' ms t e,
      handleClientRequest h (snd st) client id c = (out, st', ms) ->
      In (t, e) (allEntries (update_elections_data_client_request h st client id c)) ->
      In (t, e) (allEntries (fst st)) \/
      In e (log st').
  Proof using.
    intros.
    unfold update_elections_data_client_request in *.
    repeat break_match; repeat find_inversion; auto.
    simpl in *. intuition.
    find_inversion. repeat find_rewrite. intuition.
  Qed.

  Lemma handleClientRequest_currentTerm_leaderId :
    forall h st client id c out st' ms,
      handleClientRequest h st client id c = (out, st', ms) ->
      currentTerm st' = currentTerm st /\
      leaderId st' = leaderId st.
  Proof using.
    intros. unfold handleClientRequest in *.
    subst.
    break_match; try find_inversion; simpl in *; auto.
  Qed.

  Lemma allEntries_log_client_request :
    refined_raft_net_invariant_client_request allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros. simpl in *.
    repeat find_higher_order_rewrite.
    destruct_update; simpl in *;
    try (find_eapply_lem_hyp update_elections_data_client_request_allEntries'; eauto; [idtac]);
    intuition;
    find_copy_apply_lem_hyp handleClientRequest_log;
    find_apply_lem_hyp handleClientRequest_currentTerm_leaderId;
    intuition;
    try break_exists; intuition; repeat find_rewrite; intuition; simpl in *;
    find_apply_hyp_hyp; intuition; repeat right;
    break_exists_exists; intuition;
    repeat find_higher_order_rewrite;
    destruct_update; simpl in *; auto;
    rewrite update_elections_data_client_request_leaderLogs; auto.
  Qed.

  Lemma handleTimeout_currentTerm_leaderId :
    forall h st out st' ms,
      handleTimeout h st = (out, st', ms) ->
      currentTerm st < currentTerm st' \/
      currentTerm st' = currentTerm st /\ leaderId st' = leaderId st.
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    subst.
    break_match; try find_inversion; simpl in *; auto.
  Qed.

  Lemma allEntries_log_timeout :
    refined_raft_net_invariant_timeout allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros. simpl in *.
    repeat find_higher_order_rewrite.
    destruct_update; simpl in *;
    try find_rewrite_lem update_elections_data_timeout_allEntries;
    find_copy_apply_lem_hyp handleTimeout_log_same;
    find_apply_lem_hyp handleTimeout_currentTerm_leaderId;
    repeat find_rewrite;
    find_apply_hyp_hyp; intuition;
    right; break_exists_exists; intuition;
    repeat find_higher_order_rewrite;
    destruct_update; simpl in *; auto;
    rewrite update_elections_data_timeout_leaderLogs; auto.
  Qed.

  Lemma doLeader_currentTerm_leaderId :
    forall st h out st' m,
      doLeader st h = (out, st', m) ->
      currentTerm st' = currentTerm st /\
      leaderId st' = leaderId st.
  Proof using.
    intros. unfold doLeader, advanceCommitIndex in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Lemma allEntries_log_do_leader :
    refined_raft_net_invariant_do_leader allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. 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.
    repeat find_higher_order_rewrite.
    find_copy_apply_lem_hyp doLeader_log.
    find_apply_lem_hyp doLeader_currentTerm_leaderId.
    destruct_update; simpl in *; eauto; find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; intuition; find_higher_order_rewrite;
    destruct_update; simpl in *; auto.
  Qed.

  Lemma doGenericServer_currentTerm_leaderId :
    forall st h out st' m,
      doGenericServer h st = (out, st', m) ->
      currentTerm st' = currentTerm st /\
      leaderId st' = leaderId st.
  Proof using.
    intros. unfold doGenericServer in *.
    repeat break_match; find_inversion;
    use_applyEntries_spec; subst; simpl in *;
    auto.
  Qed.

  Lemma allEntries_log_do_generic_server :
    refined_raft_net_invariant_do_generic_server allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. 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.
    repeat find_higher_order_rewrite.
    find_copy_apply_lem_hyp doGenericServer_log.
    find_apply_lem_hyp doGenericServer_currentTerm_leaderId.
    destruct_update; simpl in *; eauto; find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; intuition; find_higher_order_rewrite;
    destruct_update; simpl in *; auto.
  Qed.

  Lemma allEntries_log_init :
    refined_raft_net_invariant_init allEntries_log.
  Proof using.
    red. unfold allEntries_log. intros. simpl in *. intuition.
  Qed.

  Lemma allEntries_log_state_same_packet_subset :
    refined_raft_net_invariant_state_same_packet_subset allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. intros.
    repeat find_reverse_higher_order_rewrite.
    find_apply_hyp_hyp. intuition. right.
    break_exists_exists. repeat find_higher_order_rewrite. auto.
  Qed.

  Lemma allEntries_log_reboot :
    refined_raft_net_invariant_reboot allEntries_log.
  Proof using.
    red. unfold allEntries_log in *. 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.
    repeat find_higher_order_rewrite.
    subst. unfold reboot in *.
    destruct_update; simpl in *; eauto; find_apply_hyp_hyp; repeat find_rewrite; intuition;
    right; break_exists_exists; intuition; find_higher_order_rewrite;
    destruct_update; simpl in *; auto.
  Qed.

  Lemma allEntries_log_invariant :
    forall net,
      refined_raft_intermediate_reachable net ->
      allEntries_log net.
  Proof using aetsi rri tsi llsi ollpti llci aellti rlmli aerlli llli.
    intros. apply refined_raft_net_invariant; auto.
    - exact allEntries_log_init.
    - exact allEntries_log_client_request.
    - exact allEntries_log_timeout.
    - exact allEntries_log_append_entries.
    - exact allEntries_log_append_entries_reply.
    - exact allEntries_log_request_vote.
    - exact allEntries_log_request_vote_reply.
    - exact allEntries_log_do_leader.
    - exact allEntries_log_do_generic_server.
    - exact allEntries_log_state_same_packet_subset.
    - exact allEntries_log_reboot.
  Qed.

  Instance aeli : allEntries_log_interface.
  split. eauto using allEntries_log_invariant.
  Defined.
End AllEntriesLog.


Require Import List DepList.
Require Import Heaps.
Require Import Expr SepExpr SymEval.

Module EvaluatorTests (B : Heap) (ST : SepTheoryX.SepTheoryXType B).
  Module Import SE := Evaluator B ST.

  Section Tests.
    Variable a b : Type.

    Variable ptsto32 : B.addr -> W -> ST.hprop a b nil.

    Ltac isConst e :=
      match e with
        | true => true
        | false => true
        | O => true
        | S ?e => isConst e
        | _ => false
      end.

    Definition addr_type : Expr.type :=
      {| Expr.Impl := B.addr
       ; Expr.Eq := fun x y => match B.addr_dec x y with
                                 | left pf => Some pf
                                 | right _ => None
                               end
       |} .

    Definition W_type : Expr.type :=
      {| Expr.Impl := W
       ; Expr.Eq := fun x y => match equiv_dec x y with
                                 | left pf => Some pf
                                 | right _ => None
                               end
       |}.

    Definition a_type : Expr.type :=
      {| Expr.Impl := a
       ; Eq := fun _ _ => None
       |}.
    Definition b_type : Expr.type :=
      {| Expr.Impl := b
       ; Eq := fun _ _ => None
       |}.

    Require Import Word.

    Definition pre_types : list Expr.type :=
      a_type :: b_type :: addr_type :: W_type :: nil.

    (** I need to universally quantify SymEval_word over the functions list
     **
     **)

    Definition funcs : functions (wtypes pre_types 2 3) := nil.

    Definition sfuncs : list (SEP.ssignature (wtypes pre_types 2 3) (tvType 0) (tvType 1)) :=
      {| SDomain := tvType 2 :: tvType 3 :: nil
       ; SDenotation := ptsto32 : functionTypeD
         (map (tvarD (wtypes pre_types 2 3)) (tvType 2 :: tvType 3 :: nil))
         (ST.hprop (tvarD (wtypes pre_types 2 3) (tvType 0))
           (tvarD (wtypes pre_types 2 3) (tvType 1)) nil)
       |} :: nil.

    Definition Satisfies cs stn (P : ST.hprop a b nil) m : Prop :=
      exists sm,
          ST.satisfies cs P stn sm
       /\ ST.HT.satisfies sm m.

    Theorem addr_not_state : 3 <> 2.
      clear; auto.
    Qed.

    Definition known : list nat := 0 :: nil.

    Definition evaluators : DepList.hlist (fun n : nat => match nth_error sfuncs n with
                                                            | None => Empty_set
                                                            | Some ss =>
                                                              SymEval_word pre_types addr_not_state
                                                                funcs
                                                                (pcIndex := 0) (stateIndex := 1) ss
                                                          end) known.
      refine (DepList.HCons _ DepList.HNil); simpl.
      refine (
        {| sym_read_word := fun _ args p =>
           match args with
             | p' :: v :: nil =>
               if seq_dec p p' then Some v else None
             | _ => None
           end
         ; sym_write_word := fun _ args p v =>
           match args with
             | p' :: v' :: nil =>
               if seq_dec p p' then Some (p' :: v :: nil) else None
             | _ => None
           end
         ; sym_read_word_correct := _
         ; sym_write_word_correct := _
         |}).
      admit.
      admit.
    Defined.

    Ltac lift_evaluator_w e nt nf :=
      let r := eval simpl sym_read_word in (sym_read_word e) in
      let w := eval simpl sym_write_word in (sym_write_word e) in
      let rc := eval simpl sym_read_word_correct in (sym_read_word_correct e) in
      let wc := eval simpl sym_write_word_correct in (sym_write_word_correct e) in
      match type of e with
        | SymEval_word _ ?stI ?pcI ?ptrI ?wI ?pf _ ?s =>
          match SE.SEP.lift_ssignatures (s :: nil) nt with
            | ?s' :: nil =>
              constr:(@Build_SymEval_word nt stI pcI ptrI wI pf nf s' r w rc wc)
          end
      end.

    Ltac lift_evaluators_w es nt nf ns :=
      let rec lift es :=
        match es with
          | @DepList.HNil _ (fun n : nat =>
            match nth_error _ n with
              | None => Empty_set
              | Some ss => @SymEval_word _ ?stI ?pcI ?ptrI ?wI ?pf _ _
            end) =>
            let k :=
              constr:(@DepList.HNil nat (fun n : nat =>
                match nth_error ns n with
                  | None => Empty_set
                  | Some ss => @SymEval_word nt stI pcI ptrI wI pf nf ss
                end))
            in k
          | @DepList.HCons _ (fun n : nat =>
            match nth_error _ n with
              | None => Empty_set
              | Some ss => @SymEval_word _ ?stI ?pcI ?ptrI ?wI ?pf _ _
            end) ?f ?ls ?e ?es =>
          idtac "ok" ;
            let es := lift es in
              idtac "here" e ;
            let e := lift_evaluator_w e nt nf in
              idtac "got here " ;
            constr:(@DepList.HCons _ (fun n : nat =>
                match nth_error ns n with
                  | None => Empty_set
                  | Some ss => @SymEval_word nt stI pcI ptrI wI pf nf ss
                end) f ls e es)
        end
      in
      lift es.

    Goal True.
      Set Printing Implicit.
      match goal with
        | [ |- _ ] =>
          let z := eval unfold evaluators in evaluators in
            idtac "foo" z ;
          let r := lift_evaluators_w z pre_types funcs sfuncs in
            idtac "here" ;
            idtac r
      end.

            constr:(@DepList.HNil nat (fun n : nat =>
              match nth_error sfuncs n with
                | None => Empty_set
                | Some ss =>
                  SymEval_word nt addr_not_state
                  funcs
                  (pcIndex := 0) (stateIndex := 1) ss
              end) nil)
(*
    Goal forall p1 p2 p3 v1 v2 v3 cs stn m,
      Satisfies cs stn (ST.star (ptsto32 p1 v1) (ST.star (ptsto32 p2 v2) (ptsto32 p3 v3))) m
      -> mem_get_word B.addr B.mem B.footprint_w B.mem_get (IL.implode stn) p1 m = Some v1.
    Proof.
      intros.
      match goal with
        | [ H : Satisfies ?CS ?STN ?P ?M
          |- context [ mem_get_word B.addr B.mem B.footprint_w B.mem_get (IL.implode stn) ?PTR ?M ] ] =>
        let Ts := constr:(@nil Type) in
        let Ts := SEP.collectAllTypes_sexpr ltac:(isConst) Ts (P :: nil) in
        let Ts := SEP.collectAllTypes_expr ltac:(isConst) Ts (PTR, tt) in
        let types := eval unfold pre_types in pre_types in
        let types := SEP.extend_all_types Ts types in
        let sexprs := constr:(P :: nil) in
        match SEP.reify_sexprs a b ltac:(isConst) types tt tt sexprs with
          | (?types, ?pcType, ?stateType, ?funcs, ?sfuncs, ?P :: nil) =>
            match SEP.reify_exprs ltac:(isConst) types funcs (PTR, tt) with
              | (?types, ?funcs, ?PTR :: nil) =>
                let hyps := constr:(@nil (expr types)) in
                let s := eval simpl in (SEP.hash P) in
                generalize (@symeval_read_word_correct types 1 0 2 3 addr_not_state funcs sfuncs known)
                  ; pose hyps
(*
                  hyps PTR (snd s) _ (refl_equal _) CS STN nil nil M I H)
*)
            end
        end
      end.
      intros.
      pose evaluators.

      specialize (H0 evaluators).
      (** TODO : the known list needs to be parameterized appropriately... **)

      simpl. auto.
    Qed.

    Goal forall p1 p2 p3 v1 v2 v3 cs stn m,
      Satisfies cs stn (ST.star (ptsto32 p1 v1) (ST.star (ptsto32 p2 v2) (ptsto32 p3 v3))) m
      -> Satisfies cs stn (ST.star (ptsto32 p1 v1) (ST.star (ptsto32 p2 v3) (ptsto32 p3 v3)))
           (mem_set_word B.addr B.mem B.footprint_w B.mem_set (IL.explode stn) p2 v3 m).
    Proof.
      intros.
      match goal with
        | [ H : Satisfies ?CS ?STN ?P ?M
          |- context [ mem_set_word B.addr B.mem B.footprint_w B.mem_set (IL.explode stn) ?PTR ?VAL ?M ] ] =>
        let Ts := constr:(@nil Type) in
        let Ts := SEP.collectAllTypes_sexpr ltac:(isConst) Ts (P :: nil) in
        let Ts := SEP.collectAllTypes_expr ltac:(isConst) Ts (PTR, (VAL, tt)) in
        let types := eval unfold pre_types in pre_types in
        let types := SEP.extend_all_types Ts types in
        let sexprs := constr:(P :: nil) in
        match SEP.reify_sexprs a b ltac:(isConst) types tt tt sexprs with
          | (?types, ?pcType, ?stateType, ?funcs, ?sfuncs, ?P :: nil) =>
             match SEP.reify_exprs ltac:(isConst) types funcs (PTR, (VAL, tt)) with
              | (?types, ?funcs, ?PTR :: ?VAL :: nil) =>
                 let hyps := constr:(@nil (expr pre_types)) in
                let s := eval simpl in (SEP.hash P) in
                generalize (@symeval_write_word_correct types 1 0 2 3 addr_not_state sfuncs known evaluators
                  hyps PTR VAL (snd s) _ (refl_equal _) CS STN funcs nil nil M _ I (refl_equal _) H)
            end
        end
      end.
      simpl; auto.
    Qed.
*)

  End Tests.
End EvaluatorTests.