Require Import RaftState.
Require Import Raft.
Require Import CommonTheorems.

Section SpecLemmas.

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

  Lemma handleRequestVote_votedFor :
    forall pDst t cid lli llt d d' m,
      handleRequestVote pDst d t cid lli llt = (d', m) ->
      currentTerm d = currentTerm d' ->
      votedFor d = None \/ votedFor d = votedFor d'.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; tuple_inversion; simpl in *; intuition; try discriminate;
    try solve [exfalso; do_bool; omega].
  Qed.

  Lemma handleRequestVote_cases :
    forall h h' t lli llt st st' m,
      handleRequestVote h st t h' lli llt = (st', m) ->
      st' = st \/
      st' = advanceCurrentTerm st t \/
      (st' = {[ advanceCurrentTerm st t with votedFor := Some h']} /\
       (votedFor st = None /\ currentTerm st = t \/ currentTerm st < t)).
  Proof using.
    unfold handleRequestVote.
    intros.
    repeat break_match; repeat find_inversion; intuition.
    - simpl in *. discriminate.
    - unfold advanceCurrentTerm in *.
      break_if; simpl in *; do_bool; intuition.
  Qed.

  Lemma handleRequestVoteReply_term_votedFor_cases :
    forall me st src t v st',
      handleRequestVoteReply me st src t v = st' ->
      (currentTerm st' = currentTerm st /\
       votedFor st' = votedFor st) \/
      (currentTerm st < currentTerm st' /\
       votedFor st' = None).
  Proof using.
    intros.
    unfold handleRequestVoteReply, advanceCurrentTerm in *; repeat break_match;
    subst; do_bool; intuition.
  Qed.

  Lemma handleAppendEntries_same_term_votedFor_preserved :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      currentTerm st' = currentTerm st ->
      votedFor st' = votedFor st.
  Proof using.
    unfold handleAppendEntries, advanceCurrentTerm.
    intros.
    repeat break_match; repeat tuple_inversion; simpl in *;
      do_bool; auto; try omega.
  Qed.

  Lemma handleAppendEntriesReply_same_term_votedFor_preserved :
    forall h st h' t es r st' ms,
      handleAppendEntriesReply h st h' t es r = (st', ms) ->
      currentTerm st' = currentTerm st ->
      votedFor st' = votedFor st.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; repeat tuple_inversion; auto.
  Qed.

  Lemma doGenericServer_currentTerm :
    forall h st os st' ms,
      doGenericServer h st = (os, st', ms) ->
      currentTerm st' = currentTerm st.
  Proof using.
    unfold doGenericServer.
    intros.
    repeat break_match; repeat find_inversion;
    use_applyEntries_spec; subst; simpl in *;
    auto.
  Qed.

  Lemma doLeader_currentTerm :
        forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      currentTerm st' = currentTerm st.
  Proof using.
    intros. unfold doLeader in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleAppendEntriesReply_currentTerm :
    forall h st h' t es r st' ms,
      handleAppendEntriesReply h st h' t es r = (st', ms) ->
      currentTerm st <= currentTerm st'.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; tuple_inversion; do_bool; auto.
  Qed.

  Lemma handleAppendEntries_currentTerm :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      currentTerm st <= currentTerm st'.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; do_bool; auto.
  Qed.

  Lemma tryToBecomeLeader_currentTerm :
    forall h st out st' l,
      tryToBecomeLeader h st = (out, st', l) ->
      currentTerm st <= currentTerm st'.
  Proof using.
    unfold tryToBecomeLeader.
    intros.
    find_inversion. simpl.
    auto with arith.
  Qed.

  Lemma handleTimeout_currentTerm :
    forall h st out st' l,
      handleTimeout h st = (out, st', l) ->
      currentTerm st <= currentTerm st'.
  Proof using.
    unfold handleTimeout.
    intros.
    break_match; eauto using tryToBecomeLeader_currentTerm.
    find_inversion. simpl.
    auto with arith.
  Qed.

  Lemma haveNewEntries_not_empty :
    forall st es,
      haveNewEntries st es = true ->
      es <> [].
  Proof using.
    intros. unfold haveNewEntries, not_empty in *.
    do_bool. intuition. repeat break_match; congruence.
  Qed.

  Lemma advanceCurrentTerm_log :
    forall st t,
      log (advanceCurrentTerm st t) = log st.
  Proof using.
    intros.
    unfold advanceCurrentTerm. break_if; simpl in *; auto.
  Qed.

  Theorem handleAppendEntries_log :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      log st' = log st \/
      (currentTerm st <= t /\ es <> [] /\ pli = 0 /\ log st' = es) \/
      (currentTerm st <= t /\ es <> [] /\ pli <> 0 /\ exists e,
         In e (log st) /\
         eIndex e = pli /\
         eTerm e = plt) /\
      log st' = es ++ (removeAfterIndex (log st) pli).
  Proof using.
    intros. unfold handleAppendEntries in *.
    break_if; [find_inversion; subst; eauto|].
    break_if;
      [do_bool; break_if; find_inversion; subst;
       try find_apply_lem_hyp haveNewEntries_not_empty; intuition; simpl in *;
       eauto using advanceCurrentTerm_log|].
    break_if.
    - break_match; [|find_inversion; subst; eauto].
      break_if; [find_inversion; subst; eauto|].
      find_inversion; subst; simpl in *.
      right. right.
      find_apply_lem_hyp findAtIndex_elim. intuition; do_bool; eauto.
      find_apply_lem_hyp haveNewEntries_not_empty. congruence.
    - repeat break_match; find_inversion; subst; eauto.
      simpl in *. eauto using advanceCurrentTerm_log.
  Qed.

  Theorem handleAppendEntries_log_ind :
    forall {h st t n pli plt es ci st' ps},
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      forall (P : list entry -> Prop),
        P (log st) ->
        (pli = 0 -> P es) ->
        (forall e,
           pli <> 0 ->
           In e (log st) ->
           eIndex e = pli ->
           eTerm e = plt ->
           P (es ++ (removeAfterIndex (log st) pli))) ->
        P (log st').
  Proof using.
    intros.
    find_apply_lem_hyp handleAppendEntries_log.
    intuition; subst; try find_rewrite; auto.
    break_exists. intuition eauto.
  Qed.

  Lemma haveNewEntries_true :
    forall st es,
      haveNewEntries st es = true ->
      (es <> nil /\
       (findAtIndex (log st) (maxIndex es) = None \/
        exists e,
          findAtIndex (log st) (maxIndex es) = Some e /\
          eTerm e <> maxTerm es)).
  Proof using.
    intros.
    unfold haveNewEntries, not_empty in *.
    repeat break_match; do_bool; intuition eauto; try congruence.
    do_bool. eauto.
  Qed.

  Lemma advanceCurrentTerm_commitIndex :
    forall st t,
      commitIndex (advanceCurrentTerm st t) = commitIndex st.
  Proof using.
    intros.
    unfold advanceCurrentTerm. break_if; simpl in *; auto.
  Qed.

  Lemma some_none :
    forall A (x : A),
      Some x = None -> False.
  Proof using.
    congruence.
  Qed.

  Lemma advanceCurrentTerm_term :
    forall st t,
      currentTerm st <= t ->
      currentTerm (advanceCurrentTerm st t) = t.
  Proof using.
    intros. unfold advanceCurrentTerm in *.
    break_if; do_bool; intuition.
  Qed.

  Theorem handleAppendEntries_log_detailed :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      (commitIndex st' = commitIndex st /\ log st' = log st) \/
      (leaderId st' <> None /\
       currentTerm st' = t /\
       commitIndex st' = max (commitIndex st) (min ci (maxIndex es)) /\
       es <> nil /\
       pli = 0 /\ t >= currentTerm st /\ log st' = es /\
       (findAtIndex (log st) (maxIndex es) = None \/
        exists e,
          findAtIndex (log st) (maxIndex es) = Some e /\
          eTerm e <> maxTerm es)) \/
      (leaderId st' <> None /\
       currentTerm st' = t /\
       commitIndex st' = max (commitIndex st)
                             (min ci (maxIndex (es ++ (removeAfterIndex (log st) pli)))) /\
       es <> nil /\
        exists e,
         In e (log st) /\
         eIndex e = pli /\
         eTerm e = plt) /\
      t >= currentTerm st /\
      log st' = es ++ (removeAfterIndex (log st) pli) /\
      (findAtIndex (log st) (maxIndex es) = None \/
       exists e,
         findAtIndex (log st) (maxIndex es) = Some e /\
         eTerm e <> maxTerm es).
  Proof using.
    intros. unfold handleAppendEntries in *.
    break_if; [find_inversion; subst; eauto|].
    break_if;
      [do_bool; break_if; find_inversion; subst;
        try find_apply_lem_hyp haveNewEntries_true;
        simpl in *; intuition eauto using advanceCurrentTerm_log, advanceCurrentTerm_commitIndex, some_none, advanceCurrentTerm_term|].
    simpl in *. intuition eauto using advanceCurrentTerm_log, advanceCurrentTerm_commitIndex.
    break_match; [|find_inversion; subst; eauto].
    break_if; [find_inversion; subst; eauto|].
    break_if; [|find_inversion; subst; eauto using advanceCurrentTerm_log, advanceCurrentTerm_commitIndex].
    find_inversion; subst; simpl in *.
    right. right.
    find_apply_lem_hyp findAtIndex_elim.
    intuition; do_bool; find_apply_lem_hyp haveNewEntries_true;
    intuition eauto using advanceCurrentTerm_term; congruence.
  Qed.

  Lemma advanceCurrentTerm_currentTerm_leaderId :
    forall st t,
      currentTerm st < currentTerm (advanceCurrentTerm st t) \/
      leaderId (advanceCurrentTerm st t) = leaderId st.
  Proof using.
    intros.
    unfold advanceCurrentTerm in *.
    break_if; simpl in *; do_bool; auto.
  Qed.

  Lemma advanceCurrentTerm_currentTerm :
    forall st t,
      currentTerm st <= currentTerm (advanceCurrentTerm st t).
  Proof using.
    intros. unfold advanceCurrentTerm in *.
    break_if; simpl in *; do_bool; omega.
  Qed.

  Lemma handleRequestVote_currentTerm_monotonic :
    forall pDst t cid lli llt d d' m,
      handleRequestVote pDst d t cid lli llt = (d', m) ->
      currentTerm d <= currentTerm d'.
  Proof using.
    intros.
    unfold handleRequestVote in *.
    repeat break_match; find_inversion; subst; auto;
    simpl in *; apply advanceCurrentTerm_currentTerm.
  Qed.

  Lemma handleRequestVote_currentTerm_votedFor :
    forall pDst t cid lli llt d d' m,
      handleRequestVote pDst d t cid lli llt = (d', m) ->
      (currentTerm d < currentTerm d' \/
       (currentTerm d = currentTerm d' /\ votedFor d = None) \/
       (currentTerm d = currentTerm d' /\ votedFor d = votedFor d')).
  Proof using.
    intros.
    find_copy_apply_lem_hyp handleRequestVote_currentTerm_monotonic.
    find_apply_lem_hyp le_lt_or_eq. intuition.
    right. find_apply_lem_hyp handleRequestVote_votedFor; intuition.
  Qed.

  Lemma handleRequestVoteReply_currentTerm' :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      currentTerm st <= currentTerm st'.
  Proof using.
    intros. unfold handleRequestVoteReply in *.
    repeat break_match; subst; do_bool; intuition.
    apply advanceCurrentTerm_currentTerm.
  Qed.

  Lemma handleRequestVote_currentTerm :
    forall st h h' t lli llt st' m,
      handleRequestVote h st t h' lli llt = (st', m) ->
      currentTerm st <= currentTerm st'.
  Proof using.
    intros. unfold handleRequestVote in *.
    repeat break_match; tuple_inversion; do_bool;
      simpl; auto using advanceCurrentTerm_currentTerm.
  Qed.

  Theorem handleAppendEntries_currentTerm_leaderId :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      currentTerm st < currentTerm st' \/
      (currentTerm st <= currentTerm st' /\ (leaderId st' = leaderId st \/ leaderId st' <> None)).
  Proof using.
    intros. unfold handleAppendEntries in *.
    repeat break_match; find_inversion; simpl in *; auto; right;
    intuition eauto using advanceCurrentTerm_currentTerm; right; congruence.
  Qed.

  Lemma handleRequestVote_currentTerm_leaderId :
    forall h st t c li lt st' m,
      handleRequestVote h st t c li lt = (st', m) ->
      currentTerm st < currentTerm st' \/
      (currentTerm st = currentTerm st' /\ leaderId st' = leaderId st).
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat (break_match; try find_inversion; simpl in *; auto);
    do_bool; auto.
  Qed.

  Lemma handleRequestVoteReply_currentTerm_leaderId :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      currentTerm st < currentTerm st' \/
      currentTerm st' = currentTerm st /\
      leaderId st' = leaderId st.
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm in *.
    subst.
    repeat (break_match; try find_inversion; simpl in *; auto);
      do_bool; auto.
  Qed.

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

  Lemma handleClientRequest_log_ind :
    forall {h st client id c out st' ps},
      handleClientRequest h st client id c = (out, st', ps) ->
      forall (P : list entry -> Prop),
        P (log st) ->
        (forall e, eIndex e = S (maxIndex (log st)) ->
                   eTerm e = currentTerm st ->
                   eClient e = client ->
                   eInput e = c ->
                   eId e = id ->
                   type st = Leader ->
                   P (e :: log st)) ->
        P (log st').
  Proof using.
    intros.
    find_apply_lem_hyp handleClientRequest_log.
    intuition; repeat find_rewrite; auto.
    break_exists. intuition. repeat find_rewrite. eauto.
  Qed.

  Lemma handleRequestVote_log :
    forall h st t candidate lli llt st' m,
      handleRequestVote h st t candidate lli llt = (st', m) ->
      log st' = log st.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion; subst; auto.
  Qed.

  Lemma handleTimeout_log_same :
    forall h d out d' l,
      handleTimeout h d = (out, d', l) ->
      log d' = log d.
  Proof using.
    unfold handleTimeout, tryToBecomeLeader.
    intros.
    repeat break_match; repeat find_inversion; auto.
  Qed.

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

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

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

  Theorem handleTimeout_not_is_append_entries :
    forall h st st' ms m,
      handleTimeout h st = (st', ms) ->
      In m ms -> ~ is_append_entries (snd m).
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    break_match; find_inversion; subst; simpl in *; eauto;
    repeat (do_in_map; subst; simpl in *); intuition; break_exists; congruence.
  Qed.

  Lemma handleAppendEntries_type :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleAppendEntries_type_term :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      (type st' = Follower /\ currentTerm st' >= currentTerm st).
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; do_bool; auto.
  Qed.

  Lemma handleAppendEntriesReply_type :
    forall h st h' t es r st' ms,
      handleAppendEntriesReply h st h' t es r = (st', ms) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleAppendEntriesReply_type_term :
    forall h st h' t es r st' ms,
      handleAppendEntriesReply h st h' t es r = (st', ms) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      (type st' = Follower /\ currentTerm st' >= currentTerm st).
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; tuple_inversion; do_bool; auto.
  Qed.

  Lemma handleRequestVote_type :
    forall st h h' t lli llt st' m,
      handleRequestVote h st t h' lli llt = (st', m) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleRequestVote_type_term :
    forall st h h' t lli llt st' m,
      handleRequestVote h st t h' lli llt = (st', m) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      (type st' = Follower /\ currentTerm st' >= currentTerm st).
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; tuple_inversion; do_bool; auto.
  Qed.

  Lemma handleRequestVoteReply_type :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      (type st' = Follower /\ currentTerm st' > currentTerm st) \/
      (type st = Candidate /\ type st' = Leader /\ currentTerm st' = currentTerm st).
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm in *.
    repeat break_match; subst; do_bool; intuition.
  Qed.

  Lemma handleClientRequest_type :
    forall h st client id c out st' l,
      handleClientRequest h st client id c = (out, st', l) ->
      type st' = type st /\ currentTerm st' = currentTerm st.
  Proof using.
    intros. unfold handleClientRequest in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleClientRequest_currentTerm :
    forall h st client id c out st' l,
      handleClientRequest h st client id c = (out, st', l) ->
      currentTerm st' = currentTerm st.
  Proof using.
    intros.
    find_apply_lem_hyp handleClientRequest_type.
    intuition.
  Qed.

  Lemma handleTimeout_type :
    forall h st out st' l,
      handleTimeout h st = (out, st', l) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      type st' = Candidate.
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleTimeout_type_strong :
    forall h st out st' l,
      handleTimeout h st = (out, st', l) ->
      (type st' = type st /\ currentTerm st' = currentTerm st) \/
      (type st' = Candidate /\ currentTerm st' = S (currentTerm st)).
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    repeat break_match; find_inversion; simpl; intuition.
  Qed.

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

  Lemma doLeader_type :
        forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      type st' = type st /\ currentTerm st' = currentTerm st.
  Proof using.
    intros. unfold doLeader in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma doLeader_log :
    forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      log st' = log st.
  Proof using.
    unfold doLeader. intros.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Lemma handleAppendEntriesReply_log :
    forall h st h' t es r st' ms,
      handleAppendEntriesReply h st h' t es r = (st', ms) ->
      log st' = log st.
  Proof using.
    unfold handleAppendEntriesReply, advanceCurrentTerm.
    intros.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Lemma handleRequestVoteReply_log :
    forall h st h' t r st',
      handleRequestVoteReply h st h' t r = st' ->
      log st' = log st.
  Proof using.
    intros.
    eapply handleRequestVoteReply_spec; eauto.
  Qed.

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

  Lemma handleAppendEntriesReply_packets :
    forall h st from t es s st' ps,
      handleAppendEntriesReply h st from t es s = (st', ps) ->
      ps = [].
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; find_inversion; subst; auto.
  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; simpl in *;
    subst; auto.
  Qed.

  Theorem handleAppendEntries_not_append_entries :
    forall h st t n pli plt es ci st' m,
      handleAppendEntries h st t n pli plt es ci = (st', m) ->
      ~ is_append_entries m.
  Proof using.
    intros. unfold handleAppendEntries in *.
    repeat break_match; find_inversion;
    intuition; break_exists; congruence.
  Qed.

  Lemma handleAppendEntries_clientCache:
    forall h st (d : raft_data)
      (m : msg) (t : term) (n : name) (pli : logIndex)
      (plt : term) (es : list entry) (ci : logIndex),
      handleAppendEntries h st t n pli plt es ci = (d, m) ->
      clientCache d = clientCache st.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Lemma handleAppendEntriesReply_clientCache:
    forall h st (d : raft_data)
      (m : list (name * msg)) (t : nat) (es : list entry)
      (res : bool) h',
      handleAppendEntriesReply h st h' t es res = (d, m) ->
      clientCache d = clientCache st.
  Proof using.
    intros.
    unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat (break_match; try find_inversion; simpl in *; auto).
  Qed.

  Lemma advanceCurrentTerm_clientCache :
    forall st t,
      clientCache (advanceCurrentTerm st t) = clientCache st.
  Proof using.
    unfold advanceCurrentTerm. intros.
    break_if; auto.
  Qed.

  Theorem handleTimeout_clientCache :
    forall h st out st' ps,
      handleTimeout h st = (out, st', ps) ->
      clientCache st' = clientCache st.
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    break_match; find_inversion; subst; auto.
  Qed.

  Theorem handleClientRequest_clientCache:
  forall h st client id c out st' ps,
    handleClientRequest h st client id c = (out, st', ps) ->
    clientCache st' = clientCache st.
  Proof using.
    intros. unfold handleClientRequest in *.
    break_match; find_inversion; subst; auto.
  Qed.

  Lemma tryToBecomeLeader_clientCache :
    forall n st out st' ms,
      tryToBecomeLeader n st = (out, st', ms) ->
      clientCache st' = clientCache st.
  Proof using.
    unfold tryToBecomeLeader.
    intros. find_inversion. auto.
  Qed.

  Lemma handleRequestVote_clientCache :
    forall n st t c li lt st' ms,
      handleRequestVote n st t c li lt = (st', ms) ->
      clientCache st' = clientCache st.
  Proof using.
    unfold handleRequestVote.
    intros.
    repeat (break_match; try discriminate; repeat (find_inversion; simpl in *));
      auto using advanceCurrentTerm_clientCache.
  Qed.

  Lemma handleRequestVoteReply_clientCache :
    forall n st src t v,
      clientCache (handleRequestVoteReply n st src t v) = clientCache st.
  Proof using.
    unfold handleRequestVoteReply.
    intros. repeat break_match; simpl; auto using advanceCurrentTerm_clientCache.
  Qed.

  Lemma doLeader_clientCache :
        forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      clientCache st' = clientCache st.
  Proof using.
    intros. unfold doLeader in *.
    repeat break_match; find_inversion; auto.
  Qed.

  Lemma handleAppendEntries_stateMachine:
    forall h st (d : raft_data)
      (m : msg) (t : term) (n : name) (pli : logIndex)
      (plt : term) (es : list entry) (ci : logIndex),
      handleAppendEntries h st t n pli plt es ci = (d, m) ->
      stateMachine d = stateMachine st.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Lemma handleAppendEntriesReply_stateMachine:
    forall h st (d : raft_data)
      (m : list (name * msg)) (t : nat) (es : list entry)
      (res : bool) h',
      handleAppendEntriesReply h st h' t es res = (d, m) ->
      stateMachine d = stateMachine st.
  Proof using.
    intros.
    unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat (break_match; try find_inversion; simpl in *; auto).
  Qed.

  Lemma advanceCurrentTerm_stateMachine :
    forall st t,
      stateMachine (advanceCurrentTerm st t) = stateMachine st.
  Proof using.
    unfold advanceCurrentTerm. intros.
    break_if; auto.
  Qed.

  Theorem handleTimeout_stateMachine :
    forall h st out st' ps,
      handleTimeout h st = (out, st', ps) ->
      stateMachine st' = stateMachine st.
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    break_match; find_inversion; subst; auto.
  Qed.

  Theorem handleClientRequest_stateMachine:
  forall h st client id c out st' ps,
    handleClientRequest h st client id c = (out, st', ps) ->
    stateMachine st' = stateMachine st.
  Proof using.
    intros. unfold handleClientRequest in *.
    break_match; find_inversion; subst; auto.
  Qed.

  Lemma tryToBecomeLeader_stateMachine :
    forall n st out st' ms,
      tryToBecomeLeader n st = (out, st', ms) ->
      stateMachine st' = stateMachine st.
  Proof using.
    unfold tryToBecomeLeader.
    intros. find_inversion. auto.
  Qed.

  Lemma handleRequestVote_stateMachine :
    forall n st t c li lt st' ms,
      handleRequestVote n st t c li lt = (st', ms) ->
      stateMachine st' = stateMachine st.
  Proof using.
    unfold handleRequestVote.
    intros.
    repeat (break_match; try discriminate; repeat (find_inversion; simpl in *));
      auto using advanceCurrentTerm_stateMachine.
  Qed.

  Lemma handleRequestVoteReply_stateMachine :
    forall n st src t v,
      stateMachine (handleRequestVoteReply n st src t v) = stateMachine st.
  Proof using.
    unfold handleRequestVoteReply.
    intros. repeat break_match; simpl; auto using advanceCurrentTerm_stateMachine.
  Qed.

  Lemma doLeader_stateMachine :
        forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      stateMachine st' = stateMachine st.
  Proof using.
    intros. unfold doLeader in *.
    repeat break_match; find_inversion; auto.
  Qed.

(* matchIndex *)
  Definition matchIndex_preserved st st' :=
    type st' = Leader ->
    (type st = Leader /\ matchIndex st' = matchIndex st /\ log st' = log st).

  Arguments matchIndex_preserved / _ _.

  Definition matchIndex_preserved_except_at_host h st st' :=
    type st' = Leader ->
    (type st = Leader /\
     forall h',
       h <> h' ->
       (assoc_default name_eq_dec (matchIndex st') h' 0) = (assoc_default name_eq_dec (matchIndex st) h') 0).

  Arguments matchIndex_preserved_except_at_host / _ _ _.

  Lemma handleAppendEntries_matchIndex_preserved:
    forall h st (d : raft_data)
      (m : msg) (t : term) (n : name) (pli : logIndex)
      (plt : term) (es : list entry) (ci : logIndex),
      handleAppendEntries h st t n pli plt es ci = (d, m) ->
      matchIndex_preserved st d.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto; intros; congruence.
  Qed.

  Lemma handleAppendEntriesReply_matchIndex_leader_preserved:
    forall h st (d : raft_data)
      (m : list (name * msg)) (t : nat) (es : list entry)
      (res : bool) h',
      handleAppendEntriesReply h st h' t es res = (d, m) ->
      matchIndex_preserved_except_at_host h' st d.
  Proof using.
    intros.
    unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat (break_match; try find_inversion; simpl in *; auto).
    intros. intuition.
    unfold assoc_default.
    repeat break_match; rewrite get_set_diff in *; repeat find_rewrite; congruence.
  Qed.

  Lemma advanceCurrentTerm_matchIndex_preserved :
    forall st t,
      matchIndex_preserved st (advanceCurrentTerm st t).
  Proof using.
    unfold advanceCurrentTerm. intros.
    break_if; simpl in *; auto; congruence.
  Qed.

  Theorem handleTimeout_matchIndex_preserved :
    forall h st out st' ps,
      handleTimeout h st = (out, st', ps) ->
      matchIndex_preserved st st'.
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    break_match; simpl in *; find_inversion; subst; simpl in *; auto; congruence.
  Qed.

  Theorem handleClientRequest_matchIndex_maxIndex:
  forall h st client id c out st' ps,
    handleClientRequest h st client id c = (out, st', ps) ->
    (maxIndex (log st') = maxIndex (log st) /\
     matchIndex st' = matchIndex st) \/
    (assoc_default name_eq_dec (matchIndex st') h 0) = maxIndex (log st').
  Proof using.
    intros. unfold handleClientRequest in *.
    break_match; find_inversion; subst; simpl in *; auto.
    unfold assoc_default. break_match;
    rewrite get_set_same in *; try congruence; find_inversion; auto.
  Qed.

  Theorem handleClientRequest_matchIndex :
    forall h st client id c out st' ps,
      handleClientRequest h st client id c = (out, st', ps) ->
      (maxIndex (log st') = maxIndex (log st) /\
       matchIndex st' = matchIndex st) \/
      matchIndex st' = assoc_set name_eq_dec (matchIndex st) h (maxIndex (log st')) /\
      maxIndex (log st') = S (maxIndex (log st)).
  Proof using.
    unfold handleClientRequest.
    intros.
    repeat break_match; repeat find_inversion; auto.
  Qed.

  Lemma tryToBecomeLeader_matchIndex_preserved :
    forall n st out st' ms,
      tryToBecomeLeader n st = (out, st', ms) ->
      matchIndex_preserved st st'.
  Proof using.
    unfold tryToBecomeLeader.
    intros. find_inversion. simpl; intros; auto; congruence.
  Qed.

  Lemma handleRequestVote_matchIndex_preserved :
    forall n st t c li lt st' ms,
      handleRequestVote n st t c li lt = (st', ms) ->
      matchIndex_preserved st st'.
  Proof using.
    unfold handleRequestVote, advanceCurrentTerm.
    intros.
    repeat (break_match; try discriminate; repeat (find_inversion; simpl in *)); auto.
  Qed.

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

  Lemma handleRequestVoteReply_matchIndex :
    forall n st src t v,
      type (handleRequestVoteReply n st src t v) = Leader ->
      (type st = Leader /\ matchIndex (handleRequestVoteReply n st src t v) = matchIndex st) \/
      (assoc_default name_eq_dec (matchIndex (handleRequestVoteReply n st src t v)) n 0
       = maxIndex (log (handleRequestVoteReply n st src t v))).
  Proof using.
    unfold handleRequestVoteReply.
    intros.
    repeat break_match; simpl; auto using advanceCurrentTerm_matchIndex_preserved;
    simpl in *; try congruence.
    unfold assoc_default. simpl.
    repeat break_match; simpl in *; try congruence; find_inversion; auto.
  Qed.

  Lemma doLeader_matchIndex_preserved :
        forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      matchIndex_preserved st st'.
  Proof using.
    intros. unfold doLeader in *. simpl; intros.
    repeat break_match; find_inversion; auto; congruence.
  Qed.

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

  Theorem handleClientRequest_no_append_entries :
    forall h st client id c out st' ps m,
      handleClientRequest h st client id c = (out, st', ps) ->
      In m ps ->
      ~ is_append_entries (snd m).
  Proof using.
    intros. unfold handleClientRequest in *.
    repeat break_match; find_inversion; subst; auto;
    intuition; break_exists; congruence.
  Qed.

  Theorem handleClientRequest_packets :
    forall h st client id c out st' ps,
      handleClientRequest h st client id c = (out, st', ps) ->
      ps = [].
  Proof using.
    intros. unfold handleClientRequest in *.
    repeat break_match; repeat find_inversion; auto.
  Qed.

  Lemma handleTimeout_packets :
    forall h d out d' ps m,
      handleTimeout h d = (out, d', ps) ->
      In m ps ->
      ~ is_append_entries (snd m).
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    repeat break_match; find_inversion; subst; auto;
    intuition; break_exists;
    do_in_map; subst; simpl in *; congruence.
  Qed.

  Lemma doLeader_messages :
    forall st h os st' ms m t n pli plt es ci,
      doLeader st h = (os, st', ms) ->
      In m ms ->
      snd m = AppendEntries t n pli plt es ci ->
      t = currentTerm st /\
      log st' = log st /\
      type st = Leader /\
      ((plt = 0) \/
       ((exists e, findAtIndex (log st) pli = Some e /\
              eTerm e = plt))).
  Proof using.
    intros. unfold doLeader, advanceCommitIndex in *.
    break_match; try solve [find_inversion; simpl in *; intuition].
    break_if; try solve [find_inversion; simpl in *; intuition].
    find_inversion. simpl. do_in_map. subst.
    simpl in *. find_inversion. intuition.
    match goal with
      | |- context [pred ?x] =>
        remember (pred x) as index
    end. break_match; simpl in *.
    - right. eauto.
    - destruct index; intuition.
  Qed.

  Lemma doLeader_message_entries :
    forall st h os st' ms m t n pli plt es ci e,
      doLeader st h = (os, st', ms) ->
      In m ms ->
      snd m = AppendEntries t n pli plt es ci ->
      In e es ->
      In e (log st).
  Proof using.
    intros. unfold doLeader, advanceCommitIndex in *.
    break_match; try solve [find_inversion; simpl in *; intuition].
    break_if; try solve [find_inversion; simpl in *; intuition].
    find_inversion. simpl. do_in_map. subst.
    simpl in *. find_inversion.
    eauto using findGtIndex_in.
  Qed.

  Theorem handleAppendEntries_log_term_type :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      (log st' = log st /\ currentTerm st' = currentTerm st /\ type st' = type st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleAppendEntries_votesReceived :
    forall h st t n pli plt es ci st' ps,
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      votesReceived st' = votesReceived st.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleAppendEntriesReply_log_term_type :
    forall h st n t es r st' ps,
      handleAppendEntriesReply h st n t es r = (st', ps) ->
      (log st' = log st /\ currentTerm st' = currentTerm st /\ type st' = type st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleAppendEntriesReply_votesReceived :
    forall h st n t es r st' ps,
      handleAppendEntriesReply h st n t es r = (st', ps) ->
      votesReceived st' = votesReceived st.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleRequestVote_log_term_type :
    forall h st t c li lt st' m,
      handleRequestVote h st t c li lt = (st', m) ->
      (log st' = log st /\ currentTerm st' = currentTerm st /\ type st' = type st) \/
      type st' = Follower.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleRequestVote_votesReceived :
    forall h st t c li lt st' m,
      handleRequestVote h st t c li lt = (st', m) ->
      votesReceived st' = votesReceived st.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleRequestVoteReply_log_term_type :
    forall h st t h' r st',
      type st' = Candidate ->
      handleRequestVoteReply h st h' t r = st' ->
      (log st' = log st /\ currentTerm st' = currentTerm st /\ type st' = type st).
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm in *.
    repeat break_match; subst; simpl in *; auto; congruence.
  Qed.

  Theorem handleRequestVoteReply_votesReceived :
    forall h st t h' r v,
      In v (votesReceived (handleRequestVoteReply h st h' t r)) ->
      In v (votesReceived st) \/
      (r = true /\ v = h' /\ currentTerm (handleRequestVoteReply h st h' t r) = t).
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm in *.
    repeat break_match; subst; simpl in *; do_bool; intuition.
  Qed.

  Theorem handleTimeout_log_term_type :
    forall h st out st' ps,
      handleTimeout h st = (out, st', ps) ->
      (log st' = log st /\ currentTerm st' = currentTerm st /\ type st' = type st) \/
      currentTerm st' = S (currentTerm st).
  Proof using.
    intros. unfold handleTimeout, tryToBecomeLeader in *.
    repeat break_match; find_inversion; simpl in *; intuition.
  Qed.

  Lemma handleClientRequest_candidate :
    forall h st client id c os st' m,
      handleClientRequest h st client id c = (os, st', m) ->
      type st' = Candidate ->
      st' = st.
  Proof using.
    intros.
    unfold handleClientRequest in *.
    repeat break_match; find_inversion; simpl in *; congruence.
  Qed.

  Lemma doLeader_candidate :
    forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      type st' = Candidate ->
      st' = st.
  Proof using.
    unfold doLeader, advanceCommitIndex in *.
    intros.
    repeat break_match; find_inversion; simpl in *; congruence.
  Qed.

  Lemma doLeader_term_votedFor :
    forall st h os st' ms,
      doLeader st h = (os, st', ms) ->
      currentTerm st' = currentTerm st /\
      votedFor st' = votedFor st.
  Proof using.
    unfold doLeader, advanceCommitIndex in *.
    intros.
    repeat break_match; find_inversion; simpl in *; intuition.
  Qed.

  Lemma doGenericServer_log_type_term_votesReceived :
    forall h st os st' ps,
      doGenericServer h st = (os, st', ps) ->
      log st' = log st /\
      type st' = type st /\
      currentTerm st' = currentTerm st /\
      votesReceived st' = votesReceived st /\
      votedFor st' = votedFor st.
  Proof using.
    intros. unfold doGenericServer in *.
    repeat break_match; find_inversion;
    use_applyEntries_spec; simpl in *;
    subst; auto.
  Qed.

  Lemma handleClientRequest_term_votedFor :
    forall h st client id c os st' m,
      handleClientRequest h st client id c = (os, st', m) ->
      type st' = type st /\
      currentTerm st' = currentTerm st /\
      votedFor st' = votedFor st.
  Proof using.
    intros.
    unfold handleClientRequest in *.
    repeat break_match; find_inversion; simpl in *; intuition.
  Qed.

  Theorem handleAppendEntries_term_votedFor :
    forall h st t n pli plt es ci st' ps h',
      handleAppendEntries h st t n pli plt es ci = (st', ps) ->
      votedFor st' = Some h' ->
      currentTerm st' = currentTerm st /\ votedFor st' = votedFor st.
  Proof using.
    intros. unfold handleAppendEntries, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto; congruence.
  Qed.

  Theorem handleAppendEntriesReply_term_votedFor :
    forall h st n t es r st' ps h',
      handleAppendEntriesReply h st n t es r = (st', ps) ->
      votedFor st' = Some h' ->
      currentTerm st' = currentTerm st /\ votedFor st' = votedFor st.
  Proof using.
    intros. unfold handleAppendEntriesReply, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; auto.
  Qed.

  Theorem handleRequestVoteReply_term_votedFor :
    forall h st t h' h'' r st',
      handleRequestVoteReply h st h' t r = st' ->
      votedFor st' = Some h'' ->
      currentTerm st' = currentTerm st /\ votedFor st' = votedFor st.
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm in *.
    repeat break_match; subst; simpl in *; auto; congruence.
  Qed.

  Lemma handleRequestVote_reply_true:
    forall h h' st t lli llt st' t',
      handleRequestVote h st t h' lli llt =
      (st', RequestVoteReply t' true) ->
      votedFor st' = Some h' /\
      currentTerm st' = t'.
  Proof using.
    intros. unfold handleRequestVote, advanceCurrentTerm in *.
    repeat break_match; find_inversion; simpl in *; intuition.
  Qed.

  Lemma handleTimeout_messages:
    forall (out : list raft_output)
      (st : raft_data) (l : list (name * msg)) h
      (mi : logIndex) (mt : term) m st' t n,
      handleTimeout h st = (out, st', l) ->
      In m l ->
      snd m = RequestVote t n mi mt ->
      maxIndex (log st') = mi /\ maxTerm (log st') = mt /\ t = currentTerm st'.
  Proof using.
    intros.
    unfold handleTimeout, tryToBecomeLeader in *.
    repeat break_match; find_inversion; simpl in *; intuition;
    do_in_map; subst; simpl in *; find_inversion; auto.
  Qed.

  Lemma handleRequestVoteReply_currentTerm :
    forall h st h' t r x,
      x <= currentTerm st ->
      x <= currentTerm (handleRequestVoteReply h st h' t r).
  Proof using.
    intros. unfold handleRequestVoteReply, advanceCurrentTerm.
    repeat break_match; subst; simpl in *; auto; try omega.
    do_bool. omega.
  Qed.

  Lemma handleRequestVote_reply_true':
  forall (h : name)
    (h' : fin N)
    (st : RaftState.raft_data term name entry logIndex serverType data output)
    (t lli llt : nat) (st' : raft_data) (t' : term),
    handleRequestVote h st t h' lli llt = (st', RequestVoteReply t' true) ->
    t' = t /\ currentTerm st' = t.
  Proof using.
    unfold handleRequestVote, advanceCurrentTerm in *.
    intros.
    repeat break_match; find_inversion; simpl in *; auto; try congruence;
    do_bool; try omega; eauto using le_antisym.
  Qed.

End SpecLemmas.


(** * Linearize: Place all and only operations in let binders *)
Require Import Crypto.Reflection.Syntax.
Require Import Crypto.Reflection.Wf.
Require Import Crypto.Reflection.Relations.
Require Import Crypto.Reflection.LinearizeWf.
Require Import Crypto.Reflection.InterpProofs.
Require Import Crypto.Reflection.Linearize.
Require Import Crypto.Util.Tactics Crypto.Util.Sigma Crypto.Util.Prod.

Local Open Scope ctype_scope.
Section language.
  Context (base_type_code : Type).
  Context (interp_base_type : base_type_code -> Type).
  Context (op : flat_type base_type_code -> flat_type base_type_code -> Type).
  Context (interp_op : forall src dst, op src dst -> interp_flat_type interp_base_type src -> interp_flat_type interp_base_type dst).

  Local Notation flat_type := (flat_type base_type_code).
  Local Notation type := (type base_type_code).
  Local Notation interp_type := (interp_type interp_base_type).
  Local Notation interp_flat_type := (interp_flat_type interp_base_type).
  Local Notation exprf := (@exprf base_type_code op).
  Local Notation expr := (@expr base_type_code op).
  Local Notation Expr := (@Expr base_type_code op).
  Local Notation wff := (@wff base_type_code op).
  Local Notation wf := (@wf base_type_code op).

  Local Hint Extern 1 => eapply interpf_SmartVarVarf.

  Local Ltac t_fin :=
    repeat match goal with
           | _ => reflexivity
           | _ => progress unfold LetIn.Let_In
           | _ => progress simpl in *
           | _ => progress intros
           | _ => progress inversion_sigma
           | _ => progress inversion_prod
           | _ => solve [ intuition eauto ]
           | _ => apply (f_equal (interp_op _ _ _))
           | _ => apply (f_equal2 (@pair _ _))
           | _ => progress specialize_by assumption
           | _ => progress subst
           | [ H : context[List.In _ (_ ++ _)] |- _ ] => setoid_rewrite List.in_app_iff in H
           | [ H : or _ _ |- _ ] => destruct H
           | _ => progress break_match
           | _ => rewrite <- !surjective_pairing
           | [ H : ?x = _, H' : context[?x] |- _ ] => rewrite H in H'
           | [ H : _ |- _ ] => apply H
           | [ H : _ |- _ ] => rewrite H
           end.

  Lemma interpf_under_letsf {t tC} (ex : exprf t) (eC : _ -> exprf tC)
    : interpf interp_op (under_letsf ex eC) = let x := interpf interp_op ex in interpf interp_op (eC x).
  Proof.
    clear.
    induction ex; t_fin.
  Qed.

  Lemma interpf_linearizef {t} e
    : interpf interp_op (linearizef (t:=t) e) = interpf interp_op e.
  Proof.
    clear.
    induction e;
      repeat first [ progress rewrite ?interpf_under_letsf, ?interpf_SmartVarf
                   | progress simpl
                   | t_fin ].
  Qed.

  Local Hint Resolve interpf_linearizef.

  Lemma interp_linearize {t} e
    : forall x, interp interp_op (linearize (t:=t) e) x = interp interp_op e x.
  Proof.
    induction e; simpl; eauto.
  Qed.

  Lemma InterpLinearize {t} (e : Expr t)
    : forall x, Interp interp_op (Linearize e) x = Interp interp_op e x.
  Proof.
    unfold Interp, Linearize.
    eapply interp_linearize.
  Qed.
End language.

Hint Rewrite @interpf_under_letsf : reflective_interp.
Hint Rewrite @InterpLinearize @interp_linearize @interpf_linearizef using solve [ eassumption | eauto with wf ] : reflective_interp.


(** %
\begin{empheq}[box=\fbox]{align*}
x , f \in \mathit{Var} &\hspace*{10pt}& \textit{Value, Function Variables}\\
l \in \mathit{RVar}  && \textit{Static Region Labels}\\
a@l \in \mathit{Addr} && \textit{Run-time Addresses}
\end{empheq}
% *)

(* begin hide *)
Require Import Ascii String.
Require Import Coq.Structures.OrderedTypeEx.
Require Import Coq.Structures.OrderedType.
Require Import Coq.Structures.DecidableTypeEx.
Require Import Coq.ZArith.Znat.
Require Import Coq.Arith.Peano_dec.

Module Type IndexedType.
  Variable t: Type.
  Variable index: t -> nat.
  Hypothesis index_inj: forall (x y: t), index x = index y <-> x = y.
  Variable eq: forall (x y: t), {x = y} + {x <> y}.
End IndexedType.

(* end hide *)
Module IndexedAscii <: IndexedType.

Definition t := ascii.
Definition index := nat_of_ascii.
Definition eq := ascii_dec.
Hypothesis index_inj: forall (x y: t), index x = index y <-> x = y.

End IndexedAscii.

(* begin hide *)

Module OrderedAscii(A: IndexedType) <: OrderedType.

Definition t := A.t.
Definition eq (x y: t) := x = y.
Definition lt (x y: t) := lt (A.index x) (A.index y).

Lemma eq_refl : forall x : t, eq x x.
Proof (@refl_equal t).
Lemma eq_sym : forall x y : t, eq x y -> eq y x.
Proof (@sym_equal t).
Lemma eq_trans : forall x y z : t, eq x y -> eq y z -> eq x z.
Proof (@trans_equal t).

Lemma lt_trans : forall x y z : t, lt x y -> lt y z -> lt x z.
Proof.
  unfold lt; intros. rewrite <- H in H0. exact H0.
Qed.

Lemma lt_not_eq : forall x y : t, lt x y -> ~ eq x y.
Proof.
  unfold lt; unfold eq; intros.
  red. intro. subst y. generalize dependent H. apply lt_irrefl.
Qed.

Lemma compare : forall x y : t, Compare lt eq x y.
Proof.
  intros.
  case_eq ( nat_compare (A.index x) (A.index y)); intro.
  - apply EQ. apply nat_compare_eq in H. apply A.index_inj. assumption.
  - apply LT. apply nat_compare_lt in H. unfold lt. assumption.
  - apply GT. apply nat_compare_gt in H. unfold lt. intuition.
Qed.

Lemma eq_dec : forall x y, { eq x y } + { ~ eq x y }.
Proof.
  intros. case_eq (nat_compare (A.index x) (A.index y)); intros.
  - left. apply nat_compare_eq in H. apply A.index_inj. assumption.
  - right. red. intro. inversion H0. apply nat_compare_lt in H. apply A.index_inj in H1.
    intuition.
  - right. red. intro. inversion H0. apply nat_compare_gt in H. apply A.index_inj in H1.
    intuition.
Defined.

End OrderedAscii.
(* end hide *)

(** printing AsciiVars $\mathit{Var}$ *)
Module AsciiVars := OrderedAscii (IndexedAscii).

Module RegionVars := PairOrderedType Nat_as_OT Nat_as_OT.


Require Import List.
Require Import String.
Require Import ZArith.

Open Scope list_scope.
Open Scope string_scope.
Open Scope Z_scope.

Require Import ImpSyntax.

(** Tests *)

Definition pred_of_dec {A B}
  (f : forall a1 a2, {B a1 a2} + {~ B a1 a2})
  (a1 a2 : A) : bool :=
  if f a1 a2 then true else false.

Definition val_eq_dec :
  forall v1 v2 : val,
    {v1 = v2} + {v1 <> v2}.
Proof.
  decide equality.
  - apply Bool.bool_dec.
  - apply Z.eq_dec.
  - apply String.string_dec.
  - apply Z.eq_dec.
Defined.

Definition imp_eq := pred_of_dec val_eq_dec.
Definition imp_lt := pred_of_dec Z_lt_dec.
Definition imp_le := pred_of_dec Z_le_dec.

(** Stores *)

Definition store_0 : store :=
  nil.

Fixpoint update (s : store)
  (x : string) (v : val) : store :=
  match s with
  | nil => (x, v) :: nil
  | (x', v') :: rest =>
      if string_dec x x' then
        (x, v) :: rest
      else
        (x', v') :: update rest x v
  end.

Fixpoint updates (s : store)
  (xs : list string) (vs : list val) : option store :=
  match xs, vs with
  | nil, nil =>
      Some s
  | x :: xs', v :: vs' =>
      updates (update s x v) xs' vs'
  | _, _ => None
  end.

Fixpoint lkup (s : store)
  (x : string) : option val :=
  match s with
  | nil => None
  | (x', v) :: rest =>
      if string_dec x x' then
        Some v
      else
        lkup rest x
  end.

(** Heaps *)

Definition heap_0 : heap :=
  nil.

Fixpoint copy (n : nat) (v : val) : list val :=
  match n with
  | O => nil
  | S m => v :: copy m v
  end.

Definition alloc (h : heap)
  (i : Z) (v : val) : heap :=
  List.app h (Vint i :: copy (Z.to_nat i) v).

Fixpoint read (h : heap)
  (i : Z) : option val :=
  match h, i with
  | nil, _ => None
  | v :: vs, 0 => Some v
  | v :: vs, _ => read vs (Zpred i)
  end.

Fixpoint write (h : heap)
  (i : Z) (v : val) : option heap :=
  match h, i with
  | nil, _ => None
  | x :: xs, 0 => Some (v :: xs)
  | x :: xs, _ =>
      match write xs (Zpred i) v with
      | Some h' => Some (x :: h')
      | None => None
      end
  end.

(** Environments *)

Definition env : Type :=
  list func.

Fixpoint locate (e : env)
  (x : string) : option func :=
  match e with
  | nil => None
  | f :: rest =>
      match f with Func x' _ _ _ =>
        if string_dec x x' then
          Some f
        else
          locate rest x
      end
  end.

(** External Calls *)

Inductive extcall_spec :
  string -> list val -> heap ->
  val -> heap -> Prop :=
| print_spec :
    forall v h v',
      extcall_spec
        "print_val" (v :: nil) h
        v' h
| flush_spec :
    forall h v',
      extcall_spec
        "flush" nil h
        v' h
| sleep_spec :
    forall i h v',
      extcall_spec
        "sleep" (Vint i :: nil) h
        v' h
| read_bool_spec :
    forall h b,
      extcall_spec
        "read_bool" nil h
        (Vbool b) h
| read_int_spec :
    forall h i,
      extcall_spec
        "read_int" nil h
        (Vint i) h
| read_str_spec :
    forall h cs,
      extcall_spec
        "read_str" nil h
        (Vstr cs) h.

Definition extcall_args_ok
  (f : string) (vs : list val) (h : heap) : bool :=
  match f, vs with
  | "print_val", v :: nil => true
  | "flush", nil => true
  | "sleep", Vint i :: nil => true
  | "read_bool", nil => true
  | "read_int", nil => true
  | "read_str", nil => true
  | _, _ => false
  end.

Axiom extcall :
  string -> list val -> heap ->
  val * heap.

Axiom extcall_ok :
  forall f vs h v' h',
    extcall f vs h = (v', h') ->
    extcall_spec f vs h v' h'.

(** Common *)

Fixpoint zlen (h : heap) : Z :=
  match h with
  | nil => 0
  | _ :: rest => 1 + zlen rest
  end.

Definition isNop (p : stmt) :
  {p = Snop} + {p <> Snop}.
Proof.
  destruct p; auto;
    right; congruence.
Qed.

Definition store_eq_dec (s1 s2 : store) :
  {s1 = s2} + {s1 <> s2}.
Proof.
  decide equality.
  decide equality.
  - apply val_eq_dec.
  - apply string_dec.
Qed.

Definition expr_eq_dec (e1 e2 : expr) :
  {e1 = e2} + {e1 <> e2}.
Proof.
  decide equality.
  - apply val_eq_dec.
  - apply string_dec.
  - decide equality.
  - decide equality.
Qed.

Fixpoint size_s (p : stmt) : nat :=
  match p with
  | Snop
  | Sset _ _
  | Salloc _ _ _
  | Swrite _ _ _
  | Scall _ _ _ => O
  | Sifelse _ p1 p2 => S (size_s p1 + size_s p2)
  | Swhile _ p1 => S (size_s p1)
  | Sseq p1 p2 => S (size_s p1 + size_s p2)
  | Sincall p1 _ _ _ => S (size_s p1)
  end%nat.

Inductive result : Type :=
| Done : heap -> val -> result
| Timeout : store -> heap -> stmt -> expr -> result
| Stuck : store -> heap -> stmt -> expr -> result.


(*

  Copyright 2014 Cornell University
  Copyright 2015 Cornell University
  Copyright 2016 Cornell University
  Copyright 2017 Cornell University
  Copyright 2018 Cornell University

  This file is part of VPrl (the Verified Nuprl project).

  VPrl is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  VPrl is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with VPrl.  If not, see <http://www.gnu.org/licenses/>.

  Websites: http://nuprl.org/html/verification/
            http://nuprl.org/html/Nuprl2Coq
            https://github.com/vrahli/NuprlInCoq

  Authors: Vincent Rahli

 *)

Require Export nuprl_props.
Require Export choice.
Require Export cvterm.

Hint Resolve computes_to_valc_refl : slow.
Hint Resolve iscvalue_mkc_disect : slow.

Lemma tequality_disect {p} :
  forall lib (A1 A2 : @CTerm p) v1 v2 B1 B2,
    tequality lib (mkc_disect A1 v1 B1)
              (mkc_disect A2 v2 B2)
    <=>
    (tequality lib A1 A2
     # forall a a', equality lib a a' A1 -> tequality lib (substc a v1 B1) (substc a' v2 B2)).
Proof.
  intros.
  sp_iff Case.

  - Case "->".
    intros teq.
    unfold tequality, nuprl in teq; exrepnd.
    inversion teq0; subst; try not_univ.

    one_dest_per_fam eqa feqb A3 A4 v3 v4 B3 B4 c1 c2 tsa tsb eqt.
    computes_to_value_isvalue.
    allfold (@nuprl p lib).
    dands.

    unfold tequality.
    exists eqa; sp.

    introv e.
    unfold tequality.
    unfold equality in e; exrepnd.
    generalize (nuprl_uniquely_valued lib A3 eqa eq0);
      intro k; repeat (dest_imp k hyp); try (complete (allapply @nuprl_refl; sp)).
    rw <- k in e0.
    generalize (tsb a a' e0); intro n.
    exists (feqb a a' e0); sp.

  - Case "<-".
    introv eqs.
    destruct eqs as [teqa teqb].
    unfold tequality in teqa; exrepnd.
    rename eq into eqa.

    generalize (choice_teq lib A1 v1 B1 v2 B2 teqb); intro n; exrepnd.

    exists (fun t1 t2 =>
              exists e, f t1 t2 (eq_equality2 lib t1 t2 A1 A2 eqa e teqa0) t1 t2).
    apply CL_disect; fold (@nuprl p lib).
    unfold per_disect.
    exists eqa.

    exists (fun a1 a2 e => f a1 a2 (eq_equality2 lib a1 a2 A1 A2 eqa e teqa0)); sp.

    exists A1 A2 v1 v2 B1 B2; sp; spcast; eauto 3 with slow.
Qed.

Lemma equality_disect {p} :
  forall lib (A1 A2 : @CTerm p) v1 v2 B1 B2 i,
    equality lib (mkc_disect A1 v1 B1)
             (mkc_disect A2 v2 B2)
             (mkc_uni i)
    <=>
    (equality lib A1 A2 (mkc_uni i)
     # forall a a',
         equality lib a a' A1
         -> equality lib (substc a v1 B1) (substc a' v2 B2) (mkc_uni i)).
Proof.
  introv.
  sp_iff Case.

  - Case "->".
    intros teq.
    unfold equality, nuprl in teq; exrepnd.
    inversion teq1; subst; try not_univ.
    duniv j h.
    allrw @univi_exists_iff; exrepd.
    computes_to_value_isvalue; GC.
    discover; exrepnd.
    rename eqa into eqi.
    ioneclose; subst; try not_univ.

    one_dest_per_fam eqa feqb A3 A4 v3 v4 B3 B4 c1 c2 tsa tsb eqt.
    computes_to_value_isvalue; GC.
    dands.

    exists eq; sp.
    allrw.
    exists eqa; sp.

    introv e.
    exists eq; sp.
    allfold (@nuprli p lib j0).
    allrw.
    unfold equality in e; exrepnd.
    generalize (nuprl_uniquely_valued lib A3 eqa eq0);
      intro k; repeat (dest_imp k hyp);
      try (complete (apply nuprli_implies_nuprl with (i := j0); sp;
                     allapply @nuprli_refl; sp)).
    rw <- k in e0.
    generalize (tsb a a' e0); intro n.
    exists (feqb a a' e0); sp.

  - Case "<-".
    intro eqs.
    destruct eqs as [eqa eqb].
    unfold equality in eqa; exrepnd.
    inversion eqa1; subst; try not_univ.
    duniv j h.
    allrw @univi_exists_iff; exrepd.
    computes_to_value_isvalue; GC.
    discover; exrepnd.
    allfold (@nuprli p lib j0).

    exists eq; sp.
    allrw.

    generalize (choice_teqi lib j0 A1 v1 B1 v2 B2 eqb); intro n; exrepnd.

    exists (fun t1 t2 =>
              exists e, f t1 t2 (eq_equality3 lib t1 t2 A1 A2 eqa j0 e h0) t1 t2).
    apply CL_disect; fold (@nuprli p lib j0).
    unfold per_disect.
    exists eqa.

    exists (fun a1 a2 e => f a1 a2 (eq_equality3 lib a1 a2 A1 A2 eqa j0 e h0)); sp.

    exists A1 A2 v1 v2 B1 B2; sp; spcast; eauto 3 with slow.
Qed.

Lemma equality_in_disect {p} :
  forall lib (t u : @CTerm p) A v B,
    equality lib t u (mkc_disect A v B)
    <=>
    (type lib A
     # (forall a a', equality lib a a' A -> tequality lib (substc a v B) (substc a' v B))
     # equality lib t u A
     # equality lib t u (substc t v B)).
Proof.
  sp; sp_iff Case; introv e.

  - Case "->".
    unfold equality in e; exrepd.
    inversion n; subst; try not_univ.
    one_dest_per_fam eqa feqb A1 A2 v1 v2 B1 B2 c1 c2 tsa tsb eqt.
    computes_to_value_isvalue.
    allfold (@nuprl p lib).
    dands.

    { exists eqa; sp. }

    { introv ea.
      unfold equality in ea; exrepnd.
      assert (eq_term_equals eq0 eqa) as eqta
          by (apply (nuprl_uniquely_valued lib) with (t := A1); sp).
      unfold eq_term_equals in eqta.
      discover.
      generalize (tsb a a' h); sp.
      exists (feqb a a' h); sp. }

    { discover; exrepnd.
      eapply eq_equality1;[|eauto]; auto. }

    { discover; exrepnd.
      eapply eq_equality1;[exact h0|].
      pose proof (tsb t u e0) as tsb.
      apply nuprl_refl in tsb; auto. }

  - Case "<-".
    repnd.
    unfold equality.
    unfold type, tequality in e0; exrepnd.
    rename eq into eqa.

    pose proof (choice_teq lib A v B v B) as q.
    repeat (autodimp q hyp);[]; exrepnd.

    exists (fun t1 t2 =>
              exists e, f t1 t2 (eq_equality1 lib t1 t2 A eqa e e3) t1 t2).
    split.

    {
      apply CL_disect; fold @nuprl; exists eqa.

      exists (fun a1 a2 e => f a1 a2 (eq_equality1 lib a1 a2 A eqa e e3)); sp.

      exists A A v v B B; sp; spcast; eauto 3 with slow;[].
      fold (nuprl lib).
      apply q0.
    }

    pose proof (equality_eq lib A t u eqa) as w; autodimp w hyp.
    apply w in e2 as ea.
    exists ea.
    pose proof (q0 t u (eq_equality1 lib t u A eqa ea e3)) as q0.
    pose proof (equality_eq1 lib (substc t v B) (substc u v B) t u (f t u (eq_equality1 lib t u A eqa ea e3))) as z.
    apply z; auto.
Qed.


(** * LogRel.LogicalRelation.Reflexivity: reflexivity of the logical relation. *)
From LogRel.AutoSubst Require Import core unscoped Ast Extra.
From LogRel Require Import Utils BasicAst Context NormalForms Weakening GenericTyping LogicalRelation DeclarativeInstance.
From LogRel.LogicalRelation Require Import Induction Escape.

Set Universe Polymorphism.

Section Reflexivities.
  Context `{GenericTypingProperties}.

  Definition LRTyEqRefl {l Γ A eqTy redTm eqTm}
    (lr : LogRel l Γ A eqTy redTm eqTm) : eqTy A.
  Proof.
    induction lr as [ ? ? [] | ? ? [] | ? ? [] ? IHdom IHcod | ?? [] | ?? []].
    all: now econstructor.
  Qed.

  Corollary LRTyEqRefl_ {l Γ A} (lr : [ Γ ||-< l > A ] ) : [ Γ ||-< l > A ≅ A | lr ].
  Proof.
    destruct lr as [[] lr].
    cbn in *.
    now eapply LRTyEqRefl.
  Qed.

  Lemma NeNfEqRefl {Γ k A} : [Γ ||-NeNf k : A] -> [Γ ||-NeNf k ≅ k : A].
  Proof.
    intros []; now econstructor.
  Qed.

  Lemma reflNatRedTmEq {Γ A} {NA : [Γ ||-Nat A]} :
      (forall t : term, [Γ ||-Nat t : A | NA] -> [Γ ||-Nat t ≅ t : A | NA])
    × (forall t : term, NatProp NA t -> NatPropEq NA t t).
  Proof.
    eapply NatRedInduction.
    1-3: now econstructor.
    intros; econstructor.
    now eapply NeNfEqRefl.
  Qed.

  Lemma reflEmptyRedTmEq {Γ A} {NA : [Γ ||-Empty A]} :
      (forall t : term, [Γ ||-Empty t : A | NA] -> [Γ ||-Empty t ≅ t : A | NA])
    × (forall t : term, @EmptyProp _ _ _ _ Γ t -> @EmptyPropEq _ _ _ Γ t t).
  Proof.
    split.
    - intros t Ht. induction Ht.
      econstructor; eauto.
      destruct prop; econstructor.
      now eapply NeNfEqRefl.
    - intros ? []. econstructor.
      now eapply NeNfEqRefl.
  Qed.

  Definition LRTmEqRefl@{h i j k l} {l Γ A eqTy redTm eqTm} (lr : LogRel@{i j k l} l Γ A eqTy redTm eqTm) :
    forall t, redTm t -> eqTm t t.
  Proof.
    induction lr as [ ? ? h | ? ? [] | ? ? [] IHdom IHcod| ?? NA | ?? NA].
    - intros t [? ? ? ? ? [[] rel]%RedTyRecFwd] ; cbn in *.
      (* Need an additional universe level h < i *)
      assert (eqTy t) by (eapply LRTyEqRefl@{h i j k}; exact rel).
      unshelve econstructor.
      all : cbn.
      1-2: econstructor ; tea ; cbn.
      1-3,5: eapply RedTyRecBwd; apply (LRbuild@{h i j k} rel).
      1: cbn; easy.
      now eapply TyEqRecBwd.
    - intros t [].
      econstructor ; cbn in *.
      all: eassumption.
    - intros t [].
      unshelve econstructor ; cbn in *.
      1-2: now econstructor.
      all: cbn.
      all: now eauto.
    - apply reflNatRedTmEq.
    - apply reflEmptyRedTmEq.
  Qed.

  Definition LREqTermRefl_ {l Γ A t} (lr : [ Γ ||-< l > A ] ) :
      [ Γ ||-< l > t : A | lr ] ->
      [ Γ ||-< l > t ≅ t : A | lr ].
  Proof.
    destruct lr as [[] lr].
    cbn in *.
    now eapply LRTmEqRefl.
  Qed.

End Reflexivities.


(*|
############################################
Proofs about constructors matched with ``_``
############################################

:Link: https://stackoverflow.com/q/19277175
|*)

(*|
Question
********

Assume I have the following ``Set``:
|*)

Inductive Many : Set :=
| aa : Many
| bb : Many
| cc : Many
(* | ... many more constructors *)
.

(*|
How can I proof in the ``_`` match, that ``y <> aa``?

.. code-block:: coq

    match x with
    | aa     => true
    | _ as y => (* how can I proof that y <> aa ? *)
|*)

(*|
Answer
******

Unfortunately, it does not seem possible to get such a proof without
some more work in pure Gallina. What you would like to write is:
|*)

Definition foo (x : Many) : bool :=
  match x with
  | aa => true
  | y =>
      let yNOTaa : y <> aa :=
        fun yaa =>
          eq_ind y (fun e => match e with aa => False | _ => True end) I aa yaa
      in false
  end.

(*|
But that does not work quite well in Gallina, as it does not expand
the wildcard into all possible cases, leaving ``y`` abstract in the
``eq_ind`` invocation. It does however work in tactic mode:
|*)

Reset foo. (* .none *)
Definition foo (x : Many) : bool.
  refine (
      match x with
      | aa => true
      | y =>
          let yNOTaa : y <> aa :=
            fun yaa =>
              eq_ind y (fun e => match e with aa => False | _ => True end) I aa yaa
          in false
      end).

(*|
But it actually builds the expanded term with all the branches.

----

I just found out that there is a way to have the Vernacular build the
same term that the refine tactic would build. To do so, you have to
force a return annotation mentioning the discriminee, like so:
|*)

Reset foo. (* .none *)
Definition foo (x : Many) : bool :=
  match x return (fun _ => bool) x with
  | aa => true
  | y =>
      let yNOTaa : y <> aa :=
        fun yaa : y = aa =>
          @eq_ind Many y (fun e => match e with aa => False | _ => True end) I aa yaa
      in false
  end.

(*|
My guess is that the term elaboration differs whether the match is
dependent or not...
|*)


(*
 This is the definition of formal syntax for Dan Grossman's Thesis,
  "SAFE PROGRAMMING AT THE C LEVEL OF ABSTRACTION".

  A variable module.

*)

Set Implicit Arguments.
Require Import List.
Export ListNotations.

Require Import ZArith.
Require Import Init.Datatypes.
Require Import Coq.Init.Logic.
Require Import Coq.Structures.Equalities.

Require Export BoolEqualitySetSigDef.

Module Type VariablesSig <: BoolEqualitySetSig.
  Inductive Var : Set :=
  | var   : nat -> Var.

 Definition  t := Var.

 Parameter eq : t -> t -> Prop.
 Parameter eq_equiv : Equivalence eq.
 Parameter eq_dec : forall x y : t, {eq x y} + {~ eq x y}.
 Parameter eqb : t -> t -> bool.
 Parameter eqb_eq : forall x y : t, eqb x y = true <-> eq x y.
 Hint Resolve eqb.

 Parameter eqb_refl:  forall a,     eqb a a = true.
 Hint Resolve eqb_refl.
 Hint Rewrite eqb_refl.

 Parameter eqb_sym:   forall a b,   eqb a b = eqb b a.
 Hint Immediate eqb_sym.
 Parameter eqb_trans: forall a b c, eqb a b = true -> eqb b c = true -> eqb a c = true.
 Hint Resolve eqb_trans.
 Parameter eq_trans: forall a b c, eq a b -> eq b c -> eq a c.

 Parameter eqb_to_eq:    forall a b, eqb a b = true -> a = b.

 Hint Resolve eqb_eq.
 Parameter eqb_to_neq:   forall a b, eqb a b = false -> a <> b.
 Hint Resolve eqb_to_neq.

 Parameter eqb_iff_eq:    forall a b, eqb a b = true <-> a = b.
 Hint Resolve eqb_iff_eq.

 Parameter eqb_iff_neq:   forall a b, eqb a b = false <-> a <> b.
 Hint Resolve eqb_iff_neq.

(* Again so you can see what you have to work with. *)
  Parameter elt : Type.
  Parameter empty : t.
  Parameter is_empty : t -> bool.
  Parameter mem : elt -> t -> bool.
  Parameter add : elt -> t -> t.
  Parameter singleton : elt -> t.
  Parameter remove : elt -> t -> t.
  Parameter union : t -> t -> t.
  Parameter inter : t -> t -> t.
  Parameter diff : t -> t -> t.
  Parameter equal : t -> t -> bool.
  Parameter subset : t -> t -> bool.
  Parameter fold : forall A : Type, (elt -> A -> A) -> t -> A -> A.
  Parameter for_all : (elt -> bool) -> t -> bool.
  Parameter exists_ : (elt -> bool) -> t -> bool.
  Parameter filter : (elt -> bool) -> t -> t.
  Parameter partition : (elt -> bool) -> t -> t * t.
  Parameter cardinal : t -> nat.
  Parameter elements : t -> list elt.
  Parameter choose : t -> option elt.

End VariablesSig.