Module NRAEnvRewrite


Section ROptimEnv.

  Require Import List String.
  Require Import ListSet.

  Require Import Utils BasicRuntime.

  Require Import NRARuntime NRAOptim.
  Require Import cNRAEnv cNRAEnvIgnore cNRAEnvEq.


  Local Open Scope nra_scope.
  Local Open Scope nraenv_core_scope.

  Context {fruntime:foreign_runtime}.


  Hint Resolve dnrec_sort_content.
  Lemma pull_nra_opt (p1 p2:nraenv_core) :
    (nra_of_nraenv_core p1) ≡ₐ (nra_of_nraenv_core p2) ->
    p1 ≡ₑ p2.
Proof.
    unfold nra_eq, nraenv_core_eq; intros.
    repeat rewrite unfold_env_nra_sort.
    rewrite H; eauto.
  Qed.


  Lemma envand_comm (p1 p2: nraenv_core) :
    p2p1 ≡ₑ p1p2.
Proof.
    apply pull_nra_opt.
    unfold nra_of_nraenv_core.
    rewrite and_comm; reflexivity.
  Qed.


  Lemma envunion_assoc (p1 p2 p3: nraenv_core):
    (p1p2) ⋃ p3 ≡ₑ p1 ⋃ (p2p3).
Proof.
    apply pull_nra_opt.
    unfold nra_of_nraenv_core.
    rewrite union_assoc; reflexivity.
  Qed.
  
  Lemma select_union_distr (q qq₂: nraenv_core) :
    σ⟨ q ⟩(q₁ ⋃ q₂) ≡ₑ σ⟨ q ⟩(q₁) ⋃ σ⟨ q ⟩(q₂).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _.
    simpl.
    generalize (h ⊢ₑ q₁ @ₑ xc;env) as d1.
    generalize (h ⊢ₑ q₂ @ₑ xc;env) as d2.
    intros.
    destruct d1; destruct d2; try (autorewrite with alg; reflexivity); simpl.
    destruct d; try reflexivity.
    destruct d0; simpl;
    try (destruct (lift_filter
                     (fun x' : data =>
                        match h ⊢ₑ q @ₑ x' ⊣ c;env with
                          | Some (dbool b) => Some b
                          | _ => None
                        end) l); simpl; reflexivity).
    induction l; simpl.
      destruct (lift_filter
         (fun x' : data =>
          match h ⊢ₑ q @ₑ x' ⊣ c;env with
          | Some (dbool b) => Some b
          | _ => None
          end) l0); reflexivity.
      generalize(h ⊢ₑ q @ₑ ac;env); intros.
      unfold bunion.
      rewrite lift_app_filter.
      destruct o; try reflexivity.
      destruct d; try reflexivity.
      revert IHl.
      generalize ((lift_filter
            (fun x' : data =>
             match h ⊢ₑ q @ₑ x' ⊣ c;env with
             | Some (dbool b0) => Some b0
             | _ => None
             end) l)).
      generalize (lift_filter
             (fun x' : data =>
              match h ⊢ₑ q @ₑ x' ⊣ c;env with
              | Some (dbool b0) => Some b0
              | _ => None
              end) l0).
      intros.
      destruct o0; try reflexivity. simpl.
      destruct o; try reflexivity.
      + destruct b; autorewrite with alg; reflexivity.
      + autorewrite with alg. reflexivity.
  Qed.


  Lemma envmap_singleton (p1 p2:nraenv_core) :
    χ⟨ p1 ⟩( ‵{| p2 |} ) ≡ₑ ‵{| p1p2 |}.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; try reflexivity; simpl.
    generalize (h ⊢ₑ p1 @ₑ dc;env); intros; simpl.
    destruct o; reflexivity.
  Qed.


  Lemma envmap_map_compose (p1 p2 p3:nraenv_core) :
    χ⟨ p1 ⟩( χ⟨ p2 ⟩( p3 ) ) ≡ₑ χ⟨ p1p2 ⟩( p3 ).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p3 @ₑ xc;env); intros.
    destruct o; try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    unfold olift, lift; simpl.
    induction l; try reflexivity; simpl.
    generalize (h ⊢ₑ p2 @ₑ ac;env); intros.
    destruct o; try reflexivity; simpl.
    revert IHl; generalize (rmap (nraenv_core_eval h c p2 env) l); intros.
    destruct o; try reflexivity; simpl.
    destruct (h ⊢ₑ p1 @ₑ dc;env); try reflexivity; simpl.
    revert IHl; generalize (rmap
              (fun x0 : data =>
               match h ⊢ₑ p2 @ₑ x0c;env with
               | Some x'0 => h ⊢ₑ p1 @ₑ x'0 ⊣ c;env
               | None => None
               end) l); intros.
    destruct o; try reflexivity; simpl in *.
    destruct (rmap (nraenv_core_eval h c p1 env) l0).
    inversion IHl; reflexivity.
    congruence.
    destruct (rmap (nraenv_core_eval h c p1 env) l0).
    congruence.
    reflexivity.
    revert IHl; generalize (rmap
              (fun x0 : data =>
               match h ⊢ₑ p2 @ₑ x0c;env with
               | Some x'0 => h ⊢ₑ p1 @ₑ x'0 ⊣ c;env
               | None => None
               end) l); intros.
    destruct o; try congruence.
    destruct (h ⊢ₑ p1 @ₑ dc;env); reflexivity.
  Qed.


  Lemma app_over_rec s p1 p2:
    ‵[| (s, p1) |] ◯ p2 ≡ₑ ‵[| (s, p1p2) |].
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma envflatten_coll_flatten p:
    ♯flatten(‵{| ♯flatten( p ) |}) ≡ₑ ♯flatten( p ).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); clear x p; intros.
    destruct o; try reflexivity; simpl.
    unfold olift; simpl.
    destruct d; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    unfold lift, rflatten in *; simpl in *.
    destruct a; try reflexivity.
    revert IHl; generalize (oflat_map
                (fun x : data =>
                 match x with
                 | dcoll y => Some y
                 | _ => None
                 end) l); intros.
    destruct o; try reflexivity; simpl.
    rewrite app_nil_r.
    reflexivity.
  Qed.
  
  
  Lemma envflatten_coll_coll p:
    ♯flatten(‵{| ‵{| p |} |}) ≡ₑ ‵{| p |}.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); clear x p; intros.
    destruct o; reflexivity.
  Qed.

  Lemma envflatten_nil :
    ♯flatten(‵{||}) ≡ₑ ‵{||}.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    trivial.
  Qed.

  
  Lemma envflatten_coll_map p1 p2 :
    ♯flatten(‵{| χ⟨ p1 ⟩( p2 ) |}) ≡ₑ χ⟨ p1 ⟩( p2 ).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); clear x p2; intros.
    destruct o; try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    unfold olift in *; simpl in *.
    generalize (h ⊢ₑ p1 @ₑ ac;env); clear a; intros.
    destruct o; try reflexivity; simpl.
    unfold lift in *; simpl in *.
    revert IHl; generalize (rmap (nraenv_core_eval h c p1 env) l); intros.
    destruct o; try reflexivity; try congruence.
    unfold lift_oncoll in *; simpl in *.
    rewrite app_nil_r.
    rewrite app_nil_r in IHl.
    inversion IHl; reflexivity.
  Qed.

  Lemma flatten_flatten_map_either_nil ppp₃ :
    ♯flatten( ♯flatten(χ⟨ANEither p₁ ‵{||} ◯ p₂⟩(p₃))) ≡ₑ
     ♯flatten( χ⟨ANEither( ♯flatten(p₁)) ‵{||} ◯ p₂⟩(p₃)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p₃ @ₑ xc; env); simpl; trivial.
    unfold olift.
    destruct d; simpl; trivial.
    induction l; simpl; trivial.
    unfold lift in *.
    destruct ( rmap
                (fun x0 : data =>
                 match h ⊢ₑ p₂ @ₑ x0c; env with
                 | Some (dleft dl) => h ⊢ₑ p₁ @ₑ dlc; env
                 | Some (dright _) => Some (dcoll nil)
                 | Some _ => None
                 | None => None
                 end) l); simpl in *;
    destruct (rmap
              (fun x0 : data =>
               match h ⊢ₑ p₂ @ₑ x0c; env with
               | Some (dleft dl) =>
                   match h ⊢ₑ p₁ @ₑ dlc; env with
                   | Some x'0 =>
                       lift_oncoll
                         (fun l1 : list data =>
                          match rflatten l1 with
                          | Some a' => Some (dcoll a')
                          | None => None
                          end) x'0
                   | None => None
                   end
               | Some (dright _) => Some (dcoll nil)
               | Some _ => None
               | None => None
               end) l); simpl in * .
    - destruct (h ⊢ₑ p₂ @ₑ ac; env); simpl; trivial.
      case_eq (rflatten l0);
        [intros ? eqq0 | intros eqq0]; rewrite eqq0 in *;
        (case_eq (rflatten l1);
         [intros ? eqq1 | intros eqq1]; rewrite eqq1 in *;
           simpl in IHl); try discriminate.
      + case_eq (rflatten l2);
        [intros ? eqq2 | intros eqq2]; try rewrite eqq2 in *;
        try discriminate.
        inversion IHl; clear IHl; subst.
        destruct d; simpl; trivial.
        * destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
          destruct d0; simpl; trivial.
          rewrite (rflatten_cons _ _ _ eqq0).
          simpl.
          { case_eq (rflatten l4);
            [intros ? eqq4 | intros eqq4]; simpl.
            - rewrite (rflatten_cons _ _ _ eqq1).
              rewrite (rflatten_app _ _ _ _ eqq4 eqq2).
              trivial.
            - rewrite (rflatten_app_none1 _ _ eqq4).
               trivial.
          }
        * rewrite (rflatten_cons _ _ _ eqq0).
          rewrite (rflatten_cons _ _ _ eqq1).
          simpl.
          rewrite eqq2.
          trivial.
      + case_eq (rflatten l2);
        [intros ? eqq2 | intros eqq2]; try rewrite eqq2 in *;
        try discriminate.
         destruct d; simpl; trivial.
        * destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
          destruct d0; simpl; trivial.
          rewrite (rflatten_cons _ _ _ eqq0).
          simpl.
          rewrite (rflatten_app_none2 _ _ eqq2).
          destruct (rflatten l3); simpl; trivial.
          rewrite (rflatten_cons_none _ _ eqq1).
          trivial.
        * rewrite (rflatten_cons _ _ _ eqq0).
          simpl.
          rewrite (rflatten_cons_none _ _ eqq1).
          rewrite eqq2.
          trivial.
      + destruct d; simpl; trivial.
        * destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
          rewrite (rflatten_cons_none _ _ eqq0).
          destruct d0; simpl; trivial.
          destruct (rflatten l2); simpl; trivial.
          rewrite (rflatten_cons_none _ _ eqq1).
          trivial.
        * rewrite (rflatten_cons_none _ _ eqq0).
          rewrite (rflatten_cons_none _ _ eqq1).
          trivial.
    - destruct (h ⊢ₑ p₂ @ₑ ac; env); simpl; trivial.
      destruct d; simpl; trivial.
      + destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
        destruct d0; simpl; trivial.
        case_eq (rflatten l0);
          [intros ? eqq0 | intros eqq0]; rewrite eqq0 in *; simpl in *.
        * case_eq (rflatten l2);
          [intros ? eqq2 | intros eqq2]; rewrite eqq2 in *; try discriminate.
          rewrite (rflatten_cons _ _ _ eqq0); simpl.
          rewrite (rflatten_app_none2 _ _ eqq2).
          destruct (rflatten l1); trivial.
        * rewrite (rflatten_cons_none _ _ eqq0).
          destruct (rflatten l1); trivial.
      + case_eq (rflatten l0);
            [intros ? eqq0 | intros eqq0]; rewrite eqq0 in *; simpl in *.
        * case_eq (rflatten l1);
          [intros ? eqq1 | intros eqq1]; rewrite eqq1 in *; try discriminate.
          rewrite (rflatten_cons _ _ _ eqq0); simpl.
          rewrite eqq1.
          trivial.
        * rewrite (rflatten_cons_none _ _ eqq0).
          trivial.
    - case_eq (rflatten l0);
      [intros ? eqq0 | intros eqq0];
       rewrite eqq0 in *; simpl in *; try discriminate.
      destruct (h ⊢ₑ p₂ @ₑ ac; env); simpl; trivial.
      destruct d; simpl; trivial.
      + destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
        destruct d0; simpl; trivial.
        destruct (rflatten l1); simpl; trivial.
        rewrite (rflatten_cons_none _ _ eqq0).
        trivial.
      + rewrite (rflatten_cons_none _ _ eqq0).
        trivial.
    - destruct (h ⊢ₑ p₂ @ₑ ac; env); simpl; trivial.
      destruct d; simpl; trivial.
      destruct (h ⊢ₑ p₁ @ₑ dc; env); simpl; trivial.
      destruct d0; simpl; trivial.
      destruct (rflatten l0); simpl; trivial.
  Qed.
      

  Lemma envflatten_coll_mergeconcat p1 p2:
    ♯flatten( ‵{| p1p2 |} ) ≡ₑ p1p2.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); clear p1; intros.
    generalize (h ⊢ₑ p2 @ₑ xc;env); clear x p2; intros.
    destruct o; destruct o0; try reflexivity; simpl.
    destruct d; destruct d0; try reflexivity; simpl.
    destruct (merge_bindings l l0); reflexivity.
  Qed.


  Lemma rmap_to_map l :
    rmap (fun x : data => Some (dcoll (x :: nil))) l =
    Some (map (fun x : data => (dcoll (x :: nil))) l).
