(****************************************************************
   File: ProtoQuipperSyntax.v                                                 
   Authors: Mohamed Yousri Mahmoud
   Date: June 2018
   Current Version: Coq V8.9
                                                                 
   The synatx of ProtoQuipper, a quantum lambda calculus
   programming language.
  ***************************************************************)

Require Export Hybrid.
Require Import ProtoQuipperTypes.
Require Import Coq.Lists.ListSet.
Require Import Coq.Lists.List.
Require Import FunInd.
Import ListNotations.


Inductive Econ: Set := qABS: Econ | qAPP: Econ | qPROD : Econ 
	|qLET:Econ |sLET:Econ| qCIRC:Econ | qIF:Econ
	|BOX:qtp->Econ|UNBOX:Econ|REV:Econ
        |TRUE:Econ|FALSE:Econ|STAR:Econ| 
         Qvar:nat->Econ|Crcons:nat->Econ.

Definition qexp : Set := expr Econ.

(****************************************************************
  Lemmas about contexts of elements of type qexp
  (could be moved to a general library about lists and sets of
   any type)
  ****************************************************************)

Theorem eq_dec: forall x y:qexp, {x=y}+{x<>y}.
Proof.
decide equality; decide equality; decide equality.
Qed.

Theorem set_add_new: forall a x,
    ~In a x -> set_add eq_dec a x = x++[a].
Proof.
  intros. induction x.
  - simpl. auto.
  - apply not_in_cons in H. inversion H. 
    apply IHx in H1. simpl.
    case (eq_dec a a0).
    + intros. contradict H0.  auto.
    + rewrite H1. auto.
Qed.

Theorem add_ident: forall x a,
    In a x -> set_add eq_dec a x =  x.
Proof.
  intros.  induction x.
  - inversion H.
  - inversion H.
    + subst.
      unfold set_add. case(eq_dec a a); auto.
      intros. contradict n. auto.
    + apply IHx in H0.
      unfold set_add.
      case (eq_dec a a0); auto.
      intros. unfold set_add in H0.  rewrite H0. auto.
Qed.

Theorem union_empty: forall (x:set qexp), 
    NoDup x ->
    set_union eq_dec [] x  = List.rev x.
Proof.
  intros. induction x.
  - simpl.  auto.
  - rewrite NoDup_cons_iff in H.
    inversion H.  simpl. apply IHx in H1.
    rewrite in_rev in H0.
    apply set_add_new in H0.
    rewrite H1,H0. auto.
Qed.
 
Fixpoint add_list (a:set qexp) (x:set qexp) :set qexp :=
    match a with
    | nil => x
    | a1 :: x1 =>
        match in_dec eq_dec a1 x with
        | left _ => add_list x1 x
        | right _ => add_list x1 x++[a1]
        end
    end.

Functional Scheme add_list_ind := Induction for add_list Sort Prop.

Theorem union_eq: forall x y,
    NoDup y ->
    set_union eq_dec x y = add_list y x.
