(****************************************************************
   File: ProtoQuipperProg.v
   Authors: Mohamed Yousri Mahmoud
   Date: June 2018
   Current Version: Coq V8.9

   Various lemmas, instantiation of atm with the basic judgments for
   Proto Quipper (which currently include well-formed terms, typing,
   and evaluation), and instantiation of prog with the inference rules
   for these judgments.
   ***************************************************************)

Require Export LSL.
Require Export ProtoQuipperSyntax.
Require Export ProtoQuipperTypes.
Require Export Coq.Lists.List.
Require Export Coq.Lists.ListSet.
Require Import FunInd.
Import ListNotations.

Section Prog.

Set Implicit Arguments.

(****************************************************************
  Some useful tactics
  ****************************************************************)

Ltac apply2 thL h1 h2 := 
  try apply thL in h1; try apply thL in h2.

Ltac destruct_conj :=
  match goal with
    | [ H : ?A/\?B |- _ ] => inversion H;clear H; destruct_conj
    | _ => idtac
  end.

Ltac rexists := 
  match goal with
    | [H:exists _, _ |-_] => inversion H; clear H;rexists
    |_ => idtac end.

(****************************************************************
   The atm type and instantiation of oo.
  ****************************************************************)

(* The atomic predicates that encode typing, well-formedness, and
   reduction and serve as a parameter to the specification logic *)
Inductive atm : Set :=
| typeof : qexp -> qtp -> atm
| is_qexp : qexp -> atm
| reduct: Econ -> qexp -> Econ -> qexp -> atm.

Definition oo_ := oo atm Econ.
Definition atom_ : atm -> oo_ := atom Econ.
Definition T_ : oo_ := T atm Econ.

Hint Unfold oo_ atom_ T_: hybrid.

(****************************************************************
  Operations and properties about atomic predicates and contexts.
  ****************************************************************)

Definition atmtype (a:atm) :qtp :=
  match a with
      typeof _ t => t
    | _ => one
  end. 

Definition atmexp (a:atm) :qexp :=
  match a with
      typeof e _ => e
    | _ => CON STAR
  end.

Inductive vlcontext  :list atm -> Prop :=
  vlc_init: vlcontext []
| vlc_general: forall a t ll, validT(bang t) -> vlcontext ll ->
                              vlcontext (typeof a t::ll).

Inductive vicontext  :list atm -> Prop :=
  vic_init: vicontext []
| vic_general: forall a ll,
                 (exists c, atmtype a = bang c /\  validT (atmtype a)) ->
                 vicontext ll -> vicontext (a::ll). 

Theorem In_vic: forall a IL, In a IL -> vicontext IL -> 
  (exists c, atmtype a = bang c /\ validT (atmtype a)). 
Proof.
intros a IL H H0. induction H0.
- contradiction in_nil with atm a.
- destruct H;auto. subst;auto.
Qed.

Theorem In_vlc: forall a t IL, In (typeof a t) IL -> vlcontext IL -> 
  validT (bang t).
Proof.
intros a t IL H H0. induction H0.
- inversion H. 
- destruct H;auto. injection H. intros. subst;auto.
Qed.

(* Assigns the type qubit to all quantum variables in the input list,
   returning a context of typed variables *)
Fixpoint toqlist (fq:set qexp) : list atm :=
  match fq with
    | [] => []
    | h::t => 
      match h with 
        | CON (Qvar x) => (typeof (CON (Qvar x)) qubit)::toqlist t
        | _ => toqlist t
      end
  end.

(* Forms a context quantum variables for the well-formedness judgment *)
Fixpoint toiqlist (fq:set qexp) : list atm :=
  match fq with
    | [] => []
    | h::t => 
      match h with 
        | CON (Qvar x) => (is_qexp (CON (Qvar x)))::toiqlist t
        | _ => toiqlist t
      end
  end.

Functional Scheme toqlist_ind := Induction for toqlist Sort Prop.

Functional Scheme toiqlist_ind := Induction for toiqlist Sort Prop.

(* Maps a set of quantum variables and a formula to an implication
   with the formula on the right, and well-formedness and typing
   assumptions about all of the quantum variables on the left *)
Fixpoint toimp (fq:set qexp) (form:oo_) : oo_ :=
  match fq with
    | [] => form
    | h::t => 
      match h with 
        | CON (Qvar x) =>
          Imp (is_qexp (CON (Qvar x)))
              (lImp (typeof (CON (Qvar x)) qubit) (toimp t form))
        | _ => toimp t form
      end
  end.

(* Similar to above, but only well-formedness assumptions included *)
Fixpoint toimpexp (fq:set qexp) (term:oo_) :oo_ :=
  match fq with
    | [] => term
    | h::t => 
      match h with 
        | CON (Qvar x) => Imp (is_qexp (CON (Qvar x))) (toimpexp t term)
        | _ => toimp t term
      end
  end.

Theorem toqlist_app: forall l1 l2,
  toqlist (l1++l2)= toqlist l1 ++ toqlist l2.
Proof.
intros l1 l2; functional induction toqlist l1;
try rewrite <- app_comm_cons; simpl; auto.
rewrite <- IHl. auto.
Qed.

Theorem toiqlist_app: forall l1 l2,
  toiqlist (l1++l2)= toiqlist l1 ++ toiqlist l2.
Proof.
intros l1 l2; functional induction toiqlist l1;
try rewrite <- app_comm_cons; simpl; auto.
rewrite <- IHl. auto.
Qed.

Theorem rev_toqlist: forall fq,
  rev (toqlist fq) = toqlist (rev fq).
Proof.
intros fq; functional induction toqlist fq; simpl; 
try rewrite toqlist_app; simpl; try rewrite app_nil_r; auto.
rewrite IHl. auto.
Qed.

Theorem rev_toiqlist: forall fq,
  rev (toiqlist fq) = toiqlist (rev fq).
Proof.
intros fq; functional induction toiqlist fq; simpl; 
try rewrite toiqlist_app; simpl; try rewrite app_nil_r; auto.
rewrite IHl. auto.
Qed.

Theorem move_to_ll: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i IL LL (toimp fq form) ->
    LSL.seq prog (i-length fq-length fq) (rev (toiqlist fq)++IL) 
            (rev (toqlist fq)++LL) form.
Proof.
intros fq H. induction fq.
- intros form i IL LL prog H0. simpl toimp in H0. simpl length.
  simpl toqlist. simpl toiqlist.
  repeat rewrite app_nil_l. assert (i-0-0 = i); try omega. rewrite  H1. auto.
- intros form i IL LL prog H0. 
  specialize in_eq with (a:=a) (l:=fq). intro H1. apply H in H1.
  inversion H1.  subst. simpl toqlist. simpl toqlist. simpl rev.
  repeat rewrite <- app_assoc. 
  simpl length. assert (forall n, i - S n - S n = i - 2 - n -  n).
  { intros. omega. }
  rewrite H2.  apply IHfq.
  + intros a H3. apply in_cons with (a:= CON (Qvar x)) in H3. auto.
  + simpl in H0. inversion H0. inversion H7. 
    assert (i1+1+1-2 = i1); try omega. 
    rewrite H15. auto. 
Qed.

Theorem move_to_il: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i IL LL (toimpexp fq form) ->
    LSL.seq prog (i-length fq) (rev (toiqlist fq)++IL) LL form.
Proof.
intros fq H. induction fq.
- intros form i IL LL prog H0. simpl toimpexp in H0. simpl length.
  simpl toqlist. simpl toiqlist.
  repeat rewrite app_nil_l. assert (i-0 = i); try omega. rewrite H1. auto.
- intros form i IL LL prog H0. specialize in_eq with (a:=a) (l:=fq).
  intro H1. apply H in H1.
  inversion H1.  subst. simpl toqlist. simpl toqlist. simpl rev.
  repeat rewrite <- app_assoc. 
  simpl length. assert (forall n, i - S n  = i - 1 - n ).
  { intros. omega. }
  rewrite H2.  apply IHfq.
  intros a H3. apply in_cons with (a:= CON (Qvar x)) in H3. auto.
  simpl in H0. inversion H0. assert (i0+1-1 = i0); try omega. 
  rewrite H9. auto. 
