Module RedM

Require Import List.
Require Import Arith.
Require Import Freevar.
Require Import Misc.
Require Import Psatz.
Require Import Star.
Require Import Coq.Logic.Eqdep_dec.
Require Import FEnv.
Require Import STerm.
Require Import Pretty.
Require Import RedE.
Require Import Inductive.

Strong call-by-need pretty-big-step semantics.

Inductive eiM :=
| eiM_lazy : term -> list freevar -> eiM
| eiM_abs1 : term -> list freevar -> eiM
| eiM_abs2 : term -> list freevar -> nfvalx_or_lam -> eiM
| eiM_constr1 : nat -> list freevar -> eiM
| eiM_constr2 : nat -> list freevar -> nfvalx_or_lam -> eiM
| eiM_val : nfvalx -> eiM.
Definition memM := list (freevar * eiM).

Definition memdom (m : memM) := map fst m.

Notation memxM := (memM * list freevar)%type.

Inductive valM : deep_flag -> Type :=
| valM_nf : forall {df}, nfvalx -> valM df
| valMs_abs : term -> list freevar -> valM shallow
| valMd_abs : term -> list freevar -> nfvalx_or_lam -> valM deep
| valMs_constr : nat -> list freevar -> valM shallow
| valMd_constr : nat -> list freevar -> nfvalx_or_lam -> valM deep.


Inductive extM : deep_flag -> Type :=
| extM_term : forall df, list freevar -> memxM -> term -> extM df
| extM_clo : forall df, memxM -> freevar -> extM df
| extM_app : forall df, list freevar -> outM (valM shallow) memxM -> term -> extM df
| extM_appnf : forall df, list freevar -> nfvalx -> outM (valM deep) memxM -> extM df
| extM_switch : forall df, list freevar -> outM (valM shallow) memxM -> list (nat * term) -> extM df
| extM_switchnf1 : forall df, list freevar -> nfvalx -> list (list freevar * nfvalx_or_lam) -> memxM -> list (nat * term) -> extM df
| extM_switchnf2 : forall df, list freevar -> nfvalx -> list (list freevar * nfvalx_or_lam) -> list freevar -> outM (valM deep) memxM -> list (nat * term) -> extM df
| extMd_abs : list freevar -> freevar -> term -> outM (valM deep) memxM -> extM deep
| extMd_constr1 : nat -> list freevar -> list nfvalx_or_lam -> memxM -> list freevar -> extM deep
| extMd_constr2 : nat -> list freevar -> list nfvalx_or_lam -> outM (valM deep) memxM -> list freevar -> extM deep.

Arguments extM_term {df} _ _ _.
Arguments extM_clo {df} _ _.
Arguments extM_app {df} _ _ _.
Arguments extM_appnf {df} _ _ _.
Arguments extM_switch {df} _ _ _.
Arguments extM_switchnf1 {df} _ _ _ _ _.
Arguments extM_switchnf2 {df} _ _ _ _ _ _.

Definition get_out_memxM {df} (o : outM (valM df) memxM) :=
  match o with
  | outM_div => None
  | outM_ret _ m => Some m
  end.

Definition get_ext_memxM {df} (e : extM df) :=
  match e with
  | extM_term _ m _ => Some m
  | extM_clo m _ => Some m
  | extM_app _ o _ => get_out_memxM o
  | extM_appnf _ _ o => get_out_memxM o
  | extM_switch _ o _ => get_out_memxM o
  | extM_switchnf1 _ _ _ m _ => Some m
  | extM_switchnf2 _ _ _ _ o _ => get_out_memxM o
  | extMd_abs _ _ _ o => get_out_memxM o
  | extMd_constr1 _ _ _ m _ => Some m
  | extMd_constr2 _ _ _ o _ => get_out_memxM o
  end.

Definition update_mem (m : memxM) x u := (update_env (fst m) x u, snd m).

Inductive update_result : forall df, freevar -> outM (valM df) memxM -> outM (valM df) memxM -> Prop :=
| upr_nf : forall df x v m,
    update_result df x (outM_ret (valM_nf v) m) (outM_ret (valM_nf v) (update_mem m x (eiM_val v)))
| upr_shallow_abs : forall x t env m,
    update_result shallow x (outM_ret (valMs_abs t env) m) (outM_ret (valMs_abs t env) (update_mem m x (eiM_abs1 t env)))