Proof.
    induction l; [reflexivity|simpl; rewrite IHl; reflexivity].
  Qed.

  Lemma double_flatten_map_coll p1 p2 :
    ♯flatten(χ⟨ χ⟨ ‵{| ID |} ⟩( ♯flatten( p1 ) ) ⟩( p2 ))
            ≡ₑ χ⟨ ‵{| ID |} ⟩(♯flatten(χ⟨ ♯flatten( p1 ) ⟩( p2 ))).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); clear x p2; intros.
    destruct o; try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    unfold olift in *; simpl in *.
    induction l; try reflexivity; simpl.
    generalize (h ⊢ₑ p1 @ₑ ac;env); clear a; intros.
    destruct o; try reflexivity; simpl.
    destruct (lift_oncoll (fun l0 : list data => lift dcoll (rflatten l0)) d); try reflexivity; simpl.
    unfold lift in *; simpl in *.
    destruct ( rmap
           (fun x : data =>
            match h ⊢ₑ p1 @ₑ xc;env with
            | Some x' =>
                lift_oncoll
                  (fun l0 : list data =>
                   match rflatten l0 with
                   | Some a' => Some (dcoll a')
                   | None => None
                   end) x'
            | None => None
            end) l); try reflexivity; try congruence; simpl in *.
    - unfold rflatten, lift_oncoll in *; simpl in *.
      destruct d0; try reflexivity; try congruence; simpl in *.
      generalize (rmap_to_map l1); intros.
      rewrite H; clear H; simpl.
      destruct ( rmap
              (fun x : data =>
               match
                 match h ⊢ₑ p1 @ₑ xc;env with
                 | Some (dcoll l1) =>
                     match
                       oflat_map
                         (fun x0 : data =>
                          match x0 with
                          | dcoll y => Some y
                          | _ => None
                          end) l1
                     with
                     | Some a' => Some (dcoll a')
                     | None => None
                     end
                 | _ => None
                 end
               with
               | Some (dcoll l1) =>
                   match
                     rmap (fun x0 : data => Some (dcoll (x0 :: nil))) l1
                   with
                   | Some a' => Some (dcoll a')
                   | None => None
                   end
               | _ => None
               end) l
               ); try reflexivity; try congruence; simpl in *.
      destruct ((oflat_map
          (fun x : data =>
           match x with
           | dcoll y => Some y
           | _ => None
           end) l2)); try reflexivity; try congruence; simpl in *.
      destruct ( (oflat_map
          (fun x : data =>
           match x with
           | dcoll y => Some y
           | _ => None
           end) l0)); try reflexivity; try congruence; simpl in *.
      generalize (rmap_to_map l4); intros.
      rewrite H in *; clear H.
      generalize (rmap_to_map (l1++l4)); intros.
      rewrite H in *; clear H.
      inversion IHl; clear IHl H0.
      * rewrite map_app; simpl; reflexivity.
      * destruct ((oflat_map
                   (fun x : data =>
                      match x with
                        | dcoll y => Some y
                        | _ => None
                      end) l0)); try reflexivity; try congruence; simpl in *.
        generalize (rmap_to_map l3); intros.
        rewrite H in *; clear H; congruence.
      * destruct ((oflat_map
                     (fun x : data =>
                        match x with
                          | dcoll y => Some y
                          | _ => None
                        end) l0)); try reflexivity; try congruence; simpl in *.
        generalize (rmap_to_map l2); intros.
        rewrite H in *; clear H; congruence.
    - destruct d0; try reflexivity; try congruence; simpl in *.
      destruct (rmap (fun x : data => Some (dcoll (x :: nil))) l0); try reflexivity; try congruence; simpl.
      destruct ( rmap
              (fun x : data =>
               match
                 match h ⊢ₑ p1 @ₑ xc;env with
                 | Some x' =>
                     lift_oncoll
                       (fun l0 : list data =>
                        match rflatten l0 with
                        | Some a' => Some (dcoll a')
                        | None => None
                        end) x'
                 | None => None
                 end
               with
               | Some x' =>
                   lift_oncoll
                     (fun c1 : list data =>
                      match
                        rmap (fun x0 : data => Some (dcoll (x0 :: nil))) c1
                      with
                      | Some a' => Some (dcoll a')
                      | None => None
                      end) x'
               | None => None
               end) l); try reflexivity; try congruence; simpl in *.
      unfold rflatten in *; simpl.
      destruct (oflat_map
            (fun x : data =>
             match x with
             | dcoll y => Some y
             | _ => None
             end) l2); try reflexivity; try congruence; simpl in *.
  Qed.

  
  Lemma envflatten_map_coll p1 p2 :
    ♯flatten(χ⟨ ‵{| p1 |} ⟩( p2 )) ≡ₑ χ⟨ p1 ⟩( p2 ).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); clear x p2; intros.
    destruct o; try reflexivity.
    destruct d; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    generalize (h ⊢ₑ p1 @ₑ ac;env); clear a; intros; simpl.
    destruct o; try reflexivity; simpl.
    unfold olift in *.
    revert IHl.
    generalize (rmap
             (fun x : data =>
              match h ⊢ₑ p1 @ₑ xc;env with
              | Some x' => Some (dcoll (x' :: nil))
              | None => None
              end) l); generalize (rmap (nraenv_core_eval h c p1 env) l); intros.
    unfold lift in *; simpl.
    destruct o; destruct o0; simpl; try reflexivity; try congruence.
    - simpl in *.
      unfold rflatten in *; simpl in *.
      revert IHl.
      generalize ((oflat_map
                     (fun x : data =>
                        match x with
                          | dcoll y => Some y
                          | _ => None
                        end) l1)); intros; simpl in *.
      destruct o; try congruence.
      inversion IHl; clear IHl H0; reflexivity.
    - simpl in *.
      unfold rflatten in *; simpl.
      revert IHl.
      generalize ((oflat_map
                     (fun x : data =>
                        match x with
                          | dcoll y => Some y
                          | _ => None
                        end) l0)); intros; simpl in *.
      destruct o; try congruence; reflexivity.
  Qed.

  Lemma select_over_nil p₁ :
    σ⟨p₁⟩(ANConst (dcoll nil)) ≡ₑ ANConst (dcoll nil).
