Module Qcert.cNNRC.Lang.cNNRCVars


Require Import String.
Require Import List.
Require Import Arith.
Require Import Max.
Require Import Peano_dec.
Require Import EquivDec.
Require Import Decidable.
Require Import Utils.
Require Import DataRuntime.
Require Import cNNRC.

Section cNNRCVars.
  Close Scope nnrc_scope.
  

  Context {fruntime:foreign_runtime}.

  Fixpoint nnrc_global_vars (e:nnrc) : list var :=
    match e with
    | NNRCGetConstant x => x :: nil
    | NNRCVar x => nil
    | NNRCConst d => nil
    | NNRCBinop bop e1 e2 => nnrc_global_vars e1 ++ nnrc_global_vars e2
    | NNRCUnop uop e1 => nnrc_global_vars e1
    | NNRCLet x e1 e2 => (nnrc_global_vars e1 ++ remove string_eqdec x (nnrc_global_vars e2))
    | NNRCFor x e1 e2 => (nnrc_global_vars e1 ++ remove string_eqdec x (nnrc_global_vars e2))
    | NNRCIf e1 e2 e3 => nnrc_global_vars e1 ++ nnrc_global_vars e2 ++ nnrc_global_vars e3
    | NNRCEither ed xl el xr er => nnrc_global_vars ed ++ (remove string_eqdec xl (nnrc_global_vars el)) ++ (remove string_eqdec xr (nnrc_global_vars er))
    | NNRCGroupBy g sl e => nnrc_global_vars e
    end.
  
  Fixpoint nnrc_bound_vars (e:nnrc) : list var :=
    match e with
    | NNRCGetConstant x => nil
    | NNRCVar x => nil
    | NNRCConst d => nil
    | NNRCBinop bop e1 e2 => nnrc_bound_vars e1 ++ nnrc_bound_vars e2
    | NNRCUnop uop e1 => nnrc_bound_vars e1
    | NNRCLet x e1 e2 => x :: (nnrc_bound_vars e1 ++ nnrc_bound_vars e2)
    | NNRCFor x e1 e2 => x :: (nnrc_bound_vars e1 ++ nnrc_bound_vars e2)
    | NNRCIf e1 e2 e3 => nnrc_bound_vars e1 ++ nnrc_bound_vars e2 ++ nnrc_bound_vars e3
    | NNRCEither ed xl el xr er => xl :: xr :: nnrc_bound_vars ed ++ nnrc_bound_vars el++ nnrc_bound_vars er
    | NNRCGroupBy g s e => nnrc_bound_vars e
    end.

  Fixpoint nnrc_free_vars (e:nnrc) : list var :=
    match e with
    | NNRCGetConstant x => nil
    | NNRCVar x => x :: nil
    | NNRCConst d => nil
    | NNRCBinop bop e1 e2 => nnrc_free_vars e1 ++ nnrc_free_vars e2
    | NNRCUnop uop e1 => nnrc_free_vars e1
    | NNRCLet x e1 e2 => (nnrc_free_vars e1 ++ remove string_eqdec x (nnrc_free_vars e2))
    | NNRCFor x e1 e2 => (nnrc_free_vars e1 ++ remove string_eqdec x (nnrc_free_vars e2))
    | NNRCIf e1 e2 e3 => nnrc_free_vars e1 ++ nnrc_free_vars e2 ++ nnrc_free_vars e3
    | NNRCEither ed xl el xr er => nnrc_free_vars ed ++ (remove string_eqdec xl (nnrc_free_vars el)) ++ (remove string_eqdec xr (nnrc_free_vars er))
    | NNRCGroupBy g sl e => nnrc_free_vars e
    end.

  Definition nnrc_vars e := nnrc_free_vars e ++ nnrc_bound_vars e.

  Fixpoint nnrc_subst (e:nnrc) (x:var) (e':nnrc) : nnrc
    := match e with
       | NNRCGetConstant y => NNRCGetConstant y
       | NNRCVar y => if y == x then e' else NNRCVar y
       | NNRCConst d => NNRCConst d
       | NNRCBinop bop e1 e2 => NNRCBinop bop
                                          (nnrc_subst e1 x e')
                                          (nnrc_subst e2 x e')
       | NNRCUnop uop e1 => NNRCUnop uop (nnrc_subst e1 x e')
       | NNRCLet y e1 e2 =>
         NNRCLet y
                 (nnrc_subst e1 x e')
                 (if y == x then e2 else nnrc_subst e2 x e')
       | NNRCFor y e1 e2 =>
         NNRCFor y
                 (nnrc_subst e1 x e')
                 (if y == x then e2 else nnrc_subst e2 x e')
       | NNRCIf e1 e2 e3 => NNRCIf
                               (nnrc_subst e1 x e')
                               (nnrc_subst e2 x e')
                               (nnrc_subst e3 x e')
       | NNRCEither ed xl el xr er =>
         NNRCEither (nnrc_subst ed x e')
                    xl
                    (if xl == x then el else nnrc_subst el x e')
                    xr
                    (if xr == x then er else nnrc_subst er x e')
       | NNRCGroupBy g sl e1 => NNRCGroupBy g sl (nnrc_subst e1 x e')
       end.

  Lemma nnrc_subst_not_free e x :
    ~ In x (nnrc_free_vars e) ->
    forall e', nnrc_subst e x e' = e.
Proof.
    induction e; simpl in *; intros.
    - intuition.
    - intuition. dest_eqdec; intuition.
    - intuition.
    - intuition; congruence.
    - intuition; congruence.
    - intuition. dest_eqdec; [congruence | ].
      f_equal; auto.
      apply nin_app_or in H.
      intuition.
      apply IHe2.
      intro inn; apply H2.
      apply remove_in_neq; auto.
    - intuition. dest_eqdec; [congruence | ].
      f_equal; auto.
      apply nin_app_or in H.
      destruct H.
      apply IHe2.
      intro inn; apply H1.
      apply remove_in_neq; auto.
    - repeat rewrite nin_app_or in H.
      intuition.
      congruence.
    - repeat rewrite nin_app_or in H.
      destruct H as [?[??]].
      rewrite IHe1; trivial.
      f_equal;dest_eqdec; trivial.
      + apply IHe2; rewrite remove_in_neq; eauto.
      + apply IHe3; rewrite remove_in_neq; eauto.
    - intuition; congruence.
  Qed.

  Lemma nnrc_subst_remove_one_free e x e' :
    nnrc_free_vars e' = nil ->
    nnrc_free_vars (nnrc_subst e x e') = remove string_eqdec x (nnrc_free_vars e).
Proof.
    intros Hfv_e'.
    nnrc_cases (induction e) Case; try reflexivity.
    - Case "NNRCVar"%string.
      simpl.
      case (equiv_dec v x).
      + intros Heq; rewrite Heq in *; clear Heq.
        destruct (string_eqdec x x); try congruence.
        assumption.
      + intros Heq.
        destruct (string_eqdec x v); try congruence.
        reflexivity.
    - Case "NNRCBinop"%string.
      simpl.
      rewrite IHe1.
      rewrite IHe2.
      apply list_remove_append_distrib.
    - Case "NNRCUnop"%string.
      simpl.
      rewrite IHe.
      reflexivity.
    - Case "NNRCLet"%string.
      simpl.
      rewrite IHe1.
      case (equiv_dec v x).
      + intros Heq.
        assert (v = x) as Hrew.
        rewrite Heq; reflexivity.
        rewrite Hrew in *; clear Hrew Heq.
        rewrite <- list_remove_append_distrib.
        rewrite list_remove_idempotent.
        reflexivity.
      + intros Heq.
        rewrite IHe2.
        rewrite <- list_remove_append_distrib.
        rewrite list_remove_commute.
        reflexivity.
    - Case "NNRCFor"%string.
      simpl.
      rewrite IHe1.
      case (equiv_dec v x).
      + intros Heq.
        assert (v = x) as Hrew.
        rewrite Heq; reflexivity.
        rewrite Hrew in *; clear Hrew Heq.
        rewrite <- list_remove_append_distrib.
        rewrite list_remove_idempotent.
        reflexivity.
      + intros Heq.
        rewrite IHe2.
        rewrite <- list_remove_append_distrib.
        rewrite list_remove_commute.
        reflexivity.
    - Case "NNRCIf"%string.
      simpl.
      rewrite IHe1.
      rewrite IHe2.
      rewrite IHe3.
      rewrite <- list_remove_append_distrib.
      rewrite <- list_remove_append_distrib.
      reflexivity.
    - Case "NNRCEither"%string.
      simpl.
      rewrite IHe1.
      case (equiv_dec v x).
      + intros Heq.
        assert (v = x) as Hrew.
        rewrite Heq; reflexivity.
        rewrite Hrew in *; clear Hrew Heq.
        rewrite <- list_remove_append_distrib.
        case (equiv_dec v0 x).
        * intros Heq.
          assert (v0 = x) as Hrew.
          rewrite Heq; reflexivity.
          rewrite Hrew in *; clear Hrew Heq.
          rewrite <- list_remove_append_distrib.
          rewrite list_remove_idempotent.
          rewrite list_remove_idempotent.
          reflexivity.
        * intros Heq.
          rewrite IHe3.
          rewrite <- list_remove_append_distrib.
          rewrite list_remove_idempotent.
          rewrite list_remove_commute.
          reflexivity.
      + intros Heq.
        case (equiv_dec v0 x).
        * intros Heq0.
          assert (v0 = x) as Hrew.
          rewrite Heq0; reflexivity.
          rewrite Hrew in *; clear Hrew Heq0.
          rewrite IHe2.
          rewrite <- list_remove_append_distrib.
          rewrite <- list_remove_append_distrib.
          rewrite list_remove_commute.
          rewrite list_remove_idempotent.
          reflexivity.
        * intros Heq0.
          rewrite IHe2.
          rewrite IHe3.
          rewrite <- list_remove_append_distrib.
          rewrite <- list_remove_append_distrib.
          rewrite list_remove_commute.
          rewrite (list_remove_commute v0 x).
          reflexivity.
    - Case "NNRCGroupBy"%string.
      simpl.
      rewrite IHe.
      reflexivity.
  Qed.

  Lemma nnrc_subst_not_in_free e x e':
    nnrc_free_vars e' = nil ->
    forall y,
      In y (nnrc_free_vars (nnrc_subst e x e')) -> x <> y.
Proof.
    intros Hfv_e'.
    nnrc_cases (induction e) Case.
    - Case "NNRCGetConstant"%string.
      contradiction.
    - Case "NNRCVar"%string.
      simpl.
      case (equiv_dec v x).
      + intros Heq y.
        rewrite Hfv_e'.
        contradiction.
      + intros Heq y.
        simpl.
        intros H; destruct H; try contradiction.
        rewrite H in *.
        congruence.
    - Case "NNRCConst"%string.
      contradiction.
    - Case "NNRCBinop"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        assumption.
      + apply IHe2 in H.
        assumption.
    - Case "NNRCUnop"%string.
      simpl.
      intros y Hy.
      apply IHe in Hy.
      assumption.
    - Case "NNRCLet"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy; auto.
      match_destr_in H; unfold equiv, complement in *; intros.
      + apply IHe2.
        rewrite e in H.
        rewrite nnrc_subst_remove_one_free; auto.
      + apply IHe2.
        apply remove_in_neq in H; auto.
        apply (list_in_remove_impl_diff _ _ (nnrc_free_vars (nnrc_subst e2 x e'))).
        assumption.
    - Case "NNRCFor"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1; auto.
      + match_destr_in H.
        * rewrite e in *; clear e.
          assert (y <> x); try congruence.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars e2)).
          assumption.
        * apply IHe2.
          apply remove_in_neq in H; auto.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars (nnrc_subst e2 x e'))).
          assumption.
    - Case "NNRCIf"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        assumption.
      + destruct H.
        * apply IHe2.
          assumption.
        * apply IHe3.
          assumption.
    - Case "NNRCEither"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        assumption.
      + rewrite in_app_iff in H.
        destruct H.
        * match_destr_in H.
          (* case "v = x" *)
          assert (v = x) as Hrew; [ rewrite e; reflexivity | ].
          rewrite Hrew in *; clear Hrew e.
          assert (y <> x); try congruence.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars e2)).
          assumption.
          (* case "v <> x" *)
          apply IHe2.
          apply remove_in_neq in H; auto.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars (nnrc_subst e2 x e'))).
          assumption.
        * match_destr_in H.
          (* case "v0 = x" *)
          assert (v0 = x) as Hrew; [ rewrite e; reflexivity | ].
          rewrite Hrew in *; clear Hrew e.
          assert (y <> x); try congruence.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars e3)).
          assumption.
          (* case "v <> x" *)
          apply IHe3.
          apply remove_in_neq in H; auto.
          apply (list_in_remove_impl_diff _ _ (nnrc_free_vars (nnrc_subst e3 x e'))).
          assumption.
    - Case "NNRCGroupBy"%string.
      simpl.
      intros y Hy.
      apply IHe in Hy.
      assumption.
  Qed.

  Lemma nnrc_subst_remove_free e x e':
    nnrc_free_vars e' = nil ->
    forall y,
      In y (nnrc_free_vars (nnrc_subst e x e')) -> In y (nnrc_free_vars e).
Proof.
    intros Hfv_e'.
    nnrc_cases (induction e) Case.
    - Case "NNRCGetConstant"%string.
      contradiction.
    - Case "NNRCVar"%string.
      simpl.
      case (equiv_dec v x); auto.
      intros Heq y; rewrite Heq in *; clear Heq.
      rewrite Hfv_e'.
      contradiction.
    - Case "NNRCConst"%string.
      contradiction.
    - Case "NNRCBinop"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        apply in_or_app; auto.
      + apply IHe2 in H.
        apply in_or_app; auto.
    - Case "NNRCUnop"%string.
      simpl.
      intros y Hy.
      apply IHe in Hy.
      assumption.
    - Case "NNRCLet"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply in_or_app; auto.
      + apply in_or_app; auto.
        right.
        match_destr_in H; auto.
        apply (list_remove_in _ _ (nnrc_free_vars (nnrc_subst e2 x e'))); auto.
    - Case "NNRCFor"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply in_or_app; auto.
      + apply in_or_app; auto.
        right.
        match_destr_in H; auto.
        apply (list_remove_in _ _ (nnrc_free_vars (nnrc_subst e2 x e'))); auto.
    - Case "NNRCIf"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        apply in_or_app; auto.
      + destruct H.
        apply IHe2 in H.
        apply in_or_app; auto.
        right.
        apply in_or_app; auto.
        apply IHe3 in H.
        apply in_or_app; auto.
        right.
        apply in_or_app; auto.
    - Case "NNRCEither"%string.
      intros y.
      simpl.
      rewrite in_app_iff.
      intros Hy.
      destruct Hy.
      + apply IHe1 in H.
        apply in_or_app; auto.
      + rewrite in_app_iff in H.
        destruct H.
        * match_destr_in H.
          (* case "v = x" *)
          assert (v = x) as Hrew; [ rewrite e; reflexivity | ].
          rewrite Hrew in *; clear Hrew e.
          apply in_or_app; auto.
          right.
          apply in_or_app; auto.
          (* case "v <> x" *)
          apply in_or_app; auto.
          right.
          apply in_or_app; auto.
          left.
          apply (list_remove_in _ _ (nnrc_free_vars (nnrc_subst e2 x e'))); auto.
        * match_destr_in H.
          (* case "v0 = x" *)
          assert (v0 = x) as Hrew; [ rewrite e; reflexivity | ].
          rewrite Hrew in *; clear Hrew e.
          apply in_or_app; auto.
          right.
          apply in_or_app; auto.
          (* case "v0 <> x" *)
          apply in_or_app; auto.
          right.
          apply in_or_app; auto.
          right.
          apply (list_remove_in _ _ (nnrc_free_vars (nnrc_subst e3 x e'))); auto.
    - Case "NNRCGroupBy"%string.
      simpl.
      intros y Hy.
      apply IHe in Hy.
      assumption.
  Qed.

  Definition really_fresh_in (sep:string) (oldvar:var) (avoid:list var) (e:nnrc)
    := fresh_var_from
         sep
         oldvar
         (avoid ++ nnrc_free_vars e ++ nnrc_bound_vars e).

  Lemma really_fresh_in_fresh (sep:string) (oldvar:var) (avoid:list var) (e:nnrc) :
    ~ In (really_fresh_in sep oldvar avoid e)
      (avoid ++ nnrc_free_vars e ++ nnrc_bound_vars e).
Proof.
    unfold really_fresh_in.
    apply fresh_var_from_fresh.
  Qed.
  
  Lemma really_fresh_from_avoid sep old avoid (e:nnrc) :
    ~ In (really_fresh_in sep old avoid e) avoid.
Proof.
    intros inn1.
    apply (really_fresh_in_fresh sep old avoid e).
    repeat rewrite in_app_iff; intuition.
  Qed.

  Lemma really_fresh_from_free sep old avoid (e:nnrc) :
    ~ In (really_fresh_in sep old avoid e) (nnrc_free_vars e).
Proof.
    intros inn1.
    apply (really_fresh_in_fresh sep old avoid e).
    repeat rewrite in_app_iff; intuition.
  Qed.

  Lemma really_fresh_from_bound sep old avoid (e:nnrc) :
    ~ In (really_fresh_in sep old avoid e) (nnrc_bound_vars e).
Proof.
    intros inn1.
    apply (really_fresh_in_fresh sep old avoid e).
    repeat rewrite in_app_iff; intuition.
  Qed.

  Lemma nnrc_bound_vars_subst_preserved e x e' :
    nnrc_bound_vars e' = nil ->
    nnrc_bound_vars (nnrc_subst e x e') = nnrc_bound_vars e.
Proof.
    induction e; simpl; intuition; repeat dest_eqdec; simpl; try congruence.
  Qed.

  Lemma nnrc_free_vars_subst e v n: ~ In n (nnrc_bound_vars e) ->
                                    nnrc_free_vars ((nnrc_subst e v (NNRCVar n))) =
                                    replace_all (nnrc_free_vars e) v n.
Proof.
    unfold var.
    induction e; simpl; trivial;
      unfold equiv_dec, nat_eq_eqdec;
      unfold replace_all in *;try repeat rewrite map_app.
    - destruct (string_eqdec v0 v); simpl; trivial.
    - intros; f_equal; intuition.
    - intuition. f_equal; auto.
      destruct (string_eqdec v0 v); unfold equiv in *; subst.
      + rewrite (replace_all_remove_eq' (nnrc_free_vars e2) v n); trivial.
      + rewrite H2. apply replace_all_remove_neq; auto.
    - intuition. f_equal; auto.
      destruct (string_eqdec v0 v); unfold equiv in *; subst.
      + rewrite (replace_all_remove_eq' (nnrc_free_vars e2) v n); trivial.
      + rewrite H2. apply replace_all_remove_neq; auto.
    - intros. rewrite nin_app_or in H; destruct H as [? HH].
      rewrite nin_app_or in HH; intuition.
      rewrite H2, H3, H4; trivial.
    - intros.
      apply not_or in H. destruct H.
      apply not_or in H0. destruct H0.
      apply nin_app_or in H1; destruct H1.
      apply nin_app_or in H2; destruct H2.
      rewrite IHe1; trivial.
      f_equal.
      match_destr; match_destr; unfold equiv in *; subst;
        try rewrite (replace_all_remove_eq' (nnrc_free_vars e2) v n);
        try rewrite (replace_all_remove_eq' (nnrc_free_vars e3) v n);
        trivial;
        repeat rewrite <- (replace_all_remove_neq) by congruence;
        try rewrite IHe2 by congruence;
        try rewrite IHe3 by congruence;
        trivial.
      (* For some reason, rewrite does not work here.  I cannot figure out the cause *)
      + f_equal.
        generalize (replace_all_remove_neq (eqdec:=string_eqdec) (nnrc_free_vars e3) v1 v n); intros eqq.
        cut_to eqq ; [| intuition..].
        etransitivity; [| eapply eqq].
        trivial.
      + f_equal.
        generalize (replace_all_remove_neq (eqdec:=string_eqdec) (nnrc_free_vars e2) v0 v n); intros eqq.
        cut_to eqq ; [| intuition..].
        etransitivity; [| eapply eqq].
        trivial.
      + f_equal.
        * generalize (replace_all_remove_neq (eqdec:=string_eqdec) (nnrc_free_vars e2) v0 v n); intros eqq.
          cut_to eqq ; [| intuition..].
          etransitivity; [| eapply eqq].
          trivial.
        * generalize (replace_all_remove_neq (eqdec:=string_eqdec) (nnrc_free_vars e3) v1 v n); intros eqq.
          cut_to eqq ; [| intuition..].
          etransitivity; [| eapply eqq].
          trivial.
  Qed.

  Lemma nnrc_subst_eq e v : nnrc_subst e v (NNRCVar v) = e.
Proof.
    induction e; simpl; repeat match_destr; congruence.
  Qed.

  Lemma incl_letvars1 v e1 e2 env :
    incl (nnrc_vars (NNRCLet v e1 e2)) env ->
    incl (nnrc_vars e1) env.
Proof.
    intros inclvars.
    transitivity env; unfold incl, nnrc_vars in *;
      simpl in *; auto 2; intros.
    apply inclvars.
    rewrite in_app_iff in H.
    repeat rewrite in_app_iff.
    simpl; rewrite in_app_iff.
    destruct H; try tauto.
  Qed.
  
  Lemma incl_letvars2 v e1 e2 env :
    incl (nnrc_vars (NNRCLet v e1 e2)) env ->
    incl (nnrc_vars e2) (env).
Proof.
    intros inclvars.
    transitivity env; unfold incl, nnrc_vars in *;
      simpl in *; auto 2; intros.
    apply inclvars.
    rewrite in_app_iff in H.
    repeat rewrite in_app_iff.
    simpl; rewrite in_app_iff.
    destruct H; try tauto.
    destruct (a == v).
    - red in e; subst; auto.
    - left; right.
      apply remove_in_neq; auto.
  Qed.
  
  Section core.
    Lemma nnrc_subst_preserve_core n1 n2 v1 :
      nnrcIsCore n1 -> nnrcIsCore n2 -> nnrcIsCore (nnrc_subst n1 v1 n2).
Proof.
      induction n1; simpl; intros; intuition;
        try (destruct (equiv_dec v v1); trivial);
        destruct (equiv_dec v0 v1); trivial.
    Qed.

  End core.
  
  Section FreeVars.
    Definition nnrc_free_variables (q:nnrc) : list var := nnrc_free_vars q.
  End FreeVars.

End cNNRCVars.