Module Qcert.Utils.Fresh


Support for creating and reasoning about fresh names (represented as strings).

Require Import String.
Require Import List.
Require Import Permutation.
Require Import Arith Min.
Require Import EquivDec.
Require Import Morphisms.
Require Import Lia.
Require Import CoqLibAdd.
Require Import ListAdd.
Require Import StringAdd.
Require Import Digits.
Require Import Lift.
Require Import Sublist.

Section Fresh.
  
  Section finder.
    Context {A:Type}.
    Context (incr:A->A).
    Context (f:A->bool).
    
    Fixpoint find_bounded (bound:nat) (init:A)
      := match bound with
         | O => None
         | S n =>
           if f init
           then Some init
           else find_bounded n (incr init)
         end.

    Lemma find_bounded_correct bound init y :
      find_bounded bound init = Some y ->
      f y = true.
Proof.
      revert init.
      induction bound; simpl; intros.
      - discriminate.
      - match_case_in H; intros eqq; rewrite eqq in H.
        + inversion H; congruence.
        + eauto.
    Qed.
  End finder.

  Lemma find_bounded_S_ge f bound init y :
    find_bounded S f bound init = Some y ->
    y >= init.
Proof.
    revert init y.
    induction bound; simpl; intros.
    - discriminate.
    - match_destr_in H.
      + inversion H; subst; lia.
      + specialize (IHbound (S init) _ H).
        lia.
  Qed.

  Lemma find_bounded_S_seq f bound init :
    find_bounded S f bound init = find f (seq init bound).
Proof.
    revert init.
    induction bound; simpl; trivial; intros.
    match_destr; auto.
  Qed.

  Lemma find_bounded_S_nin_finds' {A:Type} f {dec:EqDec A eq} (dom:list A)
        (bound:nat) (pf:bound > length dom)
        (inj:forall x y, f x = f y -> x = y) :
    {y:A | lift f (find_bounded S
                                (fun x =>
                                   if in_dec equiv_dec (f x) dom
                                   then false else true)
                                bound 0) = Some y}.
Proof.
    rewrite find_bounded_S_seq.
    rewrite <- (find_over_map (fun x => if in_dec equiv_dec x dom then false else true) f).
    apply find_fresh_from.
    - rewrite map_length.
      rewrite seq_length.
      trivial.
    - apply map_inj_NoDup; trivial.
      apply seq_NoDup.
  Defined.

  Definition find_bounded_S_nin_finds {A:Type} f {dec:EqDec A eq} (dom:list A)
             (bound:nat) (pf:bound > length dom)
             (inj:forall x y, f x = f y -> x = y) :
    {y:A | lift f (find_bounded S
                                (fun x =>
                                   if in_dec equiv_dec (f x) dom
                                   then false else true)
                                bound 0) = Some y}.