| upr_deep_abs : forall x t env v m,
    update_result deep x (outM_ret (valMd_abs t env v) m) (outM_ret (valMd_abs t env v) (update_mem m x (eiM_abs2 t env v)))
| upr_shallow_constr : forall x tag l m,
    update_result shallow x (outM_ret (valMs_constr tag l) m) (outM_ret (valMs_constr tag l) (update_mem m x (eiM_constr1 tag l)))
| upr_deep_constr : forall x tag l v m,
    update_result deep x (outM_ret (valMd_constr tag l v) m) (outM_ret (valMd_constr tag l v) (update_mem m x (eiM_constr2 tag l v)))
| upr_div : forall df x, update_result df x outM_div outM_div.

Definition getvalMd_nf (v : valM deep) : nfvalx_or_lam :=
  match v return nfvalx_or_lam with
  | valM_nf v => nxval v
  | valMd_abs t l v => v
  | valMd_constr tag l v => v
  | valMs_abs _ _ => nxval (nxvar (proj1_sig (fresh nil)))
  | valMs_constr _ _ => nxval (nxvar (proj1_sig (fresh nil)))
  end.

Definition get_outM_abort {t1 t2 m} (o : outM t1 m) : option (outM t2 m) :=
  match o with
  | outM_div => Some outM_div
  | outM_ret _ _ => None
  end.

Definition get_abortM {df t} (e : extM df) : option (outM t memxM) :=
  match e with
  | extM_term _ _ _ => None
  | extM_clo _ _ => None
  | extM_app _ o _ => get_outM_abort o
  | extM_appnf _ _ o => get_outM_abort o
  | extM_switch _ o _ => get_outM_abort o
  | extM_switchnf1 _ _ _ _ _ => None
  | extM_switchnf2 _ _ _ _ o _ => get_outM_abort o
  | extMd_abs _ _ _ o => get_outM_abort o
  | extMd_constr1 _ _ _ _ _ => None
  | extMd_constr2 _ _ _ o _ => get_outM_abort o
  end.

Definition redM_mklazy t env (m1 : memxM) x (m2 : memxM) :=
  match env_get_maybe_var env t with
  | Some y => x = y /\ m2 = m1
  | None => env_find (fst m1) x = None /\ m2 = ((x, eiM_lazy t env) :: fst m1, snd m1)
  end.

Inductive redM_mknlazy : list term -> list freevar -> memxM -> list freevar -> memxM -> Prop :=
| redM_mknlazy_nil : forall env m, redM_mknlazy nil env m nil m
| redM_mknlazy_cons : forall t l env m1 x m2 xs m3,
    redM_mklazy t env m1 x m2 -> redM_mknlazy l env m2 xs m3 -> redM_mknlazy (t :: l) env m1 (x :: xs) m3.

Definition redM_newvar (m1 m2 : memxM) a x :=
  env_find (fst m1) a = None /\ x \notin snd m1 /\ m2 = ((a, eiM_val (nxvar x)) :: fst m1, x :: snd m1).

Inductive redM_nnewvar : memxM -> memxM -> list freevar -> list freevar -> nat -> Prop :=
| redM_nnewvar_0 : forall m, redM_nnewvar m m nil nil 0
| redM_nnewvar_S : forall m1 m2 m3 a la x lx n,
    redM_newvar m1 m2 a x -> redM_nnewvar m2 m3 la lx n -> redM_nnewvar m1 m3 (a :: la) (x :: lx) (S n).

Inductive redM_ (rec : forall df, extM df -> outM (valM df) memxM -> Prop) : forall df, extM df -> outM (valM df) memxM -> Prop :=
| redM_clo_val : forall df m x v,
    env_find (fst m) x = Some (eiM_val v) ->
    redM_ rec df (extM_clo m x) (outM_ret (valM_nf v) m)

| redM_clo_abs1_shallow : forall m x t env,
    env_find (fst m) x = Some (eiM_abs1 t env) ->
    redM_ rec shallow (extM_clo m x) (outM_ret (valMs_abs t env) m)
| redM_clo_abs1_deep : forall m x t env o1 o2,
    env_find (fst m) x = Some (eiM_abs1 t env) ->
    rec deep (extM_term env m (abs t)) o1 ->
    update_result deep x o1 o2 ->
    redM_ rec deep (extM_clo m x) o2
