Module Red

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 Inductive.

Strong pretty-big-step call-by-name semantics, with substitutions.

Inductive nfval :=
| nvar : nat -> nfval
| napp : nfval -> nfval_or_lam -> nfval
| nswitch : nfval -> list (nat * nfval_or_lam) -> nfval

with nfval_or_lam :=
| nval : nfval -> nfval_or_lam
| nlam : nfval_or_lam -> nfval_or_lam
| nconstr : nat -> list nfval_or_lam -> nfval_or_lam.

Inductive val : deep_flag -> Type :=
| vals_nf : nfval -> val shallow
| vals_abs : term -> val shallow
| vals_constr : nat -> list term -> val shallow
| vald_nf : nfval_or_lam -> val deep.

Definition val_nf {df} v : val df :=
  match df with
  | shallow => vals_nf v
  | deep => vald_nf (nval v)
  end.


Inductive ext : deep_flag -> Type :=
| ext_term : forall df, term -> ext df
| ext_app : forall df, out (val shallow) -> term -> ext df
| ext_appnf : forall df, nfval -> out (val deep) -> ext df
| ext_switch : forall df, out (val shallow) -> list (nat * term) -> ext df
| ext_switchnf : forall df, nfval -> list (nat * nfval_or_lam) -> option (nat * out (val deep)) -> list (nat * term) -> ext df
| extd_abs : out (val deep) -> ext deep
| extd_constr : nat -> list nfval_or_lam -> option (out (val deep)) -> list term -> ext deep.

Arguments ext_term {df} _.
Arguments ext_app {df} _ _.
Arguments ext_appnf {df} _ _.
Arguments ext_switch {df} _ _.
Arguments ext_switchnf {df} _ _ _ _.

Definition get_abort {df t} (e : ext df) : option (out t) :=
  match e with
  | ext_term _ => None
  | ext_app o _ => get_out_abort o
  | ext_appnf _ o => get_out_abort o
  | ext_switch o _ => get_out_abort o
  | ext_switchnf _ _ o _ => match o with Some (_, o) => get_out_abort o | None => None end
  | extd_abs o => get_out_abort o
  | extd_constr _ _ o _ => match o with Some o => get_out_abort o | None => None end
  end.

Inductive red_ (rec : forall df, ext df -> out (val df) -> Prop) : forall df, ext df -> out (val df) -> Prop :=
| red_var : forall df n,
    red_ rec df (ext_term (var n)) (out_ret (val_nf (nvar n))) (* Free variables reduce to themselves *)
| red_abs_shallow : forall t,
    red_ rec shallow (ext_term (abs t)) (out_ret (vals_abs t)) (* Do not look under abstractions *)
| red_abs_deep : forall t o1 o2,
    rec deep (ext_term t) o1 ->
    rec deep (extd_abs o1) o2 ->
    red_ rec deep (ext_term (abs t)) o2
| red_abs1 : forall v,
    red_ rec deep (extd_abs (out_ret (vald_nf v))) (out_ret (vald_nf (nlam v)))
| red_app : forall df t1 o1 t2 o2,
    rec shallow (ext_term t1) o1 ->
    rec df (ext_app o1 t2) o2 ->
    red_ rec df (ext_term (app t1 t2)) o2
| red_app1_nf : forall df v o1 t2 o2,
    rec deep (ext_term t2) o1 ->
    rec df (ext_appnf v o1) o2 ->
    red_ rec df (ext_app (out_ret (vals_nf v)) t2) o2
| red_app1_abs : forall df t1 t2 o,
    rec df (ext_term (subst1 t2 t1)) o ->
    red_ rec df (ext_app (out_ret (vals_abs t1)) t2) o
| red_appnf : forall df v1 v2,
    red_ rec df (ext_appnf v1 (out_ret (vald_nf v2))) (out_ret (val_nf (napp v1 v2)))
| red_constr_shallow : forall tag l,
    red_ rec shallow (ext_term (constr tag l)) (out_ret (vals_constr tag l))
| red_constr_deep : forall tag l o,
    rec deep (extd_constr tag nil None l) o ->
    red_ rec deep (ext_term (constr tag l)) o
| red_constr1_done : forall tag l,
    red_ rec deep (extd_constr tag l None nil) (out_ret (vald_nf (nconstr tag l)))
| red_constr1_step1 : forall tag l1 l2 t o1 o2,
    rec deep (ext_term t) o1 ->
    rec deep (extd_constr tag l1 (Some o1) l2) o2 ->
    red_ rec deep (extd_constr tag l1 None (t :: l2)) o2
| red_constr1_step2 : forall tag l1 l2 v o,
    rec deep (extd_constr tag (l1 ++ v :: nil) None l2) o ->
    red_ rec deep (extd_constr tag l1 (Some (out_ret (vald_nf v))) l2) o
| red_switch : forall df t o1 m o2,
    rec shallow (ext_term t) o1 ->
    rec df (ext_switch o1 m) o2 ->
    red_ rec df (ext_term (switch t m)) o2
| red_switch1_constr : forall df tag l m t o,
    nth_error m tag = Some (length l, t) ->
    rec df (ext_term (subst (read_env l) t)) o ->
    red_ rec df (ext_switch (out_ret (vals_constr tag l)) m) o
| red_switch1_nf : forall df v m o,
    rec df (ext_switchnf v nil None m) o ->
    red_ rec df (ext_switch (out_ret (vals_nf v)) m) o
| red_switchnf_done : forall df v m,
    red_ rec df (ext_switchnf v m None nil) (out_ret (val_nf (nswitch v m)))
| red_switchnf_step1 : forall df v m1 m2 p t o1 o2,
    rec deep (ext_term t) o1 ->
    rec df (ext_switchnf v m1 (Some (p, o1)) m2) o2 ->
    red_ rec df (ext_switchnf v m1 None ((p, t) :: m2)) o2
| red_switchnf_step2 : forall df m1 m2 v1 v2 p o,
    rec df (ext_switchnf v1 (m1 ++ (p, v2) :: nil) None m2) o ->
    red_ rec df (ext_switchnf v1 m1 (Some (p, out_ret (vald_nf v2))) m2) o
| red_abort : forall df e o,
    get_abort e = Some o -> red_ rec df e o.

Inductive red : forall df, ext df -> out (val df) -> Prop :=
| mkred : forall df e o, red_ red df e o -> red df e o.

CoInductive cored : forall df, ext df -> out (val df) -> Prop :=
| mkcored : forall df e o, red_ cored df e o -> cored df e o.

Lemma red_ind3 : is_ind3 red_ red.
Proof. prove_ind3. Qed.


Lemma red_not_div_preserved_down :
  preserved_down3 red_ red (fun df e o => o <> out_div).
Proof.   intros df e o Q H1 H2.
  inversion H1; unexistT; subst; unexistT; subst;
    repeat (match goal with [ H : ?A /\ Q _ _ _ |- _ ] => destruct H end);
    try (econstructor; (eassumption || tauto)).
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H0; inversion H0; unexistT; subst; simpl in *; autoinjSome; tauto.
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H; inversion H; unexistT; subst; simpl in *; autoinjSome; tauto.
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H; inversion H; unexistT; subst; simpl in *; autoinjSome; tauto.
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H0; inversion H0; unexistT; subst; simpl in *; autoinjSome; tauto.
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H; inversion H; unexistT; subst; simpl in *; autoinjSome; tauto.
  - econstructor; splits 3; try eassumption.
    intros ->. apply (ind3_inv _ _ red_ind3) in H; inversion H; unexistT; subst; simpl in *; autoinjSome; tauto.
Qed.