Proof.
    case_eq (find_bounded S
                          (fun x : nat => if in_dec equiv_dec (f x) dom then false else true)
                          bound 0).
    - intros; simpl.
      exists (f n); reflexivity.
    - destruct (find_bounded_S_nin_finds' f dom bound pf inj); intros.
      rewrite H in e.
      simpl in e. discriminate.
  Defined.
  
  Definition find_fresh_inj_f {A:Type} {dec:EqDec A eq} f (inj:forall x y, f x = f y -> x = y) (dom:list A) : A
    := proj1_sig (find_bounded_S_nin_finds f dom (S (length dom)) (gt_Sn_n _) inj).

  Lemma find_fresh_inj_f_fresh {A:Type} {dec:EqDec A eq} f (inj:forall x y, f x = f y -> x = y) (dom:list A) :
    ~ In (find_fresh_inj_f f inj dom) dom.
Proof.
    unfold find_fresh_inj_f.
    match goal with
    | [|- context[ proj1_sig ?x ]] => destruct x
    end; simpl.
    apply some_lift in e.
    destruct e as [? e ?]; subst.
    apply find_bounded_correct in e.
    match_destr_in e.
  Qed.

  Definition find_fresh_string (dom:list string)
    := find_fresh_inj_f
         nat_to_string16
         nat_to_string16_inj
         dom.
  
  Lemma find_fresh_string_is_fresh (dom:list string) :
    ~ In (find_fresh_string dom) dom.
Proof.
    unfold find_fresh_string.
    apply find_fresh_inj_f_fresh.
  Qed.


  Definition fresh_var (pre:string) (dom:list string) :=
    let problems:=filter (prefix pre) dom in
    let problemEnds:=
        map (fun x => substring (String.length pre) (String.length x - String.length pre) x) problems in
    append pre (find_fresh_string problemEnds).

  Lemma fresh_var_fresh pre (dom:list string) :
    ~ In (fresh_var pre dom) dom.
Proof.
    unfold fresh_var.
    intros inn.
    rewrite in_of_append in inn.
    apply find_fresh_string_is_fresh in inn.
    trivial.
  Qed.

  Lemma fresh_var_fresh1 x1 pre dom :
    x1 <> fresh_var pre (x1::dom).
Proof.
    intro inn.
    apply (fresh_var_fresh pre (x1::dom)).
    rewrite <- inn.
    simpl; intuition.
  Qed.

  Lemma fresh_var_fresh2 x1 x2 pre dom :
    x2 <> fresh_var pre (x1::x2::dom).
Proof.
    intro inn.
    apply (fresh_var_fresh pre (x1::x2::dom)).
    rewrite <- inn.
    simpl; intuition.
  Qed.

  Lemma fresh_var_fresh3 x1 x2 x3 pre dom :
    x3 <> fresh_var pre (x1::x2::x3::dom).
Proof.
    intro inn.
    apply (fresh_var_fresh pre (x1::x2::x3::dom)).
    rewrite <- inn.
    simpl; intuition.
  Qed.

  Lemma fresh_var_fresh4 x1 x2 x3 x4 pre dom :
    x4 <> fresh_var pre (x1::x2::x3::x4::dom).
Proof.
    intro inn.
    apply (fresh_var_fresh pre (x1::x2::x3::x4::dom)).
    rewrite <- inn.
    simpl; intuition.
  Qed.

  Definition fresh_var2 pre1 pre2 (dom:list string) :=
    let fresh_var1 := fresh_var pre1 dom in
    (fresh_var1, fresh_var pre2 (fresh_var1::dom)).
  
  Lemma fresh_var2_distinct pre1 pre2 dom :
    (fst (fresh_var2 pre1 pre2 dom)) <>
    (snd (fresh_var2 pre1 pre2 dom)).
Proof.
    unfold fresh_var2; simpl.
    apply fresh_var_fresh1.
  Qed.

  Definition fresh_var3 pre1 pre2 pre3 (dom:list string) :=
    let fresh_var1 := fresh_var pre1 dom in
    let fresh_var2 := fresh_var pre2 (fresh_var1::dom) in
    let fresh_var3 := fresh_var pre3 (fresh_var2::fresh_var1::dom) in
    (fresh_var1, fresh_var2, fresh_var3).
  
  Lemma fresh_var3_distinct pre1 pre2 pre3 dom :
    (fst (fst (fresh_var3 pre1 pre2 pre3 dom))) <>
    (snd (fst (fresh_var3 pre1 pre2 pre3 dom))) /\
    (snd (fst (fresh_var3 pre1 pre2 pre3 dom))) <>
    (snd (fresh_var3 pre1 pre2 pre3 dom)) /\
    (fst (fst (fresh_var3 pre1 pre2 pre3 dom))) <>
    (snd (fresh_var3 pre1 pre2 pre3 dom)).
Proof.
    unfold fresh_var3; simpl.
    split;[|split].
    - apply fresh_var_fresh1.
    - apply fresh_var_fresh1.
    - apply fresh_var_fresh2.
  Qed.

  Definition get_var_base (sep:string) (var:string)
    := match index 0 (string_reverse sep) (string_reverse var) with
       | Some n => substring 0 (String.length var - (S n)) var
       | None => var
       end.

  Lemma get_var_base_pre sep var :
    prefix (get_var_base sep var) var = true.
Proof.
    unfold get_var_base.
    match_destr; simpl.
    - apply substring_prefix.
    - apply prefix_refl.
  Qed.

  Definition fresh_var_from sep (oldvar:string) (dom:list string) : string
    := if in_dec string_dec oldvar dom
       then fresh_var (append (get_var_base sep oldvar) sep) dom
       else oldvar.

  Lemma fresh_var_from_fresh sep oldvar (dom:list string) :
    ~ In (fresh_var_from sep oldvar dom) dom.
Proof.
    unfold fresh_var_from.
    match_destr.
    apply fresh_var_fresh.
  Qed.

  Lemma find_fresh_inj_f_equivlist x y :
    equivlist x y ->
    find_fresh_inj_f nat_to_string16 nat_to_string16_inj x =
    find_fresh_inj_f nat_to_string16 nat_to_string16_inj y.
Proof.
    intros.
    unfold find_fresh_inj_f; simpl.
    repeat (match goal with
            | [|- context[ proj1_sig ?x ]] => destruct x
            end; simpl).
    apply some_lift in e.
    destruct e as [? e ?]; subst.
    apply some_lift in e0.
    destruct e0 as [? e0 ?]; subst.
    f_equal.
    rewrite find_bounded_S_seq in e, e0.
    rewrite (find_ext
               (fun x0 : nat => if in_dec equiv_dec (nat_to_string16 x0) x then false else true)
               (fun x0 : nat => if in_dec equiv_dec (nat_to_string16 x0) y then false else true)) in e.
    - eapply find_seq_same; eauto.
    - intros.
      do 2 match_destr.
      + rewrite H in i; congruence.
      + rewrite <- H in i; congruence.
  Qed.
  
  Global Instance find_fresh_string_equivlist : Proper (equivlist ==> eq) find_fresh_string.
Proof.
    intros ???.
    unfold find_fresh_string.
    apply find_fresh_inj_f_equivlist; trivial.
  Qed.

  Global Instance fresh_var_equivlist : Proper (eq ==> equivlist ==> eq) fresh_var.
Proof.
    intros ??????; subst.
    unfold fresh_var.
    rewrite H0.
    reflexivity.
  Qed.

  Lemma fresh_var_from_id sep v l :
    ~ In v l ->
    fresh_var_from sep v l = v.
Proof.
    unfold fresh_var_from.
    match_destr; tauto.
  Qed.

  Lemma fresh_var_from_nincl {sep v l l2} :
    In (fresh_var_from sep v l) l2 -> ~ incl l2 l.
Proof.
    intros inn1 incl.
    apply (fresh_var_from_fresh sep v l).
    apply incl; trivial.
  Qed.

  Lemma fresh_var_from_incl_nin sep v l1 l2 :
    incl l2 l1 ->
    ~ In (fresh_var_from sep v l1) l2.
Proof.
    intros incl1 inn.
    apply (fresh_var_from_nincl inn); trivial.
  Qed.

End Fresh.

Ltac prove_fresh_nin
  := match goal with
     | [ |- ~ In (fresh_var ?pre ?l) _ ] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     | [ |- In (fresh_var ?pre ?l) _ -> False] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     | [ |- ~ (fresh_var ?pre ?l) = _ ] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     | [ |- (fresh_var ?pre ?l) <> _ ] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     | [ H:In (fresh_var ?pre ?l) _ |- _ ] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     | [ H:(fresh_var ?pre ?l) = _ |- _ ] =>
       solve[generalize (fresh_var_fresh pre l); simpl; intuition]
     end.