| redM_clo_abs2_shallow : forall m x t env body,
    env_find (fst m) x = Some (eiM_abs2 t env body) ->
    redM_ rec shallow (extM_clo m x) (outM_ret (valMs_abs t env) m)
| redM_clo_abs2_deep : forall m x t env body,
    env_find (fst m) x = Some (eiM_abs2 t env body) ->
    redM_ rec deep (extM_clo m x) (outM_ret (valMd_abs t env body) m)

| redM_clo_constr1_shallow : forall m x tag l,
    env_find (fst m) x = Some (eiM_constr1 tag l) ->
    redM_ rec shallow (extM_clo m x) (outM_ret (valMs_constr tag l) m)
| redM_clo_constr1_deep : forall m x tag l o1 o2,
    env_find (fst m) x = Some (eiM_constr1 tag l) ->
    rec deep (extMd_constr1 tag l nil m l) o1 ->
    update_result deep x o1 o2 ->
    redM_ rec deep (extM_clo m x) o2
| redM_clo_constr2_shallow : forall m x tag l v,
    env_find (fst m) x = Some (eiM_constr2 tag l v) ->
    redM_ rec shallow (extM_clo m x) (outM_ret (valMs_constr tag l) m)
| redM_clo_constr2_deep : forall m x tag l v,
    env_find (fst m) x = Some (eiM_constr2 tag l v) ->
    redM_ rec deep (extM_clo m x) (outM_ret (valMd_constr tag l v) m)

| redM_clo_lazy : forall df m x t env o1 o2,
    env_find (fst m) x = Some (eiM_lazy t env) ->
    rec df (extM_term env m t) o1 ->
    update_result df x o1 o2 ->
    redM_ rec df (extM_clo m x) o2

| redM_var_clo : forall df n env m x o,
    nth_error env n = Some x ->
    rec df (extM_clo m x) o ->
    redM_ rec df (extM_term env m (var n)) o

| redM_abs_shallow : forall t env m,
    redM_ rec shallow (extM_term env m (abs t)) (outM_ret (valMs_abs t env) m)
| redM_abs_deep : forall t env m1 m2 o1 o2 x a,
    redM_newvar m1 m2 a x ->
    rec deep (extM_term (a :: env) m2 t) o1 ->
    rec deep (extMd_abs env x t o1) o2 ->
    redM_ rec deep (extM_term env m1 (abs t)) o2
| redM_abs1 : forall x t env m v,
    redM_ rec deep (extMd_abs env x t (outM_ret v m)) (outM_ret (valMd_abs t env (nxlam x (getvalMd_nf v))) m)

| redM_app : forall df env m t1 o1 t2 o2,
    rec shallow (extM_term env m t1) o1 ->
    rec df (extM_app env o1 t2) o2 ->
    redM_ rec df (extM_term env m (app t1 t2)) o2
| redM_app1_nf : forall df env m v o1 t2 o2,
    rec deep (extM_term env m t2) o1 ->
    rec df (extM_appnf env v o1) o2 ->
    redM_ rec df (extM_app env (outM_ret (valM_nf v) m) t2) o2
| redM_app1_abs : forall df env env2 x m1 m2 t1 t2 o,
    redM_mklazy t2 env m1 x m2 ->
    rec df (extM_term (x :: env2) m2 t1) o ->
    redM_ rec df (extM_app env (outM_ret (valMs_abs t1 env2) m1) t2) o
| redM_appnf : forall df env m v1 v2,
    redM_ rec df (extM_appnf env v1 (outM_ret v2 m)) (outM_ret (valM_nf (nxapp v1 (getvalMd_nf v2))) m)

| redM_constr_shallow : forall env m1 m2 tag l lc,
    redM_mknlazy l env m1 lc m2 ->
    redM_ rec shallow (extM_term env m1 (constr tag l)) (outM_ret (valMs_constr tag lc) m2)
| redM_constr_deep : forall env m1 m2 tag l lc o,
    redM_mknlazy l env m1 lc m2 ->
    rec deep (extMd_constr1 tag lc nil m2 lc) o ->
    redM_ rec deep (extM_term env m1 (constr tag l)) o
| redM_constr1_done : forall m tag l l1,
    redM_ rec deep (extMd_constr1 tag l l1 m nil) (outM_ret (valMd_constr tag l (nxconstr tag l1)) m)