Proof.
intros. functional induction add_list y x.
- simpl. auto.
- simpl.  assert(_x':=_x).
  apply set_union_intro1 with (Aeq_dec:=eq_dec)
                              (y:=x1) in _x'.
  apply add_ident in _x'.
  rewrite _x'. 
  rewrite NoDup_cons_iff in H. inversion H.
  auto.
- simpl. rewrite NoDup_cons_iff in H. inversion H.
  assert(~ set_In a1 (set_union eq_dec x x1)).
  { contradict H0. apply set_union_elim in H0.
    destruct H0; auto.
    assert(_x':=_x). contradict _x'.  auto. }
  apply set_add_new in H2. rewrite H2.
  apply IHs in H1. rewrite H1. auto. 
Qed. 

Theorem union_same: forall (x y:set qexp),
    NoDup y -> (forall a, In a  y -> In a x) ->
    add_list y x = x.
intros. functional induction add_list y x; auto.
- rewrite NoDup_cons_iff in H. inversion H.
  apply IHs;auto. intros.
  apply H0. apply in_cons. auto.
- assert(In a1 (a1::x1)); try apply in_eq.
  apply H0 in H1. contradict H1. auto.
Qed.

Theorem union_same_alt: forall (x y:set qexp),
    NoDup y -> (forall a, In a  y -> In a x) ->
    set_union eq_dec x y= x.
Proof.
  intros. assert(h:=H). apply union_eq with (x:=x) in H.
  rewrite H. apply union_same;auto.
Qed.

Lemma set_add_iff a b l : In a (set_add eq_dec b l) <-> a = b \/ In a l.
  Proof.
  split. apply set_add_elim. apply set_add_intro.
  Qed.

Lemma set_add_nodup a l : NoDup l -> NoDup (set_add eq_dec  a l).
  Proof.
   induction 1 as [|x l H H' IH]; simpl.
   - constructor; [ tauto | constructor ].
   - destruct (eq_dec  a x) as [<-|Hax]; constructor; trivial.
     rewrite set_add_iff. intuition.
  Qed.

Lemma set_union_nodup l l':
    NoDup l -> NoDup l' -> 
    NoDup (set_union eq_dec l l').
Proof.
  induction 2 as [|x' l' ? ? IH]; simpl; trivial. 
  now apply set_add_nodup.
Qed.

Theorem nodup_app: forall (l :list qexp), 
    NoDup l -> forall l1 a, l = l1++[a] -> NoDup (a::l1).
Proof.
  intros l H. induction H.
  - intros. symmetry in H. apply app_eq_nil in H.
    inversion H. inversion H1. 
  - intros. destruct l1.
    + apply NoDup_cons.
      * apply in_nil.
      * apply NoDup_nil.
    + simpl in H1. inversion H1.
      assert(h4:=H4). apply IHNoDup in H4.
      rewrite NoDup_cons_iff in H4. inversion H4.
      repeat apply NoDup_cons; auto.
      * rewrite <- H3. contradict H.
        rewrite h4. inversion H.
        { rewrite H6. apply in_or_app. right. apply in_eq. }
        { contradict H2. auto. }
      * rewrite <- H3.
        contradict H. rewrite h4.  
        apply in_or_app. left. auto. 
Qed. 

Theorem nodup_app2: forall (l :list qexp), 
    NoDup l -> forall a, ~ In a l -> NoDup (l++[a]).
Proof.
  intros l H. induction H.
  - intros. simpl. apply NoDup_cons.
    + apply in_nil.
    + apply NoDup_nil.
  - intros. simpl.  apply NoDup_cons. 
    + contradict H1. apply in_app_or in H1.
      destruct H1.
      * contradict H. auto.
      * inversion H1.
        { rewrite H2. apply in_eq. }
        { inversion H2. }
    + apply IHNoDup. contradict H1. apply in_cons.
      auto. 
Qed.

Theorem nodup_rev: forall (l:list qexp),
    NoDup l -> NoDup (List.rev l).
Proof.
  intros.  induction H.
  - simpl. apply NoDup_nil.
  - simpl.  apply nodup_app2;auto. 
    rewrite <- in_rev. auto.
Qed. 

Theorem nodup_union: forall s1 s2,
    NoDup s1 -> NoDup s2 -> (forall a, In a s2 -> ~ In a s1) ->
    set_union eq_dec s1 s2 = s1++ (rev s2).
Proof.
  intros. apply union_eq with (x:=s1) in H0.
  rewrite H0. clear H0.  functional induction add_list s2 s1.
  - simpl. rewrite app_nil_r. auto.
  - assert(In a1 (a1::x1));try apply in_eq.
    apply H1 in H0.  contradict H0. auto.
  - simpl. rewrite IHs,app_assoc; auto.
    intros.  apply H1.  apply in_cons. auto.
Qed.

(****************************************************************
  Syntax definitions continued
  ****************************************************************)

Definition Var : var -> qexp := VAR Econ.
Definition Bnd : bnd -> qexp := BND Econ.
Definition App (e1 e2:qexp) : qexp := APP (APP (CON qAPP) e1) e2.
Definition Fun (f:qexp -> qexp) : qexp := APP (CON qABS) (lambda f).
Definition Prod (e1 e2:qexp) : qexp := APP (APP (CON qPROD) e1) e2.
Definition Let (f:qexp -> qexp-> qexp) (e1:qexp) : qexp :=
  APP (CON qLET)  (APP (lambda (fun x=> (lambda (f x)))) e1).
Definition Slet (e1 e2:qexp) : qexp := APP (APP (CON sLET) e1) e2.   
Definition If (e1 e2 e3:qexp) : qexp := APP (APP (APP (CON qIF) e1) e2) e3.
Definition Circ (e1:qexp) (i:nat) (e2:qexp) : qexp :=
  APP (APP (APP (CON qCIRC) e1) (CON (Crcons i))) e2.
Definition Box (f:qexp->qexp) (T:qtp) : qexp := 
  APP (CON (BOX T)) (lambda f).
Definition Unbox (e1:qexp) : qexp := APP (CON UNBOX) e1. 
Definition Rev (e1:qexp) : qexp := APP (CON REV) e1.

Theorem proper_CON: forall c:Econ, proper (CON c).
Proof.
apply level_CON; try apply proper_CON.
Qed.

Inductive quantum_data :qexp -> Prop := 
vQVAR: forall i,  quantum_data (CON (Qvar i))
|vSTAR:  quantum_data (CON STAR)
|vTENSOr: forall a b,  quantum_data  a ->  quantum_data b ->  
   quantum_data (Prod a b). 

Theorem quantum_data_proper: forall t, 
quantum_data t -> proper t.
Proof.
intros. induction H.
unfold proper;  apply level_CON.
unfold proper;  apply level_CON.
unfold Prod. repeat apply proper_APP;auto.
unfold proper;  apply level_CON.
Qed.

(* No longer needed

Inductive validqexp : qexp -> Prop:=
VVar : forall v, validqexp (Var v)
|VQar: forall i, validqexp (CON (Qvar i))
|VRev: validqexp (CON REV)
|VUnbox: validqexp (CON UNBOX)
|VBox: validqexp (CON BOX)
|VTrue: validqexp (CON TRUE)
|VFalse: validqexp (CON FALSE)
|VStart: validqexp (CON STAR)
|VApp : forall e1 e2, validqexp e1 ->  validqexp e2 ->
   validqexp (App e1 e2)
|VFun: forall f, abstr f -> (forall v, validqexp (f (Var v))) -> validqexp (Fun f)
|VProd: forall e1 e2, validqexp e1 ->  validqexp e2 ->
   validqexp (Prod e1 e2)
|VLet: forall f e, validqexp (lambda (fun x=> (lambda (f x))))
  -> validqexp e -> validqexp (Let f e)
|VSlet: forall e1 e2, validqexp e1 ->  validqexp e2 ->
   validqexp (Slet e1 e2)
|VIf: forall e1 e2 e3, validqexp e1 ->  validqexp e2 -> validqexp e3 ->
   validqexp (If e1 e2 e3)
|VCirc: forall e1 e2 i, validqexp e1 ->  validqexp e2 ->
   validqexp (Circ e1 i e2).

Inductive tvalidqexp : qexp -> Prop:=
tVVar : forall v, tvalidqexp (Var v)
|tVQar: forall i, tvalidqexp (CON (Qvar i))
|tVRev: tvalidqexp (CON REV)
|tVUnbox: tvalidqexp (CON UNBOX)
|tVBox: tvalidqexp (CON BOX)
|tVTrue: tvalidqexp (CON TRUE)
|tVFalse: tvalidqexp (CON FALSE)
|tVStart: tvalidqexp (CON STAR)
|tVApp : forall e1 e2, tvalidqexp e1 ->  tvalidqexp e2 ->
   tvalidqexp (App e1 e2)
|tVFun: forall f, abstr f -> (forall x, proper x -> validqexp x -> tvalidqexp (f x)) -> tvalidqexp (Fun f)
|tVProd: forall e1 e2, tvalidqexp e1 ->  tvalidqexp e2 ->
   tvalidqexp (Prod e1 e2)
|tVLet: forall f e, tvalidqexp (lambda (fun x=> (lambda (f x))))
  -> tvalidqexp e -> tvalidqexp (Let f e)
|tVSlet: forall e1 e2, tvalidqexp e1 ->  tvalidqexp e2 ->
   tvalidqexp (Slet e1 e2)
|tVIf: forall e1 e2 e3, tvalidqexp e1 -> tvalidqexp e2 -> tvalidqexp e3 ->
   tvalidqexp (If e1 e2 e3)
|tVCirc: forall e1 e2 i, tvalidqexp e1 ->  tvalidqexp e2 ->
   tvalidqexp (Circ e1 i e2).*)

Fixpoint FV (e:qexp) :set qexp :=
match e with
VAR _ x => [VAR  Econ x]
|(APP (APP (CON qPROD) e1) e2) => set_union eq_dec (FV e1) (FV e2)
|(APP (APP (CON qAPP) e1) e2)  => set_union eq_dec (FV e1) (FV e2)
|APP (CON qABS) (ABS e)    => FV e
|(APP (APP (APP (CON qIF) e1) e2) e3) => set_union eq_dec (FV e3) (set_union eq_dec (FV e1) (FV e2))
|(APP (APP (CON sLET)  e1) e2) => set_union eq_dec (FV e1) (FV e2)
| (APP (CON qLET)  (APP (ABS e1) e2)) => 
  set_union eq_dec (FV e1) (FV e2) 
|(APP (APP (APP (CON qCIRC) e1) (CON (Crcons i))) e2) => FV e2
|_ => []
end.

Fixpoint FQ (e:qexp) :set qexp :=
match e with
CON (Qvar x) => [CON  (Qvar x)]
|(APP (APP (CON qPROD) e1) e2) => set_union eq_dec (FQ e1) (FQ e2)
|(APP (APP (CON qAPP) e1) e2)  => set_union eq_dec (FQ e1) (FQ e2)
|APP (CON qABS) (ABS e)    => FQ e
|(APP (APP (APP (CON qIF) e1) e2) e3) => set_union eq_dec (FQ e3) (set_union eq_dec (FQ e1) (FQ e2))
|(APP (APP (CON sLET)  e1) e2) => set_union eq_dec (FQ e1) (FQ e2)
| (APP (CON qLET)  (APP (ABS e1) e2)) => 
  set_union eq_dec (FQ e1) (FQ e2) 
|_ => []
end.

Functional Scheme FQ_ind := Induction for FQ Sort Prop.

(* TODO: change QV to use the following relational version.

Definition QV: nat -> qexp := fun i:nat => (CON (Qvar i)).

Fixpoint FQ (e:qexp) :set qexp :=
  match e with
    (QV i) => [CON  (Qvar x)]
  | (Prod e1 e2) => set_union eq_dec (FQ e1) (FQ e2)
  | (App e1 e2) => set_union eq_dec (FQ e1) (FQ e2)
  | (Fun f) => FQ (f (Var 0))
  | (If e1 e2 e3) => set_union eq_dec (FQ e3) (set_union eq_dec (FQ e1) (FQ e2))
  | (Slet e1 e2) => set_union eq_dec (FQ e1) (FQ e2)
  | (Let f e) =>  set_union eq_dec (FQ (f (Var 0) (Var 0))) (FQ e2) 
  | _ => []
end.

Inductive FQ : qexp -> set qexp -> Prop :=
| FQ_QV : forall x, FQ (QV x) [CON  (Qvar x)]
| FQ_Prod : forall (e1 e2:qexp) (vs1 vs2:set qexp),
    FQ e1 vs1 -> FQ e2 vs2 -> FQ (Prod e1 e2) (set_union eq_dec vs1 vs2)
| FQ_App : forall (e1 e2:qexp) (vs1 vs2:set qexp),
    FQ e1 vs1 -> FQ e2 vs2 -> FQ (App e1 e2) (set_union eq_dec vs1 vs2)
| FQ_Fun : forall (f:qexp -> qexp) (vs:set qexp),
    FQ (f (Var 0)) vs -> FQ (Fun f) vs
| FQ_If : forall (e1 e2 e3:qexp) (vs1 vs2 vs3:set qexp),
    FQ e1 vs1 -> FQ e2 vs2 -> FQ e3 vs3 ->
    FQ (If e1 e2 e3) (set_union eq_dec vs3 (set_union eq_dec vs1 vs2))
| FQ_Slet : forall (e1 e2:qexp) (vs1 vs2:set qexp),
    FQ e1 vs1 -> FQ e2 vs2 -> FQ (Slet e1 e2) (set_union eq_dec vs1 vs2)
| FQ_Let : forall (f:qexp -> qexp -> qexp) (e:qexp) (vs1 vs2:set qexp),
    FQ (f (Var 0) (Var 0)) vs1 -> FQ e vs2 ->
    FQ (Let f e) (set_union eq_dec vs1 vs2). *)

Theorem fq_all_qvar: forall a, 
  (forall a0, In a0 (FQ a) -> exists x, a0 = CON  (Qvar x)).
Proof.
intros a a0 H.
functional induction FQ a; try inversion H; try inversion H;
  try inversion H0;
  subst; try exists x; auto; apply set_union_elim in H;
  destruct H; try apply IHs in H; try apply IHs0 in H; auto.  
apply set_union_elim in H;
  destruct H; try apply IHs0 in H; 
    try apply IHs1 in H; auto.
Qed.

Theorem fq_nodup: forall a, NoDup (FQ a).
Proof.
  intros. functional induction FQ a; try apply NoDup_nil;
            try apply set_union_nodup;auto.
  - apply NoDup_cons.
    + apply in_nil.
    + apply NoDup_nil.
  - apply set_union_nodup;auto.
Qed.

Theorem nodup_fq2: forall v, NoDup (FQ v).
  intros. functional induction FQ v; intros;
            simpl;try apply NoDup_nil; try apply set_union_nodup;auto.
 apply  NoDup_cons. auto. apply  NoDup_nil.
 apply set_union_nodup;auto.
Qed.

Inductive is_value :qexp->Prop := 
Varv: forall x, is_value (Var x)
|Qvarv: forall x, is_value (CON (Qvar x))
|Circv: forall a t i, quantum_data t -> quantum_data a -> is_value (Circ t i a)
|Truev: is_value (CON TRUE)
|Falsev: is_value (CON FALSE)
|Starv: is_value (CON STAR)
|Boxv: forall T, valid T -> is_value (CON (BOX T))
|Unboxv: is_value (CON UNBOX)
|Revv: is_value (CON REV)
|Funvv: forall f, abstr f -> is_value (Fun f)
|Prodv: forall v w, is_value v -> is_value w -> is_value (Prod v w)
|Unboxappv: forall v, is_value v -> is_value (App (CON UNBOX) v).

Theorem value_proper: forall v, is_value v -> proper v.
Proof.
intros.  induction H;try apply  proper_CON.
apply proper_VAR. 
unfold Circ. repeat apply proper_APP;
try apply  proper_CON;apply quantum_data_proper;auto.
unfold Fun. apply proper_APP;
try apply  proper_CON;apply  abstr_proper;auto.
unfold Prod. repeat apply proper_APP;
try apply  proper_CON;auto.
repeat apply proper_APP;
try apply  proper_CON;auto.
Qed.

Inductive bind :qexp->qexp->Prop := 
|star_bind: bind (CON STAR) (CON STAR)
|qvar_bind: forall i j, bind (CON (Qvar i)) (CON (Qvar j))  
|tensor_bind:forall a1 a2 b1 b2, bind a1 b1 ->  bind a2 b2-> 
bind (Prod a1 a2) (Prod b1 b2). 

Theorem quantum_data_bind:forall a b, 
bind a b -> quantum_data a /\ quantum_data b.
intros a b H. induction H. split;apply vSTAR.
split;apply vQVAR.
inversion IHbind1. inversion IHbind2.
split; apply vTENSOr;auto.
Qed.

Fixpoint qtyper (v:qexp) :qtp := 
match v with 
|CON STAR => one
|CON (Qvar _) => qubit
|(APP (APP (CON qPROD) e1) e2) => tensor (qtyper e1) (qtyper e2)
| _ => one
end.
Functional Scheme qtyper_ind := Induction for qtyper Sort Prop. 

Theorem valid_qtyper: forall v, valid (qtyper v).
intros. apply  qtyper_ind; intros;try apply One.
apply Qubit. apply Tensor;auto.
Qed.

Theorem qtyper_bind: forall a b, bind a b ->  qtyper a  = qtyper b.
intros a b H. induction H;simpl;auto.
rewrite IHbind1,IHbind2. auto.
Qed.
(*match u with
CON (Qvar  x) =>
 match v with
 CON (Qvar  y) => [(CON (Qvar  x),CON (Qvar  y))]
 | _ => []
 end
|(APP (APP (CON qPROD) e1) e2) => 
 match v with
 (APP (APP (CON qPROD) e3) e4) =>
    (bind e1 e3) ++ (bind e2 e4) 
  | _  => []
  end
| _ => []
end.*)

Fixpoint FQU (e:qexp) :set qexp :=
match e with
CON (Qvar x) => [CON  (Qvar x)]
|(APP (APP (CON qPROD) e1) e2) =>  (FQU e1) ++(FQU e2)
|(APP (APP (CON qAPP) e1) e2)  => (FQU e1) ++(FQU e2)
|APP (CON qABS) (ABS e)    => FQU e
|(APP (APP (APP (CON qIF) e1) e2) e3) => (FQU e1) ++(FQU e2)++(FQU e3)
|(APP (APP (CON sLET)  e1) e2) => (FQU e1) ++(FQU e2)
| (APP (CON qLET)  (APP (ABS e1) e2)) => 
  (FQU e1) ++(FQU e2)
|_ => []
end.
Functional Scheme FQU_ind := Induction for FQU Sort Prop. 

Fixpoint find (l:list (qexp*qexp)) (a:qexp) :qexp:=
   match l with
        | nil => a
        | x :: tl => if eq_dec (fst x) a then snd x else find tl a
      end.

Fixpoint binder (bl:list (qexp*qexp)) (a:qexp) :qexp :=
match a with
CON (Qvar  x) =>  find bl (CON (Qvar  x)) 
| APP e1 e2 => APP (binder bl e1) (binder bl e2)
| ABS e => ABS (binder bl e)
| y => y  
end.

Fixpoint newqvar (e:qexp): nat :=
match e with
CON (Qvar x) => x+1
|(APP (APP (CON qPROD) e1) e2) => max (newqvar e1) (newqvar e2)
|(APP (APP (CON qAPP) e1) e2)  => max (newqvar e1) (newqvar e2)
|APP (CON qABS) (ABS e)    => (newqvar e)
|(APP (APP (APP (CON qIF) e1) e2) e3) => max (max (newqvar e1) (newqvar e2)) (newqvar e3)
|(APP (APP (CON sLET)  e1) e2) => max (newqvar e1) (newqvar e2)
| (APP (CON qLET)  (APP (ABS e1) e2)) => 
  max (newqvar e1) (newqvar e2)
|_ => 0
(*match e with
  CON (Qvar x) => x+1
| APP e1 e2 => max (newqvar e1) (newqvar e2)
| ABS e' => newqvar e'
| _ => 0*)
end.

Fixpoint Spec (v:nat) (T:qtp) :qexp := 
match T with 
|qubit => CON (Qvar v)
|tensor t1 t2 => Prod (Spec v t1) (Spec (max v (newqvar (Spec v t1))) t2)
| _ => CON STAR
end.

Functional Scheme Spec_ind := Induction for Spec Sort Prop. 

Theorem Spec_quantum_data: forall v T, quantum_data (Spec (newqvar v) T).
Proof.
intros. apply Spec_ind; intros;try apply vSTAR.
apply vQVAR. 
apply vTENSOr;auto.
Qed.

Theorem nodup_fq: forall v t, NoDup (FQ(Spec v t)).
Proof.
  intros. functional induction Spec v t; intros;
            simpl;try apply NoDup_nil.
  apply NoDup_cons; auto.
  - apply  NoDup_nil.
  - apply set_union_nodup;auto.
 Qed.

Functional Scheme newqvar_ind := Induction for newqvar Sort Prop.

Lemma spec_gt:forall q1 v,
    In (CON (Qvar q1)) (FQ v) -> q1 < newqvar v.
Proof.
  intros. 
  functional induction newqvar v;simpl in H ; try inversion H; auto.
  - inversion H0.  omega.
  - contradict H0.
  - rewrite set_union_iff in H. destruct H.
    + destruct (le_ge_dec  (newqvar e9) (newqvar e7)).
      * assert(l':=l). apply max_r in l.  rewrite l.
        apply IHn in H. omega.
      * apply max_l in g. rewrite g. auto.
    + destruct (le_ge_dec  (newqvar e9) (newqvar e7)); auto.
      * assert(l':=l). apply max_r in l.  rewrite l. auto.
      * assert(g':=g).
        apply max_l in g. rewrite g. apply IHn0 in H. omega.  
  - rewrite set_union_iff in H. destruct H.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l.
        apply IHn in H. omega.
      * apply max_l in g. rewrite g.
        auto.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)); auto.
      * assert(l':=l). apply max_r in l.  rewrite l.
        auto.
      * assert(g':=g). apply max_l in g. rewrite g. apply IHn0 in H. omega.  
  - rewrite set_union_iff in H. destruct H.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l.
        apply IHn in H. omega.
      * apply max_l in g. rewrite g. auto.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l.
        auto.
      * assert(g':=g).
        apply max_l in g. rewrite g. apply IHn0 in H. omega.
  - rewrite set_union_iff in H. destruct H.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l.
        apply IHn in H. omega.
      * apply max_l in g. rewrite g.
        auto.
    + destruct (le_ge_dec  (newqvar e4) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l. auto.
      * assert(g':=g). apply max_l in g. rewrite g. apply IHn0 in H. omega.
  - repeat rewrite set_union_iff in H. destruct H.
    + destruct (le_ge_dec (Nat.max (newqvar e7) (newqvar e4)) (newqvar e2)).
      * assert(l':=l). apply max_r in l.  rewrite l. auto.
      * assert(g':=g). apply max_l in g. rewrite g. apply IHn1 in H. omega. 
    + destruct H.
      * destruct (le_ge_dec  (newqvar e7) (newqvar e4)).
        { assert(l':=l).
          apply max_r in l. rewrite l.
          destruct (le_ge_dec (newqvar e4) (newqvar e2)).
          { assert(l0':=l0). apply max_r in l0. rewrite l0.
            apply IHn in H. omega. }
          { apply max_l in g. rewrite g.
            apply IHn in H. omega. }}
        { assert(g':=g).
          apply max_l in g. rewrite g.
          destruct (le_ge_dec (newqvar e7) (newqvar e2)).
          { assert(l':=l).
            apply max_r in l. rewrite l.
            apply IHn in H. omega. }
          { apply max_l in g0. rewrite g0.
            apply IHn in H. omega. }}
      * destruct (le_ge_dec (newqvar e7) (newqvar e4)).
        { assert(l':=l).
          apply max_r in l. rewrite l.
          destruct (le_ge_dec (newqvar e4) (newqvar e2)).
          { assert(l0':=l0). apply max_r in l0. rewrite l0.
            apply IHn0 in H. omega. }
          { apply max_l in g. rewrite g.
            apply IHn0 in H. omega. }}
        { assert(g':=g).
          apply max_l in g. rewrite g.
          destruct (le_ge_dec (newqvar e7) (newqvar e2)).
          { assert(l':=l).
            apply max_r in l. rewrite l.
            apply IHn0 in H. omega. }
          { apply max_l in g0. rewrite g0.
            apply IHn0 in H. omega. }}
Qed.

Theorem spec_gt2: forall q v t,
    In (CON (Qvar q)) (FQ (Spec v t)) -> v <= q.
Proof.
  intros. functional induction Spec v t;simpl in *.
  - destruct H.
    + inversion H. 
      omega.
    + contradict H.
  - contradict H.
  - contradict H.
  - rewrite set_union_iff in H. destruct H; auto.
    apply IHq2 in H.
    destruct (le_ge_dec v (newqvar (Spec v t1))).
    + assert(l':=l). apply max_r in l. rewrite l in H. omega.
    + apply max_l in g.  rewrite g in H. auto.
  - contradict H.
  - contradict H.
  - contradict H.
Qed.

Theorem disjoint_fq: forall v t1, forall a,
      In a (FQ v) -> ~ In a (FQ (Spec (newqvar v) t1)).
Proof.
  intros. induction t1;simpl;auto.
  - assert(h:=H). apply fq_all_qvar in H. inversion H.
    subst. apply spec_gt in h.
    apply and_not_or. split;auto.
    destruct (eq_dec (CON (Qvar (newqvar v))) (CON (Qvar x))); auto.
    inversion e. omega.
  - rewrite set_union_iff. apply and_not_or.
    split; auto.
    destruct (In_dec eq_dec a
                     (FQ (Spec (Nat.max (newqvar v)
                                        (newqvar (Spec (newqvar v) t1_1)))
                               t1_2))).
    + assert(i':=i).
      apply fq_all_qvar in i. 
      inversion i. subst. apply spec_gt2 in i'.
      apply spec_gt in H.
      destruct (le_ge_dec (newqvar v) (newqvar (Spec (newqvar v) t1_1))).
      * assert(l':=l). apply max_r in l. 
        rewrite l in i'. omega.
      * apply max_l in g.  rewrite g in i'.  omega.
    + auto.
Qed.