Proof.
    red; intros br env dn_env d dn_d.
    simpl; trivial.
  Qed.

  Lemma map_over_nil p₁ :
    χ⟨p₁⟩(ANConst (dcoll nil)) ≡ₑ ANConst (dcoll nil).
Proof.
    red; intros br env dn_env d dn_d.
    simpl; trivial.
  Qed.

  Lemma map_over_flatten pp₂ :
    χ⟨p₁⟩(♯flatten(p₂)) ≡ₑ (♯flatten(χ⟨χ⟨p₁⟩(ID)⟩(p₂))).
Proof.
    red; intros br c dn_c env dn_env d dn_d.
    simpl.
    destruct (br ⊢ₑ p₂ @ₑ dc;env); simpl; trivial.
    destruct d0; simpl; trivial.
    clear d dn_d.
    induction l; simpl; trivial.
    destruct a; simpl; trivial.
    case_eq (rflatten l); [intros ? eqq1 | intros eqq1];
    rewrite eqq1 in IHl; simpl in *;
    [rewrite (rflatten_cons _ _ _ eqq1) | rewrite (rflatten_cons_none _ _ eqq1)]
    ; (case_eq (rmap
                 (fun x : data =>
                  lift_oncoll
                    (fun c1 : list data =>
                     lift dcoll (rmap (nraenv_core_eval br c penv) c1)) x) l); [intros ? eqq2 | intros eqq2];
     rewrite eqq2 in IHl; simpl in * ).
    - apply lift_injective in IHl; [ | inversion 1; trivial].
      rewrite rmap_over_app.
      rewrite IHl.
      destruct (rmap (nraenv_core_eval br c penv) l0); simpl; trivial.
    - apply none_lift in IHl.
      rewrite rmap_over_app.
      rewrite IHl; simpl.
      destruct (rmap (nraenv_core_eval br c penv) l0); simpl; trivial.
    - clear IHl.
      cut False; [intuition | ].
      revert eqq1 l1 eqq2.
      induction l; simpl in *; try discriminate.
      destruct a; simpl in *; try discriminate.
      intros.
      unfold rflatten in *.
      simpl in *.
      apply none_lift in eqq1.
      specialize (IHl eqq1); clear eqq1.
      match_destr_in eqq2.
      apply some_lift in eqq2.
      destruct eqq2 as [? eqq2 ?]; subst.
      apply (IHl _ eqq2).
    - match_destr.
  Qed.
  
  Lemma select_over_flatten pp₂ :
    σ⟨p₁⟩(♯flatten(p₂)) ≡ₑ (♯flatten(χ⟨σ⟨p₁⟩(ID)⟩(p₂))).