| redM_constr1_step : forall m tag l l1 l2 x o1 o2,
    rec deep (extM_clo m x) o1 ->
    rec deep (extMd_constr2 tag l l1 o1 l2) o2 ->
    redM_ rec deep (extMd_constr1 tag l l1 m (x :: l2)) o2
| redM_constr2 : forall m tag l l1 l2 v o,
    rec deep (extMd_constr1 tag l (l1 ++ getvalMd_nf v :: nil) m l2) o ->
    redM_ rec deep (extMd_constr2 tag l l1 (outM_ret v m) l2) o

| redM_switch : forall df m env t o1 s o2,
    rec shallow (extM_term env m t) o1 ->
    rec df (extM_switch env o1 s) o2 ->
    redM_ rec df (extM_term env m (switch t s)) o2
| redM_switch1_constr : forall df m env tag l s t o,
    nth_error s tag = Some (length l, t) ->
    rec df (extM_term (l ++ env) m t) o ->
    redM_ rec df (extM_switch env (outM_ret (valMs_constr tag l) m) s) o
| redM_switch1_nf : forall df m env v s o,
    rec df (extM_switchnf1 env v nil m s) o ->
    redM_ rec df (extM_switch env (outM_ret (valM_nf v) m) s) o
| redM_switchnf1_done : forall df m env v s,
    redM_ rec df (extM_switchnf1 env v s m nil) (outM_ret (valM_nf (nxswitch v s)) m)
| redM_switchnf1_step : forall df m1 m2 env v s1 s2 p t la lx o1 o2,
    redM_nnewvar m1 m2 la lx p ->
    rec deep (extM_term (la ++ env) m2 t) o1 ->
    rec df (extM_switchnf2 env v s1 lx o1 s2) o2 ->
    redM_ rec df (extM_switchnf1 env v s1 m1 ((p, t) :: s2)) o2
| redM_switchnf2 : forall df m env s1 s2 v1 v2 xs o,
    rec df (extM_switchnf1 env v1 (s1 ++ (xs, getvalMd_nf v2) :: nil) m s2) o ->
    redM_ rec df (extM_switchnf2 env v1 s1 xs (outM_ret v2 m) s2) o

| redM_abort : forall df e o, get_abortM e = Some o -> redM_ rec df e o.

Inductive redM : forall df, extM df -> outM (valM df) memxM -> Prop :=
| mkredM : forall df e o, redM_ redM df e o -> redM df e o.

CoInductive coredM : forall df, extM df -> outM (valM df) memxM -> Prop :=
| mkcoredM : forall df e o, redM_ coredM df e o -> coredM df e o.

Lemma redM_is_ind3 : is_ind3 redM_ redM.
Proof. prove_ind3. Qed.

Lemmas for proofs and stronger induction principles.

Lemma update_env_fst :
  forall {A : Type} (env : list (freevar * A)) x u, map fst env \subseteq map fst (update_env env x u).
Proof.   induction env as [|[y v] env]; intros x u z; simpl in *.
  - tauto.
  - destruct freevar_eq_dec.
    + simpl. subst. tauto.
    + simpl. specialize (IHenv x u z). tauto.
Qed.

Lemma update_result_memxM :
  forall df x m1 o1 o2, update_result df x o1 o2 -> get_out_memxM o1 = Some m1 -> exists m2, get_out_memxM o2 = Some m2 /\ memdom (fst m1) \subseteq memdom (fst m2) /\ snd m1 = snd m2.
Proof.   intros df x [m1 xs] o1 o2 H1 H2.
  inversion H1; unexistT; subst; unexistT; subst; simpl in *; autoinjSome;
    unfold update_mem in *; simpl in *; try (refine (ex_intro _ (_, _) _); splits 3; [reflexivity| |reflexivity]; apply update_env_fst).
  congruence.
Qed.

Definition is_divM {df} (e : extM df) :=
  match e with
  | extM_term _ _ _ => False
  | extM_clo _ _ => False
  | extM_app _ o _ => o = outM_div
  | extM_appnf _ _ o => o = outM_div
  | extM_switch _ o _ => o = outM_div
  | extM_switchnf1 _ _ _ _ _ => False
  | extM_switchnf2 _ _ _ _ o _ => o = outM_div
  | extMd_abs _ _ _ o => o = outM_div
  | extMd_constr1 _ _ _ _ _ => False
  | extMd_constr2 _ _ _ o _ => o = outM_div
  end.