Qed.

Theorem move_from_ll: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i (rev (toiqlist fq)++IL) (rev (toqlist fq)++LL) form ->
    LSL.seq prog (i+length fq+length fq) IL LL (toimp fq form).
Proof.
intros fq H. induction fq.
- intros form i IL LL prog H0. simpl toimp. simpl length. 
  simpl toqlist in H0.
  simpl rev in H0. rewrite app_nil_l in H0. assert (i+0 +0= i); try omega. 
  rewrite  H1. auto.
- intros form i IL LL prog H0. specialize in_eq with (a:=a) (l:=fq).
  intro H1. apply H in H1.
  inversion H1.  subst. simpl toqlist in H0. simpl toiqlist in H0.
  simpl rev in H0. 
  simpl length. assert (forall n, i + S n + S n= i + n + n + 1 + 1 ).
  { intros. omega. }
  rewrite H2.  repeat rewrite <- app_assoc in H0. simpl. apply i_imp,l_imp. 
  apply IHfq.
  + intros a H3. apply in_cons with (a:= CON (Qvar x)) in H3. auto.
  + assert (forall (a:atm) l, a::l = [a]++l).
    { induction l;auto. }
    repeat rewrite <- H3 in H0. auto.
Qed.

Theorem move_from_il: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i (rev (toiqlist fq)++IL) LL form ->
    LSL.seq prog (i+length fq) IL LL (toimpexp fq form).
Proof.
intros fq H. induction fq. 
- intros form i IL LL prog H0. simpl toimp. simpl length. 
  simpl toqlist in H0.
  simpl rev in H0. rewrite app_nil_l in H0. assert (i+0= i); try omega. 
  rewrite  H1. auto.
- intros form i IL LL prog H0. specialize in_eq with (a:=a) (l:=fq).
  intro H1. apply H in H1.
  inversion H1.  subst. simpl toqlist in H0. simpl toiqlist in H0.
  simpl rev in H0. 
  simpl length. assert (forall n, i  + S n= i + n + 1 ).
  { intros. omega. }
  rewrite H2.  repeat rewrite <- app_assoc in H0. simpl. apply i_imp. 
  apply IHfq.
  + intros a H3. apply in_cons with (a:= CON (Qvar x)) in H3. auto.
  + assert (forall (a:atm) l, a::l = [a]++l).
    { induction l;auto. }
    repeat rewrite <- H3 in H0. auto.
Qed.

Theorem toimp_ilength: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i IL LL (toimp fq form) -> (length fq)+(length fq)<= i.
Proof.
intros fq H. induction fq.
- intros form i IL LL prog H0. simpl. omega.
- intros form i IL LL prog H0. specialize in_eq with (a:=a) (l:=fq).
  intro H1. apply H in H1.
  inversion H1.  subst. simpl toimp in H0.
  inversion H0. inversion H6. simpl. apply IHfq in H12.
  + omega.
  + intros a H14. apply in_cons with (a:= CON (Qvar x)) in H14. auto.
Qed.

Theorem toimpexp_ilength: forall fq, 
  (forall a, In a fq -> exists x, a = CON  (Qvar x)) ->
  forall form i IL LL prog, 
    LSL.seq prog i IL LL (toimpexp fq form) -> (length fq)<= i.
Proof.
intros fq H. induction fq.
- intros form i IL LL prog H0. simpl. omega.
- intros form i IL LL prog H0. specialize in_eq with (a:=a) (l:=fq).
  intro H1. apply H in H1.
  inversion H1.  subst. simpl toimpexp in H0.
  inversion H0. simpl. apply IHfq in H6.
  + omega.
  + intros a H8. apply in_cons with (a:= CON (Qvar x)) in H8. auto.
Qed.

Lemma in_toiqlist:forall x l, In (CON (Qvar x)) l ->
  In (is_qexp (CON (Qvar x))) (toiqlist l).
Proof.
intros x l.
functional induction toiqlist l;intros; inversion H;auto; try inversion H0.
- apply in_eq.
- apply in_cons. auto.
Qed.

Theorem toiqlist_union: forall l1 l2,
  (forall a, In a  l1 -> In a l2) ->
  (forall a, In a (toiqlist l1) -> In a (toiqlist l2)).
Proof.
intros l1.
apply toiqlist_ind; intros;
try inversion H1; subst; try apply in_toiqlist; try apply H0; 
try apply in_eq;         
try apply H;auto;intros;try apply H0;try apply in_cons; auto. 
inversion H0.
Qed.

Theorem vlc_toqlist: forall fq, vlcontext (toqlist fq).
Proof.
intros. functional induction toqlist fq;auto.
- apply vlc_init.
- apply vlc_general;auto. simpl. apply bQubit.
Qed.

Lemma in_toiqlistg: forall x l,
    In x (toiqlist l) ->
    exists y, x = (is_qexp (CON (Qvar y))).
Proof.
  intros x l.
  functional induction toiqlist l;intros;
    try inversion H ;auto. exists x0. auto. 
Qed. 

Inductive Subtypecontext:
  list atm-> list atm -> list atm ->list atm -> Prop :=