Proof.
    red; intros br c dn_c env dn_env d dn_d.
    simpl.
    destruct (br ⊢ₑ p₂ @ₑ dc;env); simpl; trivial.
    destruct d0; simpl; trivial.
    clear d dn_d.
    induction l; simpl; trivial.
    simpl.
    destruct a; simpl; trivial.
    case_eq (rflatten l); [intros ? eqq1 | intros eqq1];
    rewrite eqq1 in IHl; simpl in *;
    [rewrite (rflatten_cons _ _ _ eqq1) | rewrite (rflatten_cons_none _ _ eqq1)];
    (case_eq (rmap
                (fun x : data =>
                 lift_oncoll
                   (fun c1 : list data =>
                    lift dcoll
                      (lift_filter
                         (fun x' : data =>
                          match br ⊢ₑ p₁ @ₑ x' ⊣ c;env with
                          | Some (dbool b) => Some b
                          | Some _ => None
                          | None => None
                          end) c1)) x) l); [intros ? eqq2 | intros eqq2];
     rewrite eqq2 in IHl; simpl in * ).
    - apply lift_injective in IHl; [ | inversion 1; trivial].
      rewrite lift_app_filter.
      rewrite IHl.
      destruct ( (lift_filter
           (fun x' : data =>
            match br ⊢ₑ p₁ @ₑ x' ⊣ c;env with
            | Some (dbool b) => Some b
            | Some _ => None
            | None => None
            end) l0)); simpl; trivial.
    - apply none_lift in IHl.
      rewrite lift_app_filter.
      rewrite IHl; simpl.
      destruct (lift_filter
           (fun x' : data =>
            match br ⊢ₑ p₁ @ₑ x' ⊣ c;env with
            | Some (dbool b) => Some b
            | Some _ => None
            | None => None
            end) l0); simpl; trivial.
    - clear IHl.
      cut False; [intuition | ].
      revert eqq1 l1 eqq2.
      induction l; simpl in *; try discriminate.
      destruct a; simpl in *; try discriminate.
      intros.
      unfold rflatten in *.
      simpl in *.
      apply none_lift in eqq1.
      specialize (IHl eqq1); clear eqq1.
      match_destr_in eqq2.
      apply some_lift in eqq2.
      destruct eqq2 as [? eqq2 ?]; subst.
      apply (IHl _ eqq2).
    - match_destr.
  Qed.

  Lemma select_over_either ppp₃ :
    σ⟨p₁⟩( ANEither pp₃) ≡ₑ ANEither (σ⟨p₁⟩(p₂)) (σ⟨p₁⟩(p₃)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    match_destr.
  Qed.

Now has a (better) typed variant for arbitrary p1/p2, see TOptimEnv

  Lemma envdot_from_duplicate_r s1 s2 p1 :
    (‵[| (s1, p1) |] ⊕ ‵[| (s2, p1) |])·s2 ≡ₑ p1.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); clear p1 x; intros.
    destruct o; try reflexivity.
    unfold olift; simpl.
    destruct (StringOrder.lt_dec s1 s2); try reflexivity; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s2 s2); try congruence; simpl.
    destruct (StringOrder.lt_dec s1 s2); try congruence; simpl.
    destruct (StringOrder.lt_dec s2 s1); try congruence; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s2 s1); try congruence; simpl.
    destruct (string_eqdec s2 s2); try congruence; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s2 s2); congruence.
  Qed.


  Lemma envdot_from_duplicate_l s1 s2 p1 :
    (‵[| (s1, p1) |] ⊕ ‵[| (s2, p1) |])·s1 ≡ₑ p1.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); clear p1 x; intros.
    destruct o; try reflexivity.
    unfold olift; simpl.
    destruct (StringOrder.lt_dec s1 s2); try reflexivity; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s1 s1); try congruence; simpl.
    destruct (string_eqdec s1 s2); try congruence; simpl.
    destruct (StringOrder.lt_dec s2 s1); try congruence; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s1 s1); try congruence; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s1 s2); try congruence; simpl.
    assert (StringOrder.eq s1 s2) by (apply lt_contr1; assumption).
    contradiction.
  Qed.

  Lemma envmap_over_either ppp₃ :
    χ⟨p₁⟩( ANEither pp₃) ≡ₑ ANEither (χ⟨p₁⟩(p₂)) (χ⟨p₁⟩(p₃)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    match_destr.
  Qed.

  Lemma envmap_over_either_app pppp₄:
    χ⟨p₁⟩( ANEither pp₃ ◯ p₄) ≡ₑ ANEither (χ⟨p₁⟩(p₂)) (χ⟨p₁⟩(p₃)) ◯ p₄.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    unfold olift.
    destruct (h ⊢ₑ p₄ @ₑ xc; env); simpl; trivial.
    destruct d; simpl; trivial.
  Qed.

  
  Lemma envmap_into_id p :
    χ⟨ ID ⟩(‵{| p |}) ≡ₑ ‵{| p |}.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); clear x p; intros.
    destruct o; reflexivity.
  Qed.

  

  
  Lemma envmap_into_id_flatten p :
    χ⟨ ID ⟩( ♯flatten(p) ) ≡ₑ ♯flatten(p).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); clear x p; intros.
    destruct o; try reflexivity; simpl.
    unfold lift_oncoll; simpl.
    destruct d; try reflexivity; simpl.
    unfold olift, lift; simpl.
    destruct (rflatten l); try reflexivity; clear l; simpl.
    induction l0; try reflexivity; simpl.
    unfold lift; simpl.
    revert IHl0; destruct (rmap (fun x: data => Some x) l0); congruence.
  Qed.


  Lemma product_singletons s1 s2 p1 p2:
    ‵{|‵[| (s1, p1) |] |} × ‵{| ‵[| (s2, p2) |] |} ≡ₑ
     ‵{|‵[| (s1, p1) |] ⊕ ‵[| (s2, p2) |] |}.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; destruct o0; reflexivity.
  Qed.

  
  Lemma app_over_id p:
    pID ≡ₑ p.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; reflexivity.
  Qed.

  
  Lemma appenv_over_env p:
    ENV ◯ₑ p ≡ₑ p.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct (h ⊢ₑ p @ₑ xc;env); reflexivity.
  Qed.

  
  Lemma app_over_id_l p:
    IDp ≡ₑ p.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma app_over_app p1 p2 p3:
    (p1p2) ◯ p3 ≡ₑ p1 ◯ (p2p3).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p3 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.


  Lemma app_over_unop u p1 p2:
    (ANUnop u p1) ◯ p2 ≡ₑ (ANUnop u (p1p2)).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.


  Lemma appenv_over_unop u p1 p2:
    (ANUnop u p1) ◯ₑ p2 ≡ₑ (ANUnop u (p1 ◯ₑ p2)).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  Lemma unop_over_either u pp₂ :
    ANUnop u (ANEither pp₂) ≡ₑ ANEither (ANUnop u p₁)(ANUnop u p₂).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    match_destr.
  Qed.

  Lemma unop_over_either_app u ppp₃ :
    ANUnop u (ANEither pp₂ ◯ p₃) ≡ₑ ANEither (ANUnop u p₁)(ANUnop u p₂) ◯ p₃.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    unfold olift.
    destruct (h ⊢ₑ p₃ @ₑ xc; env); simpl; trivial.
    destruct d; simpl; trivial.
  Qed.


  Lemma app_over_merge p1 p2:
    (ANEnvp1) ◯ p2 ≡ₑ ANEnv ⊗ (p1p2).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma app_over_binop_l b d p1 p2:
    (ANBinop b p2 (ANConst d) ◯ p1)
      ≡ₑ (ANBinop b (p2p1) (ANConst d)).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.
 
  
  Lemma app_concat_over_binop b p1 p2 p3 p4:
    (ANBinop b p1 p2 ◯ (p3p4) )
      ≡ₑ (ANBinop b (p1 ◯ (p3p4)) (p2 ◯ (p3p4))).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct (h ⊢ₑ p3 @ₑ xc;env); try reflexivity; simpl.
    destruct (h ⊢ₑ p4 @ₑ xc;env); try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    destruct d0; reflexivity.
  Qed.

  
  Lemma app_over_binop_env b p1 p2 s:
    (ANBinop b p1 p2 ◯ (ANUnop (ADot s) ANEnv))
      ≡ₑ (ANBinop b (p1 ◯ (ANUnop (ADot s) ANEnv)) (p2 ◯ (ANUnop (ADot s) ANEnv))).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct env; try reflexivity; simpl.
    destruct (edot l s); reflexivity.
  Qed.

  
  Lemma app_over_select p0 p1 p2:
    σ⟨ p1 ⟩( p2 ) ◯ p0 ≡ₑ σ⟨ p1 ⟩( p2p0 ).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p0 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma app_over_map p0 p1 p2:
    χ⟨ p1 ⟩( p2 ) ◯ p0 ≡ₑ χ⟨ p1 ⟩( p2p0 ).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p0 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.
  
  
  Lemma app_over_mapconcat p0 p1 p2:
    ⋈ᵈ⟨ p1 ⟩( p2 ) ◯ p0 ≡ₑ ⋈ᵈ⟨ p1 ⟩( p2p0 ).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p0 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.
  
  
  Lemma app_over_product p0 p1 p2:
    (p1 × p2) ◯ p0 ≡ₑ (p1p0) × (p2p0).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p0 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.
  
  
  Lemma appenv_over_map p0 p1 p2:
    nraenv_core_ignores_id p0 ->
    χ⟨ p1 ⟩( p2 ) ◯ₑ p0 ≡ₑ χ⟨ p1 ◯ₑ p0 ⟩( p2 ◯ₑ p0 ).