Lemma get_out_memxM_div :
  forall df (o : outM (valM df) memxM), o = outM_div <-> get_out_memxM o = None.
Proof.   intros df o; destruct o; simpl; split; intros; congruence.
Qed.

Lemma is_divM_get_ext_memxM :
  forall df (e : extM df), is_divM e <-> get_ext_memxM e = None.
Proof.   intros df e; destruct e; simpl in *; try apply get_out_memxM_div; split; intros; (discriminate || tauto).
Qed.

Lemma update_result_not_div :
  forall df x o1 o2, update_result df x o1 o2 -> o2 = outM_div -> o1 = outM_div.
Proof.   intros df x o1 o2 H. inversion H; subst; unexistT; subst; try discriminate.
  intros; reflexivity.
Qed.

Lemma redM_not_div :
  forall df e o, redM df e o -> o = outM_div -> is_divM e.
Proof.   refine (preserved3_rec _ _ redM_is_ind3 _ _).
  intros df e o H; inversion H; unexistT; subst; unexistT; subst; try discriminate; try eassumption;
    try (match goal with [ H : update_result _ _ _ _ |- _ ] => assert (Hdiv := update_result_not_div _ _ _ _ H) end); simpl in *; try tauto.
  destruct e; simpl in *; try congruence; destruct o0; simpl in *; congruence.
Qed.


Lemma redM_mklazy_usedvars_eq :
  forall t env m1 x m2, redM_mklazy t env m1 x m2 -> snd m1 = snd m2.
Proof.   intros t env m1 x m2 H; unfold redM_mklazy in H.
  destruct env_get_maybe_var; destruct H as [_ ->]; reflexivity.
Qed.

Lemma redM_mknlazy_usedvars_eq :
  forall l env m1 xs m2, redM_mknlazy l env m1 xs m2 -> snd m1 = snd m2.
Proof.   intros l env m1 xs m2 H. induction H.
  - reflexivity.
  - erewrite redM_mklazy_usedvars_eq; eassumption.
Qed.

Lemma redM_newvar_usedvars_inc :
  forall m1 m2 x a, redM_newvar m1 m2 x a -> snd m1 \subseteq snd m2.
Proof.   intros m1 m2 x a (H1 & H2 & H3). subst. simpl. prove_list_inc.
Qed.

Lemma redM_nnewvar_usedvars_inc :
  forall m1 m2 lx la n, redM_nnewvar m1 m2 lx la n -> snd m1 \subseteq snd m2.
Proof.   intros m1 m2 lx la n H; induction H.
  - reflexivity.
  - rewrite <- IHredM_nnewvar. eapply redM_newvar_usedvars_inc; eassumption.
Qed.

Lemma redM_usedvars_inc :
  forall df e o, redM df e o -> forall m1, get_ext_memxM e = Some m1 -> match get_out_memxM o with Some m2 => snd m1 \subseteq snd m2 | None => False end.
Proof.   refine (preserved3_rec _ _ redM_is_ind3 _ _).
  intros df e o H m1 Hm1.
  inversion H; unexistT; subst; unexistT; subst; simpl in *; try discriminate; autoinjSome; try reflexivity.
  Local Ltac use_rec2 H :=
    let Hred := fresh "Hred" in
    let Hrec := fresh "Hrec" in
    destruct H as [Hred Hrec]; specialize (Hrec _ eq_refl).
  - use_rec2 H6. destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    eapply update_result_memxM in H7; [|eassumption].
    destruct H7 as (m2 & -> & ? & <-). assumption.
  - use_rec2 H6. destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    eapply update_result_memxM in H7; [|eassumption].
    destruct H7 as (m2 & -> & ? & <-). assumption.
  - use_rec2 H4. destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    eapply update_result_memxM in H5; [|eassumption].
    destruct H5 as (m2 & -> & ? & <-). assumption.
  - use_rec2 H4. assumption.
  - use_rec2 H6. simpl in *.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H7.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; [|eassumption]. etransitivity; [|eassumption].
    eapply redM_newvar_usedvars_inc; eassumption.
  - use_rec2 H3. simpl in *.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H4.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; eassumption.
  - use_rec2 H3. simpl in *.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H4.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; eassumption.
  - use_rec2 H4.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    erewrite redM_mklazy_usedvars_eq; eassumption.
  - erewrite redM_mknlazy_usedvars_eq; [|eassumption]. reflexivity.
  - use_rec2 H6. erewrite redM_mknlazy_usedvars_eq; eassumption.
  - use_rec2 H4.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H6.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; eassumption.
  - use_rec2 H2. eassumption.
  - use_rec2 H3.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H4.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; eassumption.
  - use_rec2 H4. eassumption.
  - use_rec2 H3. eassumption.
  - use_rec2 H4.
    destruct (get_out_memxM o1) eqn:Ho1; [|tauto].
    use_rec2 H5.
    destruct (get_out_memxM o) eqn:Ho; [|tauto].
    etransitivity; [|eassumption]. etransitivity; [|eassumption].
    eapply redM_nnewvar_usedvars_inc; eassumption.
  - use_rec2 H3. eassumption.
  - destruct e; simpl in *; try congruence; destruct o0; simpl in *; congruence.