| subcnxt_i: Subtypecontext [] [] [] []
| subcnxt_q: forall a il il' ll ll',
             Subtypecontext il' ll' il ll -> 
             Subtypecontext (is_qexp a::il') ll' (is_qexp a::il) ll
| subcnxt_iig: forall a t1 t2 il il' ll ll',
               Subtyping t1 t2 -> (exists c, t2 = bang c) ->
               Subtypecontext il' ll' il ll ->
               Subtypecontext (is_qexp a::typeof a t1::il') ll' 
                              (is_qexp a::typeof a t2::il) ll
| subcnxt_llg: forall a t1 t2  il il' ll ll',
               validT (bang t1) ->
               validT (bang t2) ->
               Subtyping t1 t2 -> Subtypecontext il' ll' il ll ->
               Subtypecontext (is_qexp a::il') (typeof a t1::ll') 
                              (is_qexp a::il) (typeof a t2::ll)
| subcnxt_lig: forall a t1 t2 il il' ll ll',
               validT(bang t2) -> (exists c,  t1 = bang c) ->
               Subtyping t1 t2 -> Subtypecontext il' ll' il ll ->
               Subtypecontext (is_qexp a::typeof a t1::il') ll'
                              (is_qexp a::il) (typeof a t2::ll).

Theorem rev_qlist_subcnxt: forall fq,forall IL LL IL' LL',
  Subtypecontext IL' LL' IL LL ->
  Subtypecontext (List.rev (toiqlist fq) ++ IL') LL'
                 (List.rev (toiqlist fq) ++ IL) LL.
Proof.
intro fq. functional induction toqlist fq; simpl; auto.
intros IL LL IL' LL' H. apply subcnxt_q with (a:=(CON (Qvar x))) in H.
repeat rewrite <- app_assoc. simpl. apply IHl. auto.
Qed.

Definition Pqexp (il' ll' il ll:list atm): Prop :=
  forall a, In (is_qexp a) il -> In (is_qexp a) il'. 

Theorem qexp_inIL_alt: forall il' ll' il ll,
  Subtypecontext il' ll' il ll -> Pqexp il' ll' il ll.
Proof.
intros il' ll' il ll H. apply Subtypecontext_ind. 
- unfold Pqexp. intros a H0. inversion H0.  
- unfold Pqexp. intros a il0 il'0 ll0 ll'0 H0 H1 a0 H2.
  inversion H2.
  + inversion H3. apply in_eq.
  + apply in_cons. apply H1; auto.
- unfold Pqexp. intros a t1 t2 il0 il'0 ll0 ll'0 H0 H1 H2 H3 a0 H4.
  inversion H4.
  + injection H5. intros. subst. apply in_eq; auto.
  + inversion H5.
    * inversion H6.
    * apply H3 in H6. repeat apply in_cons;auto.
- unfold Pqexp. intros a t1 t2 il0 il'0 ll0 ll'0 H0 H1 H2 H3 H4 a0 H5. 
  inversion H5.
  + inversion H6. apply in_eq. 
  + apply H4 in H6. apply in_cons;auto.
- unfold Pqexp. intros a t1 t2 il0 il'0 ll0 ll'0 H0 H1 H2 H3 H4 a0 H5. 
  inversion H5.
  + inversion H6. apply in_eq.
  + repeat apply in_cons. 
    apply H4;auto.
- auto.
Qed.

Theorem qexp_inIL: forall a il' ll' il ll,
  Subtypecontext il' ll' il ll -> 
  In (is_qexp a) il -> In (is_qexp a) il'. 
Proof.
intros a il' ll' il ll H H0. apply qexp_inIL_alt in H. unfold Pqexp in H.
apply H;auto.
Qed.

Definition Pempty (il' ll' il ll:list atm): Prop :=
  ll = [] -> ll'=[]. 

Theorem inv_emptyll_alt: forall il il' ll' ll,
Subtypecontext il' ll' il ll -> Pempty il' ll' il ll.
Proof.
intros il il' ll' ll H.
apply Subtypecontext_ind;
  unfold Pempty; try auto; intros; try auto;  try inversion H6;auto.
- inversion H5.
- inversion H5.
Qed.

Theorem inv_emptyll: forall il il' ll',
  Subtypecontext il' ll' il [] -> ll' = [].
Proof.
intros il il' ll' H. apply inv_emptyll_alt in H. unfold P in H.
auto.
Qed.

Theorem In_cntxt_il: forall a t il ll il' ll', 
  In (typeof a t) il -> Subtypecontext il' ll' il ll -> 
  exists c, t = bang c.
Proof.
intros a t il ll il' ll' H H0. induction H0.
- inversion H. 
- inversion H.
  + inversion H1.
  + apply IHSubtypecontext in H1. auto.
- inversion H. inversion H3. inversion H3.
  + inversion H4. subst. auto.
  + apply IHSubtypecontext in H4. auto.
- inversion H.
  + inversion H4.
  + apply IHSubtypecontext in H4. auto.
- inversion H.
  + inversion H4.
  + apply IHSubtypecontext in H4. auto.
Qed.  

Theorem In_cntxt_il': forall a t il ll il' ll', 
  In (typeof a t) il' -> Subtypecontext il' ll' il ll -> 
  exists c, t = bang c.
Proof.
intros a t il ll il' ll' H H0. induction H0.
- inversion H.
- inversion H.
  + inversion H1.
  + apply IHSubtypecontext in H1. auto.
- inversion H.
  + inversion H3. 
  + inversion H3.
    * inversion H4. inversion H1. subst.
      apply Subtyping_bang_inv in H0. inversion H0.
      inversion H5. exists x0. auto.
    * apply IHSubtypecontext in H4. auto.
- inversion H.
  + inversion H4. 
  + apply IHSubtypecontext in H4. auto.
- inversion H.
  + inversion H4. 
  + inversion H4.
    * inversion H5. subst. auto.
    * apply IHSubtypecontext in H5. auto.
Qed.  

Theorem In_cntxt_ll: forall a t il ll il' ll', 
  In (typeof a t) ll -> Subtypecontext il' ll' il ll -> 
  validT (bang t).
Proof.
intros a t il ll il' ll' H H0. induction H0.
- inversion H. 
- auto.
- apply IHSubtypecontext in H. auto.
- inversion H.
  + inversion H4. subst. auto.
  + apply IHSubtypecontext in H4. auto.
- inversion H.
  + injection H4. intros. subst. auto.
  + apply IHSubtypecontext in H4. auto.
Qed.  

Theorem In_cntxt_ll': forall a t il ll il' ll', 
  In (typeof a t) ll' -> Subtypecontext il' ll' il ll -> 
  validT (bang t).
Proof.
intros a t il ll il' ll' H H0. induction H0.
- inversion H.
- auto.
- apply IHSubtypecontext in H. auto.
- inversion H. 
  + inversion H4. rewrite H7 in H0. auto.
  + apply IHSubtypecontext in H4. auto.
- apply IHSubtypecontext in H. auto.
Qed.  
 
Definition Psplit (il' ll' il ll:list atm): Prop :=
  forall ll1 ll2, LSL.split ll ll1 ll2 ->  
  exists il1 il2 ll1' ll2',
    LSL.split ll' ll1' ll2' /\
    (forall a, In a il -> In a il1) /\
    (forall a, In a il -> In a il2) /\
    (forall a t, In (typeof a t) il1 -> In (is_qexp a) il1) /\
    (forall  a t, In (typeof a t) ll1 -> In (is_qexp a) il1) /\
    (forall a t, In (typeof a t) il2 -> In (is_qexp a) il2) /\
    (forall  a t, In (typeof a t) il2 -> In (is_qexp a) il2) /\ 
    Subtypecontext il' ll1' il1 ll1 /\
    Subtypecontext il'  ll2' il2 ll2.

Theorem subcnxt_split_alt: forall il il' ll ll',  
  Subtypecontext il' ll' il ll -> Psplit  il' ll' il ll.
Proof.
intros il il' ll ll' H.
apply Subtypecontext_ind; unfold Psplit; intros; auto.
- apply split_nil in H0. 
  inversion H0. subst.
  repeat exists []. split.
  { apply init. }
  split.
  { intros a H1. inversion H1. }
  repeat try split; intros; try inversion H1; apply subcnxt_i.
- apply H1 in H2. inversion H2. inversion H3.
  inversion H4. inversion H5. exists (is_qexp a::x).
  exists (is_qexp a::x0). exists x1. exists x2. inversion H6.
  inversion H8.  inversion H10. inversion H12. split.
  { auto. }
  split.
  { intros a0 H15. inversion H15.
    { subst. apply in_eq. }
    { apply in_cons. apply H9 in H16. auto. }}
  split.
  { intros a0 H15. inversion H15. 
    { subst. apply in_eq. }
    { apply in_cons. apply H11 in H16. auto. }}
  { inversion H14. inversion H16. inversion H18.
    inversion H20.
    split.
    { intros a0 t H23. inversion H23.
      { subst. inversion H24. }
      { apply in_cons. apply H13 with t. auto. }}
    split.
    { intros a0 t H23. apply in_cons. apply H15 with t. auto. }
    split. 
    { intros a0 t H23. inversion H23.
      { subst. inversion H24. }
      { apply in_cons. apply H17 with t. auto. }}
    split.
    { intros a0 t H23. inversion H23. 
      { inversion H24. }
      { apply in_cons. apply H19 with t. auto. }} 
    split.
    { apply subcnxt_q. auto. }
    { apply subcnxt_q. auto. }}
(* unfold Psplit.  intros. *)
- apply H3 in  H4. inversion H4. inversion H5.
  inversion H6. inversion H7. 
  { exists (is_qexp a::typeof a t2::x). 
    exists (is_qexp a::typeof a t2::x0). exists x1. exists x2.
    inversion H8. inversion H10. inversion H12. inversion H14.
    split; auto.
    split.
    { intros a0 H17. inversion H17. 
      { subst. apply in_eq. } 
      { inversion H18.
        { subst. apply in_cons. apply in_eq. }
        { apply H11 in H19. repeat apply in_cons;auto. }}}
    split. 
    { intros a0 H17. inversion H17.
      { subst. apply in_eq. }
      { inversion H18. 
        { subst. apply in_cons. apply in_eq. }
        { apply H13 in H19. repeat apply in_cons;auto. }}}
    inversion H16. inversion H18. inversion H20. inversion H22.
    split. 
    { intros a0 t H25. inversion H25. 
      { inversion H26. }
      { inversion H26. 
        { inversion H27. apply in_eq. }
        { repeat apply in_cons. apply H15 with t. auto. }}}
    split.
    { intros a0 t H25. repeat apply in_cons. apply H17 with t. auto. }
    split.
    { intros a0 t H25. inversion H25. 
      { inversion H26. }
      { inversion H26. 
        { inversion H27. apply in_eq. }
        { repeat apply in_cons. apply H19 with t. auto. }}}
    split.
    { intros a0 t H25. inversion H25. 
      { inversion H26. } 
      { inversion H26.
        { inversion H27. apply in_eq. } 
        { repeat apply in_cons. apply H19 with t. auto. }}}
    split.
    { apply subcnxt_iig;auto. }
    { apply subcnxt_iig;auto. }}
- inversion H5;
  [apply H4 in H10; inversion H10; inversion H11;
   inversion H12; inversion H13; inversion H14; 
   inversion H16; inversion H18; inversion H20; inversion H22 as[h22 h22'];
   clear H22; inversion h22' as [H22 H22']; clear h22';
   inversion H22' as[h22' h22'']..];
  [exists (is_qexp a::x); exists (is_qexp a::x0);
          exists (typeof a t1::x1); exists (x2) |
          exists (is_qexp a::x); exists (is_qexp a::x0);
          exists x1; exists (typeof a t1::x2)];
  split;
  [apply splitr1;auto|idtac|apply splitr2;auto|idtac];
  split; repeat (split;intros); intros ;try inversion H23; subst;
  try apply in_eq;
  try assert(H24':=H24);try apply H17 in H24; try apply H19 in H24';
  try apply H21 in H24;try apply H21 in H24'; 
  try apply h22 in H24;try apply h22' in H24';
  try apply H22 in H24;try apply h22' in H24';
  try inversion H24;subst;
  try apply in_eq;auto; try apply in_cons;auto;
  auto;inversion h22'';try apply subcnxt_llg;auto;
  try apply subcnxt_q;auto.
  apply h22 in H23. auto.
- inversion H5;
  [apply H4 in H10; inversion H10; inversion H11;
   inversion H12; inversion H13; inversion H14; 
   inversion H16;  inversion H18;  inversion H20;inversion H22 as[h22 h22'];
   clear H22; inversion h22' as[H22 H22']; clear h22';
   inversion H22' as[h22' h22'']..];
  [exists (is_qexp a::x); exists (is_qexp a::typeof a t1::x0);
          exists x1; exists (x2)|
          exists(is_qexp a::typeof a t1::x); exists (is_qexp a::x0);
          exists x1; exists (x2)];
  split; [auto|idtac|auto|idtac];
  split; repeat (split;intros); intros; try inversion H23; subst;
  try apply in_eq;
  try assert(H24':=H24);try apply H17 in H24; try apply H19 in H24';
  try apply H21 in H24;try apply H21 in H24'; 
  try apply h22 in H24;try apply h22' in H24';
  try apply H22 in H24;try apply h22' in H24';
  try inversion H24;subst; try inversion H6;
  tryif assert(H6':=H6) then idtac
                else assert(H6:=In_cntxt_ll);
                  try assert(H23':=H23);
                  apply2 H17 H23 H6; apply2 H21 H23 H6;
                  apply2 h22 H23 H6 ; apply2 H22 H23 H6;
                  apply2 H19 H23' H6' ; apply2 H21 H23' H6';
                  apply2 h22' H23' H6'; apply2 h22' H6' H23';
                  try apply in_eq;auto; try apply in_cons;auto;
                  try apply in_eq;auto; try apply in_cons;auto;
                  auto;inversion h22'';try apply subcnxt_iig;auto;
                  try apply subcnxt_lig;auto; try apply sub_ref; 
                  apply SubAreVal in H2; inversion H2; auto.
Qed.

Theorem subcnxt_split: forall il il' ll ll' ll1 ll2,
  Subtypecontext il' ll' il ll -> LSL.split ll ll1 ll2 ->  
  exists il1 il2 ll1' ll2',
    LSL.split ll' ll1' ll2' /\
    (forall a, In a il -> In a il1) /\ (forall a, In a il -> In a il2) /\ 
    (forall a t, In (typeof a t) il1 -> In (is_qexp a) il1) /\
    (forall  a t, In (typeof a t) ll1 -> In (is_qexp a) il1) /\
    (forall a t, In (typeof a t) il2 -> In (is_qexp a) il2) /\
    (forall  a t, In (typeof a t) il2 -> In (is_qexp a) il2) /\ 
    Subtypecontext il' ll1' il1 ll1 /\
    Subtypecontext il'  ll2' il2 ll2.
Proof.
intros il il' ll ll' ll1 ll2 H H0. apply subcnxt_split_alt in H;auto.
Qed. 

Definition Pboth (il' ll' il ll:list atm): Prop :=
  (forall a t, In (typeof a t) il -> In (is_qexp a) il) /\
  (forall  a t, In (typeof a t) ll -> In (is_qexp a) il) /\
  (forall a t, In (typeof a t) il' -> In (is_qexp a) il') /\
  (forall  a t, In (typeof a t) ll' -> In (is_qexp a) il').

Theorem subcnxt_both_alt: forall il il' ll ll',  
  Subtypecontext il' ll' il ll -> Pboth  il' ll' il ll.
Proof.
intros il il' ll ll' H.
apply Subtypecontext_ind;unfold Pboth;intros;auto;
  repeat split;intros; destruct_conj;subst;auto;
  repeat
    ((match goal with
        |[H:forall _ _, In _ ?x -> _ ,H2:In _ ?x|-_]=> try apply H in H2
        | _=> idtac end);
     (match goal with
        |[H:In (_) (_::_)|-_]=> inversion H;clear H
                                           | _=> idtac end)) ;
    (match goal with 
       |[H:is_qexp _ = typeof _ _ |-_]=> inversion H
       | _=> idtac end);
    (match goal with
       |[H:In _ []|-_]=> inversion H
       | _=> idtac end);
    (match goal with 
       |[H:is_qexp _ = is_qexp _ |-_]=> inversion H;subst
                                                   | _=> idtac end); 
    (match goal with 
       |[H:typeof _ _= typeof _ _|-_]=> inversion H;subst
                                                   | _=> idtac end);
  repeat (try apply in_eq;try apply in_cons);auto.
Qed.

Theorem subcnxt_both: forall il il' ll ll',  
  Subtypecontext il' ll' il ll -> 
  (forall a t, In (typeof a t) il -> In (is_qexp a) il) /\
  (forall  a t, In (typeof a t) ll -> In (is_qexp a) il) /\
  (forall a t, In (typeof a t) il' -> In (is_qexp a) il') /\
  (forall  a t, In (typeof a t) ll' -> In (is_qexp a) il').
Proof.
intros il il' ll ll' H. apply subcnxt_both_alt in H;auto.
Qed.

Definition Pconcat (il' ll' il ll:list atm): Prop :=
  forall ll1 ll2,
    ll = ll1++ll2 ->  
    exists il1 il2 ll1' ll2',
      ll' = ll1'++ll2' /\
      (forall a, In a il -> In a il1) /\ (forall a, In a il -> In a il2) /\ 
      Subtypecontext il' ll1' il1 ll1 /\
      Subtypecontext il'  ll2' il2 ll2.

Theorem subcnxt_concat_alt: forall il il' ll ll',  
  Subtypecontext il' ll' il ll -> Pconcat  il' ll' il ll.
Proof.
  intros il il' ll ll' H.
  apply Subtypecontext_ind;
    unfold Pconcat; intros;auto;
      [idtac|idtac|idtac|
       destruct ll1;
       [rewrite app_nil_l in H5;
        assert(ll0=tl ll2); subst; simpl tl; auto;
        rewrite <- app_nil_l in H6|inversion H5]..];
      (match goal with
       |[H:forall _ _, ?x = _ ++ _  -> _ ,H2:?x=_ ++ _|-_]=> try apply H in H2
       | _=> idtac end);rexists;destruct_conj;
        [symmetry in H0; 
         apply app_eq_nil  in H0; inversion H0; subst;
         repeat exists []; repeat split;try apply subcnxt_i;intros; 
                       try rewrite app_nil_l;auto;
                       inversion H1|
                       exists (is_qexp a::x);
                       exists (is_qexp a::x0); exists x1; exists x2|
                       exists (is_qexp a::typeof a t2::x);
                       exists (is_qexp a::typeof a t2::x0); exists x1; exists x2|
                       exists (is_qexp a::x); 
                       exists (is_qexp a::x0);
                       exists []; exists (typeof a t1::x2)|
                       exists (is_qexp a::x); exists (is_qexp a::x0);
                       exists (typeof a t1::x1); exists x2|
                       exists (is_qexp a::typeof a t1::x); exists (is_qexp a::x0);
                       exists []; exists x2
                       |exists (is_qexp a::x); exists (is_qexp a::typeof a t1::x0);
                        exists x1; exists x2];
        [(repeat split;auto;
          intros;
          repeat ((match goal with
                   |[H:forall _ _, In _ ?x -> _ ,H2:In _ ?x|-_]=> try apply H in H2
                   | _=> idtac end);
                  (match goal with
                   |[H:In (_) (_::_)|-_]=> inversion H;clear H
                   | _=> idtac end)) ; 
          (match goal with 
           |[H:is_qexp _ = typeof _ _ |-_]=> inversion H
           | _=> idtac end);
          (match goal with
           |[H:In _ []|-_]=> inversion H
           | _=> idtac end);
          (match goal with 
           |[H:is_qexp _ = is_qexp _ |-_]=> inversion H;subst
           | _=> idtac end); 
          (match goal with 
           |[H:typeof _ _= typeof _ _|-_]=> inversion H;subst
           | _=> idtac end);subst;
          repeat (try apply in_eq;try apply in_cons);auto;
          try apply subcnxt_lig;try apply subcnxt_llg;try apply subcnxt_iig;
          try apply subcnxt_q; auto)..]; 
        match goal with | [H:_ |- exists _, bang ?x3 = _] => exists x3|_=>idtac end; auto;
          assert(H8':=H8);  try apply inv_emptyll in H8; subst; auto;
            apply sub_ref; apply SubAreVal in H2;inversion H2;auto.
Qed.

Theorem subcnxt_concat: forall il il' ll ll' ll1 ll2,
  Subtypecontext il' ll' il ll -> ll = ll1++ll2 ->  
  exists il1 il2 ll1' ll2',
    ll' = ll1'++ll2' /\
    (forall a, In a il -> In a il1) /\ (forall a, In a il -> In a il2) /\ 
    Subtypecontext il' ll1' il1 ll1 /\
    Subtypecontext il'  ll2' il2 ll2.
Proof.
intros il il' ll ll' ll1 ll2 H H0. apply subcnxt_concat_alt in H;auto.
Qed.

Theorem subcnxt_add: forall IL IL' fq,
  Subtypecontext IL' [] IL [] -> 
  Subtypecontext (toiqlist fq++IL') (toqlist fq) 
                 (toiqlist fq++IL) (toqlist fq).
Proof.
intros IL IL' fq H. functional induction toqlist fq;simpl; auto.
apply subcnxt_llg;auto;try apply bQubit; try apply QubitSub.
Qed. 

Theorem subcnxt_add2: forall IL IL' LL LL' fq,
  Subtypecontext IL' LL' IL LL -> 
  Subtypecontext (toiqlist fq++IL') LL' 
                 (toiqlist fq++IL) LL.
Proof.
intros IL IL' LL LL' fq H. functional induction toqlist fq;simpl; auto.
apply subcnxt_q;auto;try apply bQubit; try apply QubitSub.
Qed.

Definition Psinglell (il' ll' il ll:list atm): Prop := forall A a,
  ll = [typeof a A] ->
  exists B, Subtyping B A /\
            (ll' = [typeof a B] \/ (In (typeof a B)  il' /\ ll' = [])).
 
Theorem inv_singlell_alt: forall il il' ll' ll,
  Subtypecontext il' ll' il ll -> Psinglell il' ll' il ll.
Proof.
  intros il il' ll' ll H.
  apply Subtypecontext_ind;
    unfold Psinglell; intros;auto;
      [inversion H0|(match goal with
                     |[H:forall _ _, ?x = _   -> _ ,H2:?x=_ |-_]=> try apply H in H2
                     | _=> idtac end);rexists;destruct_conj..];
      [ exists x|exists x|exists t1| exists t1];
      match goal with |[H:_\/_ |- _] => destruct H|_=>idtac end;split;
        auto;destruct_conj;[right|right|idtac|left|idtac|right];
          try split;subst;auto;  try inversion H5; 
            subst;auto; repeat (try apply in_eq; try apply in_cons);auto;
              match goal with 
              |[H: Subtypecontext _ _ _ [] |- _] => 
               apply inv_emptyll in H
              |_=>idtac end;subst;auto.
Qed.

Theorem inv_singlell: forall il il' ll' A a,
  Subtypecontext il' ll' il [typeof a A] -> 
  exists B, Subtyping B A /\
            (ll' = [typeof a B] \/ (In (typeof a B)  il' /\ ll' = [])). 
Proof.
intros il il' ll' A a H. apply inv_singlell_alt in H.
unfold Psinglell;auto.
Qed.

Definition Pinil (il' ll' il ll:list atm): Prop :=
  forall A a, In (typeof a A) il ->
              exists B, Subtyping B A /\ (In (typeof a B)  il').
  
Theorem inv_inil_alt: forall IL LL IL' LL',
    Subtypecontext IL' LL' IL LL -> Pinil IL' LL' IL LL.
Proof.
  intros il il' ll' ll H.
  apply Subtypecontext_ind;
    unfold Pinil; intros;auto;
      [inversion H0|
       repeat (match goal with
               |[H:In _ (_::_)|-_]=> inversion H; clear H
               |_=>idtac end);
       (match goal with
        |[H:is_qexp _ = typeof _ _ |-_]=> inversion H
        |_=>idtac end);
       (match goal with
        |[H:forall _ _, In _ ?x   -> _ ,H2:In _ ?x |-_]=> try apply H in H2
        | _=> idtac end);rexists;destruct_conj..];
      [exists x|exists t1|exists x..]; split; 
        (match goal with
         |[H:typeof _ _ = typeof _ _|-_]=> inversion H;subst
         | _=> idtac end);
        repeat (try apply in_eq;try apply in_cons);auto.
Qed.

Theorem inv_inil: forall IL LL IL' LL' A a,
  Subtypecontext IL' LL' IL LL -> In (typeof a A) IL ->
  exists B, Subtyping B A /\ (In (typeof a B)  IL').
Proof.
intros IL LL IL' LL' A a H. apply inv_inil_alt in H. unfold Pinil in H.
auto.
Qed.

Definition Pcntxt_splits (il ll il' ll':list atm) := 
forall ll1 ll2, LSL.split ll ll1 ll2 -> 
Subtypecontext il ll1 il ll1 /\ Subtypecontext il ll2 il ll2.

Theorem subcntxt_splits_alt: forall il ll,
Subtypecontext il ll il ll -> Pcntxt_splits il ll il ll.
intros. apply Subtypecontext_ind; unfold Pcntxt_splits; 
auto; intros. apply split_nil in H0.
inversion H0. subst.  split;apply subcnxt_i.
apply H1 in H2. inversion H2. split; apply subcnxt_q;auto.
apply H3 in H4. inversion H4. split; apply subcnxt_iig;auto.
apply SubAreVal in H0. inversion H0. apply sub_ref;auto.
inversion H1. subst. apply Subtyping_bang_inv in H0.
inversion H0. inversion H7. exists x0. auto.
apply SubAreVal in H0. inversion H0. apply sub_ref;auto.
inversion H1. subst. apply Subtyping_bang_inv in H0.
inversion H0. inversion H7. exists x0. auto.
inversion H5.
apply H4 in H10. inversion H10. split.
apply subcnxt_llg;auto. 
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
apply subcnxt_q;auto. 

apply H4 in H10. inversion H10. split.
apply subcnxt_q;auto. 
apply subcnxt_llg;auto. 
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
apply H4 in H5.
inversion H5. split; apply subcnxt_iig;auto.
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
Qed.

Theorem subcntxt_splits: forall il ll ll1 ll2,
Subtypecontext il ll il ll -> LSL.split ll  ll1 ll2 -> 
Subtypecontext il ll1 il ll1 /\ Subtypecontext il ll2 il ll2.
Proof.
intros. apply subcntxt_splits_alt in H.
unfold Pcntxt_splits in H. auto.
Qed.

Definition Pcntxt_concats (il ll il' ll':list atm) := 
forall ll1 ll2, ll = ll1++ll2 -> 
Subtypecontext il ll1 il ll1 /\ Subtypecontext il ll2 il ll2.

Theorem subcntxt_concats_alt: forall il ll,
    Subtypecontext il ll il ll -> Pcntxt_concats il ll il ll.
Proof.
intros. apply Subtypecontext_ind; unfold Pcntxt_concats; 
auto; intros. symmetry in  H0. apply app_eq_nil in H0.
inversion H0. subst.  split;apply subcnxt_i.
apply H1 in H2. inversion H2. split; apply subcnxt_q;auto.
apply H3 in H4. inversion H4. split; apply subcnxt_iig;auto.
apply SubAreVal in H0. inversion H0. apply sub_ref;auto.
inversion H1. subst. apply Subtyping_bang_inv in H0.
inversion H0. inversion H7. exists x0. auto.
apply SubAreVal in H0. inversion H0. apply sub_ref;auto.
inversion H1. subst. apply Subtyping_bang_inv in H0.
inversion H0. inversion H7. exists x0. auto.
destruct ll1. rewrite app_nil_l in H5.
assert(ll'=tl ll2). subst. simpl tl. auto. rewrite <- app_nil_l in H6.
apply H4 in H6. inversion H6. split.
apply subcnxt_q. auto. subst.
apply subcnxt_llg;auto. 
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
inversion H5. apply H4 in H8. inversion H8. split. apply subcnxt_llg;auto.
apply SubAreVal in H2. inversion H2. apply sub_ref;auto. 
apply  subcnxt_q. auto. apply H4 in H5.
inversion H5. split; apply subcnxt_iig;auto.
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
apply SubAreVal in H2. inversion H2. apply sub_ref;auto.
Qed.

Theorem subcntxt_concats: forall il ll ll1 ll2,
Subtypecontext il ll il ll -> ll = ll1++ll2 -> 
Subtypecontext il ll1 il ll1 /\ Subtypecontext il ll2 il ll2.
Proof.
intros. apply subcntxt_concats_alt in H.
unfold Pcntxt_concats in H. auto.
Qed.

Lemma memb_il : forall (il il' ll ll':list atm) (M:qexp) (T:qtp),
   Subtypecontext il ll il' ll' ->
   In  (typeof M T) ll -> In (is_qexp M) il /\ In (is_qexp M)  il'.
Proof.
intros il il' ll ll' M T; induction 1; try (simpl; tauto).
intro h2; simpl in h2; destruct h2 as [h2 | h2].
injection h2; intros; subst; subst; simpl; auto.
apply IHSubtypecontext in h2. inversion h2. split;apply in_cons;auto.
Qed.

Lemma memb_il_il : forall (il il' ll ll':list atm) (M:qexp) (T:qtp),
   Subtypecontext il ll il' ll' ->
   In  (typeof M T) il -> In (is_qexp M) il /\ In (is_qexp M)  il'.
Proof.
intros il il' ll ll' M T; induction 1; try (simpl; tauto).
intro h2; simpl in h2; destruct h2 as [h2 | h2].
inversion h2;injection h2; intros; subst; subst; simpl; auto.
apply IHSubtypecontext in h2. inversion h2. split;apply in_cons;auto.
intro h2; simpl in h2; destruct h2 as [h2 | h2].
inversion h2. destruct h2. inversion H2. subst.
split;apply in_eq.
apply IHSubtypecontext in H2. inversion H2.
split; repeat apply in_cons;auto.
intro h2; simpl in h2; destruct h2 as [h2 | h2].
inversion h2. 
apply IHSubtypecontext in h2. inversion h2.
split; repeat apply in_cons;auto.
intro h2; simpl in h2; destruct h2 as [h2 | h2].
inversion h2. destruct h2. inversion H3. subst.
split;apply in_eq.
apply IHSubtypecontext in H3. inversion H3.
split; repeat apply in_cons;auto.
Qed.

Theorem intoqlist_infq:forall a fq,
    In a (toqlist fq) ->
    exists q, a = typeof (CON (Qvar q)) qubit /\ In (CON (Qvar q)) fq.
Proof.
intros. functional induction toqlist fq.
inversion H.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
inversion H. exists x. split;auto. apply in_eq.
try apply IHl in H0; inversion H0; try inversion H1;
exists x0; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
try apply IHl in H; inversion H; try inversion H0;
exists x; split;auto; try apply in_cons; auto.
Qed.

Theorem infq_intoqlist: forall q fq, In (CON (Qvar q)) fq -> 
In (typeof (CON (Qvar q)) qubit) (toqlist fq).
Proof.
intros. functional induction toqlist fq; try inversion H;
try inversion H0;subst;simpl;intuition.
Qed.

Lemma in_toqlist:forall x l, In (CON (Qvar x)) l ->
  In (typeof (CON (Qvar x)) qubit) (toqlist l).
Proof.
intros x l.
functional induction toqlist l;intros; inversion H;auto; try inversion H0.
- apply in_eq.
- apply in_cons. auto.
Qed.

Theorem toqlist_union: forall l1 l2,
  (forall a, In a  l1 -> In a l2) ->
  (forall a, In a (toqlist l1) -> In a (toqlist l2)).
Proof.
intros l1.
apply toqlist_ind;intros;
try inversion H1; subst; try apply in_toqlist; try apply H0;
try apply in_eq;  
try apply H;auto;intros;try apply H0;try apply in_cons; auto;
inversion H0.
Qed.

Theorem intoiqlist_infq:forall a fq,
    In a (toiqlist fq) ->
    exists q, a = is_qexp (CON (Qvar q))  /\ In (CON (Qvar q)) fq.
Proof.
intros. functional induction toiqlist fq;
[try inversion H;
try apply IHl in H; inversion H; 
try apply IHl in H1; try inversion H1; try inversion H2;try inversion H0;
 try exists x0; try exists x;split;auto; subst;
match goal with [ H:_ |- In ?a (?a::_)] => apply in_eq
| _ =>  try apply in_cons end; auto..]. 
Qed.

(****************************************************************
   Definition of prog
  ****************************************************************)

Variable circIn: Econ -> set qexp.
Variable circOut: Econ -> set qexp.
Variable circApp: Econ -> Econ -> Econ.
Variable circNew: list qexp -> nat.
Variable circRev: nat -> nat.

Definition  valid_c (C:Econ) (a:qexp) :Prop:=
  (exists c, C =  Crcons c) /\ 
  (forall q, In q (FQ a) -> In q (circOut C)).

Inductive prog : atm -> list oo_ -> list oo_ -> Prop :=
(* encoding of Well formedness rules *)
 | starq: prog (is_qexp (CON STAR)) [] []
 | trueq: prog (is_qexp (CON TRUE)) [] []
 | falseq: prog (is_qexp (CON FALSE)) [] []
 | boxq: forall T, valid T -> prog (is_qexp (CON (BOX T))) [] []
 | unboxq: prog (is_qexp (CON UNBOX)) [] []
 | revq: prog (is_qexp (CON REV)) [] []
 | qvar: forall i, prog (is_qexp (CON (Qvar i))) [] []
 | lambdaq: forall (E : qexp -> qexp), abstr E ->
   prog (is_qexp (Fun E))
        [(All (fun x : qexp => Imp (is_qexp x) (atom_ (is_qexp (E x)))))] []
 | apq: forall E1 E2: qexp,
   prog (is_qexp (App E1 E2))
        [And (atom_ (is_qexp E1)) (atom_ (is_qexp E2))] []
 | prodq: forall E1 E2: qexp,
   prog (is_qexp (Prod E1 E2))
        [And (atom_ (is_qexp E1)) (atom_ (is_qexp E2))] []
 | letq: forall (E:qexp -> qexp -> qexp) (b:qexp),
         abstr (fun x => lambda (E x)) ->  
         (*abstr (fun y => lambda (fun x =>  (E x y)))*)
         (forall x, proper x ->  abstr (E x)) -> 
         (*(forall y, proper y ->  abstr (fun x=> E x y))->*)
   prog (is_qexp (Let E b))
         [(All (fun x : qexp => (All (fun y:qexp => 
           Imp (is_qexp x) (Imp (is_qexp y) (atom_ (is_qexp (E x y))))))));
          (atom_ (is_qexp b))] []
 | sletq: forall E b:qexp,
   prog (is_qexp (Slet E b))
         [And (atom_ (is_qexp E))  (atom_ (is_qexp b))] []
 | ifq: forall b a1 a2:qexp,
   prog (is_qexp (If b a1 a2))
        [And (atom_ (is_qexp b))
          (And (atom_ (is_qexp a1)) (atom_(is_qexp a2)))] []
 | Circq: forall (C:nat) (t a:qexp), quantum_data t ->
   prog (is_qexp (Circ t C a))
         [toimpexp (FQ a) (atom_ (is_qexp a))] []
(* encoding of evaluation rules *)
 | sletr: forall C C' a a' b, valid_c C (Slet a b) ->
          valid_c C' (Slet a' b) -> ~(is_value a) -> 
   prog (reduct C (Slet b a) C' (Slet b a'))
         [atom_ (reduct C a C' a'); atom_ (is_qexp b)] []
 | starr: forall C  b, valid_c C (Slet  b (CON STAR))->  
   prog (reduct C (Slet b (CON STAR)) C b)
         [atom_ (is_qexp b)] []
  | ifr: forall C C' b b' a1 a2, valid_c C (If b a1 a2) ->
         valid_c C' (If b' a1 a2) -> ~(is_value b) ->
   prog (reduct C (If b a1 a2) C' (If b' a1 a2))
        [atom_(reduct C b C'  b');
         atom_ (is_qexp a1); atom_(is_qexp a2)] []
 | truer: forall C a1 a2,  valid_c C (If (CON TRUE) a1 a2) ->
   prog (reduct C (If (CON TRUE) a1 a2) C a1)
        [atom_ (is_qexp a1); atom_(is_qexp a2)] []
 | falser: forall C a1 a2, valid_c C (If (CON TRUE) a1 a2) ->
   prog (reduct C (If (CON FALSE) a1 a2) C a2)
        [atom_ (is_qexp a1); atom_(is_qexp a2)] []
 | boxr: forall T v C,  valid T  -> is_value v -> 
         circIn (Crcons (circNew (FQ(Spec (newqvar v) T)))) =
         FQ(Spec (newqvar v) T) ->
         circOut (Crcons (circNew (FQ(Spec (newqvar v) T)))) = 
         List.rev(FQ(Spec (newqvar v) T)) ->
   prog (reduct C (App (CON (BOX T)) v) C
          (Circ (Spec (newqvar v) T) (circNew (FQ(Spec (newqvar v) T)))
            (App v (Spec (newqvar v) T))))
        [atom_(is_qexp  v)] []
 | unboxr: forall u u' v C C' D b',
           quantum_data u -> quantum_data u' -> quantum_data v ->
           circApp (Crcons C) (Crcons D)  = C' ->
           bind u' b'->  bind u v  -> 
   prog (reduct (Crcons C)
                  (App (App (CON UNBOX) (Circ u D u')) v) C' b')
                [atom_ (is_qexp v); atom_ (is_qexp b')] []
 | revr: forall C t t', quantum_data t ->  quantum_data t' -> 
         circIn (Crcons (circRev C)) = FQ t' ->
         circOut (Crcons (circRev C)) = FQ t ->
   prog (reduct (Crcons C) (App (CON REV) (Circ t C t'))
                (Crcons C) (Circ t' (circRev C) t)) [] []
 | lambdar: forall (E : qexp -> qexp) v C, abstr E ->
            valid_c C (Fun E) -> is_value v -> 
   prog (reduct C (App (Fun E) v) C  (E v))
        [(All (fun x : qexp => Imp (is_qexp x) (atom_ (is_qexp (E x)))));
          atom_ (is_qexp v)] []
 | aprf: forall E1 E2 E1' C C', ~(is_value E1) -> 
         valid_c C (App E1 E2) ->  valid_c C' (App E1' E2) -> 
   prog (reduct C (App E1 E2) C' (App E1' E2))
        [atom_(reduct C E1 C' E1'); atom_ (is_qexp E2)] []
 | apra: forall E1 E2 E2' C C', is_value E1 -> 
         valid_c C (App E1 E2) ->  valid_c C' (App E1 E2') -> 
   prog (reduct C (App E1 E2) C' (App E1 E2'))
        [atom_(reduct C E2 C' E2'); atom_ (is_qexp E1)] []
 | prodrr: forall E1 E2 E2' C C', ~(is_value E2) -> 
           valid_c C (Prod E1 E2) ->  valid_c C' (Prod E1 E2') ->
   prog (reduct C (Prod E1 E2) C' (Prod E1 E2'))
        [atom_(reduct C E2 C' E2'); atom_ (is_qexp E1)] []
 | prodrl: forall E1 E2 E1' C C', is_value E2 -> 
           valid_c C (Prod E1 E2) ->  valid_c C' (Prod E1' E2) ->
   prog (reduct C (Prod E1 E2) C' (Prod E1' E2))
        [atom_(reduct C E1 C' E1'); atom_ (is_qexp E2)] []
 | letr1: forall (E:qexp -> qexp -> qexp) (b b':qexp) C C',
          (forall x, proper x ->  abstr (E x)) -> 
          (* (forall y, proper y -> abstr (fun x => E x y)) -> *) 
          abstr (fun x => lambda (E x)) ->  
          (* abstr (fun y => lambda (fun x => (E x y))) -> *)
          ~(is_value b) -> valid_c C (Let E b) ->  valid_c C'  (Let E b') -> 
   prog (reduct C (Let E b) C' (Let E b'))
        [atom_ (reduct C b C' b'); 
         (All (fun x : qexp => (All (fun y:qexp => 
           Imp (is_qexp x) (Imp (is_qexp y) (atom_ (is_qexp (E x y))))))))]
        []
 | letr2: forall (E:qexp -> qexp -> qexp) (v u:qexp) C,
          abstr (fun x => lambda (E x)) ->  
          (* abstr (fun y => lambda (fun x =>  (E x y))) -> *)
          (forall x, proper x ->  abstr (E x)) -> 
          (* (forall y, proper y -> abstr (fun x=> E x y))-> *)
          is_value v -> is_value u -> valid_c C (Let E (Prod v u))->
   prog (reduct C (Let E (Prod v u)) C (E v u))
        [(All (fun x : qexp => (All (fun y:qexp => 
           Imp (is_qexp x) (Imp (is_qexp y) (atom_ (is_qexp (E x y))))))));
         (atom_ (is_qexp v));(atom_ (is_qexp u))] []
  | Circr: forall C D D' (t a a':qexp), quantum_data t ->
           valid_c (Crcons D) a -> valid_c (Crcons D') a' ->
           circIn (Crcons D') = FQ(t) -> circOut (Crcons D') = FQ(a') ->
    prog (reduct C (Circ t D a) C (Circ t D' a'))
         [atom_(reduct (Crcons D) a (Crcons D') a')] []
(* encoding of typing rules *)
 | axc1: forall (A B:qtp) (x:qexp), validT (bang A) -> Subtyping A B ->
   prog (typeof x B) [atom_ (is_qexp x)] [atom_ (typeof x A)]
 | axc2: forall (A B:qtp) x, Subtyping (bang A) B ->
   prog (typeof x B)
        [(And (atom_ (typeof x (bang A))) (atom_ (is_qexp x)))] []
 | starl: prog (typeof (CON STAR) one) [] []
 | stari: prog (typeof (CON STAR) (bang one)) [] []
 | truel: prog (typeof (CON TRUE) bool) [] []
 | truei: prog (typeof (CON TRUE) (bang bool)) [] []
 | falsel: prog (typeof (CON FALSE) bool) [] []
 | falsei: prog (typeof (CON FALSE) (bang bool)) [] []
 | box: forall T U B, valid T -> valid U -> 
        Subtyping (bang (arrow (bang (arrow T U)) (bang (circ T U)))) B ->
   prog (typeof (CON (BOX T)) B) [] []
 | unbox: forall T U B, valid T -> valid U -> 
          Subtyping (bang (arrow (circ T U) (bang (arrow T U)))) B ->
   prog (typeof (CON UNBOX) B) [] []
 | rev: forall T U B, valid T -> valid U -> 
    Subtyping (bang(arrow (circ T U) (bang (circ U T)) )) B ->
   prog (typeof (CON REV) B) [] []  
 | lambda1l: forall (T1 T2: qtp) (E : qexp -> qexp),
             abstr E -> validT (bang T1) -> validT T2 -> 
   prog (typeof (Fun E) (arrow T1 T2))
        []  
        [(All (fun x : qexp =>
           Imp (is_qexp x) (lImp (typeof x T1) (atom_ (typeof (E x) T2)))))]
 | lambda1i: forall (T1 T2: qtp) (E : qexp -> qexp),
             abstr E -> validT (bang T1) -> validT T2 ->
   prog (typeof (Fun (fun x => E x)) (arrow (bang T1) T2))
        []
        [(All (fun x : qexp =>
           Imp (is_qexp x)
             (Imp (typeof x (bang T1)) (atom_ (typeof (E x) T2)))))] 
 | lambda2i: forall (T1 T2: qtp) (E : qexp -> qexp),
             abstr E -> validT (bang T1) -> validT T2 -> 
   prog (typeof (Fun (fun x => E x)) (bang(arrow (bang T1) T2)))
        [(All (fun x : qexp =>
           Imp (is_qexp x)
             (Imp (typeof x (bang T1)) (atom_ (typeof (E x) T2)))))]
        []
 | lambda2l: forall (T1 T2: qtp) (E : qexp -> qexp),
             abstr E ->  validT (bang T1) -> validT T2 -> 
   prog (typeof (Fun (fun x => E x)) (bang(arrow T1 T2)))
        [(All (fun x : qexp =>
           Imp (is_qexp x) (lImp (typeof x T1) (atom_ (typeof (E x) T2)))))]
        []
 | tap: forall E1 E2: qexp, forall T T' : qtp,  validT (arrow T' T) -> 
   prog (typeof (App E1 E2) T)
        []
        [(Conj (atom_ (typeof E1 (arrow T' T))) (atom_ (typeof E2 T')))]
 | ttensorl: forall E1 E2: qexp, forall T T' : qtp,
             validT (tensor T T') ->  
   prog (typeof (Prod E1 E2) (tensor T T'))
        []
        [Conj (atom_ (typeof E1 T)) (atom_ (typeof E2 T'))]
 | ttensori: forall E1 E2: qexp, forall T T' : qtp,
             validT (bang T) -> validT (bang T') ->
   prog (typeof (Prod E1 E2) (bang(tensor T T')))
        []
        [Conj (atom_ (typeof E1 (bang T))) (atom_ (typeof E2 (bang T')))]
 | tletl: forall (E:qexp -> qexp -> qexp) (b:qexp), forall T1 T2 T3 : qtp,
          abstr (fun x => lambda (E x)) ->  
          (* abstr (fun y => lambda (fun x =>  (E x y))) -> *)
          (forall x, proper x ->  abstr (E x)) -> 
          (* (forall y, proper y ->  abstr (fun x=> E x y))-> *)
          validT (bang  T1) -> validT (bang T2) ->  
   prog (typeof (Let E  b) T3)
        []
        [(All (fun x : qexp => (All( fun y:qexp =>
           Imp (is_qexp x) (Imp (is_qexp y) 
             (lImp (typeof x T1)
                (lImp (typeof y T2) (atom_ (typeof (E x y) T3)))))))));
         atom_ (typeof b (tensor T1 T2))]
 | tleti: forall (E:qexp -> qexp -> qexp) (b:qexp), forall T1 T2 T3 : qtp,
          abstr (fun x => lambda (E x)) ->  
          (* abstr (fun y => lambda (fun x =>  (E x y))) -> *)
          (forall x, proper x ->  abstr (E x)) -> 
          (* (forall y, proper y ->  abstr (fun x=> E x y))-> *)
          validT (bang  T1) -> validT (bang T2) ->
   prog (typeof (Let E  b) T3)
        []
        [(All (fun x : qexp => (All( fun y:qexp => 
           Imp (is_qexp x) (Imp (is_qexp y) 
             (Imp (typeof x (bang T1))
                (Imp (typeof y (bang T2)) (atom_ (typeof (E x y) T3)))))))));
         atom_ (typeof b (bang(tensor T1 T2)))]
 | tsletl: forall E b:qexp, forall T: qtp, validT T ->  
   prog (typeof (Slet E  b) T)
        []
        [Conj (atom_ (typeof E T)) (atom_ (typeof b one))]
 | tsleti: forall E b:qexp, forall T: qtp, validT T ->  
   prog (typeof (Slet E  b) T)
        []
        [Conj (atom_ (typeof E T)) (atom_ (typeof b (bang one)))]
 | tif: forall b a1 a2:qexp,forall T, validT T ->  FQ a1 = FQ a2 ->
   prog (typeof (If b a1 a2) T)
        []
        [Conj (atom_ (typeof b bool))
           (And (atom_ (typeof a1 T)) (atom_(typeof a2 T)))]
 | tCricl: forall (C:nat) (t a:qexp), forall T U,
           circIn (Crcons C) = FQ t -> circOut (Crcons C) = FQ a ->
           quantum_data t-> validT (circ T U) ->   
   prog (typeof (Circ t C a) (circ T U))
        [And (toimp (FQ a) (atom_(typeof a U)))
             ((toimp (FQ t) (atom_(typeof t T))))]
        []
 | tCrici: forall (C:nat) (t a:qexp), forall T U,
           circIn (Crcons C) = FQ t -> circOut (Crcons C) = FQ a ->
           quantum_data t-> validT (circ T U) -> 
   prog (typeof (Circ t C a) (bang ((circ T U))))
        [And (toimp (FQ a) (atom_(typeof a U)))
             ((toimp (FQ t) (atom_(typeof t T))))]
        [].
 
(****************************************************************
   Instantiation of seq
  ****************************************************************)

Definition seq_ : nat -> list atm -> list atm -> oo_ -> Prop := LSL.seq prog.
Definition splitseq_ : nat -> list atm -> list atm -> list oo_ -> Prop := LSL.splitseq prog.

Hint Unfold seq_ : hybrid.

Hint Resolve level_CON level_VAR level_BND level_APP level_ABS : hybrid.
Hint Resolve proper_APP abstr_proper : hybrid.
Hint Unfold proper: hybrid.
Hint Rewrite ext_eq_eta : hybrid.
Hint Unfold oo_ atom_ T_: hybrid.
Hint Unfold seq_ : hybrid.

End Prog.