Proof.
    unfold nra_eq, nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    case_eq (h ⊢ₑ p0 @ₑ xc;env); intros; try reflexivity; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;d); try reflexivity; simpl.
    destruct d0; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    rewrite (nraenv_core_ignores_id_swap p0 H h c env a x).
    rewrite H0; simpl.
    destruct (h ⊢ₑ p1 @ₑ ac;d); try reflexivity; simpl.
    f_equal; unfold lift in *; simpl in *.
    destruct (rmap (nraenv_core_eval h c p1 d) l);
      destruct (rmap
            (fun x0 : data =>
             olift (fun env' : data => h ⊢ₑ p1 @ₑ x0c;env') (h ⊢ₑ p0 @ₑ x0c;env)) l); congruence.
  Qed.

  
  Lemma appenv_over_select p0 p1 p2:
    nraenv_core_ignores_id p0 ->
    σ⟨ p1 ⟩( p2 ) ◯ₑ p0 ≡ₑ σ⟨ p1 ◯ₑ p0 ⟩( p2 ◯ₑ p0 ).
Proof.
    unfold nra_eq, nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    case_eq (h ⊢ₑ p0 @ₑ xc;env); intros; try reflexivity; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;d); try reflexivity; simpl.
    destruct d0; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    rewrite (nraenv_core_ignores_id_swap p0 H h c env a x).
    rewrite H0; simpl.
    destruct (h ⊢ₑ p1 @ₑ ac;d); try reflexivity; simpl.
    destruct d0; try reflexivity.
    destruct b; try reflexivity.
    f_equal; unfold lift in *; simpl in *.
    destruct (lift_filter
       (fun x' : data =>
        match h ⊢ₑ p1 @ₑ x' ⊣ c;d with
        | Some (dbool b) => Some b
        | _ => None
        end) l); destruct (lift_filter
       (fun x' : data =>
        match
          olift (fun env' : data => h ⊢ₑ p1 @ₑ x' ⊣ c;env') (h ⊢ₑ p0 @ₑ x' ⊣ c;env)
        with
        | Some (dbool b) => Some b
        | _ => None
        end) l); simpl in *; try congruence.
    f_equal; unfold lift in *; simpl in *.
    destruct (lift_filter
       (fun x' : data =>
        match h ⊢ₑ p1 @ₑ x' ⊣ c;d with
        | Some (dbool b) => Some b
        | _ => None
        end) l); destruct (lift_filter
       (fun x' : data =>
        match
          olift (fun env' : data => h ⊢ₑ p1 @ₑ x' ⊣ c;env') (h ⊢ₑ p0 @ₑ x' ⊣ c;env)
        with
        | Some (dbool b) => Some b
        | _ => None
        end) l); simpl in *; try congruence.
  Qed.

  
  Lemma appenv_over_mapenv p:
    ANAppEnv (ANMapEnv (ANUnop AColl ANEnv)) (ANUnop AFlatten p) ≡ₑ (ANMap (ANUnop AColl ANID) (ANUnop AFlatten p)).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.


  Lemma appenv_over_mapenv_coll p:
    ANAppEnv (ANMapEnv (ANUnop AColl (ANUnop AColl ANEnv))) (ANUnop AFlatten p) ≡ₑ (ANMap (ANUnop AColl (ANUnop AColl ANID)) (ANUnop AFlatten p)).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    generalize (h ⊢ₑ p @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma appenv_over_mapenv_merge s:
    ANAppEnv (ANMapEnv (ANUnop AColl ((ENV) · s))) (ENVID)
             ≡ₑ ANMap (ANUnop AColl ((ID) · s)) (ENVID).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct x; reflexivity.
  Qed.

  
  Lemma appenv_over_env_merge_l p1 p2:
    nraenv_core_ignores_env p1 ->
    ANAppEnv (ENVp1) p2 ≡ₑ p2p1.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); try reflexivity; simpl.
    rewrite (nraenv_core_ignores_env_swap p1 H h c d env x).
    destruct (h ⊢ₑ p1 @ₑ xc;env); reflexivity.
  Qed.

  
  Lemma appenv_over_mapenv_merge2 s:
    ANAppEnv (ANMapEnv ((ENV) · s)) (ENVID)
             ≡ₑ ANMap ((ID) · s) (ENVID).
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct x; reflexivity.
  Qed.


  Lemma env_appenv p:
    (ENV) ◯ₑ p ≡ₑ p.
Proof.
    unfold nra_eq, nraenv_core_eq; intros; simpl.
    destruct (h ⊢ₑ p @ₑ xc;env); reflexivity.
  Qed.

  
  
  Lemma envmap_singleton_rec s1 s2 :
    χ⟨‵[| (s1, ID) |] ⟩( ‵{|(ID) · s2 |}) ≡ₑ ‵{|‵[| (s1, (ID) · s2) |] |}.
Proof.
    apply envmap_singleton.
  Qed.

  
  Lemma envmap_dot_singleton a p :
    χ⟨ ID·a ⟩( ‵{| p |} ) ≡ₑ ‵{| p·a |}.
Proof.
    apply envmap_singleton.
  Qed.

  
  Lemma envunnest_singleton s1 s2 s3 p1 p2 :
    s1 <> s2 /\ s2 <> s3 /\ s3 <> s1 ->
    χ⟨ ¬π[s1](ID) ⟩(
       ⋈ᵈ⟨ χ⟨‵[| (s2,ID) |] ⟩((IDs1) ⟩( ‵{|‵[| (s3,p1) |] ⊕ ‵[| (s1,‵{| p2 |}) |]|} )
     )
     ≡ₑ ‵{|‵[| (s3, p1) |] ⊕ ‵[| (s2, p2) |]|}.
Proof.
    intros.
    elim H; clear H; intros.
    elim H0; clear H0; intros.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); generalize(h ⊢ₑ p2 @ₑ xc;env); clear p1 p2 x; intros.
    destruct o; try reflexivity; simpl.
    - unfold olift, olift2; simpl.
      destruct o0; try reflexivity; simpl.
      destruct (StringOrder.lt_dec s3 s1); try reflexivity; simpl.
      unfold lift; simpl.
      unfold rmap_concat, oomap_concat; simpl.
      unfold edot; simpl.
      unfold string_eqdec.
      destruct (string_dec s1 s1); try reflexivity; simpl.
      unfold rremove; simpl.
      unfold rec_concat_sort; simpl.
      unfold string_eqdec.
      destruct (StringOrder.lt_dec s1 s2); try reflexivity; simpl.
      destruct (StringOrder.lt_dec s3 s1); simpl.
      destruct (string_dec s1 s3); simpl.
      congruence.
      destruct (string_dec s1 s1); simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; simpl.
      destruct (string_dec s1 s2); try congruence.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; simpl.
      destruct (string_dec s1 s2); try congruence.
      assert (StringOrder.lt s3 s2).
      apply RelationClasses.transitivity with (y := s1); assumption.
      contradiction.
      destruct (string_dec s1 s2); simpl.
      assert False; auto; contradiction.
      assert (StringOrder.lt s3 s2).
      apply RelationClasses.transitivity with (y := s1); assumption.
      contradiction.
      congruence.
      congruence.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s1); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s1); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s1); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s1); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s1); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s3); try reflexivity; try congruence; simpl.
      unfold lift; simpl.
      unfold rmap_concat, oomap_concat; simpl.
      unfold edot; simpl.
      unfold string_eqdec.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      unfold rremove; simpl.
      unfold rec_concat_sort; simpl.
      unfold string_eqdec.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      unfold lift; simpl.
      unfold rmap_concat, oomap_concat; simpl.
      unfold edot; simpl.
      unfold string_eqdec.
      destruct (string_dec s1 s1); try congruence; simpl.
      unfold rremove; simpl.
      unfold rec_concat_sort; simpl.
      unfold string_eqdec.
      destruct (StringOrder.lt_dec s1 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      assert (StringOrder.eq s1 s3) by (apply lt_contr1; assumption).
      congruence.
      assert (StringOrder.eq s1 s3) by (apply lt_contr1; assumption).
      congruence.
      unfold lift; simpl.
      unfold rmap_concat, oomap_concat; simpl.
      unfold edot; simpl.
      unfold string_eqdec.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      unfold rremove; simpl.
      unfold rec_concat_sort; simpl.
      unfold string_eqdec.
      destruct (StringOrder.lt_dec s3 s2); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s3); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s2 s1); try reflexivity; try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s3); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (StringOrder.lt_dec s1 s2); try reflexivity; try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s2); try congruence; simpl.
      destruct (string_dec s1 s3); try congruence; simpl.
      destruct (string_dec s1 s1); try congruence; simpl.
      assert (StringOrder.eq s1 s3) by (apply lt_contr1; assumption).
      congruence.
      assert (StringOrder.eq s2 s3) by (apply lt_contr1; assumption).
      congruence.
    - destruct o0; simpl; reflexivity.
  Qed.


  Lemma binop_over_rec_pair b p1 p2 a1 a2:
    a1 <> a2 ->
    (ANBinop b ((ID) · a1) ((ID) · a2))
      ◯ (‵[| (a1, p1) |] ⊕ ‵[| (a2, p2) |])
      ≡ₑ (ANBinop b p1 p2).
