Library Imp_LangTrick.Stack.StackSubstitution

From Coq Require Import Arith Psatz Bool String List Nat Program.Equality.

Local Open Scope string_scope.
Local Open Scope nat_scope.
Local Open Scope list_scope.

From Imp_LangTrick Require Import Stack.StackLanguage LogicProp Stack.StackLogicBase Stack.StackFrame Stack.StackPurest
        Stack.StackExprWellFormed.

Lemma index_greater_than_one :
  forall (stk: stack) k n,
    1 <= k ->
    k <= List.length (n :: stk) ->
    exists stk',
      n :: stk' = firstn k (n :: stk).
Proof.
  destruct k; intros.
  - inversion H.
  - exists (firstn k stk). reflexivity.
Qed.

Lemma index_replacement :
  forall (stk stk': stack) (k: stack_index) (n: nat),
    1 <= k ->
    k <= List.length stk ->
    stack_mutated_at_index stk' k n stk ->
    stk' = (firstn (k - 1) stk ++ n :: skipn k stk).
Proof.
  destruct k.
  - intros. invs H.
  - revert stk' k. induction stk; intros; invs H1; auto.
    rewrite skipn_cons. destruct k.
    + invs H4. invs H3.
    + replace (S (S k) - 1) with (S (S k - 1)) by lia. rewrite firstn_cons.
      remember (S k) as k'. simpl. f_equal. subst.
      eapply IHstk; eauto; try lia.
Qed.

Lemma stack_mutation_preserves_length :
  forall (stk stk': stack) (k: stack_index) (n: nat),
    1 <= k ->
    k <= List.length stk ->
    stack_mutated_at_index stk' k n stk ->
    List.length stk = List.length stk'.
Proof.
  induction k; intros; invs H1; auto.
  - inversion H2.
  - simpl. f_equal. auto.
Qed.

Definition index_in_stack (k: stack_index) (stk: stack): Prop :=
  1 <= k /\ k <= List.length stk.

Definition nth_error_1_indexed (stk: stack) (k: stack_index): option nat :=
  nth_error stk (k - 1).

Lemma stack_mutation_other_indices_help :
  forall (stk stk': stack) (k: nat) (n n': nat) (k': nat),
    stack_mutated_at_index stk' (k + 1) n stk ->
    k <> k' ->
    k < List.length stk ->
    k' < List.length stk ->
    nth_error stk k' = Some n' ->
    nth_error stk' k' = Some n'.
Proof.
  intros stk stk' k n n' k' MUTATE NEQ IN_STK_k IN_STK_k' NTH.
  revert NTH IN_STK_k' IN_STK_k NEQ. revert n' k'.
  dependent induction MUTATE; intros.
  - destruct k; [ | inversion x; rewrite Nat.add_comm in H1; inversion H1].
    destruct k'.
    + contradiction.
    + subst. auto.
  - destruct k'.
    + auto.
    + simpl.
      assert (exists k'', k = S k'') by (destruct k; [simpl in H; inversion H; inversion H3 | ]; exists k; reflexivity).
      destruct H2.
      apply IHMUTATE with (k := x) (n' := n'0); simpl in *; auto; try lia.
      replace (k + 1 - 1) with k by lia.
      replace (x + 1) with (S x) by lia.
      rewrite <- H2. apply JMeq_refl.
Qed.

Lemma exists_successor :
  forall (n: nat),
    1 <= n ->
    exists n',
      n = S n'.
Proof.
  induction 1; eauto.
Qed.

Lemma plus_one_is_successor :
  forall (n: nat),
    n + 1 = S n.
Proof.
  intros n. lia.
Qed.

Lemma stack_mutation_other_indices_invariant :
  forall (stk stk': stack) (k: stack_index) (n n': nat) (k': stack_index),
    stack_mutated_at_index stk' k n stk ->
    k <> k' ->
    index_in_stack k stk ->
    index_in_stack k' stk ->
    nth_error_1_indexed stk k' = Some n' ->
    nth_error_1_indexed stk' k' = Some n'.
Proof.
  intros stk stk' k n n' k' MUTATE NEQ IN_STK_k IN_STK_k' NTH.
  unfold nth_error_1_indexed in *.
  unfold index_in_stack in *.
  destruct IN_STK_k.
  assert (exists k'', k = S k'') by (eapply exists_successor; eassumption).
  destruct H1.
  eapply stack_mutation_other_indices_help.
  - rewrite H1 in MUTATE. rewrite <- plus_one_is_successor in MUTATE. eassumption.
  - unfold not in *. intros. rewrite H2 in H1. rewrite <- Nat.sub_succ_l in H1; [ | intuition].
    rewrite successor_minus_one_same in H1. apply NEQ in H1. assumption.
  - rewrite H1 in H0. intuition.
  - intuition.
  - assumption.
Qed.

Lemma nth_error_of_mutated_stack :
  forall n stk stk' val,
    stack_mutated_at_index stk' (S n) val stk ->
    nth_error stk' n = Some val.
Proof.
  induction n; intros; invs H; auto.
  - inversion H0. inversion H5.
  - replace (S (S n) - 1) with (S n) in H3 by reflexivity.
    eapply IHn. eassumption.
Qed.

Ltac aexp_det aexp :=
  match goal with
  | [ H1 : aexp_stack_sem aexp ?fenv ?stk1 (?stk1, ?n1), H2 : aexp_stack_sem aexp ?fenv ?stk2 (?stk2, ?n2) |- _ ] =>
      let same := fresh "SAME" in
      assert (same : (stk1, n1) = (stk2, n2)) by (eapply aexp_stack_sem_det; eassumption);
      invs same
  end.

Ltac aexp_pure aexp :=
  match goal with
  | [ H1 : aexp_stack_sem aexp ?fenv ?stk1 (?stk2, ?n), H2: aexp_stack_pure_rel aexp ?fenv |- _ ] =>
      let same := fresh "SAME" in
      assert (same: stk1 = stk2) by (eapply aexp_stack_pure_backwards in H2; eapply H2; eassumption);
      subst
  end.

Ltac args_pure args :=
  match goal with
  | [ H1 : args_stack_sem args ?fenv ?stk1 (?stk2, ?vals), H2: args_stack_pure_rel args ?fenv |- _ ] =>
      let same := fresh "SAME" in
      assert (same: stk1 = stk2) by (eapply args_stack_pure_implication with (stk := stk1) (stk' := stk2) in H2; eassumption)
  end.

Ltac bexp_pure bexp :=
  match goal with
  | [ H1 : bexp_stack_sem bexp ?fenv ?stk1 (?stk2, ?n), H2: bexp_stack_pure_rel bexp ?fenv |- _ ] =>
      let same := fresh "SAME" in
      assert (same: stk1 = stk2) by (eapply bexp_stack_pure_implication in H2; eapply H2; eassumption);
      subst
  end.

Lemma arith_substitution_preserves_purity :
  forall k a aexp aexp',
    arith_update_rel k a aexp aexp' ->
    forall fenv,
      aexp_stack_pure_rel a fenv ->
      aexp_stack_pure_rel aexp fenv ->
      aexp_stack_pure_rel aexp' fenv.
Proof.
  apply
   (arith_update_rel_mut
     (fun k a aexp aexp' H =>
       forall fenv,
         aexp_stack_pure_rel a fenv ->
         aexp_stack_pure_rel aexp fenv ->
         aexp_stack_pure_rel aexp' fenv)
     (fun k a args args' H =>
        forall fenv,
          aexp_stack_pure_rel a fenv ->
          args_stack_pure_rel args fenv ->
          args_stack_pure_rel args' fenv)); intros; auto.
  - invs H2. econstructor; eauto.
  - invs H2. econstructor; eauto.
  - invs H1. econstructor; eauto.
  - invs H2. econstructor; eauto.
Qed.

Check args_update_rel_mut.

Lemma args_substitution_preserves_purity :
  forall k a args args',
    args_update_rel k a args args' ->
    forall fenv,
      aexp_stack_pure_rel a fenv ->
      args_stack_pure_rel args fenv ->
      args_stack_pure_rel args' fenv.
Proof.
  apply
   (args_update_rel_mut
     (fun k a aexp aexp' H =>
       forall fenv,
         aexp_stack_pure_rel a fenv ->
         aexp_stack_pure_rel aexp fenv ->
         aexp_stack_pure_rel aexp' fenv)
     (fun k a args args' H =>
        forall fenv,
          aexp_stack_pure_rel a fenv ->
          args_stack_pure_rel args fenv ->
          args_stack_pure_rel args' fenv)); intros; auto.
  - invs H2. econstructor; eauto.
  - invs H2. econstructor; eauto.
  - invs H1. econstructor; eauto.
  - invs H2. econstructor; eauto.
Qed.

Lemma bool_substitution_preserves_purity :
  forall b b' k a fenv,
    bool_update_rel k a b b' ->
    aexp_stack_pure_rel a fenv ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_pure_rel b' fenv.
Proof.
  intros b b' k a fenv H. revert fenv.
  induction H; intros; eauto.
  - invs H1. econstructor; eauto.
  - invs H2. econstructor; eauto.
  - invs H2. econstructor; eauto.
  - invs H2.
    econstructor; eauto; eapply arith_substitution_preserves_purity; eauto.
  - invs H2. econstructor; eauto; eapply arith_substitution_preserves_purity; eauto.
Qed.

Definition arith_args_substitution_vs_update_P (k: stack_index) (a: aexp_stack) (aexp aexp': aexp_stack): Prop :=
  forall aval aexpval fenv stk stk',
    aexp_stack_pure_rel a fenv ->
    aexp_stack_pure_rel aexp fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    aexp_stack_sem aexp' fenv stk (stk, aexpval) ->
    aexp_stack_sem aexp fenv stk' (stk', aexpval).

Definition arith_args_substitution_vs_update_P0 (k: stack_index) (a: aexp_stack) (args args': list aexp_stack): Prop :=
  forall aval argsvals fenv stk stk',
    aexp_stack_pure_rel a fenv ->
    args_stack_pure_rel args fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    args_stack_sem args' fenv stk (stk, argsvals) ->
    args_stack_sem args fenv stk' (stk', argsvals).

Ltac arith_sub_pure a1' :=
  match goal with
  | [ H : aexp_stack_pure_rel a1' ?fenv' |- _ ] => idtac "found stack pure"
  | [ H2 : arith_update_rel ?k ?a ?a1 a1', H1 : aexp_stack_pure_rel ?a1'' ?fenv' |- _ ] => eapply arith_substitution_preserves_purity with (fenv := fenv') in H2
  end;
  [ match goal with
    | [ H' : aexp_stack_pure_rel a1' ?fenv' |- _ ] =>
        eapply aexp_stack_pure_backwards in H';
        unfold aexp_stack_pure in H';
        match goal with
        | [ H3 : aexp_stack_sem a1' ?fenv ?stk1 (?stk2, ?n) |- _ ] =>
            let copy := fresh "H3" in
            pose proof (copy := H3); eapply H' in copy; rewrite copy in *; clear copy
        end
                                    
    end | assumption .. ]
.

Ltac bool_sub_pure a1' :=
  match goal with
  | [ H : bexp_stack_pure_rel a1' ?fenv' |- _ ] => idtac "found stack pure"
  | [ H2 : bool_update_rel ?k ?a ?a1 a1', H1 : bexp_stack_pure_rel ?a1'' ?fenv' |- _ ] => eapply bool_substitution_preserves_purity with (fenv := fenv') in H2
  end;
  [ match goal with
    | [ H' : bexp_stack_pure_rel a1' ?fenv' |- _ ] =>
        eapply bexp_stack_pure_implication in H';
        unfold bexp_stack_pure in H';
        match goal with
        | [ H3 : bexp_stack_sem a1' ?fenv ?stk1 (?stk2, ?n) |- _ ] =>
            let copy := fresh "H3" in
            pose proof (copy := H3); eapply H' in copy; rewrite copy in *; clear copy
        end
                                    
    end | assumption .. ].

Ltac args_sub_pure args' :=
  match goal with
  | [ H : args_stack_pure_rel args' ?fenv' |- _ ] => idtac "found stack pure"
  | [ H2 : args_update_rel ?k ?a ?a1 args', H1 : args_stack_pure_rel ?a1 ?fenv' |- _ ] => idtac "found alternative"; eapply args_substitution_preserves_purity with (fenv := fenv') in H2
  end;
  [ match goal with
    | [ H' : args_stack_pure_rel args' ?fenv', H'' : args_stack_sem args' ?fenv ?stk1 (?stk2, ?n) |- _ ] =>
        (eapply args_stack_pure_implication with (stk := stk1) (stk' := stk2) in H'; [ | eassumption ]);
        let copy := fresh "H3" in
        pose proof (copy := H''); rewrite <- H' in copy
    end | eassumption .. ].

Lemma args_update_preserves_length :
  forall (args args' : list aexp_stack) (k: stack_index) (a: aexp_stack),
    args_update_rel k a args args' ->
    Datatypes.length args = Datatypes.length args'.
Proof.
  intros args args' k a H. induction H; auto.
  simpl. f_equal. auto.
Qed.

Lemma same_after_popping_length :
  forall stk stk' n,
   Datatypes.length stk = n ->
   same_after_popping stk' (stk ++ stk') n.
Proof.
  induction stk; induction stk'; intros; subst; simpl; constructor; auto.
Qed.

Lemma arith_args_substitution_vs_update :
  (forall (k : stack_index) (a: aexp_stack) (aexp aexp' : aexp_stack),
      arith_update_rel k a aexp aexp' ->
      arith_args_substitution_vs_update_P k a aexp aexp') /\
    (forall (k : stack_index) (a : aexp_stack) (l l' : list aexp_stack),
        args_update_rel k a l l' ->
        arith_args_substitution_vs_update_P0 k a l l').
Proof.
  pose (fun (s: stack_index) (a a0 a1: aexp_stack) =>
        fun (_: arith_update_rel s a a0 a1) =>
          arith_args_substitution_vs_update_P s a a0 a1) as P.
  pose (fun (s: stack_index) (a: aexp_stack) (l l0: list aexp_stack) =>
        fun (_: args_update_rel s a l l0) =>
          arith_args_substitution_vs_update_P0 s a l l0) as P0.
  apply (expr_update_rel_mutind P P0); unfold P, P0; unfold arith_args_substitution_vs_update_P, arith_args_substitution_vs_update_P0; intros.
  - invs H3. econstructor.
  - invs H2.
    + constructor; auto.
      * simpl. lia.
      * simpl. f_equal. aexp_det a. auto.
    + constructor; try (simpl; lia).
      enough (exists k'', k' = S (S k'')).
      * destruct H8. subst. remember (S x) as k''.
        simpl. rewrite Nat.sub_0_r.
        simpl in H7. rewrite Nat.sub_0_r in H7. subst.
        simpl. aexp_det a.
        eapply nth_error_of_mutated_stack. eauto.
      * inversion H4.
        -- exists 0. reflexivity.
        -- inversion H8; eexists; eauto.
  - invs H3. inversion H2; eapply stack_mutation_other_indices_invariant with (stk := stk) (stk' := stk') (n' := aexpval) (n := aval) in n;
               unfold nth_error_1_indexed in n; try eassumption; unfold index_in_stack in *; try lia.
    + constructor; [ eassumption | .. ]; (subst; eassumption).
    + constructor; subst; try assumption. simpl. rewrite H8. assumption.
    + intuition. subst.
      rewrite H8 in H5. simpl. intuition.
  - invs H5. invs H2.
    econstructor.
    + eapply H; try eassumption.
      arith_sub_pure a1'. assumption.
    + eapply H0; try eassumption.
      arith_sub_pure a2'. assumption.
  - invs H5. invs H2.
    econstructor.
    + eapply H; try eassumption.
      arith_sub_pure a1'. assumption.
    + eapply H0; try eassumption.
      arith_sub_pure a2'. assumption.
  - inversion H4. revert H9 H10 H11 H12. invs H1. intros.
    rewrite Nat.add_comm in H20. pose proof (H22 := H20).
    eapply frame_implies_rest_stk_untouched_le with (stk := stk2) (stk1 := vals) (stk2 := stk1) in H20.
    destruct H20. destruct H6.
    econstructor.
    1-4: try eassumption.
    + symmetry. eapply args_update_preserves_length; eassumption.
    + eapply H; eauto.
      args_sub_pure args'.
      eassumption.
    + eapply frame_preserves_rest_stk.
      * rewrite <- H5 in * |- .
        rewrite H7 in * |- .
        rewrite <- H9 in * |- .
        rewrite <- H11 in * |- .
        rewrite <- H6 in H22.
        args_sub_pure args'. rewrite a0 in * |- .
        eapply args_stack_sem_preserves_length in H14.
        rewrite H10, H14, H9 in H22.
        eapply H22.
      * subst. eassumption.
    + pose proof (H13 := H12).
      eapply args_update_preserves_args_pure in a0; [ | eassumption .. ].
      args_sub_pure args'.
      rewrite a0 in * |- .
      rewrite H7 in * |- .
      rewrite <- H11.
      rewrite <- H9.
      rewrite <- H9 in H11. rewrite <- H11 in H18.
      aexp_pure (Return_expr (fenv f)).
      eapply aexp_frame_pure.
      eassumption.
      rewrite H6. rewrite Nat.add_comm. assumption.
    + eapply H in H0; [ | eassumption .. | args_sub_pure args'; eassumption ].
      rewrite <- H11 in H18.
      rewrite <- H9 in H18.
      arith_sub_pure (Return_expr (fenv f)).
      eapply frame_implies_rest_stk_untouched_le in H22; [ | apply args_stack_sem_preserves_length in H14; rewrite H14 in H10; rewrite <- H9 in H10; eassumption | | subst; eassumption ].
      rewrite <- H9 in H10.
      rewrite <- H10.
      rewrite <- H9 in H5. rewrite <- H5.

      eapply same_after_popping_length.
      eassumption. subst.
      rewrite Nat.add_comm. rewrite <- H10 in H19.
      eapply same_after_popping_implies_geq_popped_length; eauto.
    + rewrite H9. eapply args_stack_sem_preserves_length in H14. rewrite H14 in H10. assumption.
    + subst. rewrite <- H10 in H19.
      pose proof (same_after_popping_implies_geq_popped_length stk stk3 _ H19).
      rewrite Nat.add_comm in H5. rewrite H5.
      aexp_pure (Return_expr (fenv f)).
      eauto.
    + subst. assumption.
  - invs H3. econstructor.
  - invs H5.
    econstructor.
    + eapply H; eauto; invs H2; eauto.
      args_sub_pure args'.
      arith_sub_pure arg'.
      assumption.
    + eapply H0; eauto; invs H2; eauto.
      eapply arith_update_preserves_aexp_pure in a0; [ | eassumption | invs H2; assumption].
      arith_sub_pure arg'.
      assumption.
Qed.

Lemma arith_substitution_vs_update_folded :
  (forall (k : stack_index) (a: aexp_stack) (aexp aexp' : aexp_stack),
      arith_update_rel k a aexp aexp' ->
      arith_args_substitution_vs_update_P k a aexp aexp').
Proof.
  pose proof (arith_args_substitution_vs_update).
  destruct H. assumption.
Qed.

Lemma args_substitution_vs_update_folded :
  (forall (k : stack_index) (a : aexp_stack) (l l' : list aexp_stack),
      args_update_rel k a l l' ->
      arith_args_substitution_vs_update_P0 k a l l').
Proof.
  pose proof (arith_args_substitution_vs_update).
  destruct H. assumption.
Qed.

Lemma arith_substitution_vs_update :
  forall (k: stack_index) (a: aexp_stack) (aexp aexp': aexp_stack),
    arith_update_rel k a aexp aexp' ->
    forall aval aexpval fenv stk stk',
      aexp_stack_pure_rel a fenv ->
      aexp_stack_pure_rel aexp fenv ->
      aexp_stack_sem a fenv stk (stk, aval) ->
      stack_mutated_at_index stk' k aval stk ->
      aexp_stack_sem aexp' fenv stk (stk, aexpval) ->
      aexp_stack_sem aexp fenv stk' (stk', aexpval).
Proof.
  pose proof (arith_substitution_vs_update_folded).
  unfold arith_args_substitution_vs_update_P in H.
  assumption.
Qed.

Lemma args_substitution_vs_update :
  forall (k: stack_index) (a: aexp_stack) (args args': list aexp_stack),
    args_update_rel k a args args' ->
    forall aval argsvals fenv stk stk',
      aexp_stack_pure_rel a fenv ->
      args_stack_pure_rel args fenv ->
      aexp_stack_sem a fenv stk (stk, aval) ->
      stack_mutated_at_index stk' k aval stk ->
      args_stack_sem args' fenv stk (stk, argsvals) ->
      args_stack_sem args fenv stk' (stk', argsvals).
Proof.
  pose proof (args_substitution_vs_update_folded).
  unfold arith_args_substitution_vs_update_P0 in H.
  assumption.
Qed.

Lemma bool_substitution_vs_update :
  forall (k: stack_index) (a: aexp_stack) (b b': bexp_stack),
    bool_update_rel k a b b' ->
    forall (aval: nat) (bval: bool) (fenv: fun_env_stk) (stk stk': stack),
      aexp_stack_pure_rel a fenv ->
      bexp_stack_pure_rel b fenv ->
      aexp_stack_sem a fenv stk (stk, aval) ->
      stack_mutated_at_index stk' k aval stk ->
      bexp_stack_sem b' fenv stk (stk, bval) ->
      bexp_stack_sem b fenv stk' (stk', bval).
Proof.
  intros ? ? ? ? UPDATE.
  dependent induction UPDATE; intros ? ? ? ? ? APURE BPURE AEXP MUTATE BEXP; invs BEXP.
  - constructor.
  - constructor.
  - econstructor; eauto.
    eapply IHUPDATE; try eassumption.
    invs BPURE. assumption.
  - invs BPURE. econstructor.
    + eapply IHUPDATE1; try eassumption.
      eapply bool_update_preserves_bexp_pure in UPDATE1; [ | eassumption .. ].
      bool_sub_pure b1'. eassumption.
    + eapply IHUPDATE2; [ eassumption .. | ].
      eapply bool_update_preserves_bexp_pure in UPDATE2; [ | eassumption .. ].
      bool_sub_pure b2'. eassumption.
    + reflexivity.
  - invs BPURE. econstructor.
    + eapply IHUPDATE1; [ eassumption .. | ].
      eapply bool_update_preserves_bexp_pure in UPDATE1; [ | eassumption ..].
      bool_sub_pure b1'.
      eassumption.
    + eapply IHUPDATE2; [eassumption .. | ].
      eapply bool_update_preserves_bexp_pure in UPDATE2; [ | eassumption ..].
      bool_sub_pure b2'. eassumption.
    + reflexivity.
  - invs BPURE. econstructor.
    + pose proof (A1PURE := H). eapply arith_update_preserves_aexp_pure in H; [ | eassumption .. ].
      eapply arith_substitution_vs_update; try eassumption.
      arith_sub_pure a1'. eassumption.
    + pose proof (A2PURE := H0). eapply arith_update_preserves_aexp_pure in H0; [ | eassumption .. ].
      eapply arith_substitution_vs_update; try eassumption.
      arith_sub_pure a2'. eassumption.
    + reflexivity.
  - invs BPURE. econstructor.
    + pose proof (A1PURE := H). eapply arith_update_preserves_aexp_pure in H; [ | eassumption .. ].
      eapply arith_substitution_vs_update; try eassumption.
      arith_sub_pure a1'. eassumption.
    + pose proof (A2PURE := H0). eapply arith_update_preserves_aexp_pure in H0; [ | eassumption .. ].
      eapply arith_substitution_vs_update; try eassumption.
      arith_sub_pure a2'. eassumption.
    + reflexivity.
Qed.

Lemma arith_update_implies_pure :
  forall k aval a a',
    arith_update_rel k aval a a' ->
    aexp_well_formed a ->
    forall fenv,
      aexp_stack_pure_rel aval fenv ->
      aexp_stack_pure_rel a' fenv ->
      aexp_stack_pure_rel a fenv.
Proof.
  apply
    (arith_update_rel_mut
       (fun k aval a a' (H : arith_update_rel k aval a a') =>
          aexp_well_formed a ->
          forall fenv,
            aexp_stack_pure_rel aval fenv ->
            aexp_stack_pure_rel a' fenv ->
            aexp_stack_pure_rel a fenv)
       (fun k aval args args' (H : args_update_rel k aval args args') =>
          args_well_formed args ->
          forall fenv,
            aexp_stack_pure_rel aval fenv ->
            args_stack_pure_rel args' fenv ->
            args_stack_pure_rel args fenv)); intros; auto.
  - econstructor; eauto. subst. invs H. auto.
  - invs H3. invs H1. econstructor; eauto.
  - invs H3. invs H1. econstructor; eauto.
  - invs H2. invs H0. econstructor; eauto.
  - invs H3. invs H1. econstructor; eauto.
Qed.

Lemma bool_update_implies_pure :
  forall k aval b b',
    bool_update_rel k aval b b' ->
    bexp_well_formed b ->
    forall fenv,
      aexp_stack_pure_rel aval fenv ->
      bexp_stack_pure_rel b' fenv ->
      bexp_stack_pure_rel b fenv.
Proof.
  intros k aval b b' H. induction H; intros; auto; try solve [invs H2; invs H0; econstructor; eauto | invs H3; invs H1; econstructor; eauto | invs H3; invs H1; econstructor; eauto; eapply arith_update_implies_pure; eauto ].
Qed.

Ltac bool_sub_update :=
  match goal with
  | [ |- bexp_stack_sem ?b' ?fenv ?stk (?stk, ?bval) ] =>
      match goal with
      | [ H: bool_update_rel ?k ?a ?b ?b' |- _ ] =>
          eapply bool_substitution_vs_update; try eassumption; eapply bool_update_implies_pure; eassumption
      | [ |- _ ] => idtac "no bool_update_rel found"
      end
  | [ |- _ ] =>
      idtac "no bexp_stack_sem goal found"
  end;
  try assumption;
  try eassumption.

Ltac arith_sub_update :=
  match goal with
  | [ |- aexp_stack_sem ?b' ?fenv ?stk (?stk, ?bval) ] =>
      match goal with
      | [ H: arith_update_rel ?k ?a ?b ?b' |- _ ] =>
          eapply arith_substitution_vs_update; try eassumption; eapply arith_update_implies_pure; eassumption
      | [ |- _ ] => idtac "no arith_update_rel found"
      end
  | [ |- _ ] =>
      idtac "no aexp_stack_sem goal found"
  end;
  try assumption;
  try eassumption.

Lemma eval_prop_args_rel_help :
forall (blist blist': list bexp_stack) vals k a aval fenv stk stk',
  transformed_prop_exprs_args (V := bool) (bool_update_rel k a) blist blist' ->
  aexp_stack_pure_rel a fenv ->
  aexp_stack_sem a fenv stk (stk, aval) ->
  stack_mutated_at_index stk' k aval stk ->
  prop_args_rel (V := bool) (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) blist ->
  eval_prop_args_rel (fun (boolexpr: bexp_stack) (boolval: bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) blist' vals ->
  eval_prop_args_rel (fun (boolexpr: bexp_stack) (boolval: bool) => bexp_stack_sem boolexpr fenv stk' (stk', boolval)) blist vals.
Proof.
  induction blist; intros blist' vals k aexpr aval fenv stk stk' UPDATE PURE_A AEXP MUTATE LIST_PURE EVAL.
  - invs UPDATE.
    invs EVAL. econstructor.
  - invs UPDATE. invs EVAL.
    econstructor.
    + eapply bool_substitution_vs_update; try eassumption.
      eapply bool_update_implies_pure; try eassumption.
      invs LIST_PURE.
      * eapply bexp_purity_implies_well_formed; eassumption.
      * invs LIST_PURE.
        eapply bool_update_preserves_bexp_pure; eassumption.
    + eapply IHblist; try eassumption.
      invs LIST_PURE.
      eassumption.
Qed.


Lemma eval_prop_rel_help :
  forall (l l': LogicProp bool bexp_stack) k a aval fenv stk stk',
    transformed_prop_exprs (bool_update_rel k a) l l' ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    prop_rel (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) l ->
    eval_prop_rel (fun (boolexpr: bexp_stack) (boolval: bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) l' ->
    eval_prop_rel (fun (boolexpr: bexp_stack) (boolval: bool) => bexp_stack_sem boolexpr fenv stk' (stk', boolval)) l.
Proof.
  intros l l' k a aval fenv stk stk' H. revert aval fenv stk stk'.
  remember (bool_update_rel k a).
  dependent induction H; intros.
  - constructor.
  - invs H3.
  - invs H3. invs H4. econstructor; eauto.
    eapply bool_substitution_vs_update; eauto.
  - invs H4. invs H5.
    econstructor; eauto; eapply bool_substitution_vs_update; eauto.
  - invs H4. invs H5. econstructor; eauto.
  - invs H4. invs H5.
    + eapply RelOrPropLeft; eauto.
    + eapply RelOrPropRight; eauto.
  - invs H5. invs H6.
    econstructor; eauto; eapply bool_substitution_vs_update; eauto.
  - invs H3. invs H4.
    econstructor; eauto.
    eapply eval_prop_args_rel_help; eassumption.
Qed.

Lemma arith_eval_prop_args_rel_help :
  forall (a_list : list aexp_stack) (k : stack_index) (a : aexp_stack) (aval : nat) (fenv : fun_env_stk) (stk stk' : stack) (a_list' : list aexp_stack) (vals: list nat),
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    transformed_prop_exprs_args (V:= nat) (arith_update_rel k a) a_list a_list' ->
    eval_prop_args_rel (fun (boolexpr : aexp_stack) (boolval : nat) => aexp_stack_sem boolexpr fenv stk (stk, boolval)) a_list' vals ->
    prop_args_rel (V := nat) (fun boolexpr : aexp_stack => aexp_stack_pure_rel boolexpr fenv) a_list ->
    eval_prop_args_rel (fun (boolexpr : aexp_stack) (boolval : nat) => aexp_stack_sem boolexpr fenv stk' (stk', boolval)) a_list vals.
Proof.
  induction a_list as [ | arg args ]; intros k a aval fenv stk stk' a_list' vals PURE_A SEM MUTATE UPDATE EVAL PURE; invc UPDATE; invc PURE; invc EVAL.
  - econstructor.
  - econstructor.
    + eapply arith_substitution_vs_update; eassumption.
    + eapply IHargs; [ eapply PURE_A | .. ]; eassumption.
Qed.

Lemma arith_eval_prop_rel_help :
  forall (l l': LogicProp nat aexp_stack) k a aval fenv stk stk',
    transformed_prop_exprs (arith_update_rel k a) l l' ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    prop_rel (fun (boolexpr: aexp_stack) => aexp_stack_pure_rel boolexpr fenv) l ->
    eval_prop_rel (fun (boolexpr: aexp_stack) (boolval: nat) => aexp_stack_sem boolexpr fenv stk (stk, boolval)) l' ->
    eval_prop_rel (fun (boolexpr: aexp_stack) (boolval: nat) => aexp_stack_sem boolexpr fenv stk' (stk', boolval)) l.
Proof.
  induction l as [ | | ?f ?arg | | | | | ]; intros l' k a aval fenv stk stk' TRANSFORM PURE_A AVAL MUTATE PURE EVAL; invc TRANSFORM; invc EVAL; invc PURE; try solve [ econstructor | econstructor; try eassumption; eapply arith_substitution_vs_update; try eassumption ].
  - econstructor.
    + eapply IHl1; try eassumption.
    + eapply IHl2; eassumption.
  - econstructor. eapply IHl1; eassumption.
  - eapply RelOrPropRight. eapply IHl2; eassumption.
  - econstructor; [ | eassumption ].
    eapply arith_eval_prop_args_rel_help; eassumption.
Qed.

Lemma prop_rel_pure_help :
  forall a_list a fenv k a_list0,
    aexp_stack_pure_rel a fenv ->
    prop_args_rel (V := bool) bexp_well_formed a_list0 ->
    transformed_prop_exprs_args (V := bool) (bool_update_rel k a) a_list0 a_list ->
    prop_args_rel (V := bool) (fun (boolexpr : bexp_stack) => bexp_stack_pure_rel boolexpr fenv) a_list ->
    prop_args_rel (V := bool) (fun (boolexpr : bexp_stack) => bexp_stack_pure_rel boolexpr fenv) a_list0.
Proof.
  induction a_list as [ | arg args]; intros a fenv k a_list0 PURE_A WF UPDATE PURE; invs UPDATE; invs PURE; invs WF; econstructor.
  + eapply bool_update_pure_implies_bexp_pure; eassumption.
  + eapply IHargs; eassumption.
Qed.

Lemma arith_prop_rel_pure_help :
  forall a_list a fenv k a_list0,
    aexp_stack_pure_rel a fenv ->
    prop_args_rel (V := nat) aexp_well_formed a_list0 ->
    transformed_prop_exprs_args (V := nat) (arith_update_rel k a) a_list0 a_list ->
    prop_args_rel (V := nat) (fun (boolexpr : aexp_stack) => aexp_stack_pure_rel boolexpr fenv) a_list ->
    prop_args_rel (V := nat) (fun (boolexpr : aexp_stack) => aexp_stack_pure_rel boolexpr fenv) a_list0.
Proof.
  induction a_list as [ | arg args]; intros a fenv k a_list0 PURE_A WF UPDATE PURE; invs UPDATE; invs PURE; invs WF; econstructor.
  + eapply arith_update_pure_implies_purity; eauto.
  + apply (IHargs a fenv k args0 PURE_A H7 H4); auto.
Qed.

Lemma transformed_prop_pure_help :
  forall l a fenv k l0,
    aexp_stack_pure_rel a fenv ->
    prop_rel (V := bool) bexp_well_formed l0 ->
    prop_rel (V := bool) (fun boolexpr : bexp_stack => bexp_stack_pure_rel boolexpr fenv) l ->
    transformed_prop_exprs (bool_update_rel k a) l0 l ->
    prop_rel (V := bool) (fun boolexpr : bexp_stack => bexp_stack_pure_rel boolexpr fenv) l ->
    prop_rel (V := bool) (fun boolexpr : bexp_stack => bexp_stack_pure_rel boolexpr fenv) l0.
Proof.
  induction l; intros b fenv k l0 PURE_A WF UPDATE PURE; invs UPDATE; auto; invs PURE; invs WF;
  econstructor; try eapply bool_update_implies_pure; eauto.
  eapply prop_rel_pure_help; eauto.
Qed.

Lemma transformed_prop_aexp_pure_help :
  forall a k l0 l fenv,
    transformed_prop_exprs (arith_update_rel k a) l0 l ->
    prop_rel (V := nat) aexp_well_formed l0 ->
    aexp_stack_pure_rel a fenv ->
    prop_rel (V := nat) (fun aexpexpr : aexp_stack => aexp_stack_pure_rel aexpexpr fenv) l ->
    prop_rel (V := nat) (fun aexpexpr : aexp_stack => aexp_stack_pure_rel aexpexpr fenv) l0.
Proof.
  intros a k l0 l fenv H. remember (arith_update_rel k a).
  Ltac invc_prop_rel :=
    match goal with
    | [ H : prop_rel (fun _ => aexp_stack_pure_rel _
                     _) (_ _) |- _ ] =>
        progress invc H; try (invc_prop_rel)
    | [ H : prop_rel aexp_well_formed (_ _) |- _ ] =>
        progress invc H; try (invc_prop_rel)
    end.
  induction H; intros; auto; invc_prop_rel; econstructor; try eapply arith_update_implies_pure; eauto.
  eapply arith_prop_rel_pure_help; eauto.
Qed.

Lemma meta_match_rel_same_as_eval_under_different_state :
  forall M M' k a aval fenv stk stk',
    mv_well_formed M ->
    meta_update_rel k a M M' ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    meta_match_rel M' fenv stk ->
    meta_match_rel M fenv stk'.
Proof.
  intros M M' k a aval fenv stk stk' WF UPDATE PURE AEXP MUTATE MATCH.
  invs MATCH.
  - invs UPDATE. constructor.
    + revert H5 H0 H MUTATE AEXP UPDATE WF. revert l0.
      revert stk' PURE MATCH. revert k a aval fenv stk.
      induction l; intros; invs UPDATE; invs H6; invs H; invs H0; invs WF; invs H2.
      * econstructor.
      * econstructor; bool_sub_update.
      * econstructor; bool_sub_update.
      * econstructor; eauto.
        -- eapply IHl1; eauto; econstructor; eauto.
        -- eapply IHl2; eauto; econstructor; eauto.
      * econstructor.
        eapply IHl1; eauto; econstructor; eauto.
      * eapply RelOrPropRight.
        eapply IHl2; eauto; econstructor; eauto.
      * econstructor; bool_sub_update.
      * eapply eval_prop_rel_help; eauto.
        invs H4; econstructor; [ econstructor | .. ].
        invs H3. invs H2.
        invs H13.
        econstructor.
        eapply bool_update_pure_implies_bexp_pure; try eassumption.
        eapply prop_rel_pure_help; eauto.
    + invs WF. eapply transformed_prop_pure_help; eassumption.
  - invs UPDATE. invs WF. constructor; [ | eapply transformed_prop_aexp_pure_help; eassumption ].
    revert H5 H0 H MUTATE AEXP UPDATE H2 WF. revert l0.
    induction l; intros; invs UPDATE; invs H5; invs H; invs H0; invs H2.
    + econstructor.
    + econstructor; arith_sub_update.
    + econstructor; arith_sub_update.
    + econstructor.
      * eapply IHl1; eauto; econstructor; eauto.
      * eapply IHl2; eauto; econstructor; eauto.
    + econstructor. eapply IHl1; eauto; econstructor; eauto.
    + eapply RelOrPropRight. eapply IHl2; eauto; econstructor; eauto.
    + econstructor; arith_sub_update.
    + eapply arith_eval_prop_rel_help; try eassumption.
      econstructor.
      eapply arith_prop_rel_pure_help; eassumption.
Qed.

Lemma state_update_same_as_eval_under_different_state :
  forall P P' k (a: aexp_stack) (aval: nat) fenv stk stk',
    state_update_rel k a P P' ->
    aexp_stack_pure_rel a fenv ->
    absstate_well_formed P ->
    aexp_stack_sem a fenv stk (stk, aval) ->
    stack_mutated_at_index stk' k aval stk ->
    absstate_match_rel P' fenv stk ->
    absstate_match_rel P fenv stk'. Proof.
  induction P; intros ? ? ? ? ? ? ? ? PURE WF; intros.
  - invs H. invs H2.
    econstructor.
    + invs H7.
      * econstructor.
      * pose proof (stack_mutation_preserves_length).
        inversion H1.
        -- assert (1 <= k) by intuition.
           assert (k <= Datatypes.length (aval :: stk0)).
           simpl.
           match goal with
           | [ H : 1 = k |- _ ] =>
               rewrite <- H
           | [ H : k = 1 |- _ ] =>
               rewrite H
           end.
           intuition.
           econstructor.
           simpl.
           eapply (H4 stk stk' k aval) in H16.
           assert (Datatypes.length (new :: stk0) = Datatypes.length stk') by (f_equal; assumption).
           simpl in H18.
           rewrite H18.
           rewrite <- H16. assumption.
           subst.
           simpl in H17.
           simpl.
           assumption.
           assumption.
        -- assert (1 <= k) by intuition.
           pose proof (Hleq := H20).
           eapply H4 with (stk := stk) (stk' := stk') in H20.
           assert (Datatypes.length (n :: stk0) = Datatypes.length stk') by (f_equal; assumption).
           assert (Datatypes.length (n' :: stk'0) = Datatypes.length stk) by (f_equal; assumption).
           econstructor.
           rewrite H21.
           rewrite <- H20. assumption.
           invs H11.
           intuition.

           eassumption.
    + invs H5; invs WF; eapply meta_match_rel_same_as_eval_under_different_state; try eassumption.
  - invs WF. invs H. invs H2. econstructor.
    + eapply IHP1; eassumption.
    + eapply IHP2; eassumption.
  - invs WF. invs H. invs H2.
    + econstructor. eapply IHP1; eassumption.
    + eapply RelAbsOrRight. eapply IHP2; eassumption.
Qed.

Definition aexp_args_P' (a a': aexp_stack) (n: nat): Prop :=
  forall (fenv: fun_env_stk) (stk stk': stack) (v: nat),
    List.length stk' = n ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, v) ->
    aexp_stack_sem a' fenv (stk' ++ stk) (stk' ++ stk, v).

Definition aexp_args_P0' (l l': list aexp_stack) (n: nat): Prop :=
  forall (fenv: fun_env_stk) (stk stk': stack) (vs: list nat),
    List.length stk' = n ->
    args_stack_pure_rel l fenv ->
    args_stack_sem l fenv stk (stk, vs) ->
    args_stack_sem l' fenv (stk' ++ stk) (stk' ++ stk, vs).

Lemma aexp_args_stack_increase_preserves_eval' :
  (forall (n: nat) (a a': aexp_stack),
      aexp_stk_size_inc_rel n a a' ->
      aexp_args_P' a a' n) /\
    (forall (n: nat) (l l': list aexp_stack),
        args_stk_size_inc_rel n l l' ->
        aexp_args_P0' l l' n).
Proof.
  pose (fun (n: nat) (a a0: aexp_stack) =>
        fun (H: aexp_stk_size_inc_rel n a a0) =>
          aexp_args_P' a a0 n) as P.
  pose (fun (n: nat) (l l0 : list aexp_stack) =>
        fun (H: args_stk_size_inc_rel n l l0) =>
          aexp_args_P0' l l0 n) as P0.

  apply (aexp_args_size_inc_rel_mut_ind P P0); intros; unfold P, P0 in *; unfold aexp_args_P', aexp_args_P0' in *; intros fenv stk stk' v STKLEN PURE SEM.
  - invs SEM. econstructor.
  - invs SEM. econstructor.
    + intuition.
    + rewrite app_length. intuition.
    + enough (Datatypes.length stk' <= k + Datatypes.length stk' - 1).
      * rewrite nth_error_app2; [ | assumption].
        enough (k + Datatypes.length stk' - 1 - Datatypes.length stk' = k - 1).
        -- rewrite H0. assumption.
        -- intuition.
      * intuition.
  - invs SEM.
    invs PURE.
    assert (stk1 = stk) by (eapply aexp_stack_pure_backwards with (a := a2); eassumption).
    subst. econstructor; [ eapply H | eapply H0]; eauto.
  - invs SEM. invs PURE.
    assert (stk1 = stk) by (eapply aexp_stack_pure_backwards with (a := a2); eassumption).
    subst.
    econstructor; [ eapply H | eapply H0 ]; eauto.
  - inversion SEM. revert H4 H6 H7. subst. invs PURE. intros.
    pose proof (H10 := H9).
    pose proof (H11 := H3).
    eapply args_stack_pure_implication in H10.
    rewrite H10 in *. clear H10.
    2: assumption.
    pose proof (IMP_FRAME_COPY := H4).
    eapply frame_stk in IMP_FRAME_COPY.
    2: rewrite H0; eassumption.

    pose proof (frame_implies_rest_stk_untouched_le).
    specialize (H2 (Body func) (Args (fenv f)) (Return_pop (fenv f)) (fenv) stk2 vals stk1).
    pose proof (IMP := H12).
    pose proof (IMP_FRAME := H4).
    rewrite <- H0 in H2.
    rewrite Nat.add_comm in H4.
    apply H2 in H4.
    destruct H4.
    destruct H4.
    rewrite H10 in *.
    econstructor.
    + eassumption.
    + eassumption.
    + eassumption.
    + eassumption.
    + eapply args_stk_size_inc_preserves_args_length.
      eassumption.
    + eapply H.
      * reflexivity.
      * assumption.
      * eassumption.
    + enough (imp_stack_sem (Body func) fenv (vals ++ stk' ++ stk1) (x ++ stk' ++ stk1)).
      eassumption.
      eapply frame_implies_intervening_stk_okay; [ | subst .. ].
      rewrite <- H0. rewrite Nat.add_comm in IMP_FRAME.
      eassumption.
      rewrite H8.
      eapply args_stack_sem_preserves_length.
      eassumption.
      symmetry.

      assumption.
      assumption.
    +
      eapply aexp_frame_pure.
      rewrite <- H0 in H6.
      rewrite <- H6 in H13.
      pose proof (AEXP_RET := H13).
      eapply pure_aexp_implies_same_stack in H13; [ | eassumption ].
      rewrite <- H13 in AEXP_RET.
      rewrite H6 in AEXP_RET.
      eassumption.
      rewrite H4.
      rewrite Nat.add_comm.
      rewrite <- H6. rewrite <- H0.
      assumption.
    + rewrite <- H8. rewrite <- H1. rewrite <- H0.
      eapply same_after_popping_length.
      eassumption.
    + eapply args_stack_sem_preserves_length in H9.
      rewrite <- H9. rewrite <- H0 in H8.
      assumption.
    + subst. eapply same_after_popping_implies_geq_popped_length.
      rewrite Nat.add_comm.
      aexp_pure (Return_expr (fenv f)). eauto.
    + rewrite H0. assumption.
  - invs SEM. econstructor.
  - invs SEM. invs PURE.
    aexp_pure arg.
    econstructor; [ eapply H | eapply H0]; eauto.
Qed.

Definition aexp_args_P (a a': aexp_stack) (n: nat): Prop :=
  forall (fenv: fun_env_stk) (stk stk': stack) (v: nat),
    List.length stk' = n ->
    aexp_well_formed a ->
    aexp_stack_pure_rel a' fenv ->
    aexp_stack_sem a' fenv (stk' ++ stk) (stk' ++ stk, v) ->
    aexp_stack_sem a fenv stk (stk, v).

Definition aexp_args_P0 (l l': list aexp_stack) (n: nat): Prop :=
  forall (fenv: fun_env_stk) (stk stk': stack) (vs: list nat),
    List.length stk' = n ->
    args_well_formed l ->
    args_stack_pure_rel l' fenv ->
    args_stack_sem l' fenv (stk' ++ stk) (stk' ++ stk, vs) ->
    args_stack_sem l fenv stk (stk, vs).

Lemma m_plus_n_minus_one_minus_n_is_m_minus_one :
  forall (m n: nat),
    m + n - 1 - n = m - 1.
Proof.
  intros. lia.
Qed.

Lemma aexp_args_stack_increase_preserves_eval'' :
  (forall (n: nat) (a a': aexp_stack),
      aexp_stk_size_inc_rel n a a' ->
      aexp_args_P a a' n) /\
    (forall (n: nat) (l l': list aexp_stack),
        args_stk_size_inc_rel n l l' ->
        aexp_args_P0 l l' n).
Proof.
  pose (fun (n: nat) (a a0: aexp_stack) =>
        fun (H: aexp_stk_size_inc_rel n a a0) =>
          aexp_args_P a a0 n) as P.
  pose (fun (n: nat) (l l0 : list aexp_stack) =>
        fun (H: args_stk_size_inc_rel n l l0) =>
          aexp_args_P0 l l0 n) as P0.
  apply (aexp_args_size_inc_rel_mut_ind P P0); intros; unfold P, P0 in *; unfold aexp_args_P, aexp_args_P0 in *; intros fenv stk stk' v STKLEN WF PURE SEM; inversion SEM.
  - econstructor.
  - econstructor. rewrite List.app_length in H2.
    destruct k, stk'.
    + simpl in H1. invs H1; simpl in H1; invs H1.
    + invs PURE.
      invs WF.
      invs H0.

    + intuition.
    + intuition.
    + rewrite List.app_length in H2. intuition.
    + rewrite nth_error_app2 in H5.
      * subst. rewrite m_plus_n_minus_one_minus_n_is_m_minus_one in H5. assumption.
      * rewrite <- STKLEN. destruct (Datatypes.length stk') eqn:len.
        -- subst. rewrite Nat.add_0_r. rewrite Nat.add_0_r in H1. intuition.
        -- rewrite Nat.add_comm. invs WF. intuition.
  - inversion WF. invs PURE. econstructor.
    + eapply H.
      * reflexivity.
      * eassumption.
      * assumption.
      * assert (stk1 = stk' ++ stk) by (eapply aexp_stack_pure_backwards with (a := a2'); eassumption). subst.
        eassumption.
    + eapply H0; eauto.
      assert (stk1 = stk' ++ stk) by (eapply aexp_stack_pure_backwards with (a := a2'); eassumption). subst. eassumption.
  - inversion WF. econstructor.
    + eapply H; [ | invs PURE .. ]; try eassumption.
      aexp_pure a1'. assumption.
    + eapply H0; [ | invs PURE .. ]; try eassumption.
      aexp_pure a2'. assumption.
  - revert H4 H5 H6 H7 H8. invs PURE.
    rewrite Nat.add_comm in H8.
    assert (Datatypes.length args' = Datatypes.length vals) by (eapply args_stack_sem_preserves_length; eassumption).
    pose proof (FRAME := H8).
    intros. rewrite <- H4 in * |- .
    rewrite H2 in H5.
    eapply frame_implies_rest_stk_untouched_le in H8; [ | rewrite H0 in H5; eassumption | | eassumption ].
    destruct H8.
    destruct H3. rewrite H8 in * |- .
    rewrite <- H3 in FRAME.
    assert (Datatypes.length args' = Datatypes.length vals) by (eapply args_stack_sem_preserves_length; eassumption).
    rewrite H10 in H5.
    rewrite H5 in FRAME.
    eapply frame_preserves_rest_stk with (stk := stk1) (stk' := stk) in FRAME; [ | assumption ].
    rewrite H4 in * |- .
    rewrite <- H10 in H5.
    rewrite <- H2 in H5.
    econstructor; try eassumption.
    + eapply args_stk_size_inc_preserves_args_length in a. rewrite <- a in H2. assumption.
    + eapply H.
      * assert (Datatypes.length stk' = Datatypes.length stk') by reflexivity. eassumption.
      * invs WF. assumption.
      * invs PURE. assumption.
      * invs PURE. invs WF. eapply args_size_inc_preserves_purity' in a; [ | eassumption .. ].
        args_pure args'.
        rewrite <- SAME in H9. eassumption.
    + rewrite H6 in H17. aexp_pure ret_expr.
      eapply aexp_frame_pure.
      eassumption.
      rewrite Nat.add_comm in H15. rewrite <- H3 in H15. assumption.
    + rewrite H5 in H3. rewrite H1 in H3. eapply same_after_popping_length. assumption.
    + clear P. clear P0. subst. aexp_pure (Return_expr (fenv f)).
      rewrite Nat.add_comm in H14. rewrite <- H5 in H14.
      eapply same_after_popping_implies_geq_popped_length; eauto.
  - subst. constructor.
  - subst. econstructor.
    + eapply H.
      * assert (Datatypes.length stk' = Datatypes.length stk') by reflexivity.
        eassumption.
      * invs WF. assumption.
      * invs PURE. assumption.
      * invs PURE.
        aexp_pure arg'.
        assumption.
    + eapply H0.
      * assert (Datatypes.length stk' = Datatypes.length stk') by reflexivity.
        eassumption.
      * invs WF. assumption.
      * invs PURE. assumption.
      * invs SEM.
        invs PURE.
        aexp_pure arg'. assumption.
Qed.

Lemma cons_is_append_singleton_list :
  forall (A: Type) (a: A) (l: list A),
    a :: l = (a :: nil) ++ l.
Proof.
  intros. induction l; auto.
Qed.

For every one of the following lemmas, there's a version with a ' after it

and a version without a ' after it. This is proving both directions of what

could have been a single theorem. Essentially, our semantics only cares

about the part of the stack that is actually used in an expression.

*

The versions with no ' are used for push, and the versions with ' are used

for pop.

Lemma aexp_stack_increase_preserves_eval :
  forall a a' fenv stk v,
    aexp_stk_size_inc_rel 1 a a' ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a fenv stk (stk, v) ->
    aexp_stack_sem a' fenv (0 :: stk) (0 :: stk, v).
Proof.
  pose proof (aexp_args_stack_increase_preserves_eval') as AEXP_ARGS.
  unfold aexp_args_P' in AEXP_ARGS.
  destruct AEXP_ARGS as (AEXP & _).
  intros. specialize (AEXP 1).
  enough (0 :: stk = (0 :: nil) ++ stk).
  - rewrite H2.
    eapply AEXP; try eassumption.
    reflexivity.
  - simpl. reflexivity.
Qed.

Also, all of the versions with ' have an extra clause about the well-formed-ness

of variables inside of the expression -- i.e., all of the stack variables of

the form k have k >= 1. That way, we essentially know that a' doesn't

reference 1, since all the variables in a' have been increased by 1.

Lemma aexp_stack_increase_preserves_eval' :
  forall a a' fenv stk v f,
    aexp_stk_size_inc_rel 1 a a' ->
    aexp_well_formed a ->
    aexp_stack_pure_rel a fenv ->
    aexp_stack_sem a' fenv (f :: stk) (f :: stk, v) ->
    aexp_stack_sem a fenv stk (stk, v).
Proof.
  pose proof (aexp_args_stack_increase_preserves_eval'') as AEXP_ARGS.
  unfold aexp_args_P in AEXP_ARGS.
  destruct AEXP_ARGS as (AEXP & _).
  intros. specialize (AEXP 1).
  rewrite cons_is_append_singleton_list in H2.
  eapply AEXP; try eassumption.
  reflexivity.
  eapply aexp_size_inc_preserves_purity; eassumption.
Qed.

Lemma args_stack_increase_preserves_eval :
  forall args args' fenv stk vals,
    args_stk_size_inc_rel 1 args args' ->
    args_stack_pure_rel args fenv ->
    args_stack_sem args fenv stk (stk, vals) ->
    args_stack_sem args' fenv (0 :: stk) (0 :: stk, vals).
Proof.
  pose proof (aexp_args_stack_increase_preserves_eval') as AEXP_ARGS.
  unfold aexp_args_P0' in AEXP_ARGS.
  destruct AEXP_ARGS as (_ & ARGS).
  intros. specialize (ARGS 1).
  rewrite cons_is_append_singleton_list.
  eapply ARGS; try eassumption.
  reflexivity.
Qed.

Lemma args_stack_increase_preserves_eval' :
  forall args args' fenv stk vals v,
    args_stk_size_inc_rel 1 args args' ->
    args_well_formed args ->
    args_stack_pure_rel args fenv ->
    args_stack_sem args' fenv (v :: stk) (v :: stk, vals) ->
    args_stack_sem args fenv stk (stk, vals).
Proof.
  pose proof (aexp_args_stack_increase_preserves_eval'') as AEXP_ARGS.
  unfold aexp_args_P0 in AEXP_ARGS.
  destruct AEXP_ARGS as (_ & ARGS).
  intros. specialize (ARGS 1).
  rewrite cons_is_append_singleton_list in H2.
  eapply ARGS; try eassumption.
  reflexivity.
  eapply args_size_inc_preserves_purity; eassumption.
Qed.

Lemma bexp_stack_increase_preserves_eval_stronger :
  forall b b' fenv stk stk' stk'' v n,
    bexp_stk_size_inc_rel n b b' ->
    n = Datatypes.length stk' ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b fenv stk (stk'', v) ->
    bexp_stack_sem b' fenv (stk' ++ stk) (stk' ++ stk'', v).
Proof.
  intros b b' fenv stk stk' stk'' v n H. revert v stk'' stk' stk fenv.
  dependent induction H; intros.
  - invs H1. constructor.
  - invs H1. constructor.
  - invs H2. invs H1. eapply Stack_neg; eauto.
  - invs H2. invs H3. eapply Stack_and; eauto.
  - invs H2. invs H3. eapply Stack_or; eauto.
  - invs H2. invs H3. aexp_pure a1. aexp_pure a2.
    pose proof aexp_args_stack_increase_preserves_eval'.
    unfold aexp_args_P' in H1. unfold aexp_args_P0 in H1.
    destruct H1. econstructor; eauto.
  - invs H2. invs H3. aexp_pure a1. aexp_pure a2.
    pose proof aexp_args_stack_increase_preserves_eval'.
    unfold aexp_args_P' in H1. unfold aexp_args_P0 in H1.
    destruct H1. econstructor; eauto.
Qed.

Lemma bexp_stack_increase_preserves_eval_strong :
  forall b b' fenv stk stk' v n,
    bexp_stk_size_inc_rel n b b' ->
    n = Datatypes.length stk' ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b fenv stk (stk, v) ->
    bexp_stack_sem b' fenv (stk' ++ stk) (stk' ++ stk, v).
Proof.
  intros. eapply bexp_stack_increase_preserves_eval_stronger; eauto.
Qed.

Lemma bexp_stack_increase_preserves_eval :
  forall b b' fenv stk v,
    bexp_stk_size_inc_rel 1 b b' ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b fenv stk (stk, v) ->
    bexp_stack_sem b' fenv (0 :: stk) (0 :: stk, v).
Proof.
  intros. rewrite cons_is_append_singleton_list.
  eapply bexp_stack_increase_preserves_eval_strong; eauto.
Qed.

Lemma app_inv:
  forall {A} (l1 l2 l3 : list A),
    l1 ++ l2 = l1 ++ l3 ->
    l2 = l3.
Proof.
  induction l1; intros; auto.
  inversion H. inversion H1. auto.
Qed.

Lemma bexp_stack_increase_preserves_eval_stronger' :
  forall b b' fenv stk stk' stk'' v n,
    bexp_stk_size_inc_rel n b b' ->
    n = Datatypes.length stk' ->
    bexp_well_formed b ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b' fenv (stk' ++ stk) (stk' ++ stk'', v) ->
    bexp_stack_sem b fenv stk (stk'', v).
Proof.
  intros b b' fenv stk stk' stk'' v n H. revert v stk stk' stk'' fenv.
  dependent induction H; intros.
  - invs H2. apply app_inv in H3. subst. constructor.
  - invs H2. apply app_inv in H3. subst. constructor.
  - invs H3. eapply Stack_neg; eauto.
    eapply IHbexp_stk_size_inc_rel; eauto.
    + invs H1. auto.
    + invs H2. auto.
  - invs H4. invs H3. invs H2.
    pose proof (bexp_size_inc_preserves_purity b1 b1' (Datatypes.length stk') fenv H H6).
    pose proof (bexp_size_inc_preserves_purity b2 b2' (Datatypes.length stk') fenv H0 H8).
    bexp_pure b1'. bexp_pure b2'. eapply Stack_and; eauto.
  - invs H4. invs H3. invs H2.
    pose proof (bexp_size_inc_preserves_purity b1 b1' (Datatypes.length stk') fenv H H6).
    pose proof (bexp_size_inc_preserves_purity b2 b2' (Datatypes.length stk') fenv H0 H8).
    bexp_pure b1'. bexp_pure b2'. eapply Stack_or; eauto.
  - invs H4. invs H3. invs H2.
    pose proof (aexp_size_inc_preserves_purity a1 a1' (Datatypes.length stk') fenv H H6).
    pose proof (aexp_size_inc_preserves_purity a2 a2' (Datatypes.length stk') fenv H0 H8).
    aexp_pure a1'. aexp_pure a2'.
    pose proof aexp_args_stack_increase_preserves_eval''.
    unfold aexp_args_P in H10. unfold aexp_args_P0 in H10. destruct H10.
    eapply app_inv in SAME; subst. econstructor; eauto.
  - invs H4. invs H3. invs H2.
    pose proof (aexp_size_inc_preserves_purity a1 a1' (Datatypes.length stk') fenv H H6).
    pose proof (aexp_size_inc_preserves_purity a2 a2' (Datatypes.length stk') fenv H0 H8).
    aexp_pure a1'. aexp_pure a2'.
    pose proof aexp_args_stack_increase_preserves_eval''.
    unfold aexp_args_P in H10. unfold aexp_args_P0 in H10. destruct H10.
    eapply app_inv in SAME; subst. econstructor; eauto.
Qed.

Lemma bexp_stack_increase_preserves_eval_strong' :
  forall b b' fenv stk stk' v n,
    bexp_stk_size_inc_rel n b b' ->
    n = Datatypes.length stk' ->
    bexp_well_formed b ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b' fenv (stk' ++ stk) (stk' ++ stk, v) ->
    bexp_stack_sem b fenv stk (stk, v).
Proof.
  intros. eapply bexp_stack_increase_preserves_eval_stronger'; eauto.
Qed.

Lemma bexp_stack_increase_preserves_eval' :
  forall b b' fenv stk v f,
    bexp_stk_size_inc_rel 1 b b' ->
    bexp_well_formed b ->
    bexp_stack_pure_rel b fenv ->
    bexp_stack_sem b' fenv (f :: stk) (f :: stk, v) ->
    bexp_stack_sem b fenv stk (stk, v).
Proof.
  intros. rewrite cons_is_append_singleton_list in H2.
  eapply bexp_stack_increase_preserves_eval_strong'; eauto. auto.
Qed.

Lemma bool_args_stack_increase_preserves_eval :
  forall a_list args' fenv stk vals0,
    transformed_prop_exprs_args (V := bool) (bexp_stk_size_inc_rel 1) a_list args' ->
    prop_args_rel (V := bool) (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) a_list ->
    eval_prop_args_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) a_list vals0 ->
    eval_prop_args_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv (0 :: stk) (0 :: stk, boolval)) args' vals0.
Proof.
  intros a_list args' fenv stk vals0 H. remember (bexp_stk_size_inc_rel 1).
  revert vals0 stk fenv.
  induction H; intros.
  - invs H0. constructor.
  - invs H2. invs H1. eapply RelArgsCons; eauto.
    eapply bexp_stack_increase_preserves_eval; eauto.
Qed.

Lemma bool_args_stack_increase_preserves_eval' :
  forall a_list args' fenv stk vals0 v,
    transformed_prop_exprs_args (V := bool) (bexp_stk_size_inc_rel 1) a_list args' ->
    bool_prop_args_wf a_list ->
    prop_args_rel (V := bool) (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) a_list ->
    eval_prop_args_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv (v :: stk) (v :: stk, boolval)) args' vals0 ->
    eval_prop_args_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) a_list vals0.
Proof.
  intros a_list args' fenv stk vals0 v H. remember (bexp_stk_size_inc_rel 1).
  revert vals0 stk fenv v.
  induction H; intros.
  - invs H0. invs H1. constructor.
  - invs H2. invs H3. invs H1. eapply RelArgsCons; eauto.
    eapply bexp_stack_increase_preserves_eval'; eauto.
Qed.

Lemma nat_args_stack_increase_preserves_eval :
  forall a_list args' fenv stk vals0,
    transformed_prop_exprs_args (V := nat) (aexp_stk_size_inc_rel 1) a_list args' ->
    prop_args_rel (V := nat) (fun (natexpr: aexp_stack) => aexp_stack_pure_rel natexpr fenv) a_list ->
    eval_prop_args_rel (fun (expr : aexp_stack) (val : nat) => aexp_stack_sem expr fenv stk (stk, val)) a_list vals0 ->
    eval_prop_args_rel (fun (expr : aexp_stack) (val : nat) => aexp_stack_sem expr fenv (0 :: stk) (0 :: stk, val)) args' vals0.
Proof.
  intros a_list args' fenv stk vals0 H. remember (aexp_stk_size_inc_rel 1).
  revert vals0 stk fenv.
  induction H; intros.
  - invs H0. constructor.
  - invs H2. invs H1. eapply RelArgsCons; eauto.
    eapply aexp_stack_increase_preserves_eval; eauto.
Qed.

Lemma nat_args_stack_increase_preserves_eval' :
  forall a_list args' fenv stk vals0 v,
    transformed_prop_exprs_args (V := nat) (aexp_stk_size_inc_rel 1) a_list args' ->
    nat_prop_args_wf a_list ->
    prop_args_rel (V := nat) (fun (natexpr: aexp_stack) => aexp_stack_pure_rel natexpr fenv) a_list ->
    eval_prop_args_rel (fun (expr : aexp_stack) (val : nat) => aexp_stack_sem expr fenv (v :: stk) (v :: stk, val)) args' vals0 ->
    eval_prop_args_rel (fun (expr : aexp_stack) (val : nat) => aexp_stack_sem expr fenv stk (stk, val)) a_list vals0.
Proof.
  intros a_list args' fenv stk vals0 v H. remember (aexp_stk_size_inc_rel 1).
  revert vals0 stk fenv v.
  induction H; intros.
  - invs H0. invs H1. constructor.
  - invs H2. invs H3. invs H1. eapply RelArgsCons; eauto.
    eapply aexp_stack_increase_preserves_eval'; eauto.
Qed.

Lemma logic_stack_increase_preserves_eval :
  forall p p' fenv stk,
    transformed_prop_exprs (bexp_stk_size_inc_rel 1) p p' ->
    prop_rel (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) p ->
    eval_prop_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) p ->
    eval_prop_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv (0 :: stk) (0 :: stk, boolval)) p'.
Proof.
  intros p p' fenv stk INC; dependent induction INC; intros PURE; intros; invs PURE.
  - econstructor.
  - invs H.
  - invs H0. econstructor.
    + pose proof (H4 := H3).
      eapply bexp_size_inc_preserves_purity in H3; [ | eassumption ].
      eapply bexp_stack_increase_preserves_eval; eassumption.
    + assumption.
  - invs H1.
    econstructor.
    + eapply bexp_stack_increase_preserves_eval; eassumption.
    + eapply bexp_stack_increase_preserves_eval; eassumption.
    + assumption.
  - invs H.
    econstructor.
    + eapply IHINC1; eauto.
    + eapply IHINC2; eauto.
  - invs H; econstructor; [eapply IHINC1; [reflexivity | .. ] | eapply IHINC2; [reflexivity | .. ]]; assumption.
  - invs H2.
    econstructor.
    1-3: eapply bexp_stack_increase_preserves_eval; eassumption; eassumption.
    assumption.
  - revert H. revert a_list'. induction a_list; intros.
    + invs H. invs H0. econstructor; [ | eassumption ].
      invs H5.
      econstructor.
    + invs H.
      invs H0. invs H6. invs PURE.
      econstructor.
      econstructor.
      eapply bexp_stack_increase_preserves_eval.
      eassumption.
      invs H4.
      assumption.
      eassumption.
      eapply bool_args_stack_increase_preserves_eval.
      eassumption.
      invs H4; eauto.
      eassumption.
      eassumption.
Qed.

Lemma logic_stack_increase_preserves_eval' :
  forall p p' fenv stk v,
    transformed_prop_exprs (bexp_stk_size_inc_rel 1) p p' ->
    bool_prop_wf p ->
    prop_rel (fun (boolexpr: bexp_stack) => bexp_stack_pure_rel boolexpr fenv) p ->
    eval_prop_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv (v :: stk) (v :: stk, boolval)) p' ->
    eval_prop_rel (fun (boolexpr : bexp_stack) (boolval : bool) => bexp_stack_sem boolexpr fenv stk (stk, boolval)) p.
Proof.
  intros p p' fenv stk v H. revert v stk fenv.
  remember (bexp_stk_size_inc_rel 1).
  induction H; intros.
  - econstructor.
  - invs H1.
  - invs H1. invs H2. invs H0. econstructor; eauto.
    eapply bexp_stack_increase_preserves_eval'; eauto.
  - invs H1. invs H2. invs H3. econstructor; eauto.
    eapply bexp_stack_increase_preserves_eval'; eauto.
    eapply bexp_stack_increase_preserves_eval'; eauto.
  - invs H1. invs H2. invs H3. econstructor; eauto.
  - invs H1. invs H2. invs H3.
    + eapply RelOrPropLeft; eauto.
    + eapply RelOrPropRight; eauto.
  - invs H2. invs H3. invs H4.
    econstructor; eauto; eapply bexp_stack_increase_preserves_eval'; eauto.
  - invs H1. invs H2. invs H0.
    econstructor; eauto. eapply bool_args_stack_increase_preserves_eval'; eauto.
Qed.

Lemma nat_logic_stack_increase_preserves_eval :
  forall p p' fenv stk,
    transformed_prop_exprs (aexp_stk_size_inc_rel 1) p p' ->
    prop_rel (fun (natexpr: aexp_stack) => aexp_stack_pure_rel natexpr fenv) p ->
    eval_prop_rel (fun (natexpr : aexp_stack) (natval : nat) => aexp_stack_sem natexpr fenv stk (stk, natval)) p ->
    eval_prop_rel (fun (natexpr : aexp_stack) (natval : nat) => aexp_stack_sem natexpr fenv (0 :: stk) (0 :: stk, natval)) p'.
Proof.
  intros p p' fenv stk H. revert stk fenv.
  remember (aexp_stk_size_inc_rel 1).
  induction H; intros.
  - econstructor.
  - invs H0.
  - invs H0. invs H1. econstructor; eauto.
    eapply aexp_stack_increase_preserves_eval; eauto.
  - invs H1. invs H2.
    econstructor; eauto; eapply aexp_stack_increase_preserves_eval; eauto.
  - invs H1. invs H2. econstructor; eauto.
  - invs H1. invs H2.
    + eapply RelOrPropLeft; eauto.
    + eapply RelOrPropRight; eauto.
  - invs H2. invs H3.
    econstructor; eauto; eapply aexp_stack_increase_preserves_eval; eauto.
  - invs H0. invs H1.
    econstructor; eauto.
    eapply nat_args_stack_increase_preserves_eval; eauto.
Qed.

Lemma nat_logic_stack_increase_preserves_eval' :
  forall p p' fenv stk v,
    transformed_prop_exprs (aexp_stk_size_inc_rel 1) p p' ->
    nat_prop_wf p ->
    prop_rel (fun (natexpr: aexp_stack) => aexp_stack_pure_rel natexpr fenv) p ->
    eval_prop_rel (fun (natexpr : aexp_stack) (natval : nat) => aexp_stack_sem natexpr fenv (v :: stk) (v :: stk, natval)) p' ->
    eval_prop_rel (fun (natexpr : aexp_stack) (natval : nat) => aexp_stack_sem natexpr fenv stk (stk, natval)) p.
Proof.
  intros p p' fenv stk v H. revert fenv v stk.
  remember (aexp_stk_size_inc_rel 1).
  induction H; intros.
  - econstructor.
  - invs H1.
  - invs H0. invs H1. invs H2. econstructor; eauto.
    eapply aexp_stack_increase_preserves_eval'; eauto.
  - invs H1. invs H2. invs H3.
    econstructor; eauto; eapply aexp_stack_increase_preserves_eval'; eauto.
  - invs H1. invs H2. invs H3. econstructor; eauto.
  - invs H1. invs H2. invs H3.
    + eapply RelOrPropLeft; eauto.
    + eapply RelOrPropRight; eauto.
  - invs H2. invs H3. invs H4.
    econstructor; eauto; eapply aexp_stack_increase_preserves_eval'; eauto.
  - invs H0. invs H1. invs H2.
    econstructor; eauto.
    eapply nat_args_stack_increase_preserves_eval'; eauto.
Qed.