Qed.

Lemma redM_abs_deep_result :
  forall env m t o, redM deep (extM_term env m (abs t)) o -> exists v m2, o = outM_ret (valMd_abs t env v) m2.
Proof.   intros env m t o H.
  inversion H; unexistT; subst. inversion H3; unexistT; subst; [|simpl in *; congruence].
  inversion H7; unexistT; subst. inversion H4; unexistT; subst.
  - eexists; eexists. reflexivity.
  - exfalso. apply redM_not_div in H6; simpl in *; [assumption|].
    apply redM_not_div in H7; simpl in *; [assumption|].
    destruct o1; simpl in *; congruence.
Qed.

Lemma redM_deep_constr_aux :
  forall df e o, redM df e o -> forall (p : df = deep) tag l v m,
      (let e := match p in _ = df return extM df with eq_refl => e end in (exists l1 m l2, e = extMd_constr1 tag l l1 m l2) \/ (exists l1 o l2, e = extMd_constr2 tag l l1 o l2)) ->
      o = outM_ret v m -> exists v2, match p in _ = df return valM df with eq_refl => v end = valMd_constr tag l v2.
Proof.   refine (preserved3_rec _ _ redM_is_ind3 _ _).
  intros df e o H p tag l v m [(l1 & m2 & l2 & He) | (l1 & o2 & l2 & He)] Hv;
  inversion H; unexistT; subst; unexistT; subst; try discriminate;
    repeat (match goal with [ H : _ /\ (forall (p : _ = deep), _) |- _ ] => let Hred := fresh "Hred" in let Hrec := fresh "Hrec" in destruct H as [Hred Hrec]; specialize (Hrec eq_refl); simpl in Hrec end); simpl in *.
  - injection H3 as; injection H5 as; subst. eexists; reflexivity.
  - eapply Hrec; [|reflexivity].
    injection H1 as; subst; right; repeat eexists; reflexivity.
  - eapply Hrec; [|reflexivity].
    injection H3 as; subst; left; repeat eexists; reflexivity.
  - destruct o2; simpl in *; congruence.
Qed.

Lemma redM_constr_deep_result :
  forall m tag l1 vs l2 o, redM deep (extMd_constr1 tag l1 vs m l2) o -> exists v m2, o = outM_ret (valMd_constr tag l1 v) m2.
Proof.   intros m tag l1 vs l2 o H.
  destruct o as [v m2|]; [|eapply redM_not_div in H; simpl in *; tauto].
  eapply redM_deep_constr_aux with (p := eq_refl) in H; simpl in *; [| |reflexivity].
  - destruct H as (v2 & ->). do 2 eexists. reflexivity.
  - left. do 3 eexists. reflexivity.
Qed.

Lemma redM_nnewvar_distinct :
  forall m1 m2 la lx p, redM_nnewvar m1 m2 la lx p -> distinct (snd m1) p lx.
Proof.   intros m1 m2 la lx p H. induction H.
  - constructor.
  - destruct H as (H1 & H2 & H3); subst m2; simpl in *.
    constructor; assumption.
Qed.

Lemma redM_nnewvar_incl :
  forall m1 m2 la lx p, redM_nnewvar m1 m2 la lx p -> lx \subseteq (snd m2).
Proof.   intros m1 m2 la lx p H. induction H.
  - prove_list_inc.
  - rewrite list_inc_cons_left. split; [|assumption].
    destruct H as (H1 & H2 & H3); subst m2; simpl in *.
    eapply redM_nnewvar_usedvars_inc; [eassumption|]. simpl. tauto.
Qed.