Proof.
    unfold nra_eq, nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); intros.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; destruct o0; try reflexivity; simpl.
    destruct (StringOrder.lt_dec a1 a2); simpl.
    * unfold edot; simpl.
      destruct (string_eqdec a1 a2); try congruence; simpl.
      destruct (string_eqdec a2 a2); try congruence; simpl;
      destruct (string_eqdec a1 a1); try congruence; simpl; reflexivity.
    * destruct (StringOrder.lt_dec a2 a1); try congruence; simpl.
      + unfold edot; simpl.
        destruct (string_eqdec a1 a1); try congruence; simpl.
        destruct (string_eqdec a2 a1); try congruence; simpl.
        destruct (string_eqdec a2 a2); simpl; congruence.
      + unfold edot; simpl.
        destruct (string_eqdec a1 a2); try congruence; simpl.
        assert False.
        generalize (StringOrder.compare_spec a1 a2); intros.
        elim H0; intros; contradiction.
        contradiction.
  Qed.

  
  Lemma concat_id_left p1 p2 s1 s2 d:
    (‵[| (s1, p1) |] ⊕ ‵[| (s2, ANConst d) |])
      ◯ p2
     ≡ₑ (‵[| (s1, p1p2) |] ⊕ ‵[| (s2, ANConst d) |]).
Proof.
    unfold nra_eq, nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  
  Lemma tostring_dstring s:
    (ANUnop AToString (ANConst (dstring s))) ≡ₑ (ANConst (dstring s)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; reflexivity.
  Qed.

  
  Lemma tostring_tostring p:
    (ANUnop AToString (ANUnop AToString p)) ≡ₑ (ANUnop AToString p).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p @ₑ xc;env); reflexivity.
  Qed.

  
  Lemma app_over_env_dot s:
    (ENV ◯ (ENV) · s) · s ≡ₑ (ENV) · s.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct env; try reflexivity; simpl.
    case_eq (RRelation.edot l s); intros; try reflexivity; assumption.
  Qed.

  Lemma app_over_appenv p1 p2 p3:
    nraenv_core_ignores_id p3 ->
    ((p3 ◯ₑ p2) ◯ p1) ≡ₑ p3 ◯ₑ (p2p1).
Proof.
    unfold nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); intros.
    destruct o; try reflexivity; simpl.
    generalize (h ⊢ₑ p2 @ₑ dc;env); intros.
    destruct o; try reflexivity; simpl.
    apply nraenv_core_ignores_id_swap; assumption.
  Qed.

  Lemma appenv_over_app_ie p1 p2 p3:
    nraenv_core_ignores_env p3 ->
    ((p3p2) ◯ₑ p1) ≡ₑ p3 ◯ (p2 ◯ₑ p1).
Proof.
    unfold nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p1 @ₑ xc;env); intros.
    destruct o; try reflexivity; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;d); simpl; trivial.
    apply nraenv_core_ignores_env_swap; assumption.
  Qed.

  Lemma app_over_appenv_over_mapenv p1 p2:
    (((ANMapEnv (‵{|ENV|})) ◯ₑ p1) ◯ p2) ≡ₑ
    (((ANMapEnv (‵{|ENV|})) ◯ₑ (p1p2) )).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    generalize (h ⊢ₑ p2 @ₑ xc;env); intros.
    destruct o; reflexivity.
  Qed.

  Lemma map_full_over_select_id p0 p1 p2:
    χ⟨ p0 ⟩(σ⟨ p1 ⟩(‵{| p2 |})) ≡ₑ χ⟨ p0p2 ⟩(σ⟨ p1p2 ⟩(‵{| ID |})).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    case_eq (h ⊢ₑ p2 @ₑ xc;env); intros; try reflexivity; simpl.
    destruct (h ⊢ₑ p1 @ₑ dc;env); try reflexivity; simpl.
    destruct d0; try reflexivity; simpl.
    case_eq b; intros; try reflexivity; simpl.
    rewrite H; simpl. reflexivity.
  Qed.

  Lemma compose_selects_in_mapenv p1 p2 :
    (♯flatten(ANMapEnv (χ⟨ENV⟩(σ⟨p1⟩( ‵{| ID |}))))) ◯ₑ (χ⟨ENV⟩(σ⟨p2⟩( ‵{| ID |})))
            ≡ₑ (χ⟨ENV⟩(σ⟨p1⟩(σ⟨p2⟩( ‵{| ID |})))).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    autorewrite with alg.
    destruct (h ⊢ₑ p2 @ₑ xc;env); try reflexivity.
    destruct d; try reflexivity.
    destruct b; try reflexivity.
    autorewrite with alg; simpl.
    destruct (h ⊢ₑ p1 @ₑ xc;env); try reflexivity.
    destruct d; try reflexivity.
    destruct b; reflexivity.
  Qed.

  
  Lemma flatten_through_appenv p1 p2 :
    ♯flatten(p1 ◯ₑ p2) ≡ₑ ♯flatten(p1) ◯ₑ p2.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); reflexivity.
  Qed.

  Lemma appenv_through_either qqq₃:
    nraenv_core_ignores_id q₃ ->
    ANEither qq₂ ◯ₑ q₃ ≡ₑ ANEither (q₁ ◯ₑ q₃) (q₂ ◯ₑ q₃).
Proof.
    intros.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    unfold olift.
    generalize (nraenv_core_ignores_id_swap qH h c env x); intros eqq.
    destruct (h ⊢ₑ q₃ @ₑ xc;env); simpl in * ;
    destruct x; simpl; trivial;
    specialize (eqq x); rewrite <- eqq; trivial.
  Qed.


  
  Lemma flatten_mapenv_coll p1:
    ♯flatten(ANMapEnv (‵{| p1 |})) ≡ₑ ANMapEnv p1.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct env; try reflexivity; simpl.
    autorewrite with alg.
    induction l; try reflexivity; simpl in *.
    destruct (h ⊢ₑ p1 @ₑ xc;a); try reflexivity; simpl.
    apply f_equal.
    revert IHl.
    destruct ((rmap (fun env' : data => h ⊢ₑ p1 @ₑ xc;env') l));
      destruct ((rmap
                   (fun env' : data =>
                      olift (fun d1 : data => Some (dcoll (d1 :: nil)))
                            (h ⊢ₑ p1 @ₑ xc;env')) l)); intros; simpl in *; try congruence.
    rewrite (rflatten_cons (d::nil) l1 l0).
    auto.
    destruct (rflatten l1); simpl in *; congruence.
    unfold lift in *.
    case_eq (rflatten l0); intros; simpl in *; try (rewrite H in *; congruence).
    unfold rflatten in *; simpl.
    rewrite H.
    reflexivity.
  Qed.

  Lemma flip_env1 p :
    χ⟨ENV⟩(σ⟨ p ⟩(‵{|ID|})) ◯ₑ ID ≡ₑ (σ⟨ p ⟩(‵{|ID|})) ◯ₑ ID.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p @ₑ xc;x); try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    destruct b; reflexivity.
  Qed.

  Lemma flip_env2 p :
    (σ⟨ p ⟩(‵{|ID|}) ◯ₑ ID) ≡ₑ σ⟨ p ◯ₑ ID ⟩(‵{|ID|}).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p @ₑ xc;x); reflexivity.
  Qed.

  Lemma flip_env3 b p1 p2 :
     (ANBinop b p1 p2) ◯ₑ ID ≡ₑ (ANBinop b (p1 ◯ₑ ID) (p2 ◯ₑ ID)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; reflexivity.
  Qed.

  Lemma flip_env4 p1 p2 s :
     (ANUnop (ADot s) p1) ◯ₑ p2 ≡ₑ (ANUnop (ADot s) (p1 ◯ₑ p2)).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); reflexivity.
  Qed.

  Lemma flip_env5 p1 p2:
    ♯flatten(χ⟨σ⟨p1⟩(‵{|ID|})⟩(p2)) ≡ₑ σ⟨p1⟩(p2).
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    autorewrite with alg.
    induction l; try reflexivity; simpl.
    destruct (h ⊢ₑ p1 @ₑ ac;env); try reflexivity; simpl.
    f_equal.
    destruct d; try reflexivity; simpl.
    destruct b; try reflexivity; simpl.
    - destruct ((rmap
                (fun x0 : data =>
                 lift dcoll
                   match
                     match h ⊢ₑ p1 @ₑ x0c;env with
                     | Some (dbool b) => Some b
                     | _ => None
                     end
                   with
                   | Some true => Some (x0 :: nil)
                   | Some false => Some nil
                   | None => None
                   end) l)); destruct (lift_filter
         (fun x' : data =>
          match h ⊢ₑ p1 @ₑ x' ⊣ c;env with
          | Some (dbool b) => Some b
          | _ => None
          end) l); simpl in *.
      rewrite rflatten_cons with (r' := l1).
      simpl.
      reflexivity.
      unfold lift in IHl.
      destruct (rflatten l0); try congruence.
      unfold lift in IHl.
      unfold rflatten in *; simpl in *.
      destruct (oflat_map
            (fun x0 : data =>
             match x0 with
             | dcoll y => Some y
             | _ => None
             end) l0); try congruence; try reflexivity.
      congruence.
      reflexivity.
    - destruct ((rmap
                (fun x0 : data =>
                 lift dcoll
                   match
                     match h ⊢ₑ p1 @ₑ x0c;env with
                     | Some (dbool b) => Some b
                     | _ => None
                     end
                   with
                   | Some true => Some (x0 :: nil)
                   | Some false => Some nil
                   | None => None
                   end) l)); destruct (lift_filter
         (fun x' : data =>
          match h ⊢ₑ p1 @ₑ x' ⊣ c;env with
          | Some (dbool b) => Some b
          | _ => None
          end) l); simpl in *.
      rewrite rflatten_cons with (r' := l1).
      reflexivity.
      unfold lift in *.
      destruct (rflatten l0); simpl in *.
      inversion IHl; reflexivity.
      congruence.
      unfold rflatten in *; simpl in *.
      destruct ((oflat_map
             (fun x0 : data =>
              match x0 with
              | dcoll y => Some y
              | _ => None
              end) l0)); simpl in *.
      congruence.
      reflexivity.
      congruence.
      reflexivity.
  Qed.

  Lemma flip_env7 p1 p2:
    nraenv_core_ignores_id p1 ->
    (ANMapEnv (‵{| p1 |})) ◯ₑ p2 ≡ₑ χ⟨‵{| p1 ◯ₑ ID |}⟩(p2).
Proof.
    unfold nraenv_core_eq; intros ? ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); try reflexivity; simpl.
    destruct d; try reflexivity; simpl.
    induction l; try reflexivity; simpl.
    rewrite (nraenv_core_ignores_id_swap p1 H h c a x a).
    destruct (h ⊢ₑ p1 @ₑ ac;a); try reflexivity; simpl.
    destruct ((rmap
             (fun env' : data =>
              olift (fun d1 : data => Some (dcoll (d1 :: nil)))
                    (h ⊢ₑ p1 @ₑ xc;env')) l));
      destruct ((rmap
           (fun x0 : data =>
            olift (fun d1 : data => Some (dcoll (d1 :: nil))) (h ⊢ₑ p1 @ₑ x0c;x0))
           l)); simpl in *; congruence.
  Qed.

  
  Lemma merge_concat_to_concat s1 s2 p1 p2:
    s1 <> s2 ->
    ‵[| (s1, p1)|] ⊗ ‵[| (s2, p2)|] ≡ₑ ‵{|‵[| (s1, p1)|] ⊕ ‵[| (s2, p2)|]|}.
Proof.
    intros.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p1 @ₑ xc;env); try reflexivity; simpl.
    destruct (h ⊢ₑ p2 @ₑ xc;env); try reflexivity; simpl.
    unfold merge_bindings.
    simpl.
    unfold compatible_with; simpl.
    destruct (EquivDec.equiv_dec s1 s2); try congruence.
    simpl.
    reflexivity.
  Qed.


  Lemma dot_over_rec s p :
    (‵[| (s, p)|]) · s ≡ₑ p.
Proof.
    unfold nraenv_core_eq; intros ? ? _ ? _ ? _; simpl.
    destruct (h ⊢ₑ p @ₑ xc;env); try reflexivity; simpl.
    unfold edot; simpl.
    destruct (string_eqdec s s); congruence.
  Qed.

  Lemma either_app_over_dleft ppd :
    (ANEither pp₂) ◯ (ANConst (dleft d)) ≡ₑ p₁ ◯ (ANConst d).
Proof.
    red; simpl; intros; reflexivity.
  Qed.

  Lemma either_app_over_dright ppd :
    (ANEither pp₂) ◯ (ANConst (dright d)) ≡ₑ p₂ ◯ (ANConst d).
Proof.
    red; simpl; intros; reflexivity.
  Qed.

  Lemma either_app_over_aleft ppp :
    (ANEither pp₂) ◯ (ANUnop ALeft p) ≡ₑ p₁ ◯ p.
Proof.
    red; simpl; intros.
    unfold olift.
    destruct (h ⊢ₑ p @ₑ xc;env); trivial.
  Qed.
  
  Lemma either_app_over_aright ppp :
    (ANEither pp₂) ◯ (ANUnop ARight p) ≡ₑ p₂ ◯ p.
Proof.
    red; simpl; intros.
    unfold olift.
    destruct (h ⊢ₑ p @ₑ xc;env); trivial.
  Qed.

optimizations for record projection
  Lemma rproject_over_concat sl p1 p2 :
    π[sl](p1p2)
          ≡ₑ π[sl](p1) ⊕ π[sl](p2).
Proof.
    red; simpl; intros.
    destruct (h ⊢ₑ p1 @ₑ xc;env);
      destruct (h ⊢ₑ p2 @ₑ xc;env).
    - destruct d; destruct d0; simpl; trivial.
      rewrite rproject_rec_sort_commute, rproject_app.
      trivial.
    - destruct x; destruct d; simpl; trivial.
    - destruct x; destruct d; simpl; trivial.
    - destruct x; simpl; trivial.
  Qed.

  Lemma rproject_over_const sl l :
    π[sl](ANConst (drec l)) ≡ₑ ANConst (drec (RRelation.rproject l sl)).
Proof.
    red; simpl; intros.
    rewrite rproject_rec_sort_commute.
    rewrite rproject_map_fst_same; intuition.
  Qed.
  
  Lemma rproject_over_rec_in sl s p :
    In s sl ->
    π[sl](‵[| (s, p) |]) ≡ₑ ‵[| (s, p) |].
Proof.
    red; simpl; intros.
    destruct (h ⊢ₑ p @ₑ xc;env); simpl; trivial.
    destruct (in_dec string_dec s sl); intuition.
  Qed.

  Lemma rproject_over_rec_nin sl s p :
    ~ In s sl ->
    π[sl](‵[| (s, p) |]) ≡ₑ ‵[||] ◯ p .
Proof.
    red; simpl; intros.
    destruct (h ⊢ₑ p @ₑ xc;env); simpl; trivial.
    destruct (in_dec string_dec s sl); intuition.
  Qed.
  
  Lemma rproject_over_rproject sl1 sl2 p :
    π[sl1](π[sl2](p)) ≡ₑ π[set_inter string_dec sl2 sl1](p).
Proof.
    red; simpl; intros.
    destruct (h ⊢ₑ p @ₑ xc;env); simpl; trivial.
    destruct d; simpl; trivial.
    rewrite rproject_rproject.
    trivial.
  Qed.

  Lemma rproject_over_either sl p1 p2 :
    π[sl](ANEither p1 p2) ≡ₑ ANEither (π[sl](p1)) (π[sl](p2)).
Proof.
    red; simpl; intros.
    destruct x; simpl; trivial.
  Qed.

  Lemma map_over_map_split ppp₃ :
    χ⟨χ⟨p₁ ⟩( p₂) ⟩( p₃) ≡ₑ χ⟨χ⟨p₁ ⟩( ID) ⟩(χ⟨p₂⟩(p₃)).
Proof.
    red; simpl; intros.
    destruct (h ⊢ₑ p₃ @ₑ xc; env); simpl; trivial.
    destruct d; simpl; trivial.
    unfold lift, olift.
    induction l; simpl; trivial.
    destruct (h ⊢ₑ p₂ @ₑ ac; env); simpl; trivial.
    destruct (rmap
              (fun x0 : data =>
               match h ⊢ₑ p₂ @ₑ x0c; env with
               | Some x'0 =>
                   lift_oncoll
                     (fun c1 : list data =>
                      match rmap (nraenv_core_eval h c penv) c1 with
                      | Some a' => Some (dcoll a')
                      | None => None
                      end) x'0
               | None => None
               end) l);
      destruct (rmap (nraenv_core_eval h c penv) l);
      simpl in * .
    - destruct (rmap
            (fun x0 : data =>
             lift_oncoll
               (fun c1 : list data =>
                match rmap (nraenv_core_eval h c penv) c1 with
                | Some a' => Some (dcoll a')
                | None => None
                end) x0) l1); try discriminate.
      inversion IHl; clear IHl; subst.
      trivial.
    - discriminate.
    - destruct (rmap
            (fun x0 : data =>
             lift_oncoll
               (fun c1 : list data =>
                match rmap (nraenv_core_eval h c penv) c1 with
                | Some a' => Some (dcoll a')
                | None => None
                end) x0) l0); try discriminate.
      trivial.
    - destruct ( lift_oncoll
         (fun c1 : list data =>
          match rmap (nraenv_core_eval h c penv) c1 with
          | Some a' => Some (dcoll a')
          | None => None
          end) d); trivial.
  Qed.
  
  Definition nodupA : nraenv_core -> Prop :=
    nraenv_core_always_ensures
      (fun d => match d with
                | dcoll dl => NoDup dl
                | _ => False
                end).
  
  Lemma dup_elim (q:nraenv_core) :
    nodupA q -> ANUnop ADistinct q ≡ₑ q.
Proof.
    intros nd.
    red; intros.
    simpl.
    case_eq (h ⊢ₑ q @ₑ xc; env); simpl; trivial; intros.
    specialize (nd h c dn_c env dn_env x dn_x d H).
    simpl in nd.
    match_destr_in nd; try tauto.
    rewrite rondcoll_dcoll.
    rewrite NoDup_bdistinct; trivial.
  Qed.

Some optimizations are best seen through outlining -- the opposite of inlining. This allows sharing of common sub-expressions. To enable this, we first define the "last" part of a computation.
  
returns: (last part of the computation, rest of it)
  Fixpoint nraenv_core_split_last (e:nraenv_core) : (nraenv_core*nraenv_core)
    := match e with
         | ANID => (ANID,ANID)
         | ANConst c => (ANID,ANConst c)
         | ANBinop b e1 e2 => (ANBinop b e1 e2,ANID)
         | ANUnop u e1 =>
           match nraenv_core_split_last e1 with
             | (ANID, r) => (ANUnop u r, ANID)
             | (e1last, e1rest) => (e1last, ANUnop u e1rest)
           end
         | ANMap e1 e2 =>
           match nraenv_core_split_last e2 with
             | (ANID, r) => (ANMap e1 e2, ANID)
             | (e2last, e2rest) => (e2last, ANMap e1 e2rest)
           end
         | ANMapConcat e1 e2 =>
           match nraenv_core_split_last e2 with
             | (ANID, r) => (ANMapConcat e1 e2, ANID)
             | (e2last, e2rest) => (e2last, ANMapConcat e1 e2rest)
           end
         | ANProduct e1 e2 => (ANProduct e1 e2,ANID)
         | ANSelect e1 e2 =>
           match nraenv_core_split_last e2 with
             | (ANID, r) => (ANSelect e1 e2, ANID)
             | (e2last, e2rest) => (e2last, ANSelect e1 e2rest)
           end
         | ANDefault e1 e2 => (ANDefault e1 e2,ANID)
         | ANEither l r => (ANEither l r, ANID)
         | ANEitherConcat l r =>
           (ANEitherConcat l r, ANID)
         | ANApp e1 e2 =>
           match nraenv_core_split_last e2 with
             | (ANID, r) => (e1, r)
             | (e2last, e2rest) => (e2last, ANApp e1 e2rest)
           end
         | ANGetConstant s => (ANID,ANGetConstant s)
         | ANEnv => (ANID,ANEnv)
         | ANAppEnv e1 e2 => (ANAppEnv e1 e2, ANID)
         | ANMapEnv e => (ANMapEnv e, ANID)
       end.

  
End ROptimEnv.



Hint Rewrite @envflatten_map_coll : nraenv_core_optim.
Hint Rewrite @envmap_into_id : nraenv_core_optim.
Hint Rewrite @envmap_into_id_flatten : nraenv_core_optim.


Hint Rewrite @envmap_map_compose : nraenv_core_optim.
Hint Rewrite @envmap_singleton : nraenv_core_optim.


Hint Rewrite @envflatten_coll_mergeconcat : nraenv_core_optim.
Hint Rewrite @envflatten_coll_map : nraenv_core_optim.
Hint Rewrite @envflatten_coll_flatten : nraenv_core_optim.
Hint Rewrite @envflatten_coll_coll : nraenv_core_optim.


Hint Rewrite @app_over_id : nraenv_core_optim.
Hint Rewrite @app_over_id_l : nraenv_core_optim.
Hint Rewrite @app_over_app : nraenv_core_optim.
Hint Rewrite @app_over_map : nraenv_core_optim.
Hint Rewrite @app_over_select : nraenv_core_optim.
Hint Rewrite @app_over_unop : nraenv_core_optim.
Hint Rewrite @app_over_binop_l : nraenv_core_optim.
Hint Rewrite @app_over_merge : nraenv_core_optim.
Hint Rewrite @app_over_rec : nraenv_core_optim.
Hint Rewrite @binop_over_rec_pair : nraenv_core_optim.
Hint Rewrite @concat_id_left : nraenv_core_optim.
Hint Rewrite @app_over_env_dot : nraenv_core_optim.
Hint Rewrite @app_over_appenv_over_mapenv : nraenv_core_optim.


Hint Rewrite @product_singletons : nraenv_core_optim.
Hint Rewrite @double_flatten_map_coll : nraenv_core_optim.
Hint Rewrite @tostring_dstring : nraenv_core_optim.
Hint Rewrite @tostring_tostring : nraenv_core_optim.


Hint Rewrite @env_appenv : nraenv_core_optim.
Hint Rewrite @appenv_over_mapenv : nraenv_core_optim.
Hint Rewrite @appenv_over_mapenv_coll : nraenv_core_optim.
Hint Rewrite @appenv_over_mapenv_merge : nraenv_core_optim.
Hint Rewrite @appenv_over_mapenv_merge2 : nraenv_core_optim.
Hint Rewrite @compose_selects_in_mapenv : nraenv_core_optim.
Hint Rewrite @flatten_through_appenv : nraenv_core_optim.
Hint Rewrite @flatten_mapenv_coll : nraenv_core_optim.

Hint Rewrite @either_app_over_dleft : nraenv_core_optim.
Hint Rewrite @either_app_over_dright : nraenv_core_optim.
Hint Rewrite @either_app_over_aleft : nraenv_core_optim.
Hint Rewrite @either_app_over_aright : nraenv_core_optim.

Hint Rewrite @rproject_over_concat : nraenv_core_optim.
Hint Rewrite @rproject_over_rec_in : nraenv_core_optim.
Hint Rewrite @rproject_over_rec_nin : nraenv_core_optim.
Hint Rewrite @rproject_over_rproject : nraenv_core_optim.
Hint Rewrite @rproject_over_either : nraenv_core_optim.