Library CCSSeqSemaphore

Require Import SfLib.
Require Import LibTactics.
Require Import BisimTheory.
Require Import NameSet.
Require Import Labels.
Require Import Util.

  Inductive proc : Set :=
    | p_nil: proc
    | p_sum: procprocproc
    | p_par: procprocproc
    | p_seq: procprocproc
    | p_act: labelprocproc
    | p_res: namenatprocproc
    | p_rep: procproc
    .
  Delimit Scope proc_scope with proc.
  Bind Scope proc_scope with proc.

  Definition res_filter a la := filter (fun lif label_eq l (l_in a) then false else if label_eq l (l_out a) then false else true) la.

  Fixpoint res_count_labels a n la n' := match la with
  | nilif eq_nat_dec n' n then true else false
  | (l_in a')::laif name_eq a' a then match n with 0 ⇒ false | S n0res_count_labels a n0 la n' end else res_count_labels a n la n'
  | (l_out a')::laif name_eq a' a then res_count_labels a (S n) la n' else res_count_labels a n la n'
  | _::lares_count_labels a n la n'
  end.

Module SyntaxNotation.
  Export Labels.Notation.
  Export NameSet.Notation.

  Notation "p + q" := (p_sum p%proc q%proc) : proc_scope.
  Notation "p || q" := (p_par p%proc q%proc) (left associativity, at level 50) : proc_scope.
  Notation "p ;; q" := (p_seq p%proc q%proc) (left associativity, at level 40) : proc_scope.
  Notation "a '?.' p" := (p_act (l_in a) p%proc) (no associativity, at level 20, x at level 0, p at level 21, format "a ?. p") : proc_scope.
  Notation "a '!.' p" := (p_act (l_out a) p%proc) (no associativity, at level 20, x at level 0, p at level 21, format "a !. p") : proc_scope.
  Notation "a '#' n ':' p" := (p_res a n p%proc) (no associativity, at level 20, n at level 0, p at level 21, format "a # n : p") : proc_scope.
  Notation "a '#:' p" := (p_res a 0 p%proc) (no associativity, at level 20, n at level 0, p at level 21, format "a #: p") : proc_scope.
  Notation "'().' p" := (p_act l_tau p%proc) (left associativity, at level 20, p at level 21, format "'().' p") : proc_scope.
  Notation "'O'" := (p_nil) (no associativity) : proc_scope.
  Notation "p '!!'" := (p_rep p%proc) (no associativity, at level 20, format "p !!") : proc_scope.

  Module U.
    Notation "p ‖ q" := (p_par p%proc q%proc) (left associativity, at level 50) : proc_scope.
    Notation "a ̄ '!.' p" := (p_act (l_out a) p%proc) (no associativity, at level 20, x at level 0, p at level 21, format "a ̄ !. p") : proc_scope.
    Notation "a '?.' p" := (p_act (l_in a) p%proc) (no associativity, at level 20, x at level 0, p at level 21, format "a ?. p") : proc_scope.
    Notation "'τ.' p" := (p_act l_tau p%proc) (left associativity, at level 20, p at level 21, format "'τ.' p") : proc_scope.
    Notation "'υ' a ':' n ':' p" := (p_res a n p%proc) (left associativity, at level 20, p at level 21, format "υ a : n : p") : proc_scope.
    Notation "'υ' a ':' p" := (p_res a 0 p%proc) (left associativity, at level 20, p at level 21, format "υ a : p") : proc_scope.
    Notation "'Ø'" := (p_nil) (no associativity) : proc_scope.
  End U.


  Module TestNotation.
    Open Scope proc_scope.
    Goal 1*(2×3)=6. omega. Qed.
    Goal 1<2. omega. Qed.
    Goal 2>1. omega. Qed.
    Goal (true||false)%bool=true. apply orb_true_iff; auto. Qed.
    Goal (true||false)%bool=true. apply orb_true_iff; auto. Qed.
    Goal ( p q, p + q = p_sum p q). reflexivity. Qed.
    Goal ( p q, p || q = p_par p q). reflexivity. Qed.
    Goal ( a p, a?.p = p_act (l_in a) p). reflexivity. Qed.
    Goal ( a p, a!.p = p_act (l_out a) p). reflexivity. Qed.
    Goal ( a n p, a#n:p = p_res a n p). reflexivity. Qed.
    Goal ( a p, a#:p = p_res a 0 p). reflexivity. Qed.
    Goal ( p q r, p || q || r = (p||q)||r). reflexivity. Qed.
    Goal ( p q r, p + q || r = (p+q)||r). reflexivity. Qed.
    Goal ( a b, a!.b?.O = (a!.(b?.O))). reflexivity. Qed.
    Goal ( a b p, a!.b?.O || p = (a!.b?.O) || p). reflexivity. Qed.
    Goal ( a p, a?.O + p = (a?.O) + p). reflexivity. Qed.
    Goal ( a, a!.a?.O = (a!.(a?.O))). reflexivity. Qed.
  End TestNotation.
End SyntaxNotation.

Section Machine.
  Import SyntaxNotation.

  Inductive step : proclabelprocProp :=
  | s_act: l p,
      step (p_act l p) l p
  | s_parL: p q l p',
      step p l p'
      step (p || q) l (p' || q)
  | s_parR: p q l q',
      step q l q'
      step (p || q) l (p || q')
  | s_sumL: p q l p',
      step p l p'
      step (p + q) l p'
  | s_sumR: p q l q',
      step q l q'
      step (p + q) l q'
  | s_seq: p q l p',
      step p l p'
      step (p;; q) l (p';; q)
  | s_res: a n p l p',
      step p l p'
      l l_in al l_out a
      step (a#n: p) l (a#n: p')
  | s_res_count_in: a n p p',
      step p (l_in a) p'
      step (a#(S n): p) l_tau (a#n: p')
  | s_res_count_out: a n p p',
      step p (l_out a) p'
      step (a#n: p) l_tau (a#(S n): p')
  | s_rep: p a p',
      step (p||p!!) a p'
      step (p!!) a p'
  
  | s_par_nil:
      step (p_nil || p_nil) l_tau p_nil
  | s_sum_nil:
      step (p_nil+p_nil) l_tau p_nil
  | s_seq_nil: p,
      step (p_nil;; p) l_tau p
  | s_res_nil: a n,
      step (a#n:p_nil) l_tau p_nil

  .

End Machine.

Module LTS.
  Definition is_halted (p:proc) := p=p_nil.

  Inductive lstep p : labelprocProp :=
  | lstep_in: a p',
      step p (l_in a) p'
      lstep p (l_in a) p'
  | lstep_out: a p',
      step p (l_out a) p'
      lstep p (l_out a) p'.

  Program Definition lts : labeled_transition_system :=
    (Build_labeled_transition_system proc label lstep (fun p p'step p l_tau p') is_halted _ _).
  Next Obligation.
    red in H; subst p; inverts H0.
  Qed.
  Next Obligation.
    red in H; subst p; inverts H0; inverts H.
  Qed.

  Existing Instance lts.

  Export Stepping.

  Arguments tstep lts p%proc p'%proc : rename.
  Arguments lstep p%proc l%label p'%proc.
  Arguments step_star lts p%proc la%label p'%proc.
  Arguments wbisimilar lts p%proc q%proc.
  Arguments partial_contrasimilar lts p%proc q%proc.
  Arguments contrasimilar lts p%proc q%proc.
  Arguments par_bisimilar lts p%proc q%proc.

End LTS.

  Import LTS.

Module StepNotation.
  Export Labels.Notation.

  Notation "p ---> p' " := (step p%proc l_tau p'%proc) (no associativity, at level 55, p' at level 55, format "p ---> p' ").
  Notation "p - l -> p' " := (step p%proc l%label p'%proc) (no associativity, at level 55, p' at level 55, format "p - l -> p' ").

  Module U.
    Notation "p ― l → p'" := (step p%proc l%label p'%proc) (no associativity, at level 55, p' at level 55, format "p ― l → p'").
    Notation "p ⟶ p'" := (step p%proc l_tau p'%proc) (no associativity, at level 55, p' at level 55).
    Notation "p ― l1 → p' ― l2 → p'' " := (step p%proc l1%label p'%proc step p'%proc l2%label p''%proc) (at level 55, p' at next level, format "p ― l1 → p' ― l2 → p'' ").
    Notation "p ― l1 → p' ⟶ p'' " := (step p%proc l1%label p'%proc step p'%proc l_tau p''%proc) (at level 55, p' at next level, format "p ― l1 → p' ⟶ p'' ").
    Notation "p ⟶ p' ― l2 → p'' " := (step p%proc l_tau p'%proc step p'%proc l2%label p''%proc) (at level 55, p' at next level, format "p ⟶ p' ― l2 → p'' ").
    Notation "p ⟶ p' ⟶ p'' " := (step p%proc l_tau p'%proc step p'%proc l_tau p''%proc) (at level 55, p' at next level, format "p ⟶ p' ⟶ p'' ").
  End U.

End StepNotation.

Section FreeNames.
  Fixpoint fn (p:proc) : nameset := match p with
  | p_nilNameSet.empty
  | p_sum p1 p2NameSet.union (fn p1) (fn p2)
  | p_par p1 p2NameSet.union (fn p1) (fn p2)
  | p_act l_tau pfn p
  | p_act (l_in a) pNameSet.add a (fn p)
  | p_act (l_out a) pNameSet.add a (fn p)
  | p_seq p1 p2NameSet.union (fn p1) (fn p2)
  | p_res a n pNameSet.remove a (fn p)
  | p_rep pfn p
  end.

  Fixpoint bn p : nameset := match p with
  | p_nilNameSet.empty
  | p_sum p1 p2NameSet.union (bn p1) (bn p2)
  | p_par p1 p2NameSet.union (bn p1) (bn p2)
  | p_act _ pbn p
  | p_seq p1 p2NameSet.union (bn p1) (bn p2)
  | p_res a n pNameSet.add a (bn p)
  | p_rep pbn p
  end.

  Fixpoint fn_in (p: proc) := match p with
  | p_act (l_in a) p'NameSet.add a (fn_in p')
  | p_act _ p'fn_in p'
  | p_sum p1 p2NameSet.union (fn_in p1) (fn_in p2)
  | p_par p1 p2NameSet.union (fn_in p1) (fn_in p2)
  | p_seq p1 p2NameSet.union (fn_in p1) (fn_in p2)
  | p_res a _ p'NameSet.remove a (fn_in p')
  | p_rep pfn_in p
  | p_nilNameSet.empty
  end.

  Fixpoint fn_out (p: proc) := match p with
  | p_act (l_out a) p'NameSet.add a (fn_out p')
  | p_act _ p'fn_out p'
  | p_sum p1 p2NameSet.union (fn_out p1) (fn_out p2)
  | p_par p1 p2NameSet.union (fn_out p1) (fn_out p2)
  | p_seq p1 p2NameSet.union (fn_out p1) (fn_out p2)
  | p_res a _ p'NameSet.remove a (fn_out p')
  | p_rep pfn_out p
  | p_nilNameSet.empty
  end.

End FreeNames.

Module Notation.
  Export SyntaxNotation.
  Export StepNotation.
  Module U.
    Export SyntaxNotation.U.
    Export StepNotation.U.
  End U.
End Notation.

Section Stepping.
  Import Notation.
  Import Notation.U.

  Definition par_swap p:= (match p with p1||p2p2 || p1 | _p end)%proc.

  Lemma par_swap_swap: p, par_swap (par_swap p) = p.
  Proof. destruct p; reflexivity. Qed.

  Lemma step_star_parL_step: p q la p',
    step_star p la p'
    step_star (p||q) la (p'||q).
  Proof.
    intros.
    induction H.
    - apply step_nil.
    - eapply step_lcons; eauto.
    inverts H; constructor; apply s_parL; eauto.
    - eapply step_tcons; eauto; apply s_parL; eauto.
  Qed.

  Lemma step_star_parR_step: p q la q',
    step_star q la q'
    step_star (p||q) la (p||q').
  Proof.
    intros.
    induction H.
    - apply step_nil.
    - eapply step_lcons; eauto.
    inverts H; constructor; apply s_parR; eauto.
    - eapply step_tcons; eauto; apply s_parR; eauto.
  Qed.

  Lemma step_star_seq_app: p l p' r,
    step_star p l p'step_star (p;;r) l (p';;r).
  Proof.
    intros.
    induction H.
    - apply step_nil.
    - eapply step_lcons; eauto. inverts H; constructor; apply s_seq; auto.
    - eapply step_tcons; eauto; apply s_seq; auto.
  Qed.

  Lemma step_star_lstep_inv: p a la p',
    step_star p (a::la) p'
     p'0 p'1,
      step_star p nil p'0 lstep p'0 a p'1 step_star p'1 la p'.
  Proof.
    intros.
    remember (a::la) as la0.
    revert a la Heqla0.
    induction H; intros; subst.
    - discriminate.
    - inversion Heqla0; subst a0 la0. clear Heqla0.
     p p'; splits; auto.
    apply step_nil.
    - edestruct IHstep_star as [p'0[p'1[?[??]]]]; clear IHstep_star; eauto.
     p'0 p'1; splits; auto.
    eapply step_tcons; eauto.
  Qed.

  Lemma step_star_nil_resolve: la p',
    step_star p_nil la p'la=[] p'=p_nil.
  Proof.
    intros. remember p_nil as p.
    revert Heqp.
    induction H; intros; subst; auto.
    inverts H; inverts H1. inverts H.
  Qed.

  Lemma step_star_par_unzip: p q la pq',
    step_star (p||q) la pq'
     p' q' la1 la2,
      (pq' = p' || q' (p'=p_nil q'=p_nil pq'=p_nil))%proc
      step_star p la1 p' step_star q la2 q'
      ( r r', step_star r la1 r'step_star (r||q) la (r'||q'))
      ( r r', step_star r la2 r'step_star (p||r) la (p'||r')).
  Proof.
    intros.
    remember (p||q)%proc as pq.
    revert p q Heqpq.
    induction H; intros; subst.
    - renames p0 to p.
     p q (@nil lts_L) (@nil lts_L); splits; intros; auto.
    + apply step_nil.
    + apply step_nil.
    + apply step_star_parL_step; auto.
    + apply step_star_parR_step; auto.
    - renames p0 to p, p' to pq', p'' to pq''.
    inverts H; inverts H1.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' (l_in a0::la1) la2; splits; auto.
    eapply @step_lcons; [constructor; eauto | auto].
    intros.
    edestruct step_star_lstep_inv as [r'0[r'1[?[??]]]]; eauto.
    change (l_in a0::la) with ([l_in a0]++la).
    eapply step_star_app; eauto.
    apply step_star_parL_step.
    change [l_in a0] with ([]++[l_in a0]).
    eapply step_star_app; eauto. apply single_lstep; eauto.
    intros.
    eapply step_lcons; eauto. constructor; apply s_parL; auto.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' la1 (l_in a0::la2); splits; auto.
    eapply @step_lcons; [constructor; eauto | auto].
    intros.
    eapply step_lcons; eauto. constructor; apply s_parR; auto.
    intros.
    edestruct step_star_lstep_inv as [r'0[r'1[?[??]]]]; eauto.
    change (l_in a0::la) with ([l_in a0]++la).
    eapply step_star_app; eauto.
    apply step_star_parR_step.
    change [l_in a0] with ([]++[l_in a0]).
    eapply step_star_app; eauto. apply single_lstep; eauto.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' (l_out a0::la1) la2; splits; auto.
    eapply @step_lcons; [constructor; eauto | auto].
    intros.
    edestruct step_star_lstep_inv as [r'0[r'1[?[??]]]]; eauto.
    change (l_out a0::la) with ([l_out a0]++la).
    eapply step_star_app; eauto.
    apply step_star_parL_step.
    change [l_out a0] with ([]++[l_out a0]).
    eapply step_star_app; eauto. apply single_lstep; eauto.
    intros.
    eapply step_lcons; eauto. constructor; apply s_parL; auto.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' la1 (l_out a0::la2); splits; auto.
    eapply @step_lcons; [constructor; eauto | auto].
    intros.
    eapply step_lcons; eauto. constructor; apply s_parR; auto.
    intros.
    edestruct step_star_lstep_inv as [r'0[r'1[?[??]]]]; eauto.
    change (l_out a0::la) with ([l_out a0]++la).
    eapply step_star_app; eauto.
    apply step_star_parR_step.
    change [l_out a0] with ([]++[l_out a0]).
    eapply step_star_app; eauto. apply single_lstep; eauto.
    - renames p0 to p, p' to pq', p'' to pq''.
    inverts H.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' la1 la2; splits; intros; auto.
    eapply @step_tcons; eauto. apply H5.
    eapply @step_tcons; eauto. apply s_parL; auto.
    + edestruct IHstep_star as [p''[q''[la1[la2[?[?[?[??]]]]]]]]; auto; subst; clear IHstep_star.
     p'' q'' la1 la2; splits; intros; auto.
    eapply @step_tcons; eauto. apply H5.
    eapply @step_tcons; eauto. apply s_parR; auto.
    + edestruct step_star_nil_resolve as [??]; eauto; subst la pq''.
     p_nil p_nil (@nil lts_L) (@nil lts_L); splits; intros; auto.
    apply step_star_parL_step; auto.
    apply step_star_parR_step; auto.
  Qed.

  Lemma step_star_res_nIn: a n p la p',
    step_star p la p'
    ( l, In l lall_in a ll_out a) →
    step_star (a#n:p) la (a#n:p').
  Proof.
    intros.
    induction H.
    - apply step_nil.
    - eapply step_lcons; eauto.
    inverts H; constructor; apply s_res; eauto; apply H0; simpl; auto.
    apply IHstep_star. split; intros; apply H0; simpl; auto.
    - eapply step_tcons; eauto.
    apply s_res; eauto; congruence.
  Qed.

  Lemma step_star_res: a n p la p' la' n',
    step_star p la p'
    la' = res_filter a la
    res_count_labels a n la n'=true
    step_star (a#n:p) la' (a#n':p').
  Proof.
    intros.
    revert n n' la' H0 H1.
    induction H; intros.
    - simpl in *; subst. destruct (eq_nat_dec n' n); intuition; subst; apply @step_nil.
    - simpl in ×.
    destruct a0.
    destruct (label_eq l_tau (l_in a)); try discriminate; subst. clear n0.
    inverts H.
    destruct (name_eq n0 a); subst.
    destruct n; try discriminate.
    destruct (label_eq (l_in a) (l_in a)); try congruence. clear e.
    eapply step_tcons; eauto.
    eapply s_res_count_in. inverts H; auto.
    destruct (label_eq (l_in n0) (l_in a)); try congruence.
    destruct (label_eq (l_in n0) (l_out a)); try congruence.
    eapply @step_lcons; eauto. inverts H; constructor; apply s_res; auto.
    destruct (name_eq n0 a); subst.
    destruct (label_eq (l_out a) (l_in a)); try congruence.
    destruct (label_eq (l_out a) (l_out a)); try congruence.
    inverts H.
    eapply step_tcons; eauto. apply s_res_count_out; auto.
    destruct (label_eq (l_out n0) (l_in a)); try congruence.
    destruct (label_eq (l_out n0) (l_out a)); try congruence.
    inverts H; eapply @step_lcons; eauto. constructor; apply s_res; auto.
    - eapply step_tcons; eauto. apply s_res; auto; congruence.
  Qed.

  Lemma step_star_res_inv: a n p la pp',
    step_star (a#n:p) la pp'
     la' n' p',
      (pp'=a#n':p' (pp'=O p'=O))%proc
      la = res_filter a la'
      res_count_labels a n la' n' = true
      step_star p la' p'.
  Proof.
    intros.
    remember (a#n:p)%proc as pp.
    revert n p Heqpp.
    induction H; intros; subst.
    - (@nil lts_L) n p0; splits; auto. simpl. destruct eq_nat_dec; congruence. apply step_nil.
    - inverts H; inverts H1; edestruct IHstep_star as [la'[n'[p'[?[?[??]]]]]]; eauto.
    + (l_in a1::la') n' p'; splits; auto.
    simpl. destruct label_eq. inverts e. false.
    destruct label_eq. inverts e; false.
    f_equal; auto.
    simpl. destruct name_eq; auto. subst a1. false.
    eapply @step_lcons; eauto; constructor; auto.
    + (l_out a1::la') n' p'; splits; auto.
    simpl. destruct label_eq. inverts e.
    destruct label_eq. inverts e; false.
    f_equal; auto.
    simpl. destruct name_eq; auto. subst a1. false.
    eapply @step_lcons; eauto; constructor; auto.
    - inverts H.
    + edestruct IHstep_star as [la'[n'[p'[?[?[??]]]]]]; clear IHstep_star; eauto.
     la' n' p'; splits; auto. eapply step_tcons; eauto. apply H4.
    + edestruct IHstep_star as [la'[n'[p'[?[?[??]]]]]]; clear IHstep_star; eauto.
     (l_in a::la') n' p'; splits; auto.
    simpl. destruct label_eq; congruence.
    simpl. destruct (name_eq a a); congruence.
    eapply @step_lcons; eauto; constructor; auto.
    + edestruct IHstep_star as [la'[n'[p'[?[?[??]]]]]]; clear IHstep_star; eauto.
     (l_out a::la') n' p'; splits; auto.
    simpl. destruct label_eq; try congruence. destruct label_eq.
    simpl. destruct (name_eq a a); congruence.
    false.
    simpl. destruct name_eq; congruence.
    eapply @step_lcons; eauto; constructor; auto.
    + edestruct step_star_nil_resolve as [??]; eauto; subst la p''.
     (@nil lts_L) n p_nil; splits; auto.
    simpl. destruct eq_nat_dec; auto.
  Qed.

  Lemma step_star_res_count_in: n p a p',
    step_star p [l_in a] p'step_star (a#(S n):p) [] (a#n:p').
  Proof.
    intros.
    edestruct step_star_lstep_inv as [p'0[p'1[?[??]]]]; eauto. inverts H1.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    apply step_star_res_nIn; eauto.
    eapply step_tcons.
    apply s_res_count_in; eauto.
    apply step_star_res_nIn; eauto.
  Qed.

  Lemma step_star_res_count_out: n p a p',
    step_star p [l_out a] p'step_star (a#n:p) [] (a#(S n):p').
  Proof.
    intros.
    edestruct step_star_lstep_inv as [p'0[p'1[?[??]]]]; eauto. inverts H1.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    apply step_star_res_nIn; eauto.
    eapply step_tcons.
    apply s_res_count_out; eauto.
    apply step_star_res_nIn; eauto.
  Qed.

  Lemma step_star_l_tau_nIn: p la p',
    step_star p la p'¬In l_tau la.
  Proof.
    intros.
    induction H; simpl; auto.
    inverts H; intros [?|?]; try discriminate; contradiction.
  Qed.

  Lemma step_star_parL_seq_inv: p1 p2 p3 la p',
    step_star ((p1;; p2) || p3) la p'
    ( p1' p3',
      step_star (p1||p3) la (p1'||p3') p' = (p1';;p2)||p3')%proc
    ( la1 la2 p3',
      step_star (p1||p3) la1 (p_nil || p3') step_star (p2||p3') la2 p' la=la1++la2).
  Proof.
    intros.
    remember (p1;;p2 || p3)%proc as p.
    revert p1 p2 p3 Heqp.
    induction H; intros; subst.
    - left; p1 p3; split; auto; apply step_nil.
    - inverts H; inverts H1; [ inverts H5 | | inverts H5 | ].
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_lcons; eauto; constructor; apply s_parL; auto.
    right; (l_in a0::la1) la2 p3'; splits; auto. eapply @step_lcons; eauto; constructor; apply s_parL; auto.
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_lcons; eauto; constructor; apply s_parR; auto.
    right; (l_in a0::la1) la2 p3'; splits; auto. eapply @step_lcons; eauto; constructor; apply s_parR; auto.
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_lcons; eauto; constructor; apply s_parL; auto.
    right; (l_out a0::la1) la2 p3'; splits; auto. eapply @step_lcons; eauto; constructor; apply s_parL; auto.
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_lcons; eauto; constructor; apply s_parR; auto.
    right; (l_out a0::la1) la2 p3'; splits; auto. eapply @step_lcons; eauto; constructor; apply s_parR; auto.
    - inverts H; [ inverts H5 | ].
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_tcons; eauto; apply s_parL; auto.
    right; la1 la2 p3'; splits; auto. eapply @step_tcons; eauto; apply s_parL; auto.
    + right; (@nil lts_L) la p3; splits; auto; apply step_nil.
    + edestruct IHstep_star as [ [p1'[p3'[??]]] | [la1[la2[p3'[?[??]]]]] ]; eauto; clear IHstep_star; subst.
    left; p1' p3'; split; auto. eapply step_tcons; eauto; apply s_parR; auto.
    right; la1 la2 p3'; splits; auto. eapply @step_tcons; eauto; apply s_parR; auto.
  Qed.

  Lemma step_star_par_swap': p1 p2 la p',
    step_star (p1||p2) la p'
    step_star (p2||p1) la (par_swap p').
  Proof.
    intros.
    remember (p1||p2)%proc as p.
    revert p1 p2 Heqp.
    induction H; intros; subst.
    - apply step_nil.
    - inverts H; (inverts H1; eapply step_lcons; eauto; constructor; [apply s_parR|apply s_parL]); auto.
    - inverts H; eapply step_tcons; eauto; [ apply s_parR | apply s_parL | apply s_par_nil | ]; auto.
    edestruct step_star_nil_resolve as [??]; eauto; subst la p''; apply step_nil.
  Qed.

  Lemma step_star_par_swap: p1 p2 la p',
    step_star (p1||p2) la (par_swap p') →
    step_star (p2||p1) la p'.
  Proof.
    intros. apply step_star_par_swap' in H. rewrite par_swap_swap in H. auto.
  Qed.

  Lemma step_star_parR_seq_inv: p1 p2 p3 la p',
    step_star (p1 || p2;; p3) la p'
    ( p1' p2',
      step_star (p1 || p2) la (p1' || p2') p' = p1' || p2';;p3)%proc
    ( la1 la2 p1',
      step_star (p1 || p2) la1 (p1' || p_nil) step_star (p1'||p3) la2 p' la=la1++la2).
  Proof.
    intros.
    apply step_star_par_swap' in H.
    edestruct step_star_parL_seq_inv as [ [p2'[p1'[??]]] | [la1[la2[p1'[?[??]]]]] ]; eauto; subst.
    apply step_star_par_swap' in H0.
    left; p1' p2'; split; auto.
    destruct p'; inverts H1; reflexivity.
    apply step_star_par_swap' in H0.
    right; la1 la2 p1'; splits; auto.
    apply step_star_par_swap' in H1.
    destruct p'; auto.
  Qed.

  Lemma step_star_par_unzip': p1 p2 la p1' p2',
    step_star (p1||p2) la (p1'||p2') →
     lp la1 la2,
      step_star p1 la1 p1' step_star p2 la2 p2' la=interleaving lp la1 la2 (length lp = length la1 + length la2)%nat.
  Proof.
    intros.
    remember (p1||p2)%proc as p; remember (p1'||p2')%proc as p'.
    revert p1 p2 p1' p2' Heqp Heqp'.
    induction H; intros; subst.
    - inverts Heqp'.
     (@nil (unit+unit)) (@nil label) (@nil label); splits; auto; apply @step_nil.
    - inverts H; inverts H1.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     (inl tt::lp) (l_in a0::la1) la2; splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. f_equal; auto.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     (inr tt::lp) la1 (l_in a0::la2); splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. rewrite H3; simpl. omega.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     (inl tt::lp) (l_out a0::la1) la2; splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. f_equal; auto.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     (inr tt::lp) la1 (l_out a0::la2); splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. rewrite H3; simpl. omega.
    - inverts H.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     lp la1 la2; splits; auto. eapply @step_tcons; eauto; apply H5.
    + edestruct IHstep_star as [lp[la1[la2[?[?[??]]]]]]; eauto.
     lp la1 la2; splits; auto. eapply @step_tcons; eauto; apply H5.
    + false; clear IHstep_star. edestruct step_star_nil_resolve as [??]; eauto; discriminate.
  Qed.

  Lemma step_star_par_zip': lp p1 p2 la la1 la2 p1' p2',
    step_star p1 la1 p1'step_star p2 la2 p2'
    la = interleaving lp la1 la2
    step_star (p1||p2) la (p1'||p2').
  Proof.
    induction lp; intros.
    - simpl in *; subst.
    eapply step_star_app.
    apply step_star_parL_step; eauto.
    apply step_star_parR_step; eauto.
    - destruct a as [?|?]; destruct u; [destruct la1|destruct la2]; simpl in H1; subst.
    + rewrite<- (app_nil_r la2).
    eapply step_star_app.
    apply step_star_parR_step; eauto.
    apply step_star_parL_step; eauto.
    + edestruct step_star_lstep_inv with (p:=p1) as [p1'0[p1'1[?[??]]]]; eauto.
    change (l::interleaving lp la1 la2) with ([l]++interleaving lp la1 la2).
    eapply step_star_app; eauto.
    change [l] with ([]++[l]).
    eapply step_lsnoc; eauto. apply step_star_parL_step; eauto.
    inverts H2; constructor; apply s_parL; auto.
    + rewrite<- (app_nil_r la1).
    eapply step_star_app.
    apply step_star_parL_step; eauto.
    apply step_star_parR_step; eauto.
    + edestruct step_star_lstep_inv with (p:=p2) as [p2'0[p2'1[?[??]]]]; eauto.
    change (l::interleaving lp la1 la2) with ([l]++interleaving lp la1 la2).
    eapply step_star_app; eauto.
    change [l] with ([]++[l]).
    eapply step_lsnoc; eauto. apply step_star_parR_step; eauto.
    inverts H2; constructor; apply s_parR; auto.
  Qed.

  Lemma step_star_parL_seq_app: r p q la p' q',
    step_star (p||q) la (p'||q') → step_star (p;;r||q) la (p';;r||q').
  Proof.
    intros.
    edestruct step_star_par_unzip as [p'_[q'_[la1[la2[?[?[?[??]]]]]]]]; eauto.
    destruct H0 as [? | [_[_ ?]] ]; [| discriminate].
    inverts H0. renames p'_ to p', q'_ to q'.
    eapply H3.
    apply step_star_seq_app; auto.
  Qed.

  Lemma step_star_parR_seq_app: r p q la p' q',
    step_star (p||q) la (p'||q') → step_star (p||q;;r) la (p'||q';;r).
  Proof. intros; apply step_star_par_swap; apply step_star_parL_seq_app; auto; apply step_star_par_swap; auto. Qed.

  Lemma step_star_par_seq_inv: p1 p2 p3 p4 la pp',
    step_star (p1;;p3 || p2;;p4) la pp'
    ( p1' p2',
      step_star (p1||p2) la (p1'||p2')
      pp' = (p1';;p3||p2';;p4))%proc
    ( la1 p2' la2,
      step_star (p1||p2) la1 (p_nil||p2')
      step_star (p3||p2';;p4) la2 pp'
      la = la1++la2)
    ( la1 p1' la2,
      step_star (p1||p2) la1 (p1'||p_nil)
      step_star (p1';;p3||p4) la2 pp'
      la = la1++la2).
  Proof.
    introv Hss; intros.
    edestruct step_star_parL_seq_inv as [ [p1'[p24'[Hss' ?]]] | [la1[la2[p24'[Hss1[Hss2 ?]]]]] ]; eauto;
      clear Hss; try rename Hss' into Hss; subst.
    × edestruct step_star_parR_seq_inv as [ [p1''[p2''[Hss' ?]]] | [la1[la2[p1''[Hss1[Hss2 ?]]]]] ]; eauto;
      clear Hss; try rename Hss' into Hss.
    + inverts H. eauto.
    + right; right. la1 p1'' la2; splits; auto.
    apply step_star_parL_seq_app; auto.
    × edestruct step_star_parR_seq_inv as [ [p1''[p2''[Hss' ?]]] | [la1a[la1b[p1''[Hss1a[Hss2b ?]]]]] ]; eauto;
      clear Hss1; try rename Hss' into Hss1.
    + inverts H. right; left. jauto.
    + right; right. la1a p1'' (la1b++la2); splits; auto.
    eapply step_star_app.
    apply step_star_parL_seq_app; eauto.
    eapply step_tcons; eauto.
    apply s_parL; apply s_seq_nil.
    subst. rewrite app_assoc; reflexivity.
  Qed.

  Lemma step_star_par_swap_nil: p la,
    step_star p la p_nilstep_star (par_swap p) la p_nil.
  Proof.
    intros.
    remember (p_nil) as p'.
    revert Heqp'.
    induction H; intros; subst.
    - apply step_nil.
    - destruct p; simpl; auto; try solve [eapply @step_lcons; eauto].
    clear H0.
    eapply @step_lcons; eauto.
    inverts H; inverts H0; constructor; try solve [apply s_parR; auto|apply s_parL; auto].
    - destruct p; simpl; auto; try solve [eapply @step_tcons; eauto].
    clear H0.
    eapply @step_tcons; eauto.
    inverts H; try first [apply s_parR|apply s_parL|apply s_par_nil]; auto.
  Qed.

  Lemma step_star_par_swap_nil': p la,
    step_star (par_swap p) la p_nilstep_star p la p_nil.
  Proof.
    intros; apply step_star_par_swap_nil in H; auto; rewrite par_swap_swap in H; auto.
  Qed.

  Lemma step_star_seq_inv: p q la pq',
    step_star (p;;q) la pq'
    ( p', step_star p la p' pq' = p_seq p' q)
     ( la1 la2, step_star p la1 p_nil step_star q la2 pq' la=la1++la2).
  Proof.
    intros.
    remember (p_seq p q) as pp.
    revert p Heqpp.
    induction H; intros; subst.
    × left; p0; split; auto; apply step_nil.
    × inverts H; inverts H1.
    + edestruct IHstep_star as [[p'[??]]|[la1[la2[?[??]]]]]; eauto; subst; clear IHstep_star.
    - left; p'; split; auto. eapply step_lcons; eauto; constructor; auto.
    - right; (l_in a0::la1) la2; splits; auto. eapply @step_lcons; eauto. constructor; auto.
    + edestruct IHstep_star as [[p'[??]]|[la1[la2[?[??]]]]]; eauto; subst; clear IHstep_star.
    - left; p'; split; auto. eapply step_lcons; eauto; constructor; auto.
    - right; (l_out a0::la1) la2; splits; auto. eapply @step_lcons; eauto. constructor; auto.
    × inverts H.
    + edestruct IHstep_star as [[p'[??]]|[la1[la2[?[??]]]]]; eauto; subst; clear IHstep_star.
    - left; p'; split; auto. eapply step_tcons; eauto. apply H5.
    - right; la1 la2; splits; auto. eapply @step_tcons; eauto. apply H5.
    + right; (@nil lts_L) la; splits; auto. apply step_nil.
  Qed.

End Stepping.

Section FreeNameLemmas.
  Import Notation.
  Import Notation.U.

  Lemma fn_in_out_equiv: p,
    NameSet.Equal (fn p) (NameSet.union (fn_in p) (fn_out p)).
  Proof.
    intros.
    cut ( ab cd a b c d, NameSet.Equal ab (NameSet.union a b) → NameSet.Equal cd (NameSet.union c d) → NameSet.Equal (NameSet.union ab cd) (NameSet.union (NameSet.union a c) (NameSet.union b d))); intros.
    induction p; intros; simpl; eauto.
    × setoid_rewrite NameSetProps.union_subset_equal; reflexivity.
    × destruct l; auto.
    setoid_rewrite NameSetProps.union_add; setoid_rewrite IHp; reflexivity.
    setoid_rewrite NameSetProps.union_sym; setoid_rewrite NameSetProps.union_add;
      setoid_rewrite NameSetProps.union_sym; setoid_rewrite IHp; reflexivity.
    × apply NameSet_union_remove; auto.
    × setoid_rewrite NameSetProps.union_sym at 3.
    setoid_rewrite NameSetProps.union_assoc.
    setoid_rewrite<- NameSetProps.union_assoc at 2.
    setoid_rewrite<- H.
    setoid_rewrite NameSetProps.union_sym at 3.
    setoid_rewrite<- NameSetProps.union_assoc.
    setoid_rewrite<- H0.
    setoid_rewrite NameSetProps.union_sym at 1.
    reflexivity.
  Qed.

  Lemma fn_in_subset_step: p la p',
    step p la p'
    NameSet.Subset (fn_in p') (fn_in p).
  Proof.
    intros.
    induction H; simpl.
    - destruct l.
    + reflexivity.
    + apply NameSetProps.subset_add_2; reflexivity.
    + reflexivity.
    - apply NameSetProps.union_subset_4; auto.
    - apply NameSetProps.union_subset_5; auto.
    - etransitivity. apply NameSetProps.union_subset_1. apply NameSetProps.union_subset_4; auto.
    - etransitivity. apply NameSetProps.union_subset_2. apply NameSetProps.union_subset_5; auto.
    - apply NameSetProps.union_subset_4; auto.
    - setoid_rewrite IHstep. reflexivity.
    - setoid_rewrite IHstep. reflexivity.
    - setoid_rewrite IHstep. reflexivity.
    - etransitivity.
    apply IHstep.
    simpl; apply NameSetProps.union_subset_3; reflexivity.
    - apply NameSetProps.union_subset_2; auto.
    - apply NameSetProps.union_subset_2; auto.
    - apply NameSetProps.union_subset_2; auto.
    - setoid_rewrite NameSetProps.remove_equal. reflexivity. setoid_rewrite NameSetFacts.empty_iff. intro; false.
  Qed.

  Lemma fn_out_subset_step: p la p',
    step p la p'
    NameSet.Subset (fn_out p') (fn_out p).
  Proof.
    intros.
    induction H; simpl.
    - destruct l.
    + reflexivity.
    + reflexivity.
    + apply NameSetProps.subset_add_2; reflexivity.
    - apply NameSetProps.union_subset_4; auto.
    - apply NameSetProps.union_subset_5; auto.
    - etransitivity. apply NameSetProps.union_subset_1. apply NameSetProps.union_subset_4; auto.
    - etransitivity. apply NameSetProps.union_subset_2. apply NameSetProps.union_subset_5; auto.
    - apply NameSetProps.union_subset_4; auto.
    - setoid_rewrite IHstep. reflexivity.
    - setoid_rewrite IHstep. reflexivity.
    - setoid_rewrite IHstep. reflexivity.
    - etransitivity.
    apply IHstep.
    simpl; apply NameSetProps.union_subset_3; reflexivity.
    - apply NameSetProps.union_subset_2; auto.
    - apply NameSetProps.union_subset_2; auto.
    - apply NameSetProps.union_subset_2; auto.
    - setoid_rewrite NameSetProps.remove_equal. reflexivity. setoid_rewrite NameSetFacts.empty_iff. intro; false.
  Qed.

  Lemma fn_subset_step: p la p',
    step p la p'
    NameSet.Subset (fn p') (fn p).
  Proof.
    intros.
    setoid_rewrite fn_in_out_equiv.
    apply NameSetProps.union_subset_3.
    etransitivity.
    eapply fn_in_subset_step; eauto.
    apply NameSetProps.union_subset_1.
    etransitivity.
    eapply fn_out_subset_step; eauto.
    apply NameSetProps.union_subset_2.
  Qed.

  Lemma fn_in_subset_step_star: p la p',
    step_star p la p'
    NameSet.Subset (fn_in p') (fn_in p).
  Proof.
    intros; induction H.
    - reflexivity.
    - inverts H; rewrite IHstep_star; eapply fn_in_subset_step; eauto.
    - rewrite IHstep_star; eapply fn_in_subset_step; eauto; apply H.
  Qed.

  Lemma fn_out_subset_step_star: p la p',
    step_star p la p'
    NameSet.Subset (fn_out p') (fn_out p).
  Proof.
    intros; induction H.
    - reflexivity.
    - inverts H; rewrite IHstep_star; eapply fn_out_subset_step; eauto.
    - rewrite IHstep_star; eapply fn_out_subset_step; eauto; apply H.
  Qed.

  Lemma fn_subset_step_star: p la p',
    step_star p la p'
    NameSet.Subset (fn p') (fn p).
  Proof.
    intros; induction H.
    - reflexivity.
    - inverts H; rewrite IHstep_star; eapply fn_subset_step; eauto.
    - rewrite IHstep_star; eapply fn_subset_step; eauto; apply H.
  Qed.

  Lemma fn_in_step_In: a p p',
    step p (l_in a) p'
    NameSet.In a (fn_in p).
  Proof.
    intros.
    remember (l_in a) as l.
    revert Heql.
    induction H; simpl; intros; subst; try solve [discriminate|apply NameSet.union_spec; auto].
    apply NameSetProps.Dec.F.add_iff; auto.
    apply NameSetProps.Dec.F.remove_2; auto.
    intro; false.
    simpl in ×.
    edestruct NameSetProps.Dec.F.union_1 as [?|?]; eauto.
  Qed.

  Lemma fn_out_step_In: a p p',
    step p (l_out a) p'
    NameSet.In a (fn_out p).
  Proof.
    intros.
    remember (l_out a) as l.
    revert Heql.
    induction H; simpl; intros; subst; try solve [discriminate|apply NameSet.union_spec; auto].
    apply NameSetProps.Dec.F.add_iff; auto.
    apply NameSetProps.Dec.F.remove_2; auto. intro; false.
    simpl in ×.
    edestruct NameSetProps.Dec.F.union_1 as [?|?]; eauto.
  Qed.

  Lemma fn_in_step_star_In: a p la p',
    step_star p la p'
    In (l_in a) la
    NameSet.In a (fn_in p).
  Proof.
    intros.
    revert a H0.
    induction H; intros.
    - intuition.
    - destruct H1 as [?|?]; subst.
    inverts H; eapply fn_in_step_In; eauto.
    inverts H; apply fn_in_subset_step in H2; eauto.
    - apply fn_in_subset_step_star in H0.
    eapply fn_in_subset_step. apply H. eauto.
  Qed.

  Lemma fn_out_step_star_In: a p la p',
    step_star p la p'
    In (l_out a) la
    NameSet.In a (fn_out p).
  Proof.
    intros.
    revert a H0.
    induction H; intros.
    - intuition.
    - destruct H1 as [?|?]; subst.
    inverts H; eapply fn_out_step_In; eauto.
    inverts H; apply fn_out_subset_step in H2; eauto.
    - apply fn_out_subset_step_star in H0.
    eapply fn_out_subset_step. apply H. eauto.
  Qed.

  Lemma fn_step_star_In: a p la p',
    step_star p la p'
    (In (l_in a) la In (l_out a) la) →
    NameSet.In a (fn p).
  Proof.
    intros.
    setoid_rewrite fn_in_out_equiv.
    destruct H0.
    + apply NameSetProps.FM.union_2. eapply fn_in_step_star_In; eauto.
    + apply NameSetProps.FM.union_3. eapply fn_out_step_star_In; eauto.
  Qed.

  Lemma fn_subset_step_star_parL_In: a p1 p2 la p1' p2',
    step_star (p1||p2) la (p1'||p2') →
    NameSet.In a (fn p2') →
    NameSet.In a (fn p2).
  Proof.
    intros.
    edestruct step_star_par_unzip as [p1''[p2''[la1[la2[?[?[?[??]]]]]]]]; eauto.
    destruct H1 as [?|[?[??]]]; subst.
    inverts H1.
    eapply NameSetProps.in_subset; eauto.
    eapply fn_subset_step_star; eauto.
    discriminate.
  Qed.

  Lemma fn_subset_step_star_parR_In: a p1 p2 la p1' p2',
    step_star (p1||p2) la (p1'||p2') →
    NameSet.In a (fn p1') →
    NameSet.In a (fn p1).
  Proof.
    intros.
    edestruct step_star_par_unzip as [p1''[p2''[la1[la2[?[?[?[??]]]]]]]]; eauto.
    destruct H1 as [?|[?[??]]]; subst.
    inverts H1.
    eapply NameSetProps.in_subset; eauto.
    eapply fn_subset_step_star; eauto.
    discriminate.
  Qed.

  Lemma fn_in_subset_step_star_parL: p1 p2 la p1' p2',
    step_star (p1||p2) la (p1'||p2') → NameSet.Subset (fn_in p1') (fn_in p1).
  Proof.
    intros.
    edestruct step_star_par_unzip' as [lp[la1[la2[?[?[??]]]]]]; eauto.
    eapply fn_in_subset_step_star; eauto.
  Qed.
  Lemma fn_in_subset_step_star_parR: p1 p2 la p1' p2',
    step_star (p1||p2) la (p1'||p2') → NameSet.Subset (fn_in p2') (fn_in p2).
  Proof.
    intros.
    edestruct step_star_par_unzip' as [lp[la1[la2[?[?[??]]]]]]; eauto.
    eapply fn_in_subset_step_star; eauto.
  Qed.

End FreeNameLemmas.

Section HiddenProc.
  Import Notation.
  Import Notation.U.

  Definition hidden_fn A p := a, NameSet.In a (fn p) → In a A.

  Lemma hidden_fn_seq1: A p1 p2,
    hidden_fn A (p1;;p2) → hidden_fn A p1.
  Proof. unfold hidden_fn; intros; apply H; apply NameSetProps.Dec.F.union_2; apply H0. Qed.

  Lemma hidden_fn_seq2: A p1 p2,
    hidden_fn A (p1;;p2) → hidden_fn A p2.
  Proof. unfold hidden_fn; intros; apply H; apply NameSetProps.Dec.F.union_3; apply H0. Qed.

  Lemma hidden_fn_step_star: A p la p',
    hidden_fn A pstep_star p la p'hidden_fn A p'.
  Proof. unfold hidden_fn; intros; apply fn_subset_step_star in H0; auto. Qed.

  Lemma hidden_fn_step_star_parR: A p1 p2 la p1' p2',
    hidden_fn A p1step_star (p1||p2) la (p1'||p2') → hidden_fn A p1'.
  Proof. unfold hidden_fn; intros; apply fn_subset_step_star_parR_In with (a:=a) in H0; auto. Qed.

  Lemma hidden_fn_step_star_parL: A p1 p2 la p1' p2',
    hidden_fn A p2step_star (p1||p2) la (p1'||p2') → hidden_fn A p2'.
  Proof. unfold hidden_fn; intros; apply fn_subset_step_star_parL_In with (a:=a) in H0; auto. Qed.

  Lemma hidden_fn_seq3: A p1 p2,
    hidden_fn A p1hidden_fn A p2hidden_fn A (p1;; p2).
  Proof. unfold hidden_fn; simpl; intros; edestruct NameSetProps.Dec.F.union_1 as [?|?]; eauto. Qed.

End HiddenProc.

Section ResListFilter.
  Require Import Permutation.

  Definition res_list_filter A la := fold_right (fun a la'res_filter a la') la A.

  Lemma res_list_filter_nil2: A,
    res_list_filter A nil = nil.
  Proof. induction A; simpl; auto; rewrite IHA; reflexivity. Qed.

  Lemma res_filter_app2: a la1 la2,
    res_filter a (la1++la2) = res_filter a la1 ++ res_filter a la2.
  Proof.
    induction la1; simpl; intros; auto.
    destruct label_eq; subst; eauto.
    destruct label_eq; subst; eauto.
    simpl. congruence.
  Qed.

  Lemma res_filter_In_filter: a la,
    ( l, In l la → (l=l_in a l=l_out a)) →
    res_filter a la = [].
  Proof.
    induction la; simpl; intros; auto.
    destruct label_eq; subst; auto.
    destruct label_eq; subst; auto.
    false.
    edestruct H as [?|?]; eauto; subst.
  Qed.

  Lemma res_filter_nIn: a la,
    ¬In (l_in a) la
    ¬In (l_out a) la
    res_filter a la = la.
  Proof.
    induction la; simpl; intros; auto.
    destruct label_eq; subst.
    false; apply H; left; auto.
    destruct label_eq; subst.
    false; apply H0; left; auto.
    f_equal; apply IHla.
    intro; apply H; right; auto.
    intro; apply H0; right; auto.
  Qed.

  Lemma res_list_filter_nIn: A la,
    ( a, In (l_in a) laIn a AFalse) →
    ( a, In (l_out a) laIn a AFalse) →
    res_list_filter A la = la.
  Proof.
    induction A; simpl; intros; auto.
    rewrite IHA; intros; eauto.
    apply res_filter_nIn.
    intro; eapply H; eauto.
    intro; eapply H0; eauto.
  Qed.

  Lemma in_res_filter: a l la,
    In l (res_filter a la) → ll_in a ll_out a In l la.
  Proof.
    induction la; simpl; auto.
    destruct label_eq; [| destruct label_eq]; intros; subst; auto.
    destruct IHla as [?[??]]; auto.
    destruct IHla as [?[??]]; auto.
    destruct H; subst.
    splits; auto.
    destruct IHla as [?[??]]; auto.
  Qed.

  Lemma res_filter_cons2: a l la,
    res_filter a (l::la) = match l with
      | l_taul::res_filter a la
      | l_in b | l_out bif name_eq b a then res_filter b la else l::res_filter a la
      end.
  Proof.
    intros. simpl.
    destruct label_eq; subst.
    destruct name_eq; auto. false.
    destruct label_eq. subst.
    destruct name_eq; auto. false.
    destruct l; auto.
    destruct name_eq; auto; subst. false.
    destruct name_eq; auto; subst. false.
  Qed.

  Lemma res_list_filter_cons2: l1 l l2,
    res_list_filter l1 (l::l2) = match l with
      | l_taul::res_list_filter l1 l2
      | l_in a | l_out aif in_dec name_eq a l1 then res_list_filter l1 l2 else l::res_list_filter l1 l2
      end.
  Proof.
    induction l1; intros; simpl.
    destruct l; reflexivity.
    rewrite IHl1; clear IHl1. simpl.
    destruct l.
    - rewrite res_filter_cons2; auto.
    - destruct name_eq; subst.
    destruct in_dec; auto.
    rewrite res_filter_cons2; auto.
    destruct name_eq; subst; auto; false.
    destruct in_dec; auto.
    rewrite res_filter_cons2; auto.
    destruct name_eq; subst; auto; false.
    - destruct name_eq; subst.
    destruct in_dec; auto.
    rewrite res_filter_cons2; auto.
    destruct name_eq; subst; auto; false.
    destruct in_dec; auto.
    rewrite res_filter_cons2; auto.
    destruct name_eq; subst; auto; false.
  Qed.

  Lemma res_list_filter_app2: A la1 la2,
    res_list_filter A (la1++la2) = res_list_filter A la1 ++ res_list_filter A la2.
  Proof.
    induction A; simpl; intros; auto.
    rewrite IHA.
    rewrite res_filter_app2; eauto.
  Qed.

  Lemma res_list_filter_cons2': A l la,
    res_list_filter A (l::la) = res_list_filter A [l] ++ res_list_filter A la.
  Proof. intros; change (l::la) with ([l]++la); apply res_list_filter_app2. Qed.

  Lemma res_filter_cons2': a l la,
    res_filter a (l::la) = res_filter a [l] ++ res_filter a la.
  Proof. intros; change (l::la) with ([l]++la); apply res_filter_app2. Qed.

  Lemma in_res_list_filter: l A la,
    In l (res_list_filter A la) →
    ( a, In a All_in a ll_out a) In l la.
  Proof.
    induction A; simpl; intros; auto.
    edestruct in_res_filter as [?[??]]; eauto.
    edestruct IHA as [??]; eauto.
    split; auto. intros ? [?|?]; subst; auto.
  Qed.

  Lemma res_filter_comm: la a1 a2,
    res_filter a1 (res_filter a2 la) = res_filter a2 (res_filter a1 la).
  Proof.
    induction la; intros; auto.
    change (a::la) with ([a]++la).
    repeat rewrite res_filter_app2.
    rewrite IHla.
    f_equal.
    simpl.
    repeat (destruct label_eq; subst; simpl; auto; try congruence).
  Qed.

  Lemma res_list_filter_perm: A1 A2 la,
    Permutation A1 A2
    res_list_filter A1 la = res_list_filter A2 la.
  Proof.
    intros.
    induction H; simpl; auto; try congruence.
    rewrite res_filter_comm; auto.
  Qed.

  Lemma res_list_filter_app: A1 A2 la,
    res_list_filter (A1++A2) la = res_list_filter A1 (res_list_filter A2 la).
  Proof. induction A1; simpl; intros; auto; congruence. Qed.

  Lemma res_list_filter_rev: a A la,
    res_list_filter (a::A) la = res_list_filter A (res_filter a la).
  Proof.
    intros.
    change (a::A) with ([a]++A).
    rewrite res_list_filter_perm with (A2:=A++[a]).
    rewrite res_list_filter_app.
    reflexivity.
    apply Permutation_app_comm.
  Qed.

  Lemma res_list_filter_tau: A,
    res_list_filter A [l_tau] = [l_tau].
  Proof.
    induction A; simpl; intros; auto.
    rewrite IHA; simpl; auto.
    destruct label_eq; auto.
    false.
    destruct label_eq; auto.
    false.
  Qed.

  Lemma res_list_filter_In_filter: A la,
    ¬In l_tau la
    ( a, In (l_in a) laIn a A) →
    ( a, In (l_out a) laIn a A) →
    res_list_filter A la = [].
  Proof.
    induction A; intros; auto.
    - destruct la; auto. false. destruct l.
    eapply H; left; auto.
    eapply (H0 n); left; eauto.
    eapply (H1 n); left; eauto.
    - rewrite res_list_filter_rev.
    rewrite IHA; intros; auto.
    intro. apply in_res_filter in H2. destruct H2 as [?[??]]; contradiction.
    edestruct in_res_filter as [?[??]]; eauto.
    destruct (H0 a0) as [?|?]; subst; auto.
    false.
    edestruct in_res_filter as [?[??]]; eauto.
    destruct (H1 a0) as [?|?]; subst; auto.
    false.
  Qed.

  Lemma res_list_filter_interleaving_fn2: lp A la2a la2b,
    ¬In l_tau la2a
    ¬In l_tau la2b
    ( a, In (l_in a) la2bIn a A) →
    ( a, In (l_out a) la2bIn a A) →
    res_list_filter A (interleaving lp la2a la2b) = res_list_filter A la2a.
  Proof.
    induction lp; intros.
    - simpl.
    rewrite res_list_filter_app2.
    rewrite (res_list_filter_In_filter A la2b); auto.
    apply app_nil_r.
    - destruct a; destruct u.
    destruct la2a.
    simpl. rewrite res_list_filter_In_filter; auto. rewrite res_list_filter_nil2; auto.
    simpl. rewrite res_list_filter_cons2'.
    pattern (res_list_filter A (l::la2a)).
    rewrite res_list_filter_cons2'.
    f_equal.
    eauto.
    eapply IHlp; eauto.
    intro; apply H; right; auto.
    destruct la2b.
    rewrite interleaving_r_nil; reflexivity.
    simpl. rewrite res_list_filter_cons2'.
    rewrite (res_list_filter_In_filter A [l]); simpl; intros.
    eapply IHlp; intros; eauto.
    intro; apply H0; right; auto.
    apply H1; right; auto.
    apply H2; right; auto.
    intuition; subst. apply H0; left; auto.
    intuition; subst. apply H1; left; auto.
    intuition; subst. apply H2; left; auto.
  Qed.

  Lemma res_list_filter_single_inv: A l,
    (res_list_filter A [l] = [l] ( a, In a All_in a ll_out a))
     (res_list_filter A [l] = [] a, In a A (l=l_in al=l_out a)).
  Proof.
    induction A; intros; auto.
    rewrite res_list_filter_cons2.
    rewrite res_list_filter_nil2.
    destruct l.
    left; intuition; false.
    destruct in_dec. right; eauto.
    left; intuition; inverts H0; auto.
    destruct in_dec. right; eauto.
    left; intuition; inverts H0; auto.
  Qed.

  Lemma res_list_filter_count_occ: A la l,
    ( a, In a All_in a ll_out a) →
    count_occ label_eq (res_list_filter A la) l = count_occ label_eq la l.
  Proof.
    induction la; simpl; intros; auto.
    rewrite res_list_filter_nil2. reflexivity.
    rewrite res_list_filter_cons2.
    destruct label_eq. subst a.
    destruct l; simpl.
    destruct label_eq; [| false].
    f_equal; auto.
    destruct in_dec.
    apply H in i; destruct i; false.
    simpl.
    destruct label_eq.
    f_equal; auto.
    false.
    destruct in_dec.
    apply H in i; destruct i; false.
    simpl.
    destruct label_eq.
    f_equal; auto.
    false.
    destruct a.
    simpl.
    destruct label_eq; [false |]; auto.
    destruct in_dec; auto. simpl. destruct label_eq; auto; false.
    destruct in_dec; auto. simpl. destruct label_eq; auto; false.
  Qed.

  Lemma res_list_filter_interleaving: A lp la1 la2,
     lp',
    res_list_filter A (interleaving lp la1 la2) = interleaving lp' (res_list_filter A la1) (res_list_filter A la2).
  Proof.
    induction lp; simpl; intros.
    × (@nil (unit+unit)); rewrite res_list_filter_app2; reflexivity.
    × destruct a; destruct u.
    + destruct la1.
    - (@nil (unit+unit)). rewrite res_list_filter_nil2. rewrite interleaving_l_nil. reflexivity.
    - rewrite res_list_filter_cons2'.
    rewrite (res_list_filter_cons2' _ _ la1).
    destruct (IHlp la1 la2) as [lp' ?]; clear IHlp.
    destruct (res_list_filter_single_inv A l) as [[??]|[??]].
    rewrite H0.
     (inl tt::lp'); simpl. f_equal; auto.
    rewrite H0.
     lp'; simpl. auto.
    + destruct la2.
    - (@nil (unit+unit)). rewrite res_list_filter_nil2. rewrite interleaving_r_nil. reflexivity.
    - rewrite res_list_filter_cons2'.
    rewrite (res_list_filter_cons2' _ _ la2).
    destruct (IHlp la1 la2) as [lp' ?]; clear IHlp.
    destruct (res_list_filter_single_inv A l) as [[??]|[??]].
    rewrite H0.
     (inr tt::lp'); simpl. f_equal; auto.
    rewrite H0.
     lp'; simpl. auto.
  Qed.

End ResListFilter.

Section ResCountLabels.
  Import Notation.
  Import Notation.U.

  Lemma res_count_labels_app2: a la1 n la2 n',
    res_count_labels a n (la1++la2) n' = true
     n'0,
      res_count_labels a n la1 n'0 = true
      res_count_labels a n'0 la2 n' = true.
  Proof.
    induction la1; intros.
     n; simpl; split; auto. destruct eq_nat_dec; congruence.
    destruct a0; simpl in ×.
    - eauto.
    - destruct (name_eq n0 a). subst n0. destruct n. false.
    edestruct IHla1 as [n'0[??]]; clear IHla1; eauto.
    edestruct IHla1 as [n'0[??]]; clear IHla1; eauto.
    - destruct (name_eq n0 a). subst n0.
    edestruct IHla1 as [n'0[??]]; clear IHla1; eauto.
    edestruct IHla1 as [n'0[??]]; clear IHla1; eauto.
  Qed.

  Lemma res_count_labels_app2': a la1 n la2 n' n'0,
    res_count_labels a n la1 n'0 = true
    res_count_labels a n'0 la2 n' = true
    res_count_labels a n (la1++la2) n' = true.
  Proof.
    induction la1; simpl; intros.
    destruct (eq_nat_dec n'0 n); subst; auto. false.
    destruct a0. eauto.
    destruct name_eq; subst; eauto.
    destruct n; eauto.
    destruct name_eq; subst; eauto.
  Qed.

  Lemma res_count_labels_nil2: a n n',
    res_count_labels a n nil n' = truen'=n.
  Proof. unfold res_count_labels; intros; destruct eq_nat_dec; auto; false. Qed.

  Lemma res_count_labels_nil2': a n,
    res_count_labels a n nil n = true.
  Proof. unfold res_count_labels; intros; destruct eq_nat_dec; auto. Qed.

  Lemma res_count_labels_cons_com: a n l1 l2 la n',
    res_count_labels a n (l1::l2::la) n' = true
    (l2=l_in an>0) →
    res_count_labels a n (l2::l1::la) n' = true.
  Proof.
    unfold res_count_labels; introv Hres Hin; intros.
    destruct l1; destruct l2; auto; destruct name_eq; subst; auto; destruct n; auto; try discriminate.
    destruct name_eq; subst; auto.
    false; specialize (Hin (refl_equal _)); omega.
  Qed.

  Lemma res_count_labels_nIn: a n la n',
    res_count_labels a n la n' = true
    ¬In (l_in a) la
    ¬In (l_out a) la
    n' = n.
  Proof.
    induction la; simpl; intros; auto.
    destruct eq_nat_dec; subst; auto; false.
    destruct a0.
    eapply IHla; intros; auto.
    destruct name_eq; subst; auto.
    destruct name_eq; subst; auto.
  Qed.

  Lemma res_count_labels_nIn': la a n n',
    res_count_labels a n la n' = true
    ¬In (a?)%label la
    n' = (n + count_occ label_eq la (l_out a))%nat.
  Proof.
    induction la; simpl; intros.
    destruct eq_nat_dec; subst; auto; false.
    apply Decidable.not_or in H0.
    destruct H0.
    destruct a.
    × eapply IHla in H; eauto. subst n'.
    destruct label_eq; auto. false.
    × destruct name_eq; subst.
    false.
    destruct label_eq; auto. false.
    × destruct name_eq; subst; destruct label_eq; auto.
    eapply IHla in H; auto.
    omega.
    inverts e. false.
  Qed.

  Lemma res_count_labels_incr: a la n n' d,
    res_count_labels a n la n' = true
    res_count_labels a (d+n) la (d+n') = true.
  Proof.
    induction la; simpl; intros; auto.
    destruct eq_nat_dec; subst.
    destruct eq_nat_dec; subst; auto. false.
    destruct a0. auto.
    destruct name_eq; subst; auto.
    destruct n. false.
    replace (d+ S n)%nat with (S (d+n))%nat; try omega.
    auto.
    destruct name_eq; subst; auto.
    replace (S (d+n))%nat with (d + (S n))%nat; try omega.
    auto.
  Qed.

  Lemma res_count_labels_nIn'': a n la n',
    ¬In (l_in a) la
    (n' = n + count_occ label_eq la (l_out a))%nat
    res_count_labels a n la n' = true.
  Proof.
    induction la; simpl; intros; auto.
    replace n' with n in *; try omega.
    destruct eq_nat_dec; auto; false.
    apply Decidable.not_or in H. destruct H.
    destruct label_eq; subst.
    destruct name_eq; auto.
    change (S n) with (1+n)%nat.
    replace (n + S (count_occ label_eq la (l_out a)))%nat with (1+(n + count_occ label_eq la (l_out a)))%nat; try omega.
    apply res_count_labels_incr.
    apply IHla; auto.
    destruct a0; auto.
    destruct name_eq; subst; auto.
    destruct name_eq; subst; auto.
  Qed.

  Lemma res_count_labels_le_com: la1 a n l n' n0 n0',
    res_count_labels a n [l] n' = true
    res_count_labels a n0 la1 n0' = true
    ( a : name, In (a?)%label la1l (a?)%label) →
    n0 n
     n00 n00',
    n00 n' res_count_labels a n00 la1 n00' = true.
  Proof.
    intros.
    destruct (label_eq l (l_in a)); subst.
    × specialize (H1 a).
    simpl in H. destruct name_eq; [| false]. destruct n; [false |].
    destruct eq_nat_dec; [subst|false].
     0 (0 + count_occ label_eq la1 (l_out a))%nat; split.
    omega.
    apply res_count_labels_nIn''; auto.
    intro; eapply H1; auto.
    × destruct l; simpl in H.
    + destruct eq_nat_dec; [subst|false]. n0 n0'; split; auto.
    + destruct name_eq; subst. false.
    destruct eq_nat_dec; [subst|false].
     n0 n0'; split; auto.
    + destruct name_eq; subst.
    destruct eq_nat_dec; [subst|false].
     n0 n0'; split; auto.
    destruct eq_nat_dec; [subst|false].
     n0 n0'; split; auto.
  Qed.

  Lemma res_count_labels_nIn''': a n la,
    ¬In (l_in a) la¬In (l_out a) la
    res_count_labels a n la n = true.
  Proof.
    induction la; simpl; intros.
    destruct eq_nat_dec; auto.
    apply Decidable.not_or in H; apply Decidable.not_or in H0.
    destruct H; destruct H0.
    destruct a0; auto; destruct name_eq; subst; auto.
  Qed.

  Lemma res_count_labels_fun: a la n n1 n2,
    res_count_labels a n la n1 = true
    res_count_labels a n la n2 = true
    n2 = n1.
  Proof.
    induction la; simpl; intros.
    destruct eq_nat_dec; destruct eq_nat_dec; subst; auto; false.
    destruct a0; eauto.
    destruct name_eq; subst; eauto; destruct n; [false | eauto].
    destruct name_eq; subst; eauto.
  Qed.

  Lemma res_count_labels_exists_min: a la n n',
    res_count_labels a n la n' = true
     n0 n0',
      res_count_labels a n0 la n0' = true
      ( n n', res_count_labels a n la n' = true d, n=n0+d n'=n0'+d)%nat.
  Proof.
    induction la; simpl in *; intros.
    × destruct eq_nat_dec; subst; [| false].
     0 0; split; intros.
    destruct eq_nat_dec; auto.
    destruct eq_nat_dec; subst; [| false].
     n0; split; omega.
    × destruct a0; eauto.
    + destruct name_eq; subst; eauto.
    destruct n; [false |].
    edestruct IHla as [n0[n0'[??]]]; clear IHla; eauto.
     (S n0) n0'; simpl; split; auto.
    intros n1 n1'. intros.
    destruct n1; [false |].
    apply H1 in H2; destruct H2 as [d[??]].
    subst.
     d; split; auto.
    + destruct name_eq; subst; eauto.
    edestruct IHla as [n0[n0'[??]]]; clear IHla; eauto.
    destruct n0.
    - 0 (1+n0')%nat; split; intros; auto.
    change 1 with (1+0)%nat.
    apply res_count_labels_incr; auto.
     n0; split; auto.
    apply H1 in H2; destruct H2 as [d[??]].
    simpl in ×. subst.
    omega.
    - n0 n0'; split; auto.
    intros n1 n1'; intros.
    apply H1 in H2; destruct H2 as [d[??]].
    inverts H2. subst.
     d; split; auto.
  Qed.

  Lemma res_count_labels_positive_app_com2: la1 a n l la2 n' n0 n0',
    res_count_labels a n (l::la1++la2) n' = true
    ( a, In (l_in a) la1ll_in a ¬In (l_in a) la2) →
    res_count_labels a n0 la1 n0' = true
    n0 n
    res_count_labels a n (la1 ++ l::la2) n' = true.
  Proof.
    induction la1; intros; auto.
    change (l::(a::la1)++la2) with (l::a::la1++la2) in H.
    apply res_count_labels_cons_com in H; auto.
    × change (a::l::la1++la2) with ([a]++(l::la1++la2)) in H.
    apply res_count_labels_app2 in H.
    destruct H as [n'0[??]].
    change ((a::la1)++l::la2) with ([a]++(la1++l::la2)).
    eapply res_count_labels_app2'.
    apply H.
    change (a::la1) with ([a]++la1) in H1.
    apply res_count_labels_app2 in H1.
    destruct H1 as [n0'0[??]].
    apply IHla1 with (n0:=n0'0) (n0':=n0'); clear IHla1; auto.
    intros. apply H0; auto. right; auto.
    destruct (NPeano.Nat.le_exists_sub _ _ H2) as [d[? _]]; subst.
    apply res_count_labels_incr with (d:=d) in H1.
    generalize (res_count_labels_fun _ _ _ _ _ H1 H); intros; subst.
    omega.
    × clear IHla1. intros. subst a.
    destruct (H0 a0) as [??]; auto; clear H0.
    left; auto.
    simpl in ×.
    destruct name_eq; [| false].
    destruct n0; [false |].
    omega.
  Qed.

  Lemma res_count_labels_interleaving1: a lp n0 n0' n la1 la2 n',
    res_count_labels a n (interleaving lp la1 la2) n' = true
    ( a, In (l_in a) la1In (l_in a) la2False) →
    res_count_labels a n0 la1 n0' = true
    n0 n
    res_count_labels a n (la1++la2) n' = true.
  Proof.
    induction lp; simpl; intros; auto.
    destruct a0; destruct u.
    × destruct la1; auto.
    change (l::interleaving lp la1 la2) with ([l]++interleaving lp la1 la2) in H.
    apply res_count_labels_app2 in H.
    destruct H as [n'0[??]].
    change ((l::la1)++la2) with ([l]++(la1++la2)).
    eapply res_count_labels_app2'; eauto.
    destruct la2.
    + rewrite app_nil_r.
    rewrite interleaving_r_nil in H3. auto.
    + change (l::la1) with ([l]++la1) in H1.
    apply res_count_labels_app2 in H1.
    destruct H1 as [n0'0[??]].
    eapply IHlp with (n0:=n0'0) (n0':=n0'); clear IHlp; intros; auto.
    apply (H0 a0); auto. right; auto.
    simpl in ×.
    destruct l.
    - destruct eq_nat_dec; destruct eq_nat_dec; subst; try solve [false|auto].
    - destruct name_eq; subst.
    destruct n0; [false|]. destruct n; [false |].
    destruct eq_nat_dec; destruct eq_nat_dec; subst; try solve [false|auto].
    omega.
    destruct eq_nat_dec; destruct eq_nat_dec; subst; try solve [false|auto].
    - destruct name_eq; subst.
    destruct eq_nat_dec; destruct eq_nat_dec; subst; try solve [false|auto].
    omega.
    destruct eq_nat_dec; destruct eq_nat_dec; subst; try solve [false|auto].
    × destruct la2.
    + rewrite app_nil_r. auto.
    + change (l::interleaving lp la1 la2) with ([l]++interleaving lp la1 la2) in H.
    apply res_count_labels_app2 in H.
    destruct H as [n'0[??]].
    eapply res_count_labels_positive_app_com2; eauto.
    change (l::la1++la2) with ([l]++la1++la2).
    apply res_count_labels_app2' with n'0; auto.
    apply res_count_labels_le_com with (n0:=n0) (n0':=n0') (la1:=la1) in H; auto.
    destruct H as [n00[n00'[??]]].
    eapply IHlp with (n0:=n00) (n0':=n00'); clear IHlp; eauto.
    intros. eapply H0; eauto. right; auto.
    intros. intro; subst; apply (H0 a0); auto. left; auto.
    intros; split; intro; subst. apply (H0 a0); auto. left; auto. apply (H0 a0); auto. right; auto.
  Qed.

  Lemma res_count_labels_com: a la1 n n1' la2 n2',
    res_count_labels a n la1 n1' = true
    res_count_labels a n la2 n2' = true
    ( a, In (l_in a) la1In (l_in a) la2False) →
     n''',
      res_count_labels a n2' la1 n''' = true res_count_labels a n1' la2 n''' = true.
  Proof.
    intros.
    destruct (in_dec label_eq (l_in a) la1).
    × assert (¬In (l_in a) la2) by (intro; apply (H1 a); auto).
    generalize (res_count_labels_nIn' _ _ _ _ H0 H2); intros.
    subst n2'.
     (n1'+count_occ label_eq la2 (l_out a))%nat; split.
    rewrite (plus_comm n).
    rewrite (plus_comm n1').
    apply res_count_labels_incr; auto.
    assert (res_count_labels a n (la2++la1) (n1'+count_occ label_eq la2 (l_out a)) = true).
      apply res_count_labels_app2' with (n+count_occ label_eq la2 (l_out a))%nat; auto.
      rewrite (plus_comm n); rewrite (plus_comm n1').
      apply res_count_labels_incr; auto.
    rewrite<- interleaving_app in H3.
    apply res_count_labels_nIn''; auto.
    × apply res_count_labels_nIn' in H; auto. subst.
     (n2'+count_occ label_eq la1 (l_out a))%nat; split.
    apply res_count_labels_nIn''; auto.
    rewrite (plus_comm n).
    rewrite (plus_comm n2').
    apply res_count_labels_incr; auto.
  Qed.

End ResCountLabels.

Section ResList.
  Import Notation.
  Import Notation.U.

  Definition p_res_list AN p:= fold_right (fun an p'let '(a,n):=an in a#n:p')%proc p AN.

  Lemma p_res_list_nil: AN p,
    p_res_list AN p = p_nilp=p_nil.
  Proof.
    induction AN; intros; simpl in *; auto.
    destruct a. congruence.
  Qed.

  Lemma s_res_list: AN p l p',
    ( a, l = l_in a¬In a (fst (split AN))) →
    ( a, l = l_out a¬In a (fst (split AN))) →
    step p l p'
    step (p_res_list AN p) l (p_res_list AN p').
  Proof.
    induction AN; intros; auto.
    destruct a. simpl.
    apply s_res.
    apply IHAN; eauto.
    intros; subst; intro; eapply H; eauto. simpl. destruct (split AN); simpl in *; auto.
    intros; subst; intro; eapply H0; eauto. simpl. destruct (split AN); simpl in *; auto.
    intro. subst. eapply H; eauto. simpl. destruct (split AN); simpl; auto.
    intro. subst. eapply H0; eauto. simpl. destruct (split AN); simpl; auto.
  Qed.

  Lemma p_res_list_app: p AN AN',
    p_res_list (AN++AN') p = p_res_list AN (p_res_list AN' p).
  Proof. induction AN; intros; auto; destruct a as [a n]; simpl; congruence. Qed.

  Lemma fn_In_res_list1: a AN p,
    NameSet.In a (fn (p_res_list AN p)) → NameSet.In a (fn p) ¬In a (fst (split AN)).
  Proof.
    induction AN; simpl; intros; auto.
    destruct a0. simpl in H.
    destruct (IHAN p) as [??].
    eapply NameSetProps.Dec.F.remove_3; eauto.
    split; auto.
    rewrite fst_split.
    apply NameSetProps.Dec.F.remove_iff in H.
    destruct H.
    intros [?|?]; subst; false.
  Qed.

  Lemma fn_p_res_list: AN p,
    NameSet.Equal (fn (p_res_list AN p)) (NameSet.diff (fn p) (fold_right (fun a nsNameSet.add a ns) NameSet.empty (fst (split AN)))).
  Proof.
    intros.
    revert p.
    induction AN; intros; simpl; try reflexivity.
    rewrite NameSetProps.empty_diff_2; auto. reflexivity.
    destruct a. simpl in ×.
    rewrite<- (split_combine' AN) at 2.
    rewrite combine_split.
    simpl.
    setoid_rewrite NameSet_diff_add.
    rewrite<- IHAN.
    reflexivity.
    rewrite split_length_l. rewrite split_length_r.
    reflexivity.
  Qed.

  Lemma step_star_hidden: AN p la p',
    step_star (p_res_list AN p) la p'
    hidden_fn (fst (split AN)) p
    la = [].
  Proof.
    unfold hidden_fn; intros.
    destruct la; auto. false.
    destruct l.
    eapply step_star_l_tau_nIn; eauto. left; auto.
    apply fn_step_star_In with (a:=n) in H; eauto.
    apply fn_In_res_list1 in H.
    destruct H.
    apply H1; apply H0; auto.
    left; left; auto.
    apply fn_step_star_In with (a:=n) in H; eauto.
    apply fn_In_res_list1 in H.
    destruct H.
    apply H1; apply H0; auto.
    right; left; auto.
  Qed.

  Lemma p_res_list_inv: AN1 p1 AN2 p2,
    p_res_list AN1 p1 = p_res_list AN2 p2
     AN,
      (p1 = p_res_list AN p2 AN2 = AN1++AN)
      (p2 = p_res_list AN p1 AN1 = AN2++AN).
  Proof.
    intros.
    revert p1 p2 AN2 H.
    induction AN1; intros; destruct AN2; simpl in *; subst.
    - (@nil (name×nat)); simpl; auto.
    - destruct p. eexists; left; split; auto; simpl; auto.
    - destruct a. eexists; right; split; auto; simpl; auto.
    - destruct a; destruct p. inverts H.
    edestruct IHAN1 as [AN[[??]|[??]]]; eauto.
    + subst. eexists; left; split; auto; simpl; auto.
    + subst. eexists; right; split; auto; simpl; auto.
  Qed.

  Lemma p_res_list_inv': AN1 p1 AN2 p2,
    p_res_list AN1 p1 = p_res_list AN2 p2
    length AN2 = length AN1
    p2=p1 AN2 = AN1.
  Proof.
    intros; edestruct p_res_list_inv as [AN ?]; eauto;
      destruct H1 as [[??]|[??]]; subst; rewrite app_length in H0; (destruct AN; simpl in *; [| false;omega]);
      rewrite app_nil_r; auto.
  Qed.

  Lemma step_star_res_list_nil: AN,
    step_star (p_res_list AN p_nil) nil p_nil.
  Proof.
    induction AN; simpl; intros.
    apply @step_nil.
    destruct a.
    eapply step_tsnoc.
    eapply step_star_res.
    apply IHAN.
    reflexivity.
    apply res_count_labels_nil2'.
    apply s_res_nil.
  Qed.

  Lemma step_star_res_list_nIn: A N p la p',
    step_star p la p'
    ( l a, In l laIn a All_in a ll_out a) →
    step_star (p_res_list (combine A N) p) la (p_res_list (combine A N) p').
  Proof.
    induction A; intros; simpl in *; auto.
    destruct N; simpl in *; auto.
    eapply step_star_res_nIn; eauto.
  Qed.

  Lemma p_res_list_eq: AN1 p1 AN2 p2,
    ( a n p, p1 p_res a n p p2 p_res a n p) →
    p_res_list AN1 p1 = p_res_list AN2 p2AN2=AN1 p2=p1.
  Proof.
    induction AN1; intros; auto.
    destruct AN2; simpl in *; auto.
    destruct p; subst.
    false; edestruct H as [? _]. apply H0; reflexivity.
    destruct AN2; simpl in ×.
    destruct a. subst.
    false; edestruct H as [_ ?]. apply H0; reflexivity.
    destruct a; destruct p.
    inverts H0.
    edestruct IHAN1 as [??]; eauto; subst AN2 p2.
    auto.
  Qed.

  Lemma step_res_list_par_seq_app: p3 p4 AN p1 p2 l AN' p1' p2',
    step (p_res_list AN (p1||p2)) l (p_res_list AN' (p1'||p2')) →
    step (p_res_list AN (p1;;p3||p2;;p4)) l (p_res_list AN' (p1';;p3||p2';;p4)).
  Proof.
    induction AN; intros; simpl in ×.
    × destruct AN'; simpl in ×. inverts H.
    apply s_parL; apply s_seq; auto.
    apply s_parR; apply s_seq; auto.
    destruct p. inverts H.
    × destruct a. destruct AN'; simpl in ×. false; inverts H.
    destruct p. inverts H.
    apply s_res; auto.
    apply s_res_count_in; auto.
    apply s_res_count_out; auto.
  Qed.

End ResList.

Section ResListCountLabels.
  Import Notation.
  Import Notation.U.

  Fixpoint res_list_count_labels A N la N' := match A, N, N' with
  | a::A, n::N, n'::N'res_count_labels a n (res_list_filter A la) n' && res_list_count_labels A N la N'
  | nil, nil, niltrue
  | _, _, _false
  end.

  Definition res_list_count_labels_min A N la N':=
    res_list_count_labels A N la N' = true
     N0 N0', res_list_count_labels A N0 la N0' = true D, length D = length A N0 = list_plus N D N0' = list_plus N' D.

  Definition step_res_list_label_wf (A: list name) l l' :=
    (( a, In a A l=l_tau (l'=l_in a l'=l_out a)) (l'=l a, In a Al'l_in a l'l_out a)).

  Lemma res_list_count_labels_length: A N la N',
    res_list_count_labels A N la N' = true
    length N = length A length N' = length A.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; try solve [false]; auto.
    apply andb_true_iff in H; destruct H as [??].
    apply IHA in H0.
    destruct H0; split; congruence.
  Qed.

  Lemma res_list_count_labels_app: A1 A2 N1 N2 la N1' N2',
    length N1 = length A1length N1' = length A1
    res_list_count_labels (A1++A2) (N1++N2) la (N1'++N2')
    = res_list_count_labels A2 N2 la N2' && res_list_count_labels A1 N1 (res_list_filter A2 la) N1'.
  Proof.
    induction A1; intros.
    - simpl.
    destruct N1. destruct N1'.
    rewrite andb_true_r; reflexivity.
    false; simpl in *; omega.
    false; simpl in *; omega.
    - destruct N1.
    false; simpl in *; omega.
    destruct N1'.
    false; simpl in *; omega.
    simpl in ×.
    rewrite IHA1; auto.
    repeat rewrite andb_assoc.
    f_equal.
    rewrite andb_comm.
    f_equal.
    rewrite res_list_filter_app; reflexivity.
  Qed.

  Lemma res_list_count_labels_app2: A N la1 la2 N',
    res_list_count_labels A N (la1++la2) N' = true
     N'0,
      res_list_count_labels A N la1 N'0 = true res_list_count_labels A N'0 la2 N' = true.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; try solve [false].
    - (@nil nat); split; auto.
    - apply andb_true_iff in H. destruct H as [??].
    edestruct IHA as [N'0[??]]; eauto.
    rewrite res_list_filter_app2 in H.
    edestruct res_count_labels_app2 as [n'0[??]]; eauto.
     (n'0::N'0); splits; auto.
    apply andb_true_iff; split; auto.
    apply andb_true_iff; split; auto.
  Qed.

  Lemma res_list_count_labels_app2': A N la1 la2 N' N'0,
    res_list_count_labels A N la1 N'0 = true
    res_list_count_labels A N'0 la2 N' = true
    res_list_count_labels A N (la1++la2) N' = true.
  Proof.
    induction A; destruct N; destruct N'0; destruct N'; simpl; intros; try solve [false]; auto.
    apply andb_true_iff in H.
    apply andb_true_iff in H0.
    destruct H; destruct H0.
    apply andb_true_iff.
    rewrite res_list_filter_app2.
    rewrite IHA with (N'0:=N'0); auto.
    split; auto.
    rewrite res_count_labels_app2' with (n'0:=n0); auto.
  Qed.

  Lemma res_list_count_labels_nil2: A N,
    length N = length A
    res_list_count_labels A N nil N = true.
  Proof.
    induction A; destruct N; simpl; intros; auto; try solve [false].
    rewrite res_list_filter_nil2.
    apply andb_true_iff.
    split; auto.
    apply res_count_labels_nil2'.
  Qed.

  Lemma res_list_count_labels_single: A N l N',
    res_list_count_labels A N [l] N' = true
    ( A1 a A2 N1 n N2 n',
      A = A1++[a]++A2 N = N1++[n]++N2 N' = N1++[n']++N2
      length N1 = length A1 ¬In a A2
      ((l=l_in a n=S n') (l=l_out a S n = n')))
    (N'=N a, In a All_in a ll_out a).
  Proof.
    induction A; destruct N; destruct N'; intros; simpl in *; try solve [false; omega].
    - right; split; eauto.
    - apply andb_true_iff in H. destruct H as [??].
    edestruct IHA as [ [A1[b[A2[N1[n'[N2[n''[?[?[?[?[??]]]]]]]]]]]] | [??] ]; clear IHA; eauto.
    + subst. repeat rewrite app_length in ×. simpl in ×.
    rewrite res_list_filter_app in H.
    change (b::A2) with ([b]++A2) in H; rewrite res_list_filter_app in H.
    rewrite res_list_filter_cons2 in H. rewrite res_list_filter_nil2 in H.
    simpl in H.
    destruct H6 as [[??]|[??]]; subst.
    simpl in ×.
    destruct (in_dec name_eq b A2); simpl in *; [false |].
    destruct (label_eq (l_in b) (l_in b)); [| false].
    rewrite res_list_filter_nil2 in H. simpl in H.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    left; (a::A1) b A2 (n::N1); (S n'') N2 n''; splits; auto.
    simpl; congruence.
    destruct (in_dec name_eq b A2); simpl in *; [false |].
    destruct (label_eq (l_out b) (l_in b)); [false |].
    destruct (label_eq (l_out b) (l_out b)); [| false].
    rewrite res_list_filter_nil2 in H. simpl in H.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    left; (a::A1) b A2 (n::N1); n' N2 (S n'); splits; auto.
    simpl; congruence.
    + subst. repeat rewrite app_length in ×.
    rewrite res_list_filter_cons2 in H. rewrite res_list_filter_nil2 in ×.
    destruct l; simpl in ×.
    destruct (eq_nat_dec n0 n); try discriminate.
    subst n0.
    right; split; intros; auto.
    destruct H1; subst; auto. split; intro; discriminate.
    destruct (in_dec name_eq n1 A). simpl in H.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    apply H2 in i.
    destruct i; false.
    simpl in H.
    destruct (name_eq n1 a). subst n1.
    destruct n; try discriminate.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    left; (@nil name) a A (@nil nat) (S n); N n; splits; auto.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    right; split; intros; auto. destruct H1; subst. split; intro; subst; false. split; intro; false.
    destruct (in_dec name_eq n1 A).
    apply H2 in i. destruct i; false.
    simpl in H. destruct (name_eq n1 a). subst; auto.
    destruct (eq_nat_dec n0 (S n)); subst.
    left; (@nil name) a A (@nil nat) n; N (S n); splits; auto.
    false.
    destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    right; split; intros; auto. destruct H1; subst. split; intro; false. split; intro; false.
  Qed.

  Lemma res_list_count_labels_nil2': A N N',
    res_list_count_labels A N nil N' = true
    N' = N.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; try solve [false; omega]; auto.
    apply andb_true_iff in H. destruct H.
    rewrite res_list_filter_nil2 in H. simpl in H. destruct (eq_nat_dec n0 n); try discriminate. subst n0.
    f_equal; auto.
  Qed.

  Lemma res_list_count_labels_nIn: A N la N',
    res_list_count_labels A N la N' = true
    ( a, In a A¬In (l_in a) la ¬In (l_out a) la) →
    N'=N.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; auto; try solve [false].
    apply andb_true_iff in H; destruct H as [??].
    lapply (H0 a); intros; auto. destruct H2 as [??].
    rewrite res_list_filter_nIn in H; intros.
    apply res_count_labels_nIn in H; auto.
    subst n0; f_equal; eauto.
    destruct (H0 a0) as [? _]; auto; contradiction.
    destruct (H0 a0) as [_ ?]; auto; contradiction.
  Qed.

  Lemma res_list_count_labels_single': A1 a A2 N1 n N2 l N1' n' N2',
    res_list_count_labels (A1++a::A2) (N1++n::N2) [l] (N1'++n'::N2') = true
    length N1 = length A1
    length N1' = length A1
    ¬In a A2
    (l=l_in aS n' = n N1'=N1 N2'=N2) (l=l_out an' = S n N1'=N1 N2'=N2) (l=l_taun'=n N1'=N1 N2'=N2).
  Proof.
    intros.
    cut ( {A:Type} (a:A) l, a::l =[a]++l); intros Hcons; auto.
    rewrite (Hcons _ a) in H.
    rewrite (Hcons _ n) in H.
    rewrite (Hcons _ n') in H.
    clear Hcons.
    rewrite res_list_count_labels_app in H; auto.
    rewrite res_list_count_labels_app in H; auto.
    apply andb_true_iff in H; destruct H as [? ?].
    apply andb_true_iff in H; destruct H as [? ?].
    splits; intros; subst.
    × rewrite res_list_filter_nIn in H4.
    rewrite res_list_filter_In_filter in H3.
    apply res_list_count_labels_nil2' in H3; auto. subst.
    simpl in ×. destruct name_eq. destruct n. false.
    destruct eq_nat_dec; subst; auto.
    splits; auto.
    intros.
    apply res_list_count_labels_nIn in H; auto.
    intros. split; intro H7; destruct H7; subst; false.
    false. false.
    intro H7; destruct H7; false.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
    intros ? H5 ?; destruct H5; subst; [inverts H5 |]; contradiction.
    intros ? H5 ?; destruct H5; subst; [inverts H5 |]; contradiction.
    × rewrite res_list_filter_nIn in H4.
    rewrite res_list_filter_In_filter in H3.
    apply res_list_count_labels_nil2' in H3; auto. subst.
    simpl in ×. destruct name_eq.
    destruct eq_nat_dec; subst; auto.
    splits; auto.
    intros.
    apply res_list_count_labels_nIn in H; auto.
    intros. split; intro H7; destruct H7; subst; false.
    false. false.
    intro H7; destruct H7; false.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
    intros ? H5 ?; destruct H5; subst; [inverts H5 |]; contradiction.
    intros ? H5 ?; destruct H5; subst; [inverts H5 |]; contradiction.
    × rewrite res_list_filter_nIn in H4.
    rewrite res_list_filter_tau in H3.
    apply res_list_count_labels_single in H3; auto.
    destruct H3 as [ [_[?[_[_[?[_[?[_[_[_[_[_ ?]]]]]]]]]]]] | [? _] ].
    destruct H3 as [[??]|[??]]; discriminate.
    subst N1'.
    simpl in ×.
    destruct eq_nat_dec; subst; auto.
    splits; auto.
    intros.
    apply res_list_count_labels_single in H; auto.
    destruct H as [ [_[?[_[_[?[_[?[_[_[_[_[_ ?]]]]]]]]]]]] | [? _] ].
    destruct H as [[??]|[??]]; discriminate.
    subst N2'. auto.
    false.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
    intros ? H5; destruct H5; inverts H5; subst; left; auto.
  Qed.

  Lemma res_list_count_labels_incr: A N la N' D,
    res_list_count_labels A N la N' = true
    length D length N
    res_list_count_labels A (list_plus D N) la (list_plus D N') = true.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; try solve [false].
    rewrite list_plus_nil_r. auto.
    apply andb_true_iff in H; destruct H as [??].
    destruct D. simpl in H0; false. omega.
    simpl.
    apply andb_true_iff.
    split; auto.
    apply res_count_labels_incr; auto.
    apply IHA; auto.
    simpl in H0; omega.
  Qed.

  Lemma res_list_count_labels_list_le: A N l N' N0 N0',
    res_list_count_labels A N [l] N' = true
    res_list_count_labels A N0 [l] N0' = true
    list_le N0 N
    list_le N0' N'.
  Proof.
    intros.
    apply res_list_count_labels_single in H.
    destruct H as [ [A1[a[A2[N1[n[N2[n'[?[?[?[?[??]]]]]]]]]]]] | [??] ].
    subst A N N'.
    destruct (app_split3 (length A1) N0) as [N01[n0[N02[??]]]].
      { apply res_list_count_labels_length in H0.
      destruct H0 as [? _]. rewrite app_length in ×. simpl in ×. omega. }
    subst N0.
    destruct (app_split3 (length A1) N0') as [N01'[n0'[N02'[??]]]].
      { apply res_list_count_labels_length in H0. destruct H0. rewrite app_length in ×. simpl in ×. omega. }
    subst N0'.
    apply res_list_count_labels_single' in H0; auto.
    destruct H0 as [?[??]].
    assert (N01'=N01 N02'=N02).
      { destruct H6 as [[??]|[??]]; subst; intuition. }
    destruct H8; subst.
    apply list_le_app1 in H1. destruct H1.
    apply list_le_app1 in H8; auto. destruct H8.
    apply list_le_app2; auto.
    apply list_le_app2; auto.
    apply list_le_single; apply list_le_single in H8.
    destruct H6 as [[??]|[??]]; subst; intuition.
    congruence.
    congruence.
    subst N'.
    apply res_list_count_labels_single in H0.
    destruct H0 as [ [A1[a[A2[N01[n0[N02[n0'[?[?[?[?[??]]]]]]]]]]]] | [??] ].
    subst A N0 N0'.
    destruct (H2 a) as [??].
    apply in_app_iff. right. apply in_app_iff. left. left; auto.
    destruct H6 as [[??]|[??]]; subst; false.
    subst N0'.
    apply H1.
  Qed.

  Lemma res_list_count_labels_nIn1: A1 a A2 N1 n N2 la N1' n' N2',
    res_list_count_labels (A1++[a]++A2) (N1++[n]++N2) la (N1'++[n']++N2') =true
    length N1 = length A1length N1' = length A1
    ¬In (l_in a) la
    n'=(n + count_occ label_eq (res_list_filter A2 la) (l_out a))%nat x, res_list_count_labels (A1++[a]++A2) (N1++[x]++N2) la (N1'++[x+count_occ label_eq (res_list_filter A2 la) (l_out a)]%nat++N2') = true.
  Proof.
    intros.
    rewrite res_list_count_labels_app in H; auto.
    rewrite res_list_count_labels_app in H; auto.
    do 2 rewrite andb_true_iff in H.
    destruct H as [[??]?].
    simpl in H3; apply andb_true_iff in H3. destruct H3 as [? _].
    apply res_count_labels_nIn' in H3.
    subst n'.
    split; auto.
    intros.
    repeat rewrite res_list_count_labels_app; auto.
    simpl.
    do 3 rewrite andb_true_iff.
    split; auto.
    split; auto.
    split; auto.
    apply res_count_labels_nIn''; auto.
    intro; apply H2.
    apply in_res_list_filter in H3. apply H3.
    intro; apply H2.
    apply in_res_list_filter in H5. apply H5.
  Qed.

  Lemma res_list_count_labels_list_le_com: la1 A N l N' N0 N0',
    res_list_count_labels A N [l] N' = true
    res_list_count_labels A N0 la1 N0' = true
    ( a : name, In (l_in a) la1l l_in a) →
    list_le N0 N
     N00 N00',
      list_le N00 N' res_list_count_labels A N00 la1 N00' = true.
  Proof.
    induction A; destruct N; destruct N'; destruct N0; destruct N0'; simpl; intros; try solve [false].
     (@nil nat) (@nil nat); split; auto.
    rewrite andb_true_iff in ×.
    destruct H. destruct H0.
    apply list_le_cons1 in H2; destruct H2 as [??].
    edestruct IHA as [N00[N00'[??]]]; eauto.
    destruct (res_list_filter_single_inv A l) as [[??]|[??]].
    × rewrite H8 in ×.
    edestruct res_count_labels_le_com with (l:=l) as [n00[n00'[??]]].
    apply H. apply H0.
    intros. apply in_res_list_filter in H10. apply H1. apply H10.
    apply H2.
     (n00::N00) (n00'::N00'); split.
    apply list_le_cons2; auto.
    apply andb_true_iff; split; auto.
    × rewrite H8 in ×. apply res_count_labels_nil2 in H. subst n0.
    destruct H9 as [a'[??]].
     (n1::N00) (n2::N00'); split.
    apply list_le_cons2; auto.
    apply andb_true_iff; split; auto.
  Qed.

  Lemma res_list_count_labels_nIn': A N la,
    ( a, In a A¬In (l_in a) la ¬In (l_out a) la) →
    length N = length A
    res_list_count_labels A N la N = true.
  Proof.
    induction A; destruct N; simpl; intros; auto; try solve [false].
    inverts H0.
    destruct (H a) as [??]; auto.
    apply andb_true_iff; split; eauto.
    rewrite res_list_filter_nIn; intros.
    apply res_count_labels_nIn'''; auto.
    destruct (H a0) as [??]; auto.
    destruct (H a0) as [??]; auto.
  Qed.

  Lemma res_list_count_labels_exists_min: A N la N',
    res_list_count_labels A N la N' = true
     N0 N0',
      res_list_count_labels_min A N0 la N0'.
  Proof.
    induction A; destruct N; destruct N'; simpl; intros; try solve [false]; auto.
    × (@nil nat) (@nil nat); split; intros; auto.
    apply res_list_count_labels_length in H0; destruct H0.
    destruct N0; destruct N0'; simpl in *; try solve [false].
     (@nil nat); simpl; splits; auto.
    × apply andb_true_iff in H. destruct H.
    apply res_count_labels_exists_min in H; destruct H as [n00[n00'[??]]].
    apply IHA in H0; destruct H0 as [N0[N0'[??]]]; clear IHA.
     (n00::N0) (n00'::N0'); split; intros; auto.
    simpl. apply andb_true_iff; split; auto.
    simpl in H3.
    destruct N1; [false |].
    destruct N0'0; [false |].
    apply andb_true_iff in H3. destruct H3.
    apply H2 in H4; destruct H4 as [D[?[??]]].
    apply H1 in H3; destruct H3 as [d[??]].
    subst.
     (d::D); splits; simpl; auto.
  Qed.

  Lemma res_list_count_labels_interleaving1: lp A N0 N0' N la1 la2 N',
    res_list_count_labels A N (interleaving lp la1 la2) N' = true
    ( a, In (l_in a) la1In (l_in a) la2False) →
    (res_list_count_labels A N0 la1 N0' = true) →
    list_le N0 N
    res_list_count_labels A N (la1++la2) N' = true.
  Proof.
    induction A; destruct N0; destruct N0'; destruct N; destruct N'; simpl; intros; try solve [false]; auto.
    rewrite andb_true_iff in ×.
    destruct H1. destruct H.
    split.
    × rewrite res_list_filter_app2.
    apply list_le_cons1 in H2. destruct H2 as [??].
    destruct (res_list_filter_interleaving A lp la1 la2) as [lp' ?].
    rewrite H6 in H.
    eapply res_count_labels_interleaving1; eauto.
    intros a0 ? ?; apply (H0 a0); eapply in_res_list_filter; eauto.
    × apply list_le_cons1 in H2; destruct H2. eauto.
  Qed.

  Lemma res_list_count_labels_com: A la1 N N' la2 N'',
    res_list_count_labels A N la1 N' = true
    res_list_count_labels A N la2 N'' = true
    ( a, In (l_in a) la1In (l_in a) la2False) →
     N''',
      res_list_count_labels A N'' la1 N''' = true res_list_count_labels A N' la2 N''' = true.
  Proof.
    induction A; destruct N; destruct N'; destruct N''; simpl; intros; try solve [false].
    × (@nil nat); split; auto.
    × rewrite andb_true_iff in ×.
    destruct H as [??]; destruct H0 as [??].
    edestruct IHA with (la1:=la1) (la2:=la2) as [N'''[??]]; eauto.
    edestruct res_count_labels_com with (la1:=res_list_filter A la1) (la2:=res_list_filter A la2) as [n'''[??]]; eauto.
    intros a0 ? ?; apply (H1 a0); auto; eapply in_res_list_filter; eauto.
     (n'''::N'''); split; auto; apply andb_true_iff; split; auto.
  Qed.

  Lemma res_list_count_labels_cons: a A n N la n' N',
    length N' = length A
    res_list_count_labels (a::A) (n::N) la (n'::N') =
    res_count_labels a n (res_list_filter A la) n' &&
    res_list_count_labels A N la N'.
  Proof. intros; reflexivity. Qed.

  Lemma res_list_count_labels_nIn'': A N la,
    ( a, In a A¬In (l_in a) la) →
    length N = length A
    ¬In l_tau la
     N',
      res_list_count_labels A N la N' = true.
  Proof.
    induction A; intros.
    × (@nil nat). destruct N; auto. inverts H0.
    × destruct N; inverts H0.
    edestruct IHA with (la:=la) as [N' ?]; clear IHA; eauto.
    intros; eauto. apply H. right; auto.
     (n + count_occ label_eq (res_list_filter A la) (l_out a)::N')%nat.
    rewrite res_list_count_labels_cons.
    apply andb_true_iff; split; auto.
    apply res_count_labels_nIn''; auto.
    intro. edestruct in_res_list_filter as [??]; eauto.
    eapply H; eauto. left; auto.
    edestruct res_list_count_labels_length; eauto.
  Qed.

End ResListCountLabels.

Section ResListCountLabelsStepping.
  Import Notation.
  Import Notation.U.

  Lemma step_res_list_inv: AN p l pp',
    step (p_res_list AN p) l pp'
    ( l' AN' p',
      step p l' p'
      pp' = p_res_list AN' p'
      fst (split AN') = fst (split AN)
      step_res_list_label_wf (fst (split AN)) l l'
      res_list_count_labels (fst (split AN)) (snd (split AN)) [l'] (snd (split AN')) = true)
    ( AN1 AN2,
      p=p_nil
      l = l_tau
      AN = AN1++AN2
      AN2 nil
      pp' = p_res_list AN1 p_nil).
  Proof.
    unfold step_res_list_label_wf; induction AN; intros; simpl in ×.
    × left; l (@nil (name×nat)) pp'; splits; auto.
    × destruct a. inverts H.
    - edestruct IHAN as [ [l'[AN'[p'_[?[?[?[??]]]]]]] | [AN1[AN2[?[?[?[??]]]]]] ]; clear IHAN; eauto.
    + subst p'; rename p'_ into p'.
    left; l' ((n,n0)::AN') p'; splits; auto.
    simpl; do 2 rewrite fst_split.
    congruence.
    destruct H2 as [ [a[?[??]]] | [??] ]; subst.
    left; a; splits; auto. rewrite fst_split. right; auto.
    right; split; intros; eauto. rewrite fst_split in H0. destruct H0; subst; eauto.
    simpl. do 2 rewrite snd_split. rewrite fst_split.
    simpl. apply andb_true_iff.
    split; auto.
    rewrite res_list_filter_cons2.
    rewrite res_list_filter_nil2.
    destruct H2 as [ [a[?[?[?|?]]]] | [??] ]; subst.
    destruct in_dec; [| false; apply n1; auto].
    apply res_count_labels_nil2'.
    destruct in_dec; [| false; apply n1; auto].
    apply res_count_labels_nil2'.
    destruct l. simpl; destruct eq_nat_dec; auto; false.
    destruct in_dec. apply res_count_labels_nil2'.
    simpl. destruct name_eq; subst. false.
    simpl; destruct eq_nat_dec; auto; false.
    destruct in_dec. apply res_count_labels_nil2'.
    simpl. destruct name_eq; subst. false.
    simpl; destruct eq_nat_dec; auto; false.
    + subst. right; ((n,n0)::AN1) AN2; splits; auto.
    - edestruct IHAN as [ [l'[AN'[p'_[?[?[?[??]]]]]]] | [AN1[AN2[?[?[??]]]]] ]; clear IHAN; eauto.
    + subst p'; rename p'_ into p'.
    left; l' ((n,n1)::AN') p'; splits; auto.
    simpl; do 2 rewrite fst_split.
    congruence.
    destruct H2 as [ [a[?[??]]] | [??] ]; subst.
    left; a; splits; auto. rewrite fst_split. right; auto.
    rewrite fst_split.
    left; n; splits; auto. left; auto.
    rewrite fst_split. simpl; do 2 rewrite snd_split.
    simpl. apply andb_true_iff.
    split; auto.
    rewrite res_list_filter_cons2.
    rewrite res_list_filter_nil2.
    destruct H2 as [ [a[?[?[?|?]]]] | [??] ]; subst.
    false. false.
    destruct in_dec.
    apply H2 in i. destruct i. false.
    simpl.
    destruct name_eq. destruct eq_nat_dec; auto; false. false.
    + false.
    - edestruct IHAN as [ [l'[AN'[p'_[?[?[?[??]]]]]]] | [AN1[AN2[?[?[??]]]]] ]; clear IHAN; eauto.
    + subst p'; rename p'_ into p'.
    left; l' ((n,S n0)::AN') p'; splits; auto.
    simpl; do 2 rewrite fst_split. congruence.
    rewrite fst_split. left.
    destruct H2 as [ [a[?[??]]] | [??] ]; subst.
     a; splits; auto. right; auto.
     n; splits; auto. left; auto.
    rewrite fst_split. simpl; do 2 rewrite snd_split.
    simpl. apply andb_true_iff.
    split; auto.
    rewrite res_list_filter_cons2.
    rewrite res_list_filter_nil2.
    destruct H2 as [ [a[?[?[?|?]]]] | [??] ]; subst.
    false. false.
    destruct in_dec.
    apply H2 in i. destruct i. false.
    simpl.
    destruct name_eq. destruct eq_nat_dec; auto; false. false.
    + false.
    - clear IHAN.
    destruct AN; simpl in *; subst.
    right; (@nil (name×nat)) [(n,n0)]; splits; auto.
    intro; false.
    destruct p0. false.
  Qed.

  Lemma step_star_res_list_inv': A N p la pp',
    step_star (p_res_list (combine A N) p) la pp'
    length N = length A
     la' N' p',
      step_star p la' p'
      la = res_list_filter A la'
      res_list_count_labels A N la' N' = true
      (pp' = p_res_list (combine A N') p'
      
      ( A1 A2 N1' N2',
        p' = p_nil pp' = p_res_list (combine A1 N1') p_nil A = A1++A2
        N' = N1'++N2' length N1'=length A1 length A2>0)).
  Proof.
    introv Hss.
    remember (p_res_list (combine A N) p) as pp.
    revert A N p Heqpp.
    induction Hss; intros; subst; auto.
    × (@nil lts_L) N p0; splits; auto.
    apply step_nil. symmetry; apply res_list_filter_nil2.
    apply res_list_count_labels_nil2; auto.
    × rename p' into pp'.
    edestruct step_res_list_inv as [ [l'[AN'[p'[?[?[?[??]]]]]]] | [AN1[AN2[?[?[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    + subst.
    edestruct IHHss with (A:=fst (split AN')) (N:=snd (split AN')) as [la'[N'[p'0[?[?[??]]]]]]; clear IHHss; eauto.
    rewrite split_combine'; reflexivity.
    rewrite split_length_l; rewrite split_length_r; reflexivity.
    rewrite combine_split in *; auto. simpl in ×.
    subst.
    destruct H4 as [ [b[?[??]]] | [??] ]; subst.
    inverts H.
     (a::la') N' p'0; splits; auto.
    eapply @step_lcons; eauto.
    inverts H; constructor; auto.
    rewrite res_list_filter_cons2.
    inverts H; destruct in_dec; auto; false; apply H4 in i; intuition.
    change (a::la') with ([a]++la').
    eapply res_list_count_labels_app2'; eauto.
    + subst. inverts H.
    × rename p' into pp'.
    edestruct step_res_list_inv as [ [l'[AN'[p'[?[?[?[??]]]]]]] | [AN1[AN2[?[?[?[??]]]]]] ]; eauto.
    apply H.
    + subst.
    edestruct IHHss with (A:=fst (split AN')) (N:=snd (split AN')) as [la'[N'[p'0[?[?[??]]]]]]; clear IHHss; eauto.
    rewrite split_combine'; reflexivity.
    rewrite split_length_l; rewrite split_length_r; reflexivity.
    rewrite combine_split in *; auto. simpl in ×.
    subst.
    destruct H4 as [ [b[?[??]]] | [??] ]; subst.
     (l'::la') N' p'0; splits; auto.
    destruct H6; subst; eapply @step_lcons; eauto; constructor; auto.
    rewrite res_list_filter_cons2.
    destruct H6; subst; destruct in_dec; auto; contradiction.
    change (l'::la') with ([l']++la').
    eapply res_list_count_labels_app2'; eauto.
     (la') N' p'0; splits; auto.
    eapply @step_tcons; eauto. apply H1.
    apply res_list_count_labels_single in H5.
    destruct H5 as [ [?[?[?[?[?[?[?[_[_[_[_[_ [[??]|[??]] ]]]]]]]]]]]] | [??] ]; try discriminate.
    subst. auto.
    + subst.
    edestruct IHHss with (A:=fst (split AN1)) (N:=snd (split AN1)) as [la'[N'[p'0[?[?[??]]]]]]; clear IHHss; eauto.
    rewrite split_combine'; reflexivity.
    rewrite split_length_l; rewrite split_length_r; reflexivity.
    inverts H1; try solve [inverts H8; inverts H1].
    rewrite res_list_filter_nil2 in H5.
    subst.
     (@nil lts_L) N p_nil; splits; auto.
    apply step_nil. symmetry; apply res_list_filter_nil2.
    apply res_list_count_labels_nil2; auto.
    right.
    apply f_equal with (f:= fun asplit a) in H3.
    apply res_list_count_labels_nil2' in H6; auto.
    subst.
    destruct H7 as [? | [A1[A2[N1'[N2'[?[?[?[?[??]]]]]]]]] ]; subst; auto.
     (fst (split AN1)) (fst (split AN2)) (snd (split AN1)) (snd (split AN2)); splits; auto.
    rewrite fst_split_app. rewrite<- H3.
    rewrite combine_split; auto.
    rewrite snd_split_app. rewrite<- H3.
    rewrite combine_split; auto.
    rewrite split_length_l; rewrite split_length_r; reflexivity.
    rewrite split_length_l; destruct AN2; simpl in *; try solve [false]. omega.
     A1 (A2++fst (split AN2)) N1' (N2' ++ snd (split AN2)); splits; auto.
    rewrite app_assoc. rewrite<- H6.
    rewrite fst_split_app. rewrite <- H3.
    rewrite combine_split; auto.
    rewrite app_assoc. rewrite<- H7.
    rewrite snd_split_app. rewrite <- H3.
    rewrite combine_split; auto.
    rewrite app_length. omega.
  Qed.

  Lemma step_res_list_inv'': AN AN' p l p',
    step (p_res_list AN p) l (p_res_list AN' p') →
    length AN=length AN'
     l',
      step p l' p'
      fst (split AN') = fst (split AN)
      step_res_list_label_wf (fst (split AN)) l l'
      res_list_count_labels (fst (split AN)) (snd (split AN)) [l'] (snd (split AN')) = true.
  Proof.
    unfold step_res_list_label_wf; intros.
    edestruct step_res_list_inv as [ [l'[AN'_[p'_[?[?[?[??]]]]]]] | [AN1[AN2[?[?[?[??]]]]]] ]; eauto.
    + edestruct (p_res_list_inv' AN' p' AN'_ p'_) as [??]; eauto.
    rewrite<- H0. do 2 rewrite<- split_length_l. congruence.
    subst.
     l'; splits; auto.
    + false; subst.
    destruct AN2. false.
    rewrite app_length in H0.
    edestruct (p_res_list_inv AN' p' AN1 p_nil) as [AN[[??]|[??]]]; eauto.
    subst.
    rewrite app_length in H0.
    destruct AN.
    simpl in ×. repeat rewrite app_nil_r in ×.
    false; omega.
    simpl in ×.
    false; omega.
    destruct AN. inverts H1.
    simpl in ×. repeat rewrite app_nil_r in ×. subst.
    false; omega.
    destruct p0.
    inverts H1.
  Qed.

  Lemma step_star_res_list_inv'': AN AN' p la p',
    step_star (p_res_list AN p) la (p_res_list AN' p') →
    length AN=length AN'
     la',
      step_star p la' p'
      fst (split AN') = fst (split AN)
      la = res_list_filter (fst (split AN)) la'
      res_list_count_labels (fst (split AN)) (snd (split AN)) la' (snd (split AN')) = true.
  Proof.
    induction AN; intros; simpl in ×.
    × destruct AN'; simpl in *; [| false; omega].
     la; splits; auto.
    × destruct AN'. false; omega. simpl in H0.
    inverts H0.
    destruct a. destruct p0. simpl in ×.
    edestruct step_star_res_inv as [la'[n'[pp'[?[?[??]]]]]]; eauto.
    destruct H0 as [?|[??]]; subst.
    - inverts H0.
    edestruct IHAN as [la''[?[?[??]]]]; eauto. subst.
     la''; splits; auto.
    destruct (split AN'). destruct (split AN). simpl in *; congruence.
    simpl in ×.
    destruct (split AN); simpl. reflexivity.
    destruct (split AN); destruct (split AN'); simpl in ×.
    apply andb_true_intro; auto.
    - false; inverts H0.
  Qed.

  Lemma step_star_res_list: A N p la p' la' N',
    step_star p la p'
    la' = res_list_filter A la
    res_list_count_labels A N la N'=true
    step_star (p_res_list (combine A N) p) la' (p_res_list (combine A N') p').
  Proof.
    induction A; intros.
    - simpl in *; subst; auto.
    - simpl in ×.
    destruct N. false; omega.
    destruct N'. false; omega.
    simpl in ×.
    rewrite andb_true_iff in H1.
    destruct H1.
    eapply step_star_res; eauto.
  Qed.

  Lemma step_star_res_list': AN p la p' la' AN',
    step_star p la p'
    fst (split AN) = fst (split AN') →
    la' = res_list_filter (fst (split AN)) la
    res_list_count_labels (fst (split AN)) (snd (split AN)) la (snd (split AN'))=true
    step_star (p_res_list AN p) la' (p_res_list AN' p').
  Proof.
    intros.
    rewrite<- (split_combine' AN).
    rewrite<- (split_combine' AN').
    rewrite H0.
    eapply step_star_res_list; eauto.
    congruence.
    congruence.
  Qed.

  Lemma step_star_res_list_par_swap_nil: AN p la,
    step_star (p_res_list AN p) la p_nil
    step_star (p_res_list AN (par_swap p)) la p_nil.
  Proof.
    intros.
    edestruct step_star_res_list_inv' as [la'[N'[p'[?[?[??]]]]]]; eauto.
    rewrite split_combine'. apply H.
    rewrite split_length_l. rewrite split_length_r. reflexivity.
    destruct H3 as [? | [A1[A2[N1'[N2'[?[?[?[?[??]]]]]]]]] ]; subst.
    × destruct AN; simpl in *; subst; [| false].
    apply step_star_par_swap_nil; auto.
    destruct p0.
    rewrite fst_split in H2.
    rewrite snd_split in H2.
    rewrite fst_split in H3.
    rewrite fst_split in H.
    simpl in ×.
    destruct N'; false.
    × destruct A1; destruct N1'; simpl in *; try solve [false].
    rewrite<- (app_nil_r (res_list_filter _ _)).
    eapply step_star_app.
    eapply step_star_res_list' with (AN':=combine (fst (split AN)) N2'); eauto.
    apply step_star_par_swap_nil. auto.
    rewrite combine_split; auto.
    apply res_list_count_labels_length in H2; destruct H2; auto.
    rewrite combine_split; auto.
    apply res_list_count_labels_length in H2; destruct H2; auto.
    apply step_star_res_list_nil.
  Qed.

  Lemma step_star_res_list_seq_app: AN AN' p la p' q,
    step_star (p_res_list AN p) la (p_res_list AN' p') →
    length AN' = length AN
    step_star (p_res_list AN (p;;q)) la (p_res_list AN' (p';;q)).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    subst.
    rewrite<- (split_combine' AN).
    rewrite<- (split_combine' AN').
    rewrite H2.
    eapply step_star_res_list; eauto.
    eapply step_star_seq_app; eauto.
    rewrite split_combine'; reflexivity.
  Qed.

  Lemma step_star_res_list_par_swap: AN AN' p1 p2 la pp',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' pp') →
    length AN' = length AN
    step_star (p_res_list AN (p2||p1)) la (p_res_list AN' (par_swap pp')).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    subst.
    rewrite<- (split_combine' AN).
    rewrite<- (split_combine' AN').
    rewrite H2.
    eapply step_star_res_list; eauto.
    apply step_star_par_swap; rewrite par_swap_swap; auto.
    rewrite split_combine'; reflexivity.
  Qed.

  Lemma step_star_res_list_par_swap': AN AN' p1 p2 la pp',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (par_swap pp')) →
    length AN' = length AN
    step_star (p_res_list AN (p2||p1)) la (p_res_list AN' pp').
  Proof.
    intros; apply step_star_res_list_par_swap in H; auto; rewrite par_swap_swap in H; auto.
  Qed.

  Lemma step_star_res_list_par_swap_nil': AN p la,
    step_star (p_res_list AN (par_swap p)) la p_nilstep_star (p_res_list AN p) la p_nil.
  Proof.
    intros; apply step_star_res_list_par_swap_nil in H; auto; rewrite par_swap_swap in H; auto.
  Qed.

  Lemma step_star_res_list_parL_step: p2 AN p1 la AN' p1',
    step_star (p_res_list AN p1) la (p_res_list AN' p1') →
    length AN' = length AN
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2)).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply step_star_res_list'; eauto.
    apply step_star_parL_step; auto.
  Qed.

  Lemma step_star_res_list_parR_step: p1 AN p2 la AN' p2',
    step_star (p_res_list AN p2) la (p_res_list AN' p2') →
    length AN' = length AN
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1||p2')).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply step_star_res_list'; eauto.
    apply step_star_parR_step; auto.
  Qed.

  Lemma step_res_list: AN p l l' AN' p',
    step p l p'
    fst (split AN') = fst (split AN) →
    step_res_list_label_wf (fst (split AN)) l' l
    res_list_count_labels (fst (split AN)) (snd (split AN)) [l] (snd (split AN')) = true
    step (p_res_list AN p) l' (p_res_list AN' p').
  Proof.
    unfold step_res_list_label_wf; induction AN; intros.
    × destruct AN'; simpl in ×.
    destruct H1 as [ [a[? _]] | [? _] ]; [false | subst l'; auto].
    destruct p0. rewrite fst_split in H0. false.
    × destruct AN'; simpl in ×.
    destruct a. rewrite fst_split in H0. false.
    destruct a; destruct p0.
    do 2 rewrite snd_split in H2.
    rewrite fst_split in H2.
    rewrite fst_split in H1.
    do 2 rewrite fst_split in H0.
    inverts H0.
    simpl in H2. apply andb_true_iff in H2. destruct H2.
    rewrite res_list_filter_cons2 in H0. rewrite res_list_filter_nil2 in H0.
    destruct H1 as [ [a[?[??]]] | [??] ]; subst.
    assert (res_count_labels n n0 (if in_dec name_eq a (fst (split AN)) then [] else [l]) n2 = true).
      destruct H4; subst; auto.
    clear H0.
    destruct (in_dec name_eq a (fst (split AN))).
    simpl in H3.
    destruct (eq_nat_dec n2 n0); try discriminate. subst n2.
    apply s_res; try solve [intro; discriminate].
    eapply IHAN; eauto.
    simpl in H3.
    destruct H4; subst.
    destruct (name_eq a n). subst.
    destruct n0; try discriminate.
    destruct (eq_nat_dec n2 n0); try discriminate.
    subst n2.
    apply s_res_count_in.
    eapply IHAN; eauto.
    right; split; intros; auto. split; intro Hh; inverts Hh; subst; contradiction.
    destruct H1; subst; false. contradiction.
    destruct (name_eq a n). subst.
    destruct (eq_nat_dec n2 (S n0)); try discriminate.
    subst n2.
    apply s_res_count_out.
    eapply IHAN; eauto.
    right; split; intros; auto. split; intro Hh; inverts Hh; subst; contradiction.
    destruct H1; subst; false. contradiction.
    destruct (H3 n) as [??]. left; auto.
    destruct l'.
    simpl in H0.
    destruct (eq_nat_dec n2 n0); try discriminate. subst n2.
    apply s_res; try solve [intro; false].
    eapply IHAN; eauto.
    right; split; intros; eauto. split; intro; discriminate.
    destruct (in_dec name_eq n1 (fst (split AN))).
    destruct (H3 n1) as [??]. right; auto.
    false.
    simpl in H0.
    destruct (name_eq n1 n); subst. false.
    destruct (eq_nat_dec n2 n0); try discriminate. subst n2.
    apply s_res; auto.
    eapply IHAN; eauto.
    right; split; intros; auto. split; intro Hh; inverts Hh; contradiction.
    destruct (in_dec name_eq n1 (fst (split AN))).
    destruct (H3 n1) as [??]. right; auto. false.
    simpl in H0.
    destruct (name_eq n1 n). subst n1.
    false.
    destruct (eq_nat_dec n2 n0); try discriminate. subst n2.
    apply s_res; auto.
    eapply IHAN; eauto.
    right; split; intros; auto. split; intro Hh; inverts Hh; subst; contradiction.
  Qed.

  Lemma res_list_count_labels_fun: A N la N'1 N'2,
    res_list_count_labels A N la N'1 = true
    res_list_count_labels A N la N'2 = true
    N'2 = N'1.
  Proof.
    induction A; destruct N; destruct N'1; destruct N'2; simpl; intros; try solve [false]; auto.
    rewrite andb_true_iff in ×.
    destruct H as [??]; destruct H0 as [??].
    f_equal; eauto.
    eapply res_count_labels_fun; eauto.
  Qed.

End ResListCountLabelsStepping.

Section ResListFN.
  Import Notation.
  Import Notation.U.

  Lemma fn_subset_step_star_p_res_list_In: a AN p la AN' p',
    step_star (p_res_list AN p) la (p_res_list AN' p') →
    length AN' = length AN
    NameSet.In a (fn p') →
    NameSet.In a (fn p).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply NameSetProps.in_subset; eauto.
    eapply fn_subset_step_star; eauto.
  Qed.

  Lemma fn_subset_step_star_res_list_parL_In: a AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    NameSet.In a (fn p1') →
    NameSet.In a (fn p1).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply fn_subset_step_star_parR_In; eauto.
  Qed.

  Lemma fn_subset_step_star_res_list_parR_In: a AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    NameSet.In a (fn p2') →
    NameSet.In a (fn p2).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply fn_subset_step_star_parL_In; eauto.
  Qed.

  Lemma fn_in_subset_step_star_res_list_parL: AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    NameSet.Subset (fn_in p1') (fn_in p1).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply fn_in_subset_step_star_parL; eauto.
  Qed.

  Lemma fn_in_subset_step_star_res_list_parR: AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    NameSet.Subset (fn_in p2') (fn_in p2).
  Proof.
    intros.
    edestruct step_star_res_list_inv'' as [la'[?[?[??]]]]; eauto.
    eapply fn_in_subset_step_star_parR; eauto.
  Qed.

  Lemma hidden_fn_step_star_res_list_parR: AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    hidden_fn (fst (split AN)) p2
    hidden_fn (fst (split AN)) p2'.
  Proof. unfold hidden_fn; intros; apply H1; eapply fn_subset_step_star_res_list_parR_In; eauto. Qed.

  Lemma hidden_fn_step_star_res_list_parL: AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    length AN' = length AN
    hidden_fn (fst (split AN)) p1
    hidden_fn (fst (split AN)) p1'.
  Proof. unfold hidden_fn; intros; apply H1; eapply fn_subset_step_star_res_list_parL_In; eauto. Qed.

End ResListFN.

Add Parametric Relation : lts_S wbisimilar
    reflexivity proved by (wbisim_refl lts)
    symmetry proved by (wbisim_symm lts)
    transitivity proved by (wbisim_trans lts)
    as wbisimilar_rel.

Add Parametric Relation : lts_S contrasimilar
    reflexivity proved by (csim_refl lts)
    symmetry proved by (csim_symm lts)
    transitivity proved by (csim_trans lts)
    as contrasimilar_rel.

Section Algebraic.
  Import Notation.
  Import Notation.U.
  Open Scope proc.

  Lemma wbisim_nil_seq: p,
    wbisimilar (p_nil;; p) p.
  Proof.
    intros.
    apply wbisim_step.
    - intros; false.
    - intros. apply single_tstep. apply s_seq_nil.
    - intros. inverts H. inverts H4. p'; split. apply step_nil. reflexivity.
    - intros. q'; split. eapply step_tcons. apply s_seq_nil. apply single_tstep; eauto. reflexivity.
    - intros. inverts H; inverts H0; inverts H4.
    - intros. q'; split. eapply step_tcons. apply s_seq_nil. apply single_lstep; eauto. reflexivity.
  Qed.

  Lemma wbisim_seq_nil: p,
    wbisimilar (p;; p_nil) p.
  Proof.
    intros.
     (fun p qp=q;;p_nil (p=p_nilq=p_nil)).
    split; auto.
    clear p.
    splits; intros q p [?|[??]]; intros; subst.
    × false.
    × apply step_nil.
    × inverts H0. apply single_tstep; apply s_seq_nil.
    × apply step_nil.
    × split; intros.
    - inverts H.
     p'0; split; auto. apply single_tstep; auto.
     p_nil; split; auto. apply step_nil.
    - inverts H; inverts H0; p'0; split; auto; apply single_lstep; constructor; eauto.
    × split; intros; inverts H; inverts H0.
    × unfold inv; split; [intros q' | intros a q']; intros.
    - (q';;p_nil); split; auto. apply single_tstep; apply s_seq; auto.
    - (q';;p_nil); split; auto. apply single_lstep. inverts H; constructor; apply s_seq; auto.
    × split; intros; inverts H; inverts H0.
  Qed.

  Lemma sbisim_seq_assoc: p q r,
    sbisimilar (p;; (q;; r)) (p;; q;; r).
  Proof.
    intros.
     (fun pp qq( p, pp=p;;(q;;r) qq=p;;q;;r) qq=pp).
    split; eauto.
    clear p.
    splits; intros pp qq [ [p[??]] | ? ]; subst; unfold inv in *; intros.
    × false.
    × reflexivity.
    × false.
    × reflexivity.
    × split; intros.
    + inverts H.
    eexists; split. apply s_seq; apply s_seq; eauto. eauto.
    eexists; split; eauto. apply s_seq; apply s_seq_nil.
    + inverts H; inverts H0.
    eexists; split. constructor; apply s_seq; apply s_seq; eauto. eauto.
    eexists; split. constructor; apply s_seq; apply s_seq; eauto. eauto.
    × split; intros.
    + p'; split; auto.
    + p'; split; auto.
    × split; intros.
    + inverts H. inverts H4.
    - eexists; split. apply s_seq; eauto. eauto.
    - eexists; split; eauto. apply s_seq_nil.
    + inverts H; inverts H0; inverts H4.
    - eexists; split. constructor; apply s_seq; eauto. eauto.
    - eexists; split. constructor; apply s_seq; eauto. eauto.
    × split; intros.
    + p'; split; auto.
    + p'; split; auto.
  Qed.
  Lemma wbisim_seq_assoc: p q r,
    wbisimilar (p;; (q;; r)) (p;; q;; r).
  Proof. intros; apply sbisim_wbisim; apply sbisim_seq_assoc. Qed.

  Lemma sbisim_par_swap: p q,
    sbisimilar (p||q) (q||p).
  Proof.
    intros.
     (fun pp qq( p q, pp=p||q qq=q||p) (pp=p_nil qq=p_nil))%proc.
    split; eauto.
    clear p q.
    apply strong_bisimulation_symm; intros pp qq [ [p[q[??]]] | [??] ]; subst; unfold inv; intros.
    × false.
    × reflexivity.
    × split; intros.
    - inverts H.
    + (q||p'0)%proc; split; eauto; eapply s_parR; eauto.
    + (q'||p)%proc; split; eauto; eapply s_parL; eauto.
    + p_nil; split; eauto. apply s_par_nil.
    - inverts H; inverts H0.
    + (q||p'0)%proc; split; eauto; constructor; eapply s_parR; eauto.
    + (q'||p)%proc; split; eauto; constructor; eapply s_parL; eauto.
    + (q||p'0)%proc; split; eauto; constructor; eapply s_parR; eauto.
    + (q'||p)%proc; split; eauto; constructor; eapply s_parL; eauto.
    × split; intros; inverts H; inverts H0.
    × eauto.
    × auto.
  Qed.

  Lemma wbisim_par_swap: p q,
    wbisimilar (p||q) (q||p).
  Proof. intros; apply sbisim_wbisim; apply sbisim_par_swap. Qed.

  Lemma wbisim_par_nil1: p,
    wbisimilar (p||O) p.
  Proof.
    intros.
     (fun p qp=q||O (p=p_nil q=p_nil))%proc.
    split; auto.
    clear p.
    splits; (intros p q [? | [??]]; intros; [ subst; try rename q into p | subst p q]; unfold inv in *).
    × false.
    × apply step_nil.
    × simpl in H0; red in H0. subst p. apply single_tstep; apply s_par_nil.
    × apply step_nil.
    × split; intros.
    - inverts H.
    rename p'0 into p'.
     p'; split; auto. apply single_tstep; auto.
    inverts H4.
     p_nil; split; auto. apply step_nil.
    - inverts H; inverts H0; try rename p'0 into p'.
     p'; split; auto; apply single_lstep; constructor; auto.
    inverts H4.
     p'; split; auto; apply single_lstep; constructor; auto.
    inverts H4.
    × split; intros; inverts H; inverts H0.
    × split; intros.
    - (p'||O)%proc; split; auto. apply single_tstep; apply s_parL; auto.
    - inverts H.
     (p'||O)%proc; split; auto; apply single_lstep; constructor; apply s_parL; auto.
     (p'||O)%proc; split; auto; apply single_lstep; constructor; apply s_parL; auto.
    × splits; intros; try solve [inverts H; inverts H0 | apply step_nil].
  Qed.

  Lemma wbisim_par_nil2: p,
    wbisimilar (O||p) p.
  Proof. intros; eapply wbisim_trans. apply wbisim_par_swap. apply wbisim_par_nil1. Qed.

  Lemma wbisim_par_assoc: p q r,
    wbisimilar (p||(q||r)) ((p||q)||r).
  Proof.
    intros.
     (fun pp qq( p q r, pp=p||(q||r) qq=(p||q)||r) wbisimilar pp qq)%proc.
    split; iauto.
    clear p q r.
    splits; (intros pp qq [ [p[q[r[??]]]] | ?]; intros; [subst | renames pp to p, qq to q]; unfold inv in *).
    × false.
    × apply halted_wbisim1; auto.
    × false.
    × apply halted_wbisim2; auto.
    × split; intros.
    - inverts H; try renames p'0 to p'; try renames q' to qr'.
    + (p'||q||r); split; iauto. apply single_tstep; apply s_parL; apply s_parL; auto.
    + inverts H4; try rename q' into r'; try rename p' into q'.
      (p||q'||r); split; iauto. apply single_tstep. apply s_parL; apply s_parR; auto.
      (p||q||r'); split; iauto. apply single_tstep. apply s_parR; auto.
      (p||p_nil||p_nil); split. apply step_nil.
     right. symmetry. apply wbisim_par_nil1.
    - inverts H.
    + rename p' into pqr'. inverts H0.
     (p'||q||r); split; iauto. apply single_lstep; constructor; apply s_parL; apply s_parL; auto.
    rename q' into qr'. inverts H4.
    rename p' into q'. (p||q'||r); split; iauto. apply single_lstep; constructor; apply s_parL; apply s_parR; auto.
    rename q' into r'. (p||q||r'); split; iauto. apply single_lstep; constructor; apply s_parR; auto.
    + rename p' into pqr'. inverts H0.
     (p'||q||r); split; iauto. apply single_lstep; constructor; apply s_parL; apply s_parL; auto.
    rename q' into qr'. inverts H4.
    rename p' into q'. (p||q'||r); split; iauto. apply single_lstep; constructor; apply s_parL; apply s_parR; auto.
    rename q' into r'. (p||q||r'); split; iauto. apply single_lstep; constructor; apply s_parR; auto.
    × split; intros.
    - edestruct tstep_wbisim1 as [q'[??]]; eauto.
    - edestruct lstep_wbisim1 as [q'[??]]; eauto.
    × split; intros.
    - inverts H; try renames p'0 to p'; try renames q' to qr'.
    + rename p' into pq'.
    inverts H4.
      (p'||(q||r)); split; iauto. apply single_tstep. apply s_parL; auto.
      (p||(q'||r)); split; iauto. apply single_tstep. apply s_parR; apply s_parL; auto.
      (p_nil||(p_nil||r)); split. apply step_nil. right. apply wbisim_par_nil2.
    + rename qr' into r'. (p||(q||r')); split; iauto. apply single_tstep; apply s_parR; apply s_parR; auto.
    - inverts H.
    + rename p' into pqr'. inverts H0.
    rename p' into pq'. inverts H4.
     (p'||(q||r)); split; iauto. apply single_lstep; constructor; apply s_parL; auto.
     (p||(q'||r)); split; iauto. apply single_lstep; constructor; apply s_parR; apply s_parL; auto.
    rename q' into r'. (p||(q||r')); split; iauto. apply single_lstep; constructor; apply s_parR; apply s_parR; auto.
    + rename p' into pqr'. inverts H0.
    rename p' into pq'. inverts H4.
     (p'||(q||r)); split; iauto. apply single_lstep; constructor; apply s_parL; auto.
     (p||(q'||r)); split; iauto. apply single_lstep; constructor; apply s_parR; apply s_parL; auto.
    rename q' into r'. (p||(q||r')); split; iauto. apply single_lstep; constructor; apply s_parR; apply s_parR; auto.
    × split; intros.
    - edestruct tstep_wbisim2 as [q'[??]]; eauto.
    - edestruct lstep_wbisim2 as [q'[??]]; eauto.
  Qed.

  Lemma sbisim_sum_comm: p q,
    sbisimilar (p+q) (q+p).
  Proof.
    intros.
     (fun pp qq( p q, pp=p+q qq=q+p) pp=qq).
    split; iauto.
    clear p q.
    apply strong_bisimulation_symm; (intros pp qq [ [p[q[??]]] | ?]; [subst pp qq | subst qq; rename pp into p]; unfold inv in *; intros).
    × false.
    × reflexivity.
    × split; intros.
    - inverts H.
     p'; split; auto. apply s_sumR; auto.
     p'; split; auto. apply s_sumL; auto.
     p_nil; split; auto; apply s_sum_nil.
    - inverts H; inverts H0; p'; split; auto; constructor.
    apply s_sumR; auto.
    apply s_sumL; auto.
    apply s_sumR; auto.
    apply s_sumL; auto.
    × split; intros.
    - p'; split; auto.
    - p'; split; auto.
    × jauto.
    × auto.
  Qed.

  Lemma wbisim_sum_comm: p q,
    wbisimilar (p+q) (q+p).
  Proof. intros; apply sbisim_wbisim; apply sbisim_sum_comm. Qed.

  Lemma wbisim_intro_tau: p,
    wbisimilar (().p) p.
  Proof.
    intros.
     (fun p qp=().q wbisimilar p q).
    split; auto.
    clear p.
    splits; intros p q [?|?]; subst; intros; unfold inv in ×.
    × false.
    × apply halted_wbisim1; auto.
    × apply single_tstep; apply s_act.
    × apply halted_wbisim2; auto.
    × split; intros.
    - inverts H. p'; split. apply step_nil. right; apply wbisim_refl.
    - inverts H; inverts H0.
    × split; intros.
    - edestruct tstep_wbisim1 as [q'[??]]; eauto.
    - edestruct lstep_wbisim1 as [q'[??]]; eauto.
    × split; intros.
    - p'; split. eapply step_tcons. apply s_act. apply single_tstep; auto. right; apply wbisim_refl.
    - p'; split. eapply step_tcons. apply s_act. apply single_lstep; auto. right; apply wbisim_refl.
    × split; intros.
    - edestruct tstep_wbisim2 as [q'[??]]; eauto.
    - edestruct lstep_wbisim2 as [q'[??]]; eauto.
  Qed.

  Lemma wbisim_res_nil: a n,
    wbisimilar (a#n:O) O.
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false]; try (eexists; split; [| apply wbisim_refl] ).
    × apply single_tstep; constructor.
    × inverts H. inverts H3. inverts H4. inverts H4. apply step_nil.
    × inverts H.
    × inverts H; inverts H0; inverts H3.
    × inverts H; inverts H0.
  Qed.

  Lemma wbisim_res_comm: a n m b p,
    a b
    wbisimilar (a#n:b#m:p) (b#m:a#n:p).
  Proof.
    intros.
     (fun pp qq( a n m b p, ab pp=a#n:b#m:p qq=b#m:a#n:p) wbisimilar pp qq).
    split; [| left; jauto].
    clear a m n b p H.
    apply weak_bisimulation_symm;
      (intros pp qq [ [a[n[m[b[p[Hab[??]]]]]]] | ?]; [subst pp qq | renames pp to p, qq to q]; intros; unfold inv in *).
    × false.
    × apply halted_wbisim1; auto.
    × split; intros.
    - inverts H. inverts H3.
    + (b#m:a#n:p'); split.
    apply single_tstep; apply s_res; auto; apply s_res; auto.
    left; jauto.
    + renames n0 to m.
     (b#m:a#n:p'); split.
    apply single_tstep; apply s_res_count_in. apply s_res; auto; congruence.
    left; jauto.
    + (b#(S m):a#n:p'); split.
    apply single_tstep; apply s_res_count_out. apply s_res; auto; congruence.
    left; jauto.
    + p_nil; split.
    eapply step_tcons. apply s_res; try congruence. apply s_res_nil; congruence.
    apply single_tstep. apply s_res_nil.
    right. apply wbisim_res_nil.
    + inverts H4. rename n0 into n.
     (b#m:a#n:p'); split.
    apply single_tstep; apply s_res; try congruence. apply s_res_count_in; auto; congruence.
    left; jauto.
    + inverts H4.
     (b#m:a#(S n):p'); split.
    apply single_tstep; apply s_res; try congruence. apply s_res_count_out; auto; congruence.
    left; jauto.
    - inverts H.
    + inverts H0. inverts H3.
     (b#m:a#n:p'); split.
    apply single_lstep; constructor. apply s_res; try congruence. apply s_res; try congruence.
    left; jauto.
    + inverts H0. inverts H3.
     (b#m:a#n:p'); split.
    apply single_lstep; constructor. apply s_res; try congruence. apply s_res; try congruence.
    left; jauto.
    × split; intros.
    - edestruct tstep_wbisim1 as [q'[??]]; eauto.
    - edestruct lstep_wbisim1 as [q'[??]]; eauto.
    × left; b m n a p; splits; auto.
    × right; symmetry; auto.
  Qed.

  Lemma wbisim_res_act_comm: a n l p,
    l l_in al l_out a
    wbisimilar (a#n: p_act l p) (p_act l (a#n:p)).
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false].
    × inverts H1. inverts H5.
    + eexists; split; [| apply wbisim_refl]. apply single_tstep; constructor.
    + inverts H6. false.
    + inverts H6. false.
    × inverts H1.
    + eexists; split; [| apply wbisim_refl]. apply single_tstep; repeat constructor; auto.
    × inverts H1; inverts H2; inverts H5.
    + eexists; split; [| apply wbisim_refl]. apply single_lstep; repeat constructor.
    + eexists; split; [| apply wbisim_refl]. apply single_lstep; repeat constructor.
    × inverts H1; inverts H2.
    + eexists; split; [| apply wbisim_refl]. apply single_lstep; repeat constructor; intro HH; inverts HH; false.
    + eexists; split; [| apply wbisim_refl]. apply single_lstep; repeat constructor; intro HH; inverts HH; false.
  Qed.

  Lemma wbisim_res_in_comm: a n b p,
    a b
    wbisimilar (a#n:b?.p) (b?.a#n:p).
  Proof. intros; apply wbisim_res_act_comm; congruence. Qed.

  Lemma wbisim_res_out_comm: a n b p,
    a b
    wbisimilar (a#n:b!.p) (b!.a#n:p).
  Proof. intros; apply wbisim_res_act_comm; congruence. Qed.


  Inductive rep_step_result p' p : procProp :=
  | rep_step_result0: rep_step_result p' p (p' || p!!)
  | rep_step_result1: p'p,
    rep_step_result p' p p'p
    rep_step_result p' p (p||p'p).

  Lemma proc_eq_dec: (p1 p2: proc), {p1=p2}+{p1p2}.
  Proof. intros. repeat decide equality. Qed.

  Fixpoint get_step_rep_p' p pp' := match pp' with
  | p1 || p2if proc_eq_dec p2 (p!!) then p1 else get_step_rep_p' p p2
  | pp
  end.

  Lemma rep_step_result_get: p p' pp',
    rep_step_result p' p pp'p' = get_step_rep_p' p pp'.
  Proof.
    intros.
    induction H; simpl.
    destruct proc_eq_dec; auto. false.
    subst.
    destruct proc_eq_dec; auto. subst.
    inverts H.
  Qed.

  Lemma step_rep_inv: p a pp',
    step (p!!) a pp'
    step p a (get_step_rep_p' p pp') rep_step_result (get_step_rep_p' p pp') p pp'.
  Proof.
    intros.
    revert p H.
    induction pp'; intros; inverts H; inverts H1; simpl in ×.
    × destruct proc_eq_dec; try discriminate.
    split; auto. apply rep_step_result0.
    false.
    × destruct proc_eq_dec. subst.
    clear -H3; false.
    inverts H3. inverts H0.
    edestruct IHpp'2 as [??]; eauto.
    split; auto.
    apply rep_step_result1; auto.
  Qed.

  Inductive step__ : proclabelprocProp :=
  | s_act__: l p,
      step__ (p_act l p) l p
  | s_parL__: p q l p',
      step__ p l p'
      step__ (p || q) l (p' || q)
  | s_parR__: p q l q',
      step__ q l q'
      step__ (p || q) l (p || q')
  | s_sumL__: p q l p',
      step__ p l p'
      step__ (p + q) l p'
  | s_sumR__: p q l q',
      step__ q l q'
      step__ (p + q) l q'
  | s_seq__: p q l p',
      step__ p l p'
      step__ (p;; q) l (p';; q)
  | s_res__: a n p l p',
      step__ p l p'
      l l_in al l_out a
      step__ (a#n: p) l (a#n: p')
  | s_res_count_in__: a n p p',
      step__ p (l_in a) p'
      step__ (a#(S n): p) l_tau (a#n: p')
  | s_res_count_out__: a n p p',
      step__ p (l_out a) p'
      step__ (a#n: p) l_tau (a#(S n): p')
  | s_rep__: p a p' pp',
      step__ p a p'
      rep_step_result p' p pp'
      step__ (p!!) a pp'
  
  | s_par_nil__:
      step__ (p_nil || p_nil) l_tau p_nil
  | s_sum_nil__:
      step__ (p_nil+p_nil) l_tau p_nil
  | s_seq_nil__: p,
      step__ (p_nil;; p) l_tau p
  | s_res_nil__: a n,
      step__ (a#n:p_nil) l_tau p_nil

  .

  Lemma step_rep_result: l p' p pp',
    step p l p'rep_step_result p' p pp'step (p!!) l pp'.
  Proof.
    intros.
    induction H0.
    apply s_rep. apply s_parL; auto.
    apply s_rep; auto.
    apply s_parR; auto.
  Qed.

  Lemma step_step__: p l p',
    step p l p' step__ p l p'.
  Proof.
    split; intros.
    × induction H; intros; try solve [constructor; auto].
    apply s_sumR__; auto.
    inverts IHstep.
    apply s_rep__ with p'0; auto.
    apply rep_step_result0; auto.
    inverts H4.
    apply s_rep__ with p'; auto.
    apply rep_step_result1; auto.
    apply s_sum_nil__.
    × induction H; intros; try solve [constructor; auto].
    apply s_sumR; auto.
    eapply step_rep_result; eauto.
    apply s_sum_nil.
  Qed.

  Definition step_ind' (P : proclabelprocProp)
  (H0: (l : label) (p : proc), P (p_act l p) l p)
  (H1: (p q : proc) (l : label) (p' : proc),
   step p l p'P p l p'P (p || q) l (p' || q))
  (H2: (p q : proc) (l : label) (q' : proc),
   step q l q'P q l q'P (p || q) l (p || q'))
  (H3: (p q : proc) (l : label) (p' : proc),
   step p l p'P p l p'P (p + q) l p')
  (H4: (p q : proc) (l : label) (q' : proc),
   step q l q'P q l q'P (p + q) l q')
  (H5: (p q : proc) (l : label) (p' : proc),
   step p l p'P p l p'P (p;; q) l (p';; q))
  (H6: (a : name) (n : nat) (p : proc) (l : label) (p' : proc),
   step p l p'
   P p l p'l (l_in a)l (l_out a)P (a#n:p) l (a#n:p'))
  (H7: (a : name) (n : nat) (p p' : proc),
   step p (l_in a) p'P p (l_in a) p'P (p_res a (S n) p) l_tau (a#n:p'))
  (H8: (a : name) (n : nat) (p p' : proc),
   step p (l_out a) p'P p (l_out a) p'P (a#n:p) l_tau (p_res a (S n) p'))
  (H9: (p : proc) (a : label) (p' : proc) pp',
   step p a p'P p a p'rep_step_result p' p pp'P (p!!) a pp')
  (H10: P (O || O) l_tau O)
  (H11: P (O + O) l_tau O)
  (H12: p : proc, P (O;; p) l_tau p)
  (H13: (a : name) (n : nat), P (a#n:Ø) l_tau Ø):
  ( p a p' (H: step p a p'), P p a p').
  Proof.
    intros.
    apply step___ind; intros; eauto; try solve [apply step_step__ in H14; eauto].
    apply step_step__; auto.
  Qed.

  Lemma sbisim_par_rep: p,
    sbisimilar (p || p!!) (p!!).
  Proof.
    intros.
     (fun pp qq(pp=p||p!! qq=p!!) (pp=qq)).
    split; auto.
    splits; intros pp qq [[??]|?]; intros; subst.
    × false.
    × reflexivity.
    × false.
    × reflexivity.
    × split; intros; p'; split; auto.
    - apply s_rep; auto.
    - inverts H; constructor; apply s_rep; auto.
    × split; intros; p'; split; auto.
    × unfold inv; split; intros; p'; split; auto.
    - inverts H; auto.
    - inverts H; inverts H0; constructor; auto.
    × unfold inv; split; intros; p'; split; auto.
  Qed.

  Lemma wbisim_par_rep: p,
    wbisimilar (p || p!!) (p!!).
  Proof. intros; apply sbisim_wbisim; apply sbisim_par_rep. Qed.

  Lemma wbisim_sum_nilL: p,
    wbisimilar (O+p) p.
  Proof.
    intros.
     (fun p qp=O+q q=p).
    split; auto.
    clear p.
    splits; intros p q [?|?]; intros; unfold inv in *; subst.
    × false.
    × apply step_nil.
    × simpl in H0. red in H0. subst. apply single_tstep. apply s_sum_nil.
    × apply step_nil.
    × split; intros.
    + inverts H. inverts H4.
     p'; split; auto. apply single_tstep; auto.
     O; split; auto. apply step_nil.
    + inverts H; inverts H0.
    - inverts H4.
    - p'; split; auto. apply single_lstep; auto. constructor; auto.
    - inverts H4.
    - p'; split; auto. apply single_lstep; constructor; auto.
    × split; intros.
    + p'; split; auto. apply single_tstep; auto.
    + p'; split; auto. apply single_lstep; auto.
    × split; intros.
    + p'; split; auto. apply single_tstep; apply s_sumR; auto.
    + p'; split; auto. apply single_lstep. inverts H; constructor; apply s_sumR; auto.
    × split; intros.
    + p'; split; auto. apply single_tstep; auto.
    + p'; split; auto. apply single_lstep; auto.
  Qed.
  Lemma wbisim_sum_nilR: p,
    wbisimilar (p+O) p.
  Proof. intros; rewrite wbisim_sum_comm; apply wbisim_sum_nilL. Qed.

  Lemma wbisim_res_tau_sum: a n p q,
    wbisimilar (p_res a n (().p+().q)) ((().p_res a n p)+(().p_res a n q)).
  Proof.
    intros.
    apply wbisim_step; intros.
    × false.
    × false.
    × inverts H.
    + inverts H3; inverts H4.
     (a#n:p'0); split. apply single_tstep. apply s_sumL; apply s_act; apply s_res; auto. apply wbisim_refl.
     (a#n:p'0); split. apply single_tstep. apply s_sumR; apply s_act; apply s_res; auto. apply wbisim_refl.
    + inverts H4; inverts H3.
    + inverts H4; inverts H3.
    × inverts H.
    inverts H4.
     (a#n:p); split. apply single_tstep. apply s_res; auto. apply s_sumL; apply s_act. intro; false. intro; false. apply wbisim_refl.
    inverts H4.
     (a#n:q); split. apply single_tstep. apply s_res; auto. apply s_sumR; apply s_act. intro; false. intro; false. apply wbisim_refl.
    × inverts H; inverts H0; inverts H3; inverts H4.
    × inverts H; inverts H0; inverts H4; inverts H2.
  Qed.

  Lemma wbisim_res_in: a n p,
    wbisimilar (p_res a (S n) (a?.p)) (p_res a n p).
  Proof.
    intros.
    apply wbisim_step; intros.
    × false.
    × false.
    × inverts H.
    + inverts H3; inverts H4.
    + inverts H4.
    eexists; split. apply step_nil. apply wbisim_refl.
    + inverts H4.
    × inverts H.
     (a#n:p'); split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_tstep. apply s_res; auto. apply wbisim_refl.
     (a#n0:p'); split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_tstep. apply s_res_count_in; auto. apply wbisim_refl.
     (a#(S n):p'); split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_tstep. apply s_res_count_out; auto. apply wbisim_refl.
     O; split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_tstep. apply s_res_nil. apply wbisim_refl.
    × inverts H; inverts H0; inverts H3. false.
    × inverts H; inverts H0.
     (a#n:p'); split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_lstep; constructor; apply s_res; auto. apply wbisim_refl.
     (a#n:p'); split. eapply step_tcons. apply s_res_count_in. apply s_act. apply single_lstep; constructor; apply s_res; auto. apply wbisim_refl.
  Qed.

  Lemma wbisim_res_out: a n p,
    wbisimilar (p_res a n (a!.p)) (p_res a (S n) p).
  Proof.
    intros.
    apply wbisim_step; intros.
    × false.
    × false.
    × inverts H.
    + inverts H3; inverts H4.
    + inverts H4; inverts H3.
    + inverts H4.
    eexists; split. apply step_nil. apply wbisim_refl.
    × inverts H.
     (a#(S n):p'); split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_tstep. apply s_res; auto. apply wbisim_refl.
     (a#n:p'); split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_tstep. apply s_res_count_in; auto. apply wbisim_refl.
     (a#(S (S n)):p'); split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_tstep. apply s_res_count_out; auto. apply wbisim_refl.
     O; split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_tstep. apply s_res_nil. apply wbisim_refl.
    × inverts H; inverts H0; inverts H3. false.
    × inverts H; inverts H0.
     (a#(S n):p'); split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_lstep; constructor; apply s_res; auto. apply wbisim_refl.
     (a#(S n):p'); split. eapply step_tcons. apply s_res_count_out. apply s_act. apply single_lstep; constructor; apply s_res; auto. apply wbisim_refl.
  Qed.

  Lemma wbisim_sum_dup: p,
    wbisimilar (p+p) p.
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false].
    × simpl in H; red in H; subst p. apply single_tstep. apply s_sum_nil.
    × p'; split; [ | apply wbisim_refl].
    inverts H. apply single_tstep; auto. apply single_tstep; auto. apply step_nil.
    × q'; split; [ | apply wbisim_refl].
    apply single_tstep; apply s_sumL; auto.
    × p'; split; [ | apply wbisim_refl].
    inverts H; inverts H0; apply single_lstep; constructor; auto.
    × q'; split; [ | apply wbisim_refl].
    apply single_lstep; inverts H; constructor; apply s_sumL; auto.
  Qed.

End Algebraic.

Section ContrasimulationAlgebraic.
  Import Notation.
  Import Notation.U.

  Lemma csim_par_swap: p q,
    contrasimilar (p||q) (q||p).
  Proof. intros; apply wbisim_csim; apply wbisim_par_swap. Qed.

  Lemma csim_par_nil1: p,
    contrasimilar (p||O) p.
  Proof. intros; apply wbisim_csim; apply wbisim_par_nil1. Qed.

  Lemma csim_par_nil2: p,
    contrasimilar (O||p) p.
  Proof. intros; apply wbisim_csim; apply wbisim_par_nil2. Qed.

  Lemma csim_seq_assoc: p q r,
    contrasimilar (p;;(q;;r)) ((p;;q);;r).
  Proof. intros; apply wbisim_csim; apply wbisim_seq_assoc. Qed.

  Lemma csim_par_assoc: p q r,
    contrasimilar (p||(q||r)) ((p||q)||r).
  Proof. intros; apply wbisim_csim; apply wbisim_par_assoc. Qed.

  Lemma csim_sum_comm: p q,
    contrasimilar (p+q) (q+p).
  Proof. intros; apply wbisim_csim; apply wbisim_sum_comm; auto. Qed.

  Lemma csim_intro_tau: p,
    contrasimilar (().p) p.
  Proof. intros; apply wbisim_csim; apply wbisim_intro_tau; auto. Qed.

End ContrasimulationAlgebraic.

Section Congruence.
  Import Notation.
  Import Notation.U.
  Open Scope proc.

  Hint Unfold tstep.

  Lemma wbisim_seq1: p q r,
    wbisimilar p q
    wbisimilar (p;; r) (q;; r).
  Proof.
    intros.
     (fun pp qq( p q, pp=p;;rqq=q;;r wbisimilar p q) qq=pp).
    split; [| left; eauto].
    clear p q H.
    apply weak_bisimulation_symm; (intros pp qq [ [p[q[?[??]]]] | ?]; subst; [| rename pp into p]); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    edestruct tstep_wbisim1 as [q'[??]]; eauto. apply H5.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
     p'; split; auto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    apply step_star_seq_app. apply halted_wbisim1; eauto. reflexivity.
    apply single_tstep; apply s_seq_nil.
    - inverts H; inverts H0.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
    × split; intros.
    - p'; split; eauto. apply single_tstep; auto.
    - p'; split; eauto. apply single_lstep; auto.
    × left; q p; splits; auto. symmetry; auto.
    × auto.
  Qed.

  Lemma wbisim_seq2: p q r,
    wbisimilar p q
    wbisimilar (r;; p) (r;; q).
  Proof.
    intros.
     (fun pp qq( p q r, pp=r;;pqq=r;;q wbisimilar p q) wbisimilar pp qq).
    split; [| left; jauto].
    clear p q r H.
    apply weak_bisimulation_symm; (intros pp qq [ [p[q[r[?[??]]]]] | ?]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_wbisim1; auto.
    × split; intros.
    - inverts H.
     (p'0;;q); split. apply single_tstep; apply s_seq; auto. left; jauto.
     q; split; auto. apply single_tstep; apply s_seq_nil.
    - inverts H; inverts H0.
     (p'0;;q); split. apply single_lstep; constructor; apply s_seq; auto. left; jauto.
     (p'0;;q); split. apply single_lstep; constructor; apply s_seq; auto. left; jauto.
    × split; intros.
    - edestruct tstep_wbisim1 as [q'[??]]; eauto.
    - edestruct lstep_wbisim1 as [q'[??]]; eauto.
    × left; q p r; splits; auto. symmetry; auto.
    × right; symmetry; auto.
  Qed.

  Lemma wbisim_seq: p1 p2 q1 q2,
    wbisimilar p1 q1wbisimilar p2 q2
    wbisimilar (p1;;p2) (q1;;q2).
  Proof. intros; etransitivity; [eapply wbisim_seq1; eauto | apply wbisim_seq2; auto]. Qed.

  Lemma sbisim_parL: p q r,
    sbisimilar p q
    sbisimilar (p||r) (q||r).
  Proof.
    intros.
     (fun pr qr( p q r, pr=p||r qr=q||r sbisimilar p q) (pr=O qr=O)).
    split; [| left; jauto].
    clear p q r H.
    apply strong_bisimulation_symm; (intros pr qr [ [p[q[r[?[??]]]]] | [??]]; subst); intros; unfold inv in ×.
    × false.
    × auto.
    × split; intros.
    - inverts H.
    + renames p'0 to p'.
    edestruct tstep_sbisim1 as [q'[??]]; eauto. apply H5.
     (q'||r); split. apply s_parL; auto. left; jauto.
    + renames q' to r'.
     (q||r'); split. apply s_parR; auto. left; jauto.
    + p_nil; split; auto.
    apply halted_sbisim1 in H1. subst.
    apply s_par_nil. reflexivity.
    - inverts H; inverts H0.
    + edestruct lstep_sbisim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (q'||r); split. inverts H; constructor; apply s_parL; auto. left; jauto.
    + renames q' to r'. (q||r'); split. constructor. apply s_parR; auto. left; jauto.
    + edestruct lstep_sbisim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (q'||r); split. inverts H; constructor; apply s_parL; auto. left; jauto.
    + renames q' to r'. (q||r'); split. constructor. apply s_parR; auto. left; jauto.
    × split; intros; inverts H; inverts H0.
    × left; q p r; splits; auto. apply sbisim_symm; auto.
    × auto.
  Qed.

  Lemma sbisim_parR: p q r,
    sbisimilar p q
    sbisimilar (r||p) (r||q).
  Proof.
    intros.
    eapply sbisim_trans.
    apply sbisim_par_swap.
    eapply sbisim_trans.
    apply sbisim_parL; eauto.
    apply sbisim_par_swap.
  Qed.

  Lemma sbisim_par: p q r s,
    sbisimilar p q
    sbisimilar r s
    sbisimilar (p||r) (q||s).
  Proof. intros; eapply sbisim_trans. apply sbisim_parL; eauto. eapply sbisim_parR; eauto. Qed.

  Lemma wbisim_parL: p q r,
    wbisimilar p q
    wbisimilar (p||r) (q||r).
  Proof.
    intros.
     (fun pr qr( p q r, pr=p||r qr=q||r wbisimilar p q) (pr=O qr=O)).
    split; [| left; jauto].
    clear p q r H.
    apply weak_bisimulation_symm; (intros pr qr [ [p[q[r[?[??]]]]] | [??]]; subst); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    + renames p'0 to p'.
    edestruct tstep_wbisim1 as [q'[??]]; eauto. apply H5.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'.
     (q||r'); split. apply single_tstep; apply s_parR; auto. left; jauto.
    + p_nil; split; auto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app. apply step_star_parL_step. apply halted_wbisim1; eauto. reflexivity.
    apply single_tstep. apply s_par_nil.
    - inverts H; inverts H0.
    + edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'. (q||r'); split. apply single_lstep; constructor. apply s_parR; auto. left; jauto.
    + edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'. (q||r'); split. apply single_lstep; constructor. apply s_parR; auto. left; jauto.
    × split; intros; inverts H; inverts H0.
    × left; q p r; splits; auto. symmetry; auto.
    × auto.
  Qed.

  Lemma wbisim_parR: p q r,
    wbisimilar p q
    wbisimilar (r||p) (r||q).
  Proof.
    intros.
    eapply wbisim_trans.
    apply wbisim_par_swap.
    eapply wbisim_trans.
    apply wbisim_parL; eauto.
    apply wbisim_par_swap.
  Qed.

  Lemma wbisim_par: p q r s,
    wbisimilar p q
    wbisimilar r s
    wbisimilar (p||r) (q||s).
  Proof. intros; eapply wbisim_trans. apply wbisim_parL; eauto. eapply wbisim_parR; eauto. Qed.

  Lemma wbisim_res: a n p q,
    wbisimilar p qwbisimilar (a#n:p) (a#n:q).
  Proof.
    intros.
     (fun pp qq( n p q, pp=a#n:p qq=a#n:q wbisimilar p q) (pp=Oqq=O)).
    split; [| left; jauto].
    clear n p q H.
    apply weak_bisimulation_symm; (intros pp qq [ [n[p[q[?[??]]]]] | [??] ]; subst); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    + rename p'0 into p'.
    edestruct tstep_wbisim1 as [q'[??]]; eauto. apply H4.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto.
    + renames p'0 to p', n0 to n.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_count_in; auto.
    + renames p'0 to p'.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (a#(S n):q'); split; [| left; jauto].
    apply step_star_res_count_out; auto.
    + O; split; auto.
    eapply step_tsnoc. apply step_star_res_nIn. apply halted_wbisim1; eauto. reflexivity.
    intros ? [ ].
    apply s_res_nil.
    - inverts H; inverts H0.
    + rename p'0 into p'.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto. intros. inverts H2. auto. inverts H3.
    + rename p'0 into p'.
    edestruct lstep_wbisim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto. intros. inverts H2. auto. inverts H3.
    × split; intros; inverts H; inverts H0.
    × left; n q p; splits; auto. symmetry; auto.
    × auto.
  Qed.

  Lemma wbisim_res_list: AN p q,
    wbisimilar p qwbisimilar (p_res_list AN p) (p_res_list AN q).
  Proof.
    induction AN; intros; auto. destruct a; simpl.
    apply wbisim_res; eauto.
  Qed.

  Lemma wbisim_stable_sum: p q r,
    wbisimilar p q
    stable lts pstable lts q
    wbisimilar (p+r) (q+r).
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false].
    × inverts H2. false; eapply H0. apply H7.
     p'; split. apply single_tstep; apply s_sumR; auto. apply wbisim_refl.
     (q+O); split. apply step_nil. rewrite wbisim_sum_nilR. auto.
    × inverts H2. false; eapply H1. apply H7.
     q'; split. apply single_tstep; apply s_sumR; auto. apply wbisim_refl.
     (p+O); split. apply step_nil. rewrite wbisim_sum_nilR. auto.
    × assert (lstep p a p' lstep r a p').
      inverts H2; (inverts H3; [left|right]; constructor; auto).
    clear H2; destruct H3.
    + edestruct lstep_wbisim1 as [q'[??]]; eauto.
     q'; split; auto.
    inverts H3.
    eapply step_lcons; eauto. inverts H8; constructor; apply s_sumL; eauto.
    eapply step_tcons; eauto. apply s_sumL; auto.
    + p'; split. apply single_lstep; inverts H2; constructor; apply s_sumR; auto. apply wbisim_refl.
    × assert (lstep q a q' lstep r a q').
      inverts H2; (inverts H3; [left|right]; constructor; auto).
    clear H2; destruct H3.
    + edestruct lstep_wbisim2 as [p'[??]]; eauto.
     p'; split; auto.
    inverts H3.
    eapply step_lcons; eauto. inverts H8; constructor; apply s_sumL; eauto.
    eapply step_tcons; eauto. apply s_sumL; auto.
    + q'; split. apply single_lstep; inverts H2; constructor; apply s_sumR; auto. apply wbisim_refl.
  Qed.

  Lemma wbisim_internal_sum: p q r,
    wbisimilar p q
    wbisimilar (().p+r) (().q+r).
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false].
    × inverts H0. inverts H5.
    eexists; split; eauto. apply single_tstep; apply s_sumL; constructor.
     p'; split. apply single_tstep; apply s_sumR; auto. apply wbisim_refl.
    × inverts H0. inverts H5.
    eexists; split; eauto. apply single_tstep; apply s_sumL; constructor.
     q'; split. apply single_tstep; apply s_sumR; auto. apply wbisim_refl.
    × inverts H0; inverts H1.
    inverts H5.
     p'; split. apply single_lstep; constructor; apply s_sumR; auto. apply wbisim_refl.
    inverts H5.
     p'; split. apply single_lstep; constructor; apply s_sumR; auto. apply wbisim_refl.
    × inverts H0; inverts H1.
    inverts H5.
     q'; split. apply single_lstep; constructor; apply s_sumR; auto. apply wbisim_refl.
    inverts H5.
     q'; split. apply single_lstep; constructor; apply s_sumR; auto. apply wbisim_refl.
  Qed.

  Lemma wbisim_act: l p q,
    wbisimilar p qwbisimilar (p_act l p) (p_act l q).
  Proof.
    intros.
    apply wbisim_step; intros; try solve [false].
    × inverts H0. q; split; auto. apply single_tstep; constructor.
    × inverts H0. p; split; auto. apply single_tstep; constructor.
    × q; split. apply single_lstep; inverts H0; inverts H1; repeat constructor.
    inverts H0; inverts H1; auto.
    × p; split. apply single_lstep; inverts H0; inverts H1; repeat constructor.
    inverts H0; inverts H1; auto.
  Qed.

  Lemma interleaving_nil: lp la1 la2,
    [] = interleaving lp la1 la2la1=[] la2=(@nil lts_L).
  Proof.
    intros.
    destruct lp; simpl in H; [| destruct s; destruct u];
      destruct la1; destruct la2; simpl in H; try discriminate; auto.
  Qed.

  Lemma In_combine_swap: (A B:Type) (x:A) (y:B) l1 l2,
    In (x,y) (combine l1 l2) → In (y,x) (combine l2 l1).
  Proof.
    induction l1; destruct l2; simpl; intros; auto.
    destruct H; eauto.
    inverts H; eauto.
  Qed.

  Lemma Forall2_symm: A (R: AAProp) lp lq,
    ( a b, R a bR b a) →
    Forall2 R lp lqForall2 R lq lp.
  Proof. intros; induction H0; constructor; eauto. Qed.

  Lemma Forall2_list_rep: (A B: Type) (R: ABProp) x y n,
    R x yForall2 R (list_rep n x) (list_rep n y).
  Proof. intros; induction n; simpl; constructor; auto. Qed.

  Definition par_list lp p := fold_right (fun q rq||r) p lp.

  Fixpoint list_rep {A:Type} n (p:A) : list A := match n with
  | 0 ⇒ nil
  | S np::list_rep n p
  end.

  Definition esim_list := Forall2 eventually_similar.
  Definition wbisim_list := Forall2 wbisimilar.

  Lemma wbisim_list_cons: p lp q lq,
    wbisimilar p qwbisim_list lp lqwbisim_list (p::lp) (q::lq).
  Proof. intros; eapply Forall2_cons; eauto. Qed.

  Lemma wbisim_list_app: lp1 lp2 lq1 lq2,
    wbisim_list lp1 lq1wbisim_list lp2 lq2wbisim_list (lp1++lp2) (lq1++lq2).
  Proof. intros; eapply Forall2_app; eauto. Qed.

  Lemma wbisim_list_nil: wbisim_list [] [].
  Proof. intros; apply Forall2_nil. Qed.

  Lemma wbisim_list_single: p q,
    wbisim_list [p] [q] wbisimilar p q.
  Proof. split; intros. inverts H; auto. constructor; auto. Qed.

  Lemma wbisim_list_same_split: lp1 lp2 lq,
    wbisim_list (lp1++lp2) lq lq1 lq2, lq=lq1++lq2 wbisim_list lp1 lq1 wbisim_list lp2 lq2.
  Proof. intros; edestruct Forall2_app_inv_l as [lq1[lq2[?[??]]]]; eauto. Qed.

  Lemma wbisim_list_symm: lp lq,
    wbisim_list lp lqwbisim_list lq lp.
  Proof. intros; apply Forall2_symm; auto; apply wbisim_symm. Qed.

  Lemma esim_list_cons: p lp q lq,
    eventually_similar p qesim_list lp lqesim_list (p::lp) (q::lq).
  Proof. intros; apply Forall2_cons; eauto. Qed.

  Lemma esim_list_app: lp1 lp2 lq1 lq2,
    esim_list lp1 lq1esim_list lp2 lq2esim_list (lp1++lp2) (lq1++lq2).
  Proof. intros; apply Forall2_app; eauto. Qed.

  Lemma esim_list_nil: esim_list [] [].
  Proof. apply Forall2_nil. Qed.

  Lemma esim_list_single: p q,
    esim_list [p] [q] eventually_similar p q.
  Proof. split; intros. inverts H; auto. apply Forall2_cons; eauto. Qed.

  Lemma esim_list_same_split: lp1 lp2 lq,
    esim_list (lp1++lp2) lq lq1 lq2, lq=lq1++lq2 esim_list lp1 lq1 esim_list lp2 lq2.
  Proof. intros; edestruct Forall2_app_inv_l as [lq1[lq2[?[??]]]]; eauto. Qed.

  Lemma rep_step_result_list_rep: p' p pp',
    rep_step_result p' p pp'
     n,
    pp' = par_list (list_rep n p++[p']) (p!!).
  Proof.
    intros.
    induction H.
    × 0; reflexivity.
    × destruct IHrep_step_result as [n ?].
     (S n); simpl; congruence.
  Qed.

  Lemma list_rep_snoc: (A:Type) (p:A) n,
    list_rep (S n) p = list_rep n p ++ [p].
  Proof. induction n; simpl in *; intros; congruence. Qed.

  Lemma list_rep_mid: (A:Type) (p:A) n1 n2,
    list_rep (S (n1+n2)) p = list_rep n1 p ++ [p] ++ list_rep n2 p.
  Proof. induction n1; simpl in *; intros; congruence. Qed.

  Lemma par_list_not_halted1: lp p, ¬is_halted p¬is_halted (par_list lp p).
  Proof. destruct lp; simpl; intros; intro; false. Qed.

  Lemma par_list_app: lp1 lp2 p,
    par_list (lp1++lp2) p = par_list lp1 (par_list lp2 p).
  Proof. apply fold_right_app. Qed.

  Lemma step_par_list_inv: lp p l pp',
    step (par_list lp p) l pp'
    ¬is_halted p
    ( p', step p l p' pp' = par_list lp p')
    ( lp1 q lp2 q',
     lp = lp1 ++ [q] ++ lp2
     step q l q' pp' = par_list (lp1 ++ [q'] ++ lp2) p).
  Proof.
    induction lp; simpl; intros; eauto.
    inverts H.
    × right; (@nil proc) a lp p'; splits; auto.
    × renames q' to pp''.
    edestruct IHlp as [ [p'[??]] | [lp1[q[lp2[q'[?[??]]]]]] ]; eauto.
    - left; p'; split; auto. congruence.
    - subst; right; (a::lp1) q lp2 q'; splits; auto.
    × destruct lp; simpl in H3. subst; false. inverts H3.
  Qed.

  Lemma step_rep_inv': p l pp',
    step (p!!) l pp'
     n p',
      step p l p' pp' = par_list (list_rep n p++[p']) (p!!).
  Proof.
    intros.
    edestruct step_rep_inv; eauto.
    edestruct rep_step_result_list_rep as [n ?]; eauto.
  Qed.

  Lemma par_list_rep_nterm: lp p la pp',
    step_star (par_list lp (p!!)) la pp'¬is_halted pp'.
  Proof.
    intros.
    remember (par_list lp (p!!)) as pp.
    revert lp Heqpp.
    induction H; intros; subst.
    × destruct lp; simpl; intro; false.
    × renames p' to pp'.
    edestruct step_par_list_inv as [ [pp''[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    intro; false.
    subst pp'.
    edestruct step_rep_inv' as [n[p'[??]]]; eauto.
    apply (IHstep_star (lp++list_rep n p++[p'])).
    subst pp''.
    repeat rewrite par_list_app. reflexivity.
    × renames p' to pp'.
    edestruct step_par_list_inv as [ [pp''[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    apply H.
    intro; false.
    subst pp'.
    edestruct step_rep_inv' as [n[p'[??]]]; eauto.
    apply (IHstep_star (lp++list_rep n p++[p'])).
    subst pp''.
    repeat rewrite par_list_app. reflexivity.
  Qed.

  Lemma par_nterm: p la pp',
    step_star (p!!) la pp'¬is_halted pp'.
  Proof. intros; eapply (par_list_rep_nterm nil); eauto. Qed.

  Lemma list_rep_length: (A:Type) (p:A) n,
    length (list_rep n p) = n.
  Proof. induction n; simpl; intros; auto. Qed.

  Lemma step_par_list1_app_l: lp1 lp2 p l lp1',
    ( q, step (par_list lp1 q) l (par_list lp1' q)) →
    step (par_list (lp1++lp2) p) l (par_list (lp1'++lp2) p).
  Proof. intros; repeat rewrite par_list_app; eauto. Qed.

  Lemma step_par_list1_app_r: lp1 lp2 p l lp2',
    step (par_list lp2 p) l (par_list lp2' p) →
    step (par_list (lp1++lp2) p) l (par_list (lp1++lp2') p).
  Proof. induction lp1; simpl; intros; auto; apply s_parR; auto. Qed.

  Lemma step_par_list1_cons: r lp p l r',
    step r l r'
    step (par_list (r::lp) p) l (par_list (r'::lp) p).
  Proof. simpl; intros; apply s_parL; auto. Qed.

  Lemma step_par_list1: lp1 r lp2 p l r',
    step r l r'
    step (par_list (lp1++[r]++lp2) p) l (par_list (lp1++[r']++lp2) p).
  Proof.
    induction lp1; simpl; intros.
    apply s_parL; auto.
    apply s_parR.
    change (r::lp2) with ([r]++lp2).
    change (r'::lp2) with ([r']++lp2).
    auto.
  Qed.

  Lemma step_par_list2: lp p l p',
    step p l p'
    step (par_list lp p) l (par_list lp p').
  Proof.
    induction lp; simpl; intros; eauto.
    apply s_parR; auto.
  Qed.

  Lemma step_star_par_list1: lp1 r lp2 p la r',
    step_star r la r'
    step_star (par_list (lp1++[r]++lp2) p) la (par_list (lp1++[r']++lp2) p).
  Proof.
    intros.
    induction H.
    × apply step_nil.
    × eapply step_lcons; eauto. inverts H; constructor; apply step_par_list1; eauto.
    × eapply step_tcons; eauto. apply step_par_list1; eauto.
  Qed.

  Lemma step_star_par_list2: lp p l p',
    step_star p l p'
    step_star (par_list lp p) l (par_list lp p').
  Proof.
    intros.
    induction H.
    × apply step_nil.
    × eapply step_lcons; eauto. inverts H; constructor; apply step_par_list2; eauto.
    × eapply step_tcons; eauto. apply step_par_list2; eauto.
  Qed.

  Lemma sbisim_par_list: lp p q,
    sbisimilar p qsbisimilar (par_list lp p) (par_list lp q).
  Proof.
    intros.
    induction lp; simpl; auto.
    apply sbisim_parR; auto.
  Qed.

  Lemma step_star_par_list_unzip: lp p la pp',
    step_star (par_list lp p) la pp'
    ( la p', step_star p la p'¬is_halted p') →
     lip la1 la2 lp1' p',
      ( q, step_star (par_list lp q) la1 (par_list lp1' q))
      step_star p la2 p'
      pp' = par_list lp1' p'
      la = interleaving lip la1 la2.
  Proof.
    intros.
    remember (par_list lp p) as pp.
    revert lp p H0 Heqpp.
    induction H; intros; subst.
    × renames p0 to p.
     (@nil (unit+unit)) (@nil label) (@nil label) lp p; splits; intros; auto; apply @step_nil.
    × renames p0 to p, p' to pp', p'' to pp''.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[q[lp2[q'[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    eapply H1; apply step_nil.
    - subst.
    edestruct (IHstep_star lp p') as [lip[la1[la2[lp1'[p''[?[?[??]]]]]]]]; eauto; clear IHstep_star.
    intros; eapply H1; inverts H.
    eapply step_lcons with (a:=l_in a0); eauto; constructor; apply H2.
    eapply step_lcons with (a:=l_out a0); eauto; constructor; apply H2.
    subst pp'' la.
     (inr tt::lip) la1 (a::la2) lp1' p''; splits; auto.
    eapply step_lcons; eauto. inverts H; constructor; eauto.
    - subst lp pp'.
    edestruct (IHstep_star (lp1++[q']++lp2) p) as [lip[la1[la2[lp1'[p''[?[?[??]]]]]]]]; eauto; clear IHstep_star.
    subst pp'' la.
     (inl tt::lip) (a::la1) la2 lp1' p''; splits; auto.
    intros; eapply step_lcons; eauto.
    inverts H; constructor; apply step_par_list1; auto.
    × renames p0 to p, p' to pp', p'' to pp''.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[q[lp2[q'[?[??]]]]]] ]; eauto.
    apply H.
    eapply H1; apply step_nil.
    - subst pp'.
    edestruct (IHstep_star lp p') as [lip[la1[la2[lp1'[p''[?[?[??]]]]]]]]; eauto; clear IHstep_star.
    intros; eapply H1. eapply step_tcons; eauto. apply H2.
    subst pp'' la.
     lip la1 la2 lp1' p''; splits; auto.
    eapply step_tcons; eauto. apply H2.
    - subst lp pp'.
    edestruct (IHstep_star (lp1++[q']++lp2) p) as [lip[la1[la2[lp1'[p''[?[?[??]]]]]]]]; eauto; clear IHstep_star.
    subst pp'' la.
     lip la1 la2 lp1' p''; splits; auto.
    intros; eapply step_tcons; eauto.
    apply step_par_list1; auto.
  Qed.

  Lemma sbisim_nterm_par_assoc: p q r,
    ( la q', step_star q la q'¬is_halted q') →
    sbisimilar (p || (q || r)) ((p||q)||r).
  Proof.
    intros.
     (fun pp qq p q r, pp=p||(q||r) qq=(p||q)||r ( la q', step_star q la q'¬is_halted q')).
    split; [| p q r; splits; intros; auto; eapply H; eauto].
    clear p q r H.
    splits; intros pp qq [p[q[r[?[??]]]]]; unfold inv; intros; subst pp qq.
    × false.
    × false.
    × split; intros.
    - inverts H; [| inverts H5].
    + renames p'0 to p'. (p' || q || r); split.
    apply s_parL; apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + renames p' to q'. (p || q' || r); split.
    apply s_parL; apply s_parR; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_tcons; eauto. apply H4.
    + renames q'0 to r'. (p || q || r'); split.
    apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
    + false; eapply H1. apply step_nil. reflexivity.
    - inverts H; (inverts H0; [| inverts H5]).
    + renames p'0 to p'. (p' || q || r); split.
    constructor; apply s_parL; apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + renames p' to q'. (p || q' || r); split.
    constructor; apply s_parL; apply s_parR; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_lcons; eauto. constructor; eauto.
    + renames q'0 to r'. (p || q || r'); split.
    constructor; apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
    + renames p'0 to p'. (p' || q || r); split.
    constructor; apply s_parL; apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + renames p' to q'. (p || q' || r); split.
    constructor; apply s_parL; apply s_parR; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_lcons; eauto. constructor 2; eauto.
    + renames q'0 to r'. (p || q || r'); split.
    constructor; apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
    × split; intros.
    - inverts H; [inverts H5 | ].
    + (p' || (q || r)); split.
    apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + (p || (q' || r)); split.
    apply s_parR; apply s_parL; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_tcons; eauto. apply H4.
    + false; eapply H1. apply step_nil. reflexivity.
    + renames q' to r'. (p || (q || r')); split.
    apply s_parR; apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
    - inverts H; (inverts H0; [inverts H5 | ]).
    + (p' || (q || r)); split.
    constructor; apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + (p || (q' || r)); split.
    constructor; apply s_parR; apply s_parL; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_lcons; eauto. constructor; eauto.
    + renames q' to r'. (p || (q || r')); split.
    constructor; apply s_parR; apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
    + (p' || (q || r)); split.
    constructor; apply s_parL; auto.
     p' q r; splits; intros; auto.
    eapply H1; eauto.
    + (p || (q' || r)); split.
    constructor; apply s_parR; apply s_parL; auto.
     p q' r; splits; intros; auto.
    eapply H1. eapply step_lcons; eauto. constructor 2; eauto.
    + renames q' to r'. (p || (q || r')); split.
    constructor; apply s_parR; apply s_parR; auto.
     p q r'; splits; intros; auto.
    eapply H1; eauto.
  Qed.

  Lemma sbisim_par_list_rep: lp p,
    sbisimilar (par_list (p::lp) (p!!)) (par_list lp (p!!)).
  Proof.
    intros lp p; induction lp; simpl in *; intros.
    apply sbisim_par_rep.
    renames a to q.
    eapply sbisim_trans.
    apply sbisim_parR. apply sbisim_par_swap.
    eapply sbisim_trans.
    apply sbisim_nterm_par_assoc.
    apply par_list_rep_nterm.
    eapply sbisim_trans.
    apply sbisim_parL; eauto.
    apply sbisim_par_swap.
  Qed.

  Lemma sbisim_list_rep_par_list: p n,
      sbisimilar (p!!) (par_list (list_rep n p) (p!!)).
  Proof.
    induction n; intros.
    apply sbisim_refl.
    eapply sbisim_trans; eauto.
    apply sbisim_symm.
    apply sbisim_par_list_rep.
  Qed.

  Lemma sbisim_list_rep_par_list': p lp n,
      sbisimilar (par_list lp (p!!)) (par_list (list_rep n p++lp) (p!!)).
  Proof.
    induction n; intros.
    apply sbisim_refl.
    eapply sbisim_trans; eauto.
    apply sbisim_symm.
    apply sbisim_par_list_rep.
  Qed.

  Lemma step_star_rep_par_list: lp p la pp',
    step_star (par_list lp (p!!)) la pp'
     n lp',
      ( q, step_star (par_list (lp++list_rep n p) q) la (par_list lp' q))
      pp' = (par_list lp' (p!!))
      length lp' = length (lp++list_rep n p).
  Proof.
    intros.
    remember (par_list lp (p!!)) as pp.
    revert lp Heqpp.
    induction H; intros; subst.
    × 0 lp; simpl; splits; intros.
    rewrite app_nil_r. apply @step_nil. reflexivity. rewrite app_length. auto.
    × renames p' to pp', p'' to pp''.
    edestruct step_par_list_inv as [ [ppp'[??]] | [lp1'[q[lp2'[q'[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    eapply par_nterm. apply step_nil.
    - subst pp'.
    edestruct step_rep_inv; eauto.
    edestruct rep_step_result_list_rep as [n ?]; eauto.
    remember (get_step_rep_p' p ppp') as p'.
    clear Heqp'.
    subst ppp'.
    edestruct (IHstep_star (lp++list_rep n p++[p'])) as [n'[lp'[?[??]]]]; eauto; clear IHstep_star.
    repeat rewrite par_list_app. reflexivity.
    subst pp''.
     (S (n+n')) lp'; splits; auto.
    intros.
    eapply step_lcons; eauto.
    repeat rewrite<- app_assoc.
    rewrite (list_rep_mid _ p n n').
    inverts H; constructor; do 2 apply step_par_list1_app_r; apply step_par_list1_cons; auto.
    rewrite H6.
    repeat rewrite app_length; repeat rewrite list_rep_length. simpl. omega.
    - subst lp pp'.
    edestruct IHstep_star as [n[lp'[?[??]]]]; eauto; clear IHstep_star.
    subst pp''.
     n lp'; splits; intros; auto.
    eapply step_lcons; eauto.
    repeat rewrite<- app_assoc.
    inverts H; constructor; apply step_par_list1; auto.
    rewrite H4. repeat rewrite app_length; reflexivity.
    × renames p' to pp', p'' to pp''.
    edestruct step_par_list_inv as [ [ppp'[??]] | [lp1'[q[lp2'[q'[?[??]]]]]] ]; eauto.
    apply H.
    eapply par_nterm. apply step_nil.
    - subst pp'.
    edestruct step_rep_inv; eauto.
    edestruct rep_step_result_list_rep as [n ?]; eauto.
    remember (get_step_rep_p' p ppp') as p'.
    clear Heqp'.
    subst ppp'.
    edestruct (IHstep_star (lp++list_rep n p++[p'])) as [n'[lp'[?[??]]]]; eauto; clear IHstep_star.
    repeat rewrite par_list_app. reflexivity.
    subst pp''.
     (S (n+n')) lp'; splits; auto.
    intros.
    eapply step_tcons; eauto.
    repeat rewrite<- app_assoc.
    rewrite (list_rep_mid _ p n n').
    do 2 apply step_par_list1_app_r; apply step_par_list1_cons; auto.
    rewrite H6.
    repeat rewrite app_length; repeat rewrite list_rep_length. simpl. omega.
    - subst lp pp'.
    edestruct IHstep_star as [n[lp'[?[??]]]]; eauto; clear IHstep_star.
    subst pp''.
     n lp'; splits; intros; auto.
    eapply step_tcons; eauto.
    repeat rewrite<- app_assoc.
    apply step_par_list1; auto.
    rewrite H4. repeat rewrite app_length; reflexivity.
  Qed.

  Lemma sbisim_rep_step_result: p' p pp',
    rep_step_result p' p pp'sbisimilar pp' (p'||p!!).
  Proof.
    intros.
    edestruct rep_step_result_list_rep as [n ?]; eauto.
    subst.
    eapply sbisim_trans.
    apply sbisim_symm.
    apply sbisim_list_rep_par_list'.
    apply sbisim_refl.
  Qed.

  Lemma step_star_par_unzip'': p1 p2 la p',
    step_star (p1||p2) la p'
     p1' p2' lp la1 la2,
      (p' = p1'||p2' (p1'=O p2'=O p'=O))
      step_star p1 la1 p1' step_star p2 la2 p2'
      la=interleaving lp la1 la2 (length lp = length la1 + length la2)%nat.
  Proof.
    intros.
    remember (p1||p2)%proc as p.
    revert p1 p2 Heqp.
    induction H; intros; subst.
    - p1 p2 (@nil (unit+unit)) (@nil label) (@nil label); splits; auto; apply @step_nil.
    - inverts H; inverts H1.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' (inl tt::lp) (l_in a0::la1) la2; splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. f_equal; auto.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' (inr tt::lp) la1 (l_in a0::la2); splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. rewrite H4; simpl. omega.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' (inl tt::lp) (l_out a0::la1) la2; splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. f_equal; auto.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' (inr tt::lp) la1 (l_out a0::la2); splits; auto.
    eapply @step_lcons; eauto; constructor; eauto. subst; reflexivity.
    simpl. rewrite H4; simpl. omega.
    - inverts H.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' lp la1 la2; splits; auto. eapply @step_tcons; eauto; apply H5.
    + edestruct IHstep_star as [p1'[p2'[lp[la1[la2[?[?[?[??]]]]]]]]]; eauto.
     p1' p2' lp la1 la2; splits; auto. eapply @step_tcons; eauto; apply H5.
    + clear IHstep_star.
    edestruct step_star_nil_resolve as [?]; eauto; subst la p''.
     O O (@nil (unit+unit)) (@nil lts_L) (@nil lts_L); splits; auto.
  Qed.

  Lemma step_par_rep_resolve: lp p l pp',
    step (par_list lp (p!!)) l pp'
    ( p', step (p!!) l p' pp' = par_list lp p')
     ( lp1 q lp2 q', lp = lp1++[q]++lp2 step q l q' pp'=par_list (lp1++[q']++lp2) (p!!)).
  Proof.
    intro lp.
    rewrite<- (rev_involutive lp).
    pattern (rev lp).
    apply rev_list_ind; simpl; intros.
    × edestruct step_rep_inv; eauto.
    × clear lp. renames l to lp. rewrite rev_app_distr in ×.
    simpl in ×.
    rewrite rev_involutive in ×.
    inverts H0.
    right. (@nil lts_S) a lp p'; splits; auto.
    edestruct H as [ [pp'[??]] | [lp1[q[lp2[q''[?[??]]]]]] ]; eauto.
    subst.
    left; pp'; split; auto.
    subst.
    right; (a::lp1) q lp2 q''; splits; auto.
    false. clear -H3.
    destruct lp; simpl in H3; discriminate.
  Qed.

  Lemma tstep_rep_wbisim1_par_list: lp p lq q pp',
    tstep (par_list lp (p!!)) pp'
    wbisimilar p q
    wbisim_list lp lq
     lp' lq' qq',
      step_star (par_list lq (q!!)) nil qq'
      pp' = (par_list lp' (p!!))
      sbisimilar qq' (par_list lq' (q!!))
      wbisim_list lp' lq'.
  Proof.
    intros.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    apply H.
    intro; false.
    × subst.
    edestruct step_rep_inv' as [n[p'_[??]]]; eauto.
    subst p'.
    renames p'_ to p'.
    edestruct tstep_wbisim1 with (p:=p) as [q'[??]]; eauto. apply H3.
    inverts H4.
    - renames q' to q.
     (lp++list_rep n p ++ [p']) (lq++list_rep (S n) q) (par_list lq (q!!)); splits; auto.
    apply step_nil.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list.
    rewrite list_rep_snoc.
    apply wbisim_list_app; auto.
    apply wbisim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply wbisim_list_single; auto.
    - (lp++list_rep n p ++ [p']) (lq++list_rep n q++[q']) (par_list lq (q'||q!!)); splits; auto.
    apply step_star_par_list2.
    eapply step_tcons; eauto. apply s_rep. apply s_parL. apply H6.
    apply step_star_parL_step; auto.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list' with (lp:=[q']).
    apply wbisim_list_app; auto.
    apply wbisim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply wbisim_list_single; auto.
    × subst lp pp'.
    edestruct wbisim_list_same_split as [lq1[slq2[?[??]]]]; eauto; subst lq.
    clear H1.
    edestruct wbisim_list_same_split as [s[lq2[?[??]]]]; eauto; subst slq2.
    clear H5.
    destruct s. inverts H2.
    destruct s; [ | solve [inverts H2; inverts H10] ].
    apply wbisim_list_single in H2.
    edestruct tstep_wbisim1 with (p:=r) as [l'[??]]; eauto. apply H3.
    clear H2.
     (lp1++[r']++lp2) (lq1++[l']++lq2) (par_list (lq1++[l']++lq2) (q!!)); splits; auto.
    apply step_star_par_list1; auto.
    apply sbisim_refl.
    apply wbisim_list_app; auto.
    apply wbisim_list_app; auto.
    apply wbisim_list_single; auto.
  Qed.

  Lemma lstep_rep_wbisim1_par_list: lp p lq q l pp',
    lstep (par_list lp (p!!)) l pp'
    wbisimilar p q
    wbisim_list lp lq
     lp' lq' qq',
      step_star (par_list lq (q!!)) [l] qq'
      pp' = (par_list lp' (p!!))
      sbisimilar qq' (par_list lq' (q!!))
      wbisim_list lp' lq'.
  Proof.
    intros.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    intro; false.
    × subst.
    edestruct step_rep_inv' as [n[p'_[??]]]; eauto.
    subst p'. renames p'_ to p'.
    edestruct lstep_wbisim1 with (a:=l) (p:=p) as [q'[??]]; eauto. inverts H; constructor; eauto.
     (lp++list_rep n p ++ [p']) (lq++list_rep n q++[q']) (par_list lq (q'||q!!)); splits; auto.
    apply step_star_par_list2.
    edestruct step_star_lstep_inv as [q'0[q'1[?[??]]]]; eauto.
    inverts H6.
    eapply @step_lcons; eauto. inverts H7; constructor; apply s_rep; apply s_parL; eauto.
    apply step_star_parL_step; auto.
    eapply step_tcons; eauto. apply s_rep. apply s_parL. apply H9.
    apply step_star_parL_step.
    change [l] with ([]++[l]). eapply step_star_app; eauto. eapply @step_lcons; eauto.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list' with (lp:=[q']).
    apply wbisim_list_app; auto.
    apply wbisim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply wbisim_list_single; auto.
    × subst lp pp'.
    edestruct wbisim_list_same_split as [lq1[slq2[?[??]]]]; eauto; subst lq.
    clear H1.
    edestruct wbisim_list_same_split as [s[lq2[?[??]]]]; eauto; subst slq2.
    clear H5.
    destruct s. inverts H2.
    destruct s; [ | solve [inverts H2; inverts H10] ].
    apply wbisim_list_single in H2.
    edestruct lstep_wbisim1 with (a:=l) (p:=r) as [l'[??]]; eauto.
    inverts H; constructor; eauto.
    clear H2.
     (lp1++[r']++lp2) (lq1++[l']++lq2) (par_list (lq1++[l']++lq2) (q!!)); splits; auto.
    apply step_star_par_list1; auto.
    apply sbisim_refl.
    apply wbisim_list_app; auto.
    apply wbisim_list_app; auto.
    apply wbisim_list_single; auto.
  Qed.

  Lemma wbisim_rep: p q,
    wbisimilar p qwbisimilar (p_rep p) (p_rep q).
  Proof.
    intros.
     (fun pp qq p q lp lq, sbisimilar pp (par_list lp (p!!)) sbisimilar qq (par_list lq (q!!)) wbisim_list lp lq wbisimilar p q).
    split.
    clear p q H.
    apply weak_bisimulation_symm; (intros pp qq [p[q[lp[lq[?[?[? H]]]]]]]; subst); intros; unfold inv in ×.
    × false.
    apply halted_sbisim1 in H0; auto; subst.
    eapply (par_list_not_halted1 lp (p!!)); eauto. intro; false.
    × split; intros.
    - renames p' to pp'.
    edestruct tstep_sbisim1 as [pp''[??]]; eauto.
    clear pp H0 H3.
    edestruct tstep_rep_wbisim1_par_list as [lp'[lq'[qq'[?[?[??]]]]]]; eauto.
    subst pp''.
    edestruct step_star_sbisim2 with (p:=qq) as [qq''[??]]; eauto.
     qq''; split; auto.
     p q lp' lq'; repeat rewrite par_list_app; splits; auto.
    eapply sbisim_trans; eauto.
    - renames p' to pp'.
    edestruct lstep_sbisim1 as [pp''[??]]; eauto.
    clear pp H0 H3.
    edestruct lstep_rep_wbisim1_par_list as [lp'[lq'[qq'[?[?[??]]]]]]; eauto.
    subst pp''.
    edestruct step_star_sbisim2 with (p:=qq) as [qq''[??]]; eauto.
     qq''; split; auto.
     p q lp' lq'; repeat rewrite par_list_app; splits; auto.
    eapply sbisim_trans; eauto.
    × q p lq lp; splits; auto.
    apply wbisim_list_symm; auto.
    apply wbisim_symm; auto.
    × p q (@nil lts_S) (@nil lts_S); simpl; splits; auto.
    apply sbisim_refl.
    apply sbisim_refl.
    apply wbisim_list_nil.
  Qed.

End Congruence.

Section EventualSimulationCompositionality.
  Import Notation.
  Import Notation.U.
  Open Scope proc.

  Hint Unfold tstep.

  Lemma esim_seq1: p q r,
    eventually_similar p q
    eventually_similar (p;; r) (q;; r).
  Proof.
    intros.
     (fun pp qq( p q, pp=p;;rqq=q;;r eventually_similar p q) qq=pp).
    splits; [ | | | | left; eauto]; clear p q H;
      (intros pp qq [ [p[q[?[??]]]] | ?]; subst; [| try rename pp into p]); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    edestruct tstep_esim1 as [q'[??]]; eauto. apply H5.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
     p'; split; auto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    apply step_star_seq_app. apply halted_esim1; eauto. reflexivity.
    apply single_tstep; apply s_seq_nil.
    - inverts H; inverts H0.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (q';;r); split. apply step_star_seq_app; auto. left; eauto.
    × split; intros.
    - p'; split; eauto. apply single_tstep; auto.
    - p'; split; eauto. apply single_lstep; auto.
    × renames p' to q'.
    edestruct step_star_seq_inv as [ [q''[??]] | [la1[la2[?[??]]]] ]; eauto.
    + subst q'.
    edestruct step_star_esim2 with (q:=q) as [p'''[q'''[?[??]]]]; eauto.
     (q''';; r) (p''';; r); splits; iauto;
      eapply step_star_seq_app; auto.
    + subst.
     q' q'; splits; eauto.
    apply step_nil.
    eapply step_star_app; eauto.
    eapply step_tsnoc.
    apply step_star_seq_app.
    eapply halted_converge_esim2; eauto.
    reflexivity.
    apply s_seq_nil.
    × p' p'; splits; auto. apply step_nil.
  Qed.

  Lemma esim_seq2: p q r,
    eventually_similar p q
    eventually_similar (r;; p) (r;; q).
  Proof.
    intros.
     (fun pp qq( p q r, pp=r;;pqq=r;;q eventually_similar p q) eventually_similar pp qq).
    splits; [ | | | | left; jauto]; clear p q r H;
      (intros pp qq [ [p[q[r[?[??]]]]] | ?]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_esim1; auto.
    × false.
    × apply halted_esim2; auto.
    × split; intros.
    - inverts H.
     (p'0;;q); split. apply single_tstep; apply s_seq; auto. left; jauto.
     q; split; auto. apply single_tstep; apply s_seq_nil.
    - inverts H; inverts H0.
     (p'0;;q); split. apply single_lstep; constructor; apply s_seq; auto. left; jauto.
     (p'0;;q); split. apply single_lstep; constructor; apply s_seq; auto. left; jauto.
    × split; intros.
    - edestruct tstep_esim1 as [q'[??]]; eauto.
    - edestruct lstep_esim1 as [q'[??]]; eauto.
    × edestruct step_star_seq_inv as [ [r'[??]] | [la1[la2[?[??]]]] ]; eauto.
    + subst p'.
     (r';; q) (r';; p); splits.
    apply step_nil.
    apply step_star_seq_app; auto.
    left; p q r'; splits; auto.
    + subst la. renames p' to q'.
    edestruct step_star_esim2 with (q:=q) as [p''[q''[?[??]]]]; eauto.
     q'' p''; splits; eauto.
    eapply step_star_app; eauto.
    eapply step_tsnoc.
    apply step_star_seq_app; eauto.
    apply s_seq_nil.
    × edestruct step_star_esim2 as [q''[p''[?[??]]]]; jauto.
  Qed.

  Lemma esim_seq: p1 p2 q1 q2,
    eventually_similar p1 q1
    eventually_similar p2 q2
    eventually_similar (p1;;p2) (q1;;q2).
  Proof. intros; eapply esim_trans; [eapply esim_seq1; eauto | apply esim_seq2; auto]. Qed.

  Lemma esim_parL: p q r,
    eventually_similar p q
    eventually_similar (p||r) (q||r).
  Proof.
    intros.
     (fun pr qr( p q r, pr=p||r qr=q||r eventually_similar p q) (pr=O qr=O)).
    splits; [| | | | left; jauto]; clear p q r H;
      (intros pr qr [ [p[q[r[?[??]]]]] | [??]]; subst); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    + renames p'0 to p'.
    edestruct tstep_esim1 as [q'[??]]; eauto. apply H5.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'.
     (q||r'); split. apply single_tstep; apply s_parR; auto. left; jauto.
    + p_nil; split; auto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app. apply step_star_parL_step. apply halted_esim1; eauto. reflexivity.
    apply single_tstep. apply s_par_nil.
    - inverts H; inverts H0.
    + edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'. (q||r'); split. apply single_lstep; constructor. apply s_parR; auto. left; jauto.
    + edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (q'||r); split. apply step_star_parL_step; auto. left; jauto.
    + renames q' to r'. (q||r'); split. apply single_lstep; constructor. apply s_parR; auto. left; jauto.
    × split; intros; inverts H; inverts H0.
    × renames p' to pr'.
    edestruct step_star_par_unzip as [q'[r'[la1[la2[?[?[?[??]]]]]]]]; eauto; substs.
    edestruct step_star_esim2 with (q:=q) as [p''[q''[?[??]]]]; eauto.
    destruct H0 as [?|[?[??]]]; subst.
     (q''||r') (p''||r'); splits; eauto.
    apply step_star_parL_step; auto.
    left; jauto.
     O O; splits; auto.
    apply step_nil.
    rewrite<- (app_nil_r la).
    eapply step_star_app; eauto.
    apply step_star_nil_resolve in H7. destruct H7 as [??]; subst q''.
    eapply step_tsnoc; eauto.
    apply step_star_parL_step; eauto.
    apply halted_esim2; eauto. reflexivity.
    apply s_par_nil.
    × edestruct step_star_nil_resolve as [??]; eauto; subst la p'; jauto.
  Qed.

  Lemma esim_parR: p q r,
    eventually_similar p q
    eventually_similar (r||p) (r||q).
  Proof.
    intros.
    eapply esim_trans.
    apply wbisim_esim.
    apply wbisim_par_swap.
    eapply esim_trans.
    apply esim_parL; eauto.
    apply wbisim_esim.
    apply wbisim_par_swap.
  Qed.

  Lemma esim_par: p q r s,
    eventually_similar p q
    eventually_similar r s
    eventually_similar (p||r) (q||s).
  Proof. intros; eapply esim_trans. apply esim_parL; eauto. eapply esim_parR; eauto. Qed.

  Lemma esim_res: a n p q,
    eventually_similar p qeventually_similar (a#n:p) (a#n:q).
  Proof.
    intros.
     (fun pp qq( n p q, pp=a#n:p qq=a#n:q eventually_similar p q) (pp=Oqq=O)).
    splits; [| | | | left; jauto]; clear n p q H;
      (intros pp qq [ [n[p[q[?[??]]]]] | [??] ]; subst); intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × false.
    × apply step_nil.
    × split; intros.
    - inverts H.
    + rename p'0 into p'.
    edestruct tstep_esim1 as [q'[??]]; eauto. apply H4.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto.
    + renames p'0 to p', n0 to n.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_count_in; auto.
    + renames p'0 to p'.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (a#(S n):q'); split; [| left; jauto].
    apply step_star_res_count_out; auto.
    + O; split; auto.
    eapply step_tsnoc. apply step_star_res_nIn. apply halted_esim1; eauto. reflexivity.
    intros ? [ ].
    apply s_res_nil.
    - inverts H; inverts H0.
    + rename p'0 into p'.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_in; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto. intros. inverts H2. auto. inverts H3.
    + rename p'0 into p'.
    edestruct lstep_esim1 as [q'[??]]; eauto. apply lstep_out; eauto.
     (a#n:q'); split; [| left; jauto].
    apply step_star_res_nIn; auto. intros. inverts H2. auto. inverts H3.
    × split; intros; inverts H; inverts H0.
    × rename p' into qq'; edestruct step_star_res_inv as [la'[n'[q'[?[?[??]]]]]]; eauto.
    edestruct step_star_esim2 as [p''[q''[?[??]]]]; eauto.
    destruct H0 as [?|[??]]; subst.
     (a#n':q'') (a#n':p''); splits; eauto.
    eapply step_star_res; eauto.
    apply res_count_labels_nil2'.
    eapply step_star_res; eauto.
    left; n' p'' q''; splits; auto.
    apply step_star_nil_resolve in H6; destruct H6 as [??]; subst q''.
     O O; splits; eauto.
    apply step_nil.
    rewrite<- (app_nil_r (res_filter _ _)).
    eapply step_star_app.
    eapply step_star_res; eauto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    eapply step_tsnoc.
    apply step_star_res_nIn; eauto.
    eapply halted_esim2; eauto. reflexivity.
    apply s_res_nil. apply step_nil.
    × edestruct step_star_nil_resolve as [??]; eauto; subst la p'. jauto.
  Qed.

  Lemma esim_res_list: AN p q,
    eventually_similar p qeventually_similar (p_res_list AN p) (p_res_list AN q).
  Proof.
    induction AN; intros; auto. destruct a; simpl.
    apply esim_res; eauto.
  Qed.

  Lemma esim_stable_sum: p q r,
    eventually_similar p q
    stable lts pstable lts q
    eventually_similar (p+r) (q+r).
  Proof.
    intros.
     (fun pp qq
      ( p q r, stable lts p stable lts q pp=p+r qq=q+r eventually_similar p q)
      eventually_similar pp qq).
    splits; [| | | | left; jauto];
      try (clear p q r H H0 H1; intros pp qq [ [p[q[r[?[?[?[??]]]]]]] | ? ]); subst; intros; unfold inv in ×.
    × false.
    × apply halted_esim1; auto.
    × false.
    × apply halted_esim2; auto.
    × split; intros.
    - inverts H1.
    + false; eapply H; apply H7.
    + renames p' to r'.
     r'; split.
    apply single_tstep; apply s_sumR; auto.
    right; apply esim_refl.
    + apply halted_esim1 in H3.
     O; split.
    inverts H3. apply single_tstep; apply s_sum_nil. eapply step_tcons; eauto. apply s_sumL; auto.
    right; apply esim_refl.
    reflexivity.
    - inverts H1; inverts H2.
    + edestruct lstep_esim1 as [q'[??]]; eauto. constructor; apply H7.
     q'; split; eauto.
    edestruct step_star_lstep_inv as [q'0[q'1[?[??]]]]; eauto.
    inverts H4.
    eapply step_lcons; eauto. constructor; apply s_sumL; auto.
    inverts H5; auto.
    eapply step_tcons with p'0; eauto. apply s_sumL; auto.
    setoid_rewrite<- (@app_nil_l lts_L).
    eapply step_star_app; eauto.
    eapply step_lcons; eauto.
    + renames p' to r'.
     r'; split.
    apply single_lstep. constructor; apply s_sumR; auto.
    right; apply esim_refl.
    + edestruct lstep_esim1 as [q'[??]]; eauto. constructor 2; apply H7.
     q'; split; eauto.
    edestruct step_star_lstep_inv as [q'0[q'1[?[??]]]]; eauto.
    inverts H4.
    eapply step_lcons; eauto. constructor; apply s_sumL; auto.
    inverts H5; auto.
    eapply step_tcons with p'0; eauto. apply s_sumL; auto.
    setoid_rewrite<- (@app_nil_l lts_L).
    eapply step_star_app; eauto.
    eapply step_lcons; eauto.
    + renames p' to r'.
     r'; split.
    apply single_lstep. constructor; apply s_sumR; auto.
    right; apply esim_refl.
    × split; intros.
    - edestruct tstep_esim1 as [q'[??]]; eauto.
    - edestruct lstep_esim1 as [q'[??]]; eauto.
    × inverts H1.
    - (q+r) (p+r); splits.
    apply step_nil.
    apply step_nil.
    left; p q r; splits; auto.
    - renames p'0 to q'0, p' to q'.
    assert (lstep q a q'0 lstep r a q'0).
      { inverts H2; (inverts H1; [left|right]; constructor; auto). }
    destruct H1 as [?|?].
    + edestruct step_star_esim2 with (q:=q) as [p''[q''[?[??]]]]; eauto.
    eapply step_lcons; eauto.
     q'' p''; splits; eauto.
    inverts H5.
    inverts H11; eapply step_lcons; eauto; constructor; apply s_sumL; eauto.
    eapply step_tcons; eauto.
    apply s_sumL; auto.
    + q' q'; splits.
    apply step_nil.
    inverts H1; eapply step_lcons; eauto; constructor; apply s_sumR; auto.
    right; apply esim_refl.
    - inverts H2.
    + false. eapply H0. apply H8.
    + p' p'; splits.
    apply step_nil.
    eapply step_tcons; eauto. apply s_sumR; auto.
    right; apply esim_refl.
    + edestruct step_star_nil_resolve as [??]; eauto; subst la p'.
     O O; splits.
    apply step_nil.
    apply halted_esim2 in H3.
    inverts H3.
    apply single_tstep; apply s_sum_nil.
    false; eapply H; apply H1.
    reflexivity.
    right; apply esim_refl.
    × edestruct step_star_esim2 as [q''[p''[?[??]]]]; jauto.
    × p q r; splits; auto.
  Qed.

  Lemma esim_internal_sum: p q r,
    eventually_similar p q
    eventually_similar (().p+r) (().q+r).
  Proof.
    intros.
     (fun pp qq( p q, pp=().p+r qq=().q+r eventually_similar p q) eventually_similar pp qq).
    splits; iauto;
      try (clear p q H; intros pp qq [ [p[q[?[??]]]] | ?]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_esim1; auto.
    × false.
    × apply halted_esim2; auto.
    × split; intros.
    - inverts H. inverts H5.
    eexists; split; eauto. apply single_tstep; apply s_sumL; constructor.
     p'; split. apply single_tstep; apply s_sumR; auto. right; apply esim_refl.
    - inverts H; inverts H0.
    + inverts H5.
    + p'; split. apply single_lstep; constructor; apply s_sumR; auto. right; apply esim_refl.
    + inverts H5.
    + p'; split. apply single_lstep; constructor; apply s_sumR; auto. right; apply esim_refl.
    × split; intros.
    - edestruct tstep_esim1 as [q'[??]]; eauto.
    - edestruct lstep_esim1 as [q'[??]]; eauto.
    × inverts H.
    + (().q + r) (().p + r); splits. apply step_nil. apply step_nil. left; p q; splits; auto.
    + inverts H0; inverts H.
    inverts H6.
     p' p'; splits. apply step_nil. eapply step_lcons; eauto; constructor; apply s_sumR; auto. right; apply esim_refl.
    inverts H6.
     p' p'; splits. apply step_nil. eapply step_lcons; eauto; constructor; apply s_sumR; auto. right; apply esim_refl.
    + inverts H0.
    - inverts H6. renames p'0 to q, p' to q'.
    edestruct step_star_esim2 as [p''[q''[?[??]]]]; eauto.
     q'' p''; splits; eauto.
    eapply step_tcons; eauto. apply s_sumL; apply s_act.
    - p' p'; splits. apply step_nil. eapply step_tcons; eauto; apply s_sumR; auto. right; apply esim_refl.
    × edestruct step_star_esim2 as [p''[q''[?[??]]]]; jauto.
  Qed.

  Lemma esim_act: l p q,
    eventually_similar p qeventually_similar (p_act l p) (p_act l q).
  Proof.
    intros.
     (fun pp qq( p q, pp=p_act l p qq=p_act l q eventually_similar p q) eventually_similar pp qq).
    splits; [ | | | | left; jauto]; clear p q H; intros pp qq [ [p[q[?[??]]]] | ?]; subst; intros; unfold inv in ×.
    × false.
    × eapply halted_esim1; eauto.
    × false.
    × eapply halted_esim2; eauto.
    × split; intros.
    - inverts H. q; split; auto. apply single_tstep; constructor.
    - inverts H; inverts H0.
    + q; split; auto. apply single_lstep; do 2 constructor.
    + q; split; auto. apply single_lstep; do 2 constructor.
    × split; intros.
    - edestruct tstep_esim1 as [q'[??]]; eauto.
    - edestruct lstep_esim1 as [q'[??]]; eauto.
    × inverts H.
    - (p_act l q) (p_act l p); splits.
    apply step_nil.
    apply step_nil.
    left; p q; splits; auto.
    - renames p'0 to q'0, p' to q'.
    inverts H0; inverts H;
      (edestruct step_star_esim2 as [p''[q''[?[??]]]]; eauto;
      ( q'' p''; splits; eauto; eapply step_lcons; eauto; do 2 constructor)).
    - inverts H0.
    edestruct step_star_esim2 as [p''[q''[?[??]]]]; eauto.
     q'' p''; splits; auto; eapply step_tcons; eauto. constructor.
    × edestruct step_star_esim2 as [q''[p''[?[??]]]]; jauto.
  Qed.

  Lemma step_star_par_list_esim2: r1 r2 lp lq la lq',
    step_star (par_list lq O) la (par_list lq' O) →
    esim_list lp lq
    length lq' = length lq
     lp'' lq'',
      step_star (par_list lp r1) la (par_list lp'' r1)
      step_star (par_list lq' r2) nil (par_list lq'' r2)
      length lq'' = length lq
      esim_list lp'' lq''.
  Proof.
    induction lp; destruct lq; intros; inverts H0.
    × simpl in ×.
    edestruct step_star_nil_resolve; eauto.
    destruct lq'; try discriminate. subst la.
     (@nil proc) (@nil proc); simpl; splits; auto.
    apply @step_nil. apply @step_nil. apply esim_list_nil.
    × simpl in ×.
    renames p to q, a to p.
    edestruct step_star_par_unzip'' as [q'[lq2'[lip[la1[la2[?[?[?[??]]]]]]]]]; eauto.
    destruct H0 as [?|[?[??]]].
    - destruct lq'; inverts H0.
    clear H.
    edestruct IHlp as [lp''[lq''[?[?[??]]]]]; eauto; clear IHlp.
    edestruct step_star_esim2 with (q:=q) as [p''[q''[?[??]]]]; eauto.
     (p''::lp'') (q''::lq''); splits; auto.
    eapply step_star_par_zip'; eauto.
    eapply step_star_par_zip' with (lp:=nil); eauto.
    simpl; congruence.
    apply esim_list_cons; auto.
    - false. subst. destruct lq'; inverts H1. inverts H9.
  Qed.

  Lemma step_star_rep_esim2_par_list: lp p lq q la qq',
    step_star (par_list lq (q!!)) la qq'
    eventually_similar p q
    esim_list lp lq
     lp' lq' pp'' qq'',
      step_star (par_list lp (p!!)) la pp''
      step_star qq' nil qq''
      sbisimilar pp'' (par_list lp' (p!!))
      qq'' = (par_list lq' (q!!))
      esim_list lp' lq'.
  Proof.
    intros.
    edestruct step_star_rep_par_list as [n[lq'[?[??]]]]; eauto.
    subst qq'.
    edestruct step_star_par_list_esim2 with (r1:=p!!) (r2:=q!!) as [lp''[lq''[?[?[??]]]]]; eauto.
    apply esim_list_app; eauto.
    apply Forall2_list_rep; eauto.
    edestruct step_star_sbisim1 with (p:=(par_list (lp++list_rep n p) (p!!))) (q:=par_list lp (p!!)) as [pp''[??]]; eauto.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_symm; apply sbisim_list_rep_par_list.
     lp'' lq'' pp'' (par_list lq'' (q!!)); splits; auto.
    apply sbisim_symm; auto.
  Qed.

  Lemma tstep_rep_esim1_par_list: lp p lq q pp',
    tstep (par_list lp (p!!)) pp'
    eventually_similar p q
    esim_list lp lq
     lp' lq' qq',
      step_star (par_list lq (q!!)) nil qq'
      pp' = (par_list lp' (p!!))
      sbisimilar qq' (par_list lq' (q!!))
      esim_list lp' lq'.
  Proof.
    intros.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    apply H.
    eapply par_nterm; eauto. apply step_nil.
    × subst.
    edestruct step_rep_inv; eauto.
    apply rep_step_result_list_rep in H4.
    destruct H4 as [n ?].
    remember (get_step_rep_p' p p') as p'_.
    rewrite H4 in ×.
    clear p' H4 Heqp'_; renames p'_ to p'.
    edestruct tstep_esim1 with (p:=p) as [q'[??]]; eauto. apply H3.
    inverts H4.
    - renames q' to q.
     (lp++list_rep n p ++ [p']) (lq++list_rep (S n) q) (par_list lq (q!!)); splits; auto.
    apply step_nil.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list.
    rewrite list_rep_snoc.
    apply esim_list_app; auto.
    apply esim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply esim_list_single; auto.
    - (lp++list_rep n p ++ [p']) (lq++list_rep n q++[q']) (par_list lq (q'||q!!)); splits; auto.
    apply step_star_par_list2.
    eapply step_tcons; eauto. apply s_rep. apply s_parL. apply H6.
    apply step_star_parL_step; auto.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list' with (lp:=[q']).
    apply esim_list_app; auto.
    apply esim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply esim_list_single; auto.
    × subst lp pp'.
    edestruct esim_list_same_split as [lq1[slq2[?[??]]]]; eauto; subst lq.
    clear H1.
    edestruct esim_list_same_split as [s[lq2[?[??]]]]; eauto; subst slq2.
    clear H5.
    destruct s. inverts H2.
    destruct s; [ | solve [inverts H2; inverts H10] ].
    apply esim_list_single in H2.
    edestruct tstep_esim1 with (p:=r) as [l'[??]]; eauto. apply H3.
    clear H2.
     (lp1++[r']++lp2) (lq1++[l']++lq2) (par_list (lq1++[l']++lq2) (q!!)); splits; auto.
    apply step_star_par_list1; auto.
    apply sbisim_refl.
    apply esim_list_app; auto.
    apply esim_list_app; auto.
    apply esim_list_single; auto.
  Qed.

  Lemma lstep_rep_esim1_par_list: lp p lq q l pp',
    lstep (par_list lp (p!!)) l pp'
    eventually_similar p q
    esim_list lp lq
     lp' lq' qq',
      step_star (par_list lq (q!!)) [l] qq'
      pp' = (par_list lp' (p!!))
      sbisimilar qq' (par_list lq' (q!!))
      esim_list lp' lq'.
  Proof.
    intros.
    edestruct step_par_list_inv as [ [p'[??]] | [lp1[r[lp2[r'[?[??]]]]]] ]; eauto.
    inverts H; eauto.
    eapply par_nterm; eauto. apply step_nil.
    × subst.
    edestruct step_rep_inv; eauto.
    apply rep_step_result_list_rep in H4.
    destruct H4 as [n ?].
    remember (get_step_rep_p' p p') as p'_.
    rewrite H4 in ×.
    clear p' H4 Heqp'_; renames p'_ to p'.
    edestruct lstep_esim1 with (a:=l) (p:=p) as [q'[??]]; eauto. inverts H; constructor; eauto.
     (lp++list_rep n p ++ [p']) (lq++list_rep n q++[q']) (par_list lq (q'||q!!)); splits; auto.
    apply step_star_par_list2.
    edestruct step_star_lstep_inv as [q'0[q'1[?[??]]]]; eauto.
    inverts H6.
    eapply @step_lcons; eauto. inverts H7; constructor; apply s_rep; apply s_parL; eauto.
    apply step_star_parL_step; auto.
    eapply step_tcons; eauto. apply s_rep. apply s_parL. apply H9.
    apply step_star_parL_step.
    change [l] with ([]++[l]). eapply step_star_app; eauto. eapply @step_lcons; eauto.
    repeat rewrite par_list_app. reflexivity.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_list_rep_par_list' with (lp:=[q']).
    apply esim_list_app; auto.
    apply esim_list_app; auto.
    apply Forall2_list_rep; auto.
    apply esim_list_single; auto.
    × subst lp pp'.
    edestruct esim_list_same_split as [lq1[slq2[?[??]]]]; eauto; subst lq.
    clear H1.
    edestruct esim_list_same_split as [s[lq2[?[??]]]]; eauto; subst slq2.
    clear H5.
    destruct s. inverts H2.
    destruct s; [ | solve [inverts H2; inverts H10] ].
    apply esim_list_single in H2.
    edestruct lstep_esim1 with (a:=l) (p:=r) as [l'[??]]; eauto.
    inverts H; constructor; eauto.
    clear H2.
     (lp1++[r']++lp2) (lq1++[l']++lq2) (par_list (lq1++[l']++lq2) (q!!)); splits; auto.
    apply step_star_par_list1; auto.
    apply sbisim_refl.
    apply esim_list_app; auto.
    apply esim_list_app; auto.
    apply esim_list_single; auto.
  Qed.

  Lemma esim_rep: p q,
    eventually_similar p qeventually_similar (p_rep p) (p_rep q).
  Proof.
    intros.
     (fun pp qq lp lq, sbisimilar pp (par_list lp (p!!)) sbisimilar qq (par_list lq (q!!)) esim_list lp lq).
    splits; try (intros pp qq [lp[lq[?[??]]]]; subst; intros; unfold inv in *).
    × false.
    apply halted_sbisim1 in H0; auto; subst.
    eapply (par_list_not_halted1 lp (p!!)); eauto. intro; false.
    × false.
    apply halted_sbisim1 in H1; auto; subst.
    eapply (par_list_not_halted1 lq (q!!)); eauto. intro; false.
    × split; intros.
    - renames p' to pp'.
    edestruct tstep_sbisim1 as [pp''[??]]; eauto.
    clear pp H0 H3.
    edestruct tstep_rep_esim1_par_list with (p:=p) (q:=q) as [lp'[lq'[qq'[?[?[??]]]]]]; eauto.
    subst pp''.
    edestruct step_star_sbisim2 with (p:=qq) as [qq''[??]]; eauto.
     qq''; split; auto.
     lp' lq'; repeat rewrite par_list_app; splits; auto.
    eapply sbisim_trans; eauto.
    - renames p' to pp'.
    edestruct lstep_sbisim1 as [pp''[??]]; eauto.
    clear pp H0 H3.
    edestruct lstep_rep_esim1_par_list with (p:=p) (q:=q) as [lp'[lq'[qq'[?[?[??]]]]]]; eauto.
    subst pp''.
    edestruct step_star_sbisim2 with (p:=qq) as [qq''[??]]; eauto.
     qq''; split; auto.
     lp' lq'; repeat rewrite par_list_app; splits; auto.
    eapply sbisim_trans; eauto.
    × renames pp to qqq, qq to pp, qqq to qq, p' to qq'.
    edestruct step_star_sbisim1 as [qq''[??]]; eauto.
    clear qq H1 H3.
    edestruct step_star_rep_esim2_par_list as [lp'[lq'[pp'''[qq'''[?[?[?[??]]]]]]]]; eauto.
    subst qq'''.
    edestruct step_star_sbisim2 with (p:=qq') (q:=qq'') as [qq''''[??]]; eauto.
    edestruct step_star_sbisim2 with (p:=pp)as [pp''''[??]]; eauto.
     qq'''' pp''''; splits; auto.
     lp' lq'; splits; auto.
    eapply sbisim_trans; eauto.
    × (@nil lts_S) (@nil lts_S); simpl; splits; auto.
    apply sbisim_refl.
    apply sbisim_refl.
    apply esim_list_nil.
  Qed.

End EventualSimulationCompositionality.

Section ContrasimulationCongruence.
  Import Notation.
  Import Notation.U.
  Open Scope proc.

  Lemma csim_seq1: p q r,
    contrasimilar p q
    contrasimilar (p;; r) (q;; r).
  Proof.
    intros.
     (fun pp qq( p q, pp=p;;rqq=q;;r partial_contrasimilar p q) qq=pp).
    splits; try (clear p q H; intros pp qq [ [p[q[?[??]]]] | ? ]; subst; [| rename pp into p]; intros; unfold inv in *).
    × false.
    × apply step_nil.
    × edestruct step_star_seq_inv as [ [p''[??]] | [la1[la2[?[??]]]] ]; eauto.
    + subst p'.
    edestruct step_star_pcsim with (p:=p) as [q''[??]]; eauto.
     (q'';; r); split; iauto.
    eapply step_star_seq_app; auto.
    + subst.
     p'; split; eauto.
    eapply step_star_app; eauto.
    eapply step_tsnoc.
    apply step_star_seq_app.
    eapply halted_converge_pcsim; eauto.
    reflexivity.
    apply s_seq_nil.
    × p'; auto.
    × left; p q; splits; auto. apply csim_pcsim1; auto.
    × left; q p; splits; auto. apply csim_pcsim2; auto.
  Qed.

  Lemma csim_seq2: p q r,
    contrasimilar p q
    contrasimilar (r;; p) (r;; q).
  Proof.
    intros.
     (fun pp qq( p q r, pp=r;;pqq=r;;q contrasimilar p q) partial_contrasimilar pp qq).
    splits; try (clear p q r H; intros pp qq [ [p[q[r[?[??]]]]] | ?]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_pcsim1; auto.
    × edestruct step_star_seq_inv as [ [r'[??]] | [la1[la2[?[??]]]] ]; eauto.
    + subst p'.
     (r';; q); split. apply step_star_seq_app; auto. left; q p r'; splits; auto. symmetry; auto.
    + subst la.
    edestruct step_star_pcsim with (p:=p) as [q'[??]]; eauto.
    apply csim_pcsim1; eauto.
     q'; split; eauto.
    eapply step_star_app; eauto.
    eapply step_tsnoc.
    apply step_star_seq_app; eauto.
    apply s_seq_nil.
    × edestruct step_star_pcsim as [q'[??]]; eauto.
    × left; jauto.
    × apply csim_symm in H; left; jauto.
  Qed.

  Lemma csim_seq: p1 p2 q1 q2,
    contrasimilar p1 q1contrasimilar p2 q2
    contrasimilar (p1;;p2) (q1;;q2).
  Proof. intros; etransitivity; [eapply csim_seq1; eauto | apply csim_seq2; auto]. Qed.

  Lemma pcsim_parL: p q r,
    partial_contrasimilar p q
    partial_contrasimilar (p||r) (q||r).
  Proof.
    intros.
     (fun pr qr( p q r, pr = p||r qr=q||r partial_contrasimilar p q) (pr=Oqr=O)).
    splits; [| | left; jauto];
      clear p q r H; intros pr qr [ [p[q[r[?[??]]]]] | [??] ]; subst; intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × renames p' to pr'.
    edestruct step_star_par_unzip as [p'[r'[la1[la2[?[?[?[??]]]]]]]]; eauto; substs.
    edestruct step_star_pcsim with (p:=p) as [q'[??]]; eauto.
    destruct H0 as [?|[?[??]]]; subst.
     (q'||r'); split; eauto. left; jauto.
     O; split; auto.
    rewrite<- (app_nil_r la).
    eapply step_star_app; eauto.
    eapply step_tsnoc; eauto.
    apply step_star_parL_step; eauto.
    apply halted_pcsim2; eauto. reflexivity.
    apply s_par_nil.
    × edestruct step_star_nil_resolve as [??]; eauto.
  Qed.

  Lemma pcsim_parR: p q r,
    partial_contrasimilar p q
    partial_contrasimilar (r||p) (r||q).
  Proof.
    intros.
    change [] with (([]++[])++@nil lts_L).
    eapply pcsim_trans.
    apply wbisim_pcsim; apply wbisim_par_swap.
    eapply pcsim_trans.
    apply pcsim_parL; eauto.
    apply wbisim_pcsim; apply wbisim_par_swap.
  Qed.

  Lemma csim_parL: p q r,
    contrasimilar p q
    contrasimilar (p||r) (q||r).
  Proof. intros; apply pcsim_csim; apply pcsim_parL; [apply csim_pcsim1 | apply csim_pcsim2]; auto. Qed.

  Lemma csim_parR: p q r,
    contrasimilar p q
    contrasimilar (r||p) (r||q).
  Proof. intros; apply pcsim_csim; apply pcsim_parR; [apply csim_pcsim1 | apply csim_pcsim2]; auto. Qed.

  Lemma csim_par: p q r s,
    contrasimilar p q
    contrasimilar r s
    contrasimilar (p||r) (q||s).
  Proof. intros; eapply csim_trans. apply csim_parL; eauto. apply csim_parR; auto. Qed.

  Lemma pcsim_act: l p q,
    contrasimilar p qpartial_contrasimilar (p_act l p) (p_act l q).
  Proof.
    intros.
     (fun pp qq( p q, pp=p_act l p qq=p_act l q contrasimilar p q) partial_contrasimilar pp qq).
    splits; [ | | left; jauto]; clear p q H; intros pp qq [ [p[q[?[??]]]] | ?]; subst; intros; unfold inv in ×.
    × false.
    × eapply halted_pcsim1; eauto.
    × inverts H.
    - (p_act l q); split.
    apply step_nil.
    left; q p; splits; auto; apply csim_symm; auto.
    - inverts H0; inverts H;
      (edestruct step_star_pcsim as [q'[??]]; eauto;
        [apply csim_pcsim1; eauto
        | q'; split; auto; eapply step_lcons; eauto; constructor; constructor]).
    - inverts H0.
    edestruct step_star_pcsim as [q'[??]].
    eauto.
    apply csim_pcsim1; eauto.
     q'; split; auto; eapply step_tcons; eauto. constructor.
    × edestruct step_star_pcsim as [q'[??]]; eauto.
  Qed.

  Lemma pcsim_tau: p q,
    partial_contrasimilar (().p) (().q) partial_contrasimilar p q.
  Proof.
    split; intros.
    × change [] with (([]++[])++@nil lts_L).
    eapply pcsim_trans.
    apply wbisim_pcsim; apply wbisim_symm; apply wbisim_intro_tau.
    eapply pcsim_trans.
    apply H.
    apply wbisim_pcsim; apply wbisim_intro_tau.
    × change [] with (([]++[])++@nil lts_L).
    eapply pcsim_trans.
    apply wbisim_pcsim; apply wbisim_intro_tau.
    eapply pcsim_trans.
    apply H.
    apply wbisim_pcsim; apply wbisim_symm; apply wbisim_intro_tau.
  Qed.

  Lemma pcsim_in: a p q,
    contrasimilar p qpartial_contrasimilar (a?.p) (a?.q).
  Proof. intros; apply pcsim_act; auto. Qed.

  Lemma csim_act: l p q,
    contrasimilar p qcontrasimilar (p_act l p) (p_act l q).
  Proof.
    intros; apply pcsim_csim; apply pcsim_act; auto. symmetry; auto.
  Qed.

  Lemma csim_in: a p q,
    contrasimilar p qcontrasimilar (a?.p) (a?.q).
  Proof. intros; apply pcsim_csim; apply pcsim_in; auto; apply csim_symm; auto. Qed.

  Lemma pcsim_out: a p q,
    contrasimilar p qpartial_contrasimilar (a!.p) (a!.q).
  Proof. intros; apply pcsim_act; auto. Qed.

  Lemma csim_out: a p q,
    contrasimilar p qcontrasimilar (a!.p) (a!.q).
  Proof. intros; apply pcsim_csim; apply pcsim_out; auto; apply csim_symm; auto. Qed.

  Lemma pcsim_res: a n p q,
    partial_contrasimilar p qpartial_contrasimilar (a#n:p) (a#n:q).
  Proof.
    intros.
     (fun pp qq( n p q, pp=a#n:p qq=a#n:q partial_contrasimilar p q) (pp=Oqq=O)).
    splits; [ | | left; jauto]; clear n p q H; intros pp qq [ [n[p[q[?[??]]]]] | [??] ]; subst; intros; unfold inv in ×.
    × false.
    × apply step_nil.
    × rename p' into pp'; edestruct step_star_res_inv as [la'[n'[p'[?[?[??]]]]]]; eauto.
    edestruct step_star_pcsim as [q'[??]]; eauto.
    destruct H0 as [?|[??]]; subst.
     (a#n':q'); split; eauto.
    eapply step_star_res; eauto.
    left; n' q' p'; splits; auto.
     O; split; eauto.
    rewrite<- (app_nil_r (res_filter _ _)).
    eapply step_star_app.
    eapply step_star_res; eauto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    eapply step_tsnoc.
    apply step_star_res_nIn; eauto.
    eapply halted_pcsim2; eauto. reflexivity.
    apply s_res_nil. apply step_nil.
    × edestruct step_star_nil_resolve as [??]; eauto.
  Qed.

  Lemma csim_res: a n p q,
    contrasimilar p qcontrasimilar (a#n:p) (a#n:q).
  Proof. intros; apply pcsim_csim; apply pcsim_res; auto; [ apply csim_pcsim1 | apply csim_pcsim2 ]; auto. Qed.

  Lemma csim_stable_sum: p q r,
    contrasimilar p q
    stable lts pstable lts q
    contrasimilar (p+r) (q+r).
  Proof.
    intros.
     (fun pp qq
      ( p q r, stable lts p stable lts q pp=p+r qq=q+r contrasimilar p q)
      partial_contrasimilar pp qq).
    splits; [| | left; jauto |];
      try (clear p q r H H0 H1; intros pp qq [ [p[q[r[?[?[?[??]]]]]]] | ? ]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_pcsim1; auto.
    × inverts H1.
    - (q+r); split.
    apply step_nil.
    left; q p r; splits; auto.
    apply csim_symm; auto.
    - assert (lstep p a p'0 lstep r a p'0).
      { inverts H2; (inverts H1; [left|right]; constructor; auto). }
    destruct H1 as [?|?].
    + edestruct step_star_pcsim as [q'[??]].
    eapply step_lcons; eauto.
    apply csim_pcsim1; eauto.
     q'; split; eauto.
    inverts H5.
    inverts H10; eapply step_lcons; eauto; constructor; apply s_sumL; eauto.
    eapply step_tcons; eauto.
    apply s_sumL; auto.
    + p'; split.
    inverts H1; eapply step_lcons; eauto; constructor; apply s_sumR; auto.
    right; apply pcsim_refl.
    - inverts H2.
    + false. eapply H. apply H8.
    + p'; split.
    eapply step_tcons; eauto. apply s_sumR; auto.
    right; apply pcsim_refl.
    + edestruct step_star_nil_resolve as [??]; eauto; subst la p'.
     p_nil; split.
    apply csim_pcsim1 in H3.
    apply halted_pcsim1 in H3.
    inverts H3.
    apply single_tstep; apply s_sum_nil.
    false; eapply H0; apply H1.
    reflexivity.
    right; apply pcsim_refl.
    × edestruct step_star_pcsim as [q'[??]]; eauto.
    × p q r; splits; auto.
    × left; q p r; splits; auto. apply csim_symm; auto.
  Qed.

  Lemma csim_internal_sum: p q r,
    contrasimilar p q
    contrasimilar (().p+r) (().q+r).
  Proof.
    intros.
     (fun pp qq( p q, pp=().p+r qq=().q+r contrasimilar p q) partial_contrasimilar pp qq).
    splits; iauto;
      try (clear p q H; intros pp qq [ [p[q[?[??]]]] | ?]; subst); intros; unfold inv in ×.
    × false.
    × apply halted_pcsim1; auto.
    × inverts H.
    + (().q + r); split. apply step_nil. left; q p; splits; auto. symmetry; auto.
    + inverts H0; inverts H.
    inverts H6.
     p'; split. eapply step_lcons; eauto; constructor; apply s_sumR; auto. right; apply pcsim_refl.
    inverts H6.
     p'; split. eapply step_lcons; eauto; constructor; apply s_sumR; auto. right; apply pcsim_refl.
    + inverts H0.
    - inverts H6.
    edestruct step_star_pcsim with (q:=q) as [q'[??]]; eauto.
    apply csim_pcsim1; auto.
     q'; split; auto. eapply step_tcons; eauto. apply s_sumL; apply s_act.
    - p'; split. eapply step_tcons; eauto; apply s_sumR; auto. right; apply pcsim_refl.
    × edestruct step_star_pcsim as [q'[??]]; eauto.
    × left; q p; splits; auto. symmetry; auto.
  Qed.

  Lemma csim_res_list: AN p q,
    contrasimilar p qcontrasimilar (p_res_list AN p) (p_res_list AN q).
  Proof.
    induction AN; intros; auto. destruct a; simpl.
    apply csim_res; eauto.
  Qed.

  Definition pcsim_list := Forall2 partial_contrasimilar.

  Lemma step_star_par_list_pcsim: r lp lq la lp',
    step_star (par_list lp O) la (par_list lp' O) →
    pcsim_list lp lq
    length lp' = length lp
     lq',
      step_star (par_list lq r) la (par_list lq' r)
      length lq' = length lq
      pcsim_list lq' lp'.
  Proof.
    induction lp; destruct lq; intros; inverts H0.
    × simpl in ×.
    edestruct step_star_nil_resolve; eauto.
    destruct lp'; try discriminate. subst la.
     (@nil proc); simpl; splits; auto.
    apply @step_nil. apply Forall2_nil.
    × simpl in ×.
    renames a to p.
    edestruct step_star_par_unzip'' as [p'[lq2'[lip[la1[la2[?[?[?[??]]]]]]]]]; eauto.
    destruct H0 as [?|[?[??]]].
    - destruct lp'; inverts H0.
    clear H.
    edestruct IHlp as [lq'[?[??]]]; eauto; clear IHlp.
    edestruct step_star_pcsim with (p:=p) as [q'[??]]; eauto.
     (q'::lq'); splits; auto.
    eapply step_star_par_zip'; eauto.
    simpl; congruence.
    apply Forall2_cons; auto.
    - false. subst. destruct lp'; inverts H1. inverts H9.
  Qed.

  Lemma step_star_rep_pcsim_par_list: lp p lq q la pp',
    step_star (par_list lp (p!!)) la pp'
    partial_contrasimilar p q
    pcsim_list lp lq
     lp' lq' qq',
      step_star (par_list lq (q!!)) la qq'
      sbisimilar qq' (par_list lq' (q!!))
      pp' = (par_list lp' (p!!))
      pcsim_list lq' lp'.
  Proof.
    intros.
    edestruct step_star_rep_par_list as [n[lp'[?[??]]]]; eauto.
    subst pp'.
    edestruct step_star_par_list_pcsim with (r:=q!!) as [lq'[?[??]]]; eauto.
    apply Forall2_app; eauto.
    apply Forall2_list_rep; eauto.
    edestruct step_star_sbisim1 with (p:=(par_list (lq++list_rep n q) (q!!))) (q:=par_list lq (q!!)) as [qq'[??]]; eauto.
    rewrite par_list_app.
    apply sbisim_par_list.
    apply sbisim_symm; apply sbisim_list_rep_par_list.
     lp' lq' qq'; splits; auto.
    apply sbisim_symm; auto.
  Qed.

  Lemma pcsim_rep: p q,
    contrasimilar p qpartial_contrasimilar (p_rep p) (p_rep q).
  Proof.
    intros.
     (fun pp qq p q lp lq, sbisimilar pp (par_list lp (p!!)) sbisimilar qq (par_list lq (q!!)) contrasimilar p q pcsim_list lp lq).
    splits; try (clear p q H; intros pp qq [p[q[lp[lq[?[?[??]]]]]]]; subst; intros).
    × false.
    apply halted_sbisim1 in H; auto; subst.
    eapply (par_list_not_halted1 lp (p!!)); eauto. intro; false.
    × renames p' to pp'.
    edestruct step_star_sbisim1 with (p:=pp) as [pp''[??]]; eauto.
    clear pp H H3.
    edestruct step_star_rep_pcsim_par_list as [lp'[lq'[qq'''[?[?[??]]]]]]; eauto.
    apply csim_pcsim1; eauto.
    subst pp''.
    edestruct step_star_sbisim2 with (p:=qq) as [qq''''[??]]; eauto.
     qq''''; splits; auto.
     q p lq' lp'; splits; auto.
    eapply sbisim_trans; eauto.
    apply csim_symm; auto.
    × p q (@nil lts_S) (@nil lts_S); simpl; splits; auto.
    apply sbisim_refl.
    apply sbisim_refl.
    apply Forall2_nil.
  Qed.

  Lemma csim_rep: p q,
    contrasimilar p qcontrasimilar (p_rep p) (p_rep q).
  Proof. intros; apply pcsim_csim; apply pcsim_rep; [| apply csim_symm]; auto. Qed.

End ContrasimulationCongruence.

Module Morphisms.
  Require Import Setoid.
  Require Export Morphisms.
  Export SetoidTactics.
  Require Export SetoidClass.

  Program Instance CBisimCsSetoid : Setoid lts_S :=
      { equiv:= contrasimilar }.
  Program Instance WBisimCsSetoid : Setoid lts_S :=
      { equiv:= wbisimilar }.

  Add Parametric Relation (lts: labeled_transition_system) : lts_S eventually_similar
   reflexivity proved by (esim_refl lts)
   transitivity proved by (esim_trans lts)
  as esim_relation.

  Add Parametric Morphism: (wbisimilar)
  with signature equiv ==> equiv ==> equiv
  as wbisim_m.
  Proof.
    split; intros;
      cut (x==x0); intros; eauto;
      cut (y==y0); intros; eauto.
    rewrite<- H; rewrite<- H0; auto.
    rewrite H; rewrite H0; auto.
  Qed.

  Add Parametric Morphism: p_par
  with signature wbisimilar ==> wbisimilar ==> wbisimilar
  as wbisim_par_m.
  Proof. intros; apply wbisim_par; auto. Qed.

  Add Parametric Morphism: p_seq
  with signature wbisimilar ==> wbisimilar ==> wbisimilar
  as wbisim_seq_m.
  Proof. intros; apply wbisim_seq; auto. Qed.

  Add Parametric Morphism: p_res
  with signature eq ==> eq ==> wbisimilar ==> wbisimilar
  as wbisim_res_m.
  Proof. intros; apply wbisim_res; auto. Qed.

  Add Parametric Morphism: p_res_list
  with signature eq ==> wbisimilar ==> wbisimilar
  as wbisim_res_list_m.
  Proof. intros; apply wbisim_res_list; auto. Qed.

  Definition p_ssumL l p q := p_sum (p_act l p) q.
  Definition p_ssumR l p q := p_sum p (p_act l q).
  Definition p_ssum l1 p l2 q := p_sum (p_act l1 p) (p_act l2 q).
  Lemma eq_ssumL: l p q, p_sum (p_act l p) q = p_ssumL l p q.
  Proof. reflexivity. Qed.
  Lemma eq_ssumR: l p q, p_sum p (p_act l q) = p_ssumR l p q.
  Proof. reflexivity. Qed.
  Lemma eq_ssum: l1 l2 p q, p_sum (p_act l1 p) (p_act l2 q) = p_ssum l1 p l2 q.
  Proof. reflexivity. Qed.

  Add Parametric Morphism: p_ssumL
  with signature eq ==> wbisimilar ==> eq ==> wbisimilar
  as wbisim_ssumL_m.
  Proof.
    intros; repeat rewrite<- eq_ssumL.
    destruct y.
    × apply wbisim_internal_sum; eauto.
    × apply wbisim_stable_sum. apply wbisim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
    × apply wbisim_stable_sum. apply wbisim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
  Qed.

  Add Parametric Morphism: p_ssumR
  with signature eq ==> eq ==> wbisimilar ==> wbisimilar
  as wbisim_ssumR_m.
  Proof. intros; repeat rewrite<- eq_ssumR; setoid_rewrite wbisim_sum_comm; apply wbisim_ssumL_m; auto. Qed.

  Add Parametric Morphism: p_ssum
  with signature eq ==> wbisimilar ==> eq ==> wbisimilar ==> wbisimilar
  as wbisim_ssum.
  Proof.
    intros.
    eapply transitivity.
    apply wbisim_ssumL_m; eauto.
    apply wbisim_ssumR_m; eauto.
  Qed.

  Add Parametric Morphism: p_act
  with signature eq ==> wbisimilar ==> wbisimilar
  as wbisim_act.
  Proof. intros; apply wbisim_act; eauto. Qed.

  Add Parametric Morphism: (eventually_similar)
  with signature eventually_similar --> eventually_similar ++> Basics.impl
  as esim_m.
  Proof.
    repeat intro.
    eapply esim_trans; eauto.
    eapply esim_trans; eauto.
  Qed.

  Add Parametric Morphism: (eventually_similar)
  with signature wbisimilar ==> wbisimilar ==> iff
  as wbisim_esim_m.
  Proof.
    split; intros.
    apply wbisim_symm, wbisim_esim in H.
    apply wbisim_esim in H0.
    eapply esim_trans; eauto.
    eapply esim_trans; eauto.
    apply wbisim_esim in H.
    apply wbisim_symm, wbisim_esim in H0.
    eapply esim_trans; eauto.
    eapply esim_trans; eauto.
  Qed.

  Add Parametric Morphism: p_par
  with signature eventually_similar ++> eventually_similar ++> eventually_similar
  as esim_par_m.
  Proof. intros; apply esim_par; auto. Qed.

  Add Parametric Morphism: p_seq
  with signature eventually_similar ++> eventually_similar ++> eventually_similar
  as esim_seq_m.
  Proof. intros; apply esim_seq; auto. Qed.

  Add Parametric Morphism: p_res
  with signature eq ==> eq ==> eventually_similar ++> eventually_similar
  as esim_res_m.
  Proof. intros; apply esim_res; auto. Qed.

  Add Parametric Morphism: p_res_list
  with signature eq ==> eventually_similar ++> eventually_similar
  as esim_res_list_m.
  Proof. intros; apply esim_res_list; auto. Qed.

  Add Parametric Morphism: p_act
  with signature eq ==> eventually_similar ++> eventually_similar
  as esim_act_m.
  Proof. intros; apply esim_act; auto. Qed.

  Add Parametric Morphism: p_ssumL
  with signature eq ==> eventually_similar ++> eq ==> eventually_similar
  as esim_ssumL_m.
  Proof.
    intros; repeat rewrite<- eq_ssumL.
    destruct y.
    × apply esim_internal_sum; eauto.
    × apply esim_stable_sum. apply esim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
    × apply esim_stable_sum. apply esim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
  Qed.

  Add Parametric Morphism: p_ssumR
  with signature eq ==> eq ==> eventually_similar ++> eventually_similar
  as esim_ssumR_m.
  Proof. intros; repeat rewrite<- eq_ssumR; setoid_rewrite wbisim_sum_comm; apply esim_ssumL_m; auto. Qed.

  Add Parametric Morphism: p_ssum
  with signature eq ==> eventually_similar ++> eq ==> eventually_similar ++> eventually_similar
  as esim_ssum.
  Proof.
    intros.
    eapply transitivity.
    apply esim_ssumL_m; eauto.
    apply esim_ssumR_m; eauto.
  Qed.

  Add Parametric Morphism: (contrasimilar)
  with signature contrasimilar ==> contrasimilar ==> equiv
  as csim_m.
  Proof.
    split; intros;
      cut (contrasimilar x x0); intros; eauto;
      cut (contrasimilar y y0); intros; eauto.
    rewrite<- H; rewrite<- H0; auto.
    rewrite H; rewrite H0; auto.
  Qed.

  Add Parametric Morphism: (contrasimilar)
  with signature eventually_similar ++> eventually_similar ++> equiv
  as esim_csim_m.
  Proof.
    split; intros.
    apply esim_ebisim2, ebisim_csim in H.
    apply esim_ebisim1, ebisim_csim in H0.
    eapply csim_trans; eauto.
    eapply csim_trans; eauto.
    apply esim_ebisim1, ebisim_csim in H.
    apply esim_ebisim2, ebisim_csim in H0.
    eapply csim_trans; eauto.
    eapply csim_trans; eauto.
  Qed.

  Add Parametric Morphism: (contrasimilar)
  with signature wbisimilar ==> wbisimilar ==> equiv
  as wbisim_csim_m.
  Proof.
    split; intros; apply wbisim_csim in H; apply wbisim_csim in H0.
    rewrite<- H; rewrite<- H0; auto.
    rewrite H; rewrite H0; auto.
  Qed.

  Add Parametric Morphism: p_par
  with signature contrasimilar ==> contrasimilar ==> contrasimilar
  as csim_par_m.
  Proof. intros; apply csim_par; auto. Qed.

  Add Parametric Morphism: p_seq
  with signature contrasimilar ==> contrasimilar ==> contrasimilar
  as csim_seq_m.
  Proof. intros; apply csim_seq; auto. Qed.

  Add Parametric Morphism: p_res
  with signature eq ==> eq ==> contrasimilar ==> contrasimilar
  as csim_res_m.
  Proof. intros; apply csim_res; auto. Qed.

  Add Parametric Morphism: p_res_list
  with signature eq ==> contrasimilar ==> contrasimilar
  as csim_res_list_m.
  Proof. intros; apply csim_res_list; auto. Qed.

  Add Parametric Morphism: p_act
  with signature eq ==> contrasimilar ==> contrasimilar
  as csim_act_m.
  Proof. intros; apply csim_act; auto. Qed.

  Add Parametric Morphism: p_ssumL
  with signature eq ==> contrasimilar ==> eq ==> contrasimilar
  as csim_ssumL_m.
  Proof.
    intros; repeat rewrite<- eq_ssumL.
    destruct y.
    × apply csim_internal_sum; eauto.
    × apply csim_stable_sum. apply csim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
    × apply csim_stable_sum. apply csim_act; eauto.
    intro; introv HH; inverts HH.
    intro; introv HH; inverts HH.
  Qed.

  Add Parametric Morphism: p_ssumR
  with signature eq ==> eq ==> contrasimilar ==> contrasimilar
  as csim_ssumR_m.
  Proof. intros; repeat rewrite<- eq_ssumR; setoid_rewrite csim_sum_comm; apply csim_ssumL_m; auto. Qed.

  Add Parametric Morphism: p_ssum
  with signature eq ==> contrasimilar ==> eq ==> contrasimilar ==> contrasimilar
  as csim_ssum.
  Proof.
    intros.
    eapply transitivity.
    apply csim_ssumL_m; eauto.
    apply csim_ssumR_m; eauto.
  Qed.

End Morphisms.

Section MiscLemmas.
  Import Notation.
  Import Notation.U.
  Open Scope proc.

  Lemma wbisim_res_list_tau_sum: AN p q,
    wbisimilar (p_res_list AN (().p+().q)) ((().p_res_list AN p)+(().p_res_list AN q)).
  Proof.
    induction AN; intros.
    simpl. apply wbisim_refl.
    simpl. destruct a.
    setoid_rewrite IHAN.
    apply wbisim_res_tau_sum.
  Qed.

  Lemma wbisim_res_list_act_comm: AN l p,
    ( a, l=l_in a¬In a (fst (split AN))) →
    ( a, l=l_out a¬In a (fst (split AN))) →
    wbisimilar (p_res_list AN (p_act l p)) (p_act l (p_res_list AN p)).
  Proof.
    induction AN; intros.
    apply wbisim_refl.
    simpl. destruct a.
    simpl in ×. setoid_rewrite fst_split in H. setoid_rewrite fst_split in H0.
    simpl in ×.
    setoid_rewrite IHAN; jauto.
    apply wbisim_res_act_comm; jauto.
  Qed.

  Lemma wbisim_res_list_in: AN1 AN2 n AN AN' a p,
    AN = AN1 ++ [(a,S n)] ++ AN2
    AN' = AN1 ++ [(a,n)] ++ AN2
    ¬In a (fst (split AN2)) →
    wbisimilar (p_res_list AN (a?.p)) (p_res_list AN' p).
  Proof.
    intros; subst.
    repeat rewrite p_res_list_app.
    simpl.
    setoid_rewrite wbisim_res_list_act_comm; try solve [introv HH; inverts HH; auto].
    setoid_rewrite wbisim_res_in.
    reflexivity.
  Qed.

  Lemma wbisim_res_list_out: AN1 AN2 n AN AN' a p,
    AN = AN1 ++ [(a,n)] ++ AN2
    AN' = AN1 ++ [(a,S n)] ++ AN2
    ¬In a (fst (split AN2)) →
    wbisimilar (p_res_list AN (a!.p)) (p_res_list AN' p).
  Proof.
    intros; subst.
    repeat rewrite p_res_list_app.
    simpl.
    setoid_rewrite wbisim_res_list_act_comm; try solve [introv HH; inverts HH; auto].
    setoid_rewrite wbisim_res_out.
    reflexivity.
  Qed.

End MiscLemmas.

Section Parallelization.
  Import Notation.
  Import Notation.U.
  Import Morphisms.

  Definition converges AN p :=
     la1 p',
      step_star p la1 p'
       la2 N' N'',
        step_star p' la2 p_nil
        ( a, In (l_out a) la2In a (fst (split AN)))
        ( a, In (l_in a) la2In a (fst (split AN)))
        res_list_count_labels (fst (split AN)) (snd (split AN)) la1 N' = true
        res_list_count_labels (fst (split AN)) N' la2 N'' = true.

  Definition converges' AN p :=
     la1 p',
      step_star p la1 p'
       N',
        res_list_count_labels (fst (split AN)) (snd (split AN)) la1 N' = true
        step_star (p_res_list (combine (fst (split AN)) N') p') nil p_nil.

  Lemma res_list_filter_nil_in_In: A la a,
    res_list_filter A la = []In (l_in a) laIn a A.
  Proof.
    induction la; simpl; intros.
    false.
    rewrite res_list_filter_cons2 in H.
    destruct a.
    false.
    destruct in_dec.
    destruct H0; auto.
    inverts H0; auto.
    false.
    destruct in_dec.
    destruct H0; auto.
    inverts H0; auto.
    false.
  Qed.
  Lemma res_list_filter_nil_out_In: A la a,
    res_list_filter A la = []In (l_out a) laIn a A.
  Proof.
    induction la; simpl; intros.
    false.
    rewrite res_list_filter_cons2 in H.
    destruct a.
    false.
    destruct in_dec.
    destruct H0; auto.
    inverts H0; auto.
    false.
    destruct in_dec.
    destruct H0; auto.
    inverts H0; auto.
    false.
  Qed.

  Lemma converges_converges'_equiv: AN p,
    converges AN p converges' AN p.
  Proof.
    unfold converges, converges'; split; intros.
    × edestruct H as [la2[N'[N''[?[?[?[??]]]]]]]; clear H; eauto.
     N'; split; auto.
    change [] with ([]++@nil lts_L).
    eapply step_star_app.
    apply step_star_res_list with la2; eauto.
    symmetry.
    apply res_list_filter_In_filter; auto.
    eapply step_star_l_tau_nIn; eauto.
    apply step_star_res_list_nil.
    × edestruct H as [N'[??]]; clear H; eauto.
    edestruct step_star_res_list_inv' as [la2[N''[p''[?[?[??]]]]]]; eauto.
    apply res_list_count_labels_length in H1; destruct H1; auto.
    destruct H5.
    + assert (N'' = nil AN=nil).
      apply res_list_count_labels_length in H4.
      destruct H4.
      rewrite split_length_l in ×.
      destruct N''; destruct AN; auto; false.
      destruct p0; simpl in ×.
      rewrite fst_split in H5.
      simpl in H5; discriminate.
    destruct H6; subst.
     (@nil lts_L) (@nil nat) (@nil nat); splits; auto.
    + destruct H5 as [A1[A2[N1'[N2'[?[?[?[?[??]]]]]]]]].
    subst.
    assert (A1 = nil N1'=nil).
      destruct A1; destruct N1'; auto; false.
    destruct H5; subst.
    rewrite app_nil_l in ×.
     la2 N' N2'; splits; auto.
    intros; eapply res_list_filter_nil_out_In; eauto.
    intros; eapply res_list_filter_nil_in_In; eauto.
  Qed.

  Definition term_impl AN p1 p2 := AN' la p2',
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p_nil||p2')) → converges AN' p2'.

  Lemma term_impl_step_star1: AN p1 p2 la AN' p1' p2',
    term_impl AN p1 p2
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    term_impl AN' p1' p2'.
  Proof.
    unfold term_impl; intros.
    eapply H; eauto.
    eapply step_star_app; eauto.
  Qed.

  Lemma term_impl_step_star2: AN p1 p2 la AN' p1' p2',
    step_star (p_res_list AN (p2||p1)) la (p_res_list AN' (p2'||p1')) →
    length AN' = length AN
    term_impl AN p1 p2
    term_impl AN' p1' p2'.
  Proof.
    intros.
    intro; intros.
    eapply H1; eauto.
    apply step_star_res_list_par_swap in H; eauto.
    eapply step_star_app; eauto.
  Qed.

  Lemma converges_nil: AN,
    converges AN p_nil.
  Proof.
    unfold converges; intros.
    apply step_star_nil_resolve in H. destruct H as [??]; subst.
     (@nil lts_L) (snd (split AN)) (snd (split AN)); splits; intros.
    - apply step_nil.
    - false.
    - false.
    - apply res_list_count_labels_nil2.
    rewrite split_length_l, split_length_r; reflexivity.
    - apply res_list_count_labels_nil2.
    rewrite split_length_l, split_length_r; reflexivity.
  Qed.

  Lemma term_impl_nil: AN p,
    term_impl AN p p_nil.
  Proof.
    unfold term_impl; intros.
    edestruct step_star_res_list_inv' with (A:=fst(split AN)) (N:=snd(split AN)) as [la'[N'[p'[?[?[??]]]]]]; eauto.
    rewrite split_combine'; eauto.
    rewrite split_length_l, split_length_r; reflexivity.
    destruct H3 as [? | [?[_[?[_[?[? _]]]]]]].
    × apply p_res_list_eq in H3.
    destruct H3 as [??]; subst.
    edestruct step_star_par_unzip' as [lp[la1[la2[?[?[??]]]]]]; eauto.
    apply step_star_nil_resolve in H3; destruct H3 as [??]; subst.
    apply converges_nil.
    intros; split; intro.
    false.
    subst.
    edestruct step_star_par_unzip as [p1'[p2'_[la1[la2[?[?[? _]]]]]]]; eauto.
    destruct H1 as [? | [?[??]] ]; subst; false.
    × subst. false.
    apply p_res_list_eq in H4.
    destruct H4 as [??]; subst.
    false.
    intros; split; intro; false.
  Qed.

  Lemma converges_step_star: AN p la AN' p',
    converges AN p
    step_star p la p'
    res_list_count_labels (fst (split AN)) (snd (split AN)) la (snd (split AN')) = true
    fst (split AN') = fst (split AN) →
    converges AN' p'.
  Proof.
    unfold converges; intros.
    edestruct H with (p':=p'0) as [la2[N'[N''[?[?[?[??]]]]]]]; clear H.
    eapply step_star_app; eauto.
    rewrite H2.
    apply res_list_count_labels_app2 in H7.
    destruct H7 as [N'0[??]].
    apply res_list_count_labels_fun with (N'1:=snd (split AN')) in H; eauto; subst N'0.
     la2 N' N''; splits; intros; auto.
  Qed.

  Lemma converges_term_impl: AN p,
    converges AN p
    term_impl AN p_nil p.
  Proof.
    unfold term_impl; intros.
    edestruct step_star_res_list_inv' with (A:=fst(split AN)) (N:=snd(split AN)) as [la'[N'[p'[?[?[??]]]]]]; eauto.
    rewrite split_combine'; eauto.
    rewrite split_length_l, split_length_r; reflexivity.
    destruct H4 as [? | [?[_[?[_[?[? _]]]]]]].
    × apply p_res_list_eq in H4.
    destruct H4 as [??]; subst.
    edestruct step_star_par_unzip' as [lp[la1[la2[?[?[??]]]]]]; eauto.
    apply step_star_nil_resolve in H2; destruct H2 as [??]; subst.
    rewrite interleaving_l_nil in H3.
    rewrite interleaving_l_nil in H0.
    eapply converges_step_star; eauto.
    rewrite combine_split.
    exact H3.
    apply res_list_count_labels_length in H3; destruct H3; auto.
    rewrite combine_split; auto.
    apply res_list_count_labels_length in H3; destruct H3; auto.
    intros; split; intro.
    false.
    subst.
    edestruct step_star_par_unzip as [p1'[p2'_[la1[la2[?[?[? _]]]]]]]; eauto.
    destruct H2 as [? | [?[??]] ]; subst; false.
    × subst. false.
    apply p_res_list_eq in H5.
    destruct H5 as [??]; subst.
    false.
    intros; split; intro; false.
  Qed.

  Lemma noninterference_step_star_res_list1: AN p1 p2 p3 p4 la AN' p1' p2',
    NameSet.Equal (NameSet.inter (fn_in (p1;;p3)) (fn_in (p2;; p4))) NameSet.empty
    step_star (p_res_list AN (p1||p2)) la (p_res_list AN' (p1'||p2')) →
    fst (split AN') = fst (split AN) →
    NameSet.Equal (NameSet.inter (fn_in (p1';;p3)) (fn_in (p2';; p4))) NameSet.empty.
  Proof.
    introv Hnoninterference Hss HA.
    apply NameSetProps.subset_antisym.
    setoid_rewrite<- Hnoninterference.
    apply NameSet_Subset_inter.
    simpl.
    apply NameSetProps.union_subset_4.
    eapply fn_in_subset_step_star_res_list_parL; eauto.
    simpl.
    rewrite<- (split_length_l AN'). rewrite HA. apply split_length_l.
    apply NameSetProps.union_subset_4.
    eapply fn_in_subset_step_star_res_list_parR; eauto.
    rewrite<- (split_length_l AN'). rewrite HA. apply split_length_l.
    apply NameSetProps.subset_empty.
  Qed.

  Lemma seq_par_inversion: AN p1 p2 p3 p4 l p',
    step (p_res_list AN ((p1||p2);;(p3||p4))) l p'
    ( AN' p1' p2',
      p' = p_res_list AN' ((p1'||p2');; (p3||p4))
      step (p_res_list AN (p1||p2)) l (p_res_list AN' (p1'||p2'))
      fst (split AN)=fst (split AN'))
    (p1=p_nil p2=p_nil p'=p_res_list AN (p_nil;; (p3||p4))).
  Proof.
    intros.
    edestruct step_res_list_inv as [ [l'[AN'[p'_[?[?[?[??]]]]]]] | [AN1[AN2[?[?[?[??]]]]]] ]; eauto.
    × subst. inverts H0. inverts H8.
    - left; AN' p'0 p2; splits; auto.
    eapply step_res_list; eauto.
    apply s_parL; auto.
    - left; AN' p1 q'; splits; auto.
    eapply step_res_list; eauto.
    apply s_parR; auto.
    - right; splits; auto.
    apply res_list_count_labels_single in H4.
    destruct H4 as [ [A1[a[A2[N1[n[N2[n'[?[?[?[?[??]]]]]]]]]]]] | [??] ].
    destruct H7 as [[??]|[??]]; false.
    rewrite<- (split_combine' AN).
    rewrite<- (split_combine' AN').
    congruence.
    × false.
  Qed.

  Lemma step_star_par_seq_converges: AN p3 p2 p4 la AN' p',
    step_star (p3 || p2;; p4) la p'
    converges AN p2
    fst (split AN') = fst (split AN) →
    res_list_count_labels (fst (split AN)) (snd (split AN)) la (snd (split AN')) = true
    (hidden_fn (fst (split AN)) p3 hidden_fn (fst (split AN)) p2) →
    NameSet.Equal (NameSet.inter (fn_in p3) (fn_in (p2;;p4))) NameSet.empty
     la1 la2 pp'' AN'0 AN'1,
      (res_list_filter (fst (split AN)) la) = la1 ++ la2
      fst (split AN'0) = fst (split AN)
      (( p3' p24', p'=p3'||p24' fst (split AN'1)=fst (split AN)) (p'=p_nil pp'' =p_nil AN'1=nil))%proc
      step_star (p_res_list AN' p') nil (p_res_list AN'1 pp'')
      step_star (p_res_list AN p2) la1 (p_res_list AN'0 p_nil)
      step_star (p_res_list AN'0 (p3||p4)) la2 (p_res_list AN'1 pp'').
  Proof.
    introv Hss Hconv HA Hres Hhidden Hfn_in; intros.
    edestruct step_star_parR_seq_inv as [ [p3'[p2'[Hss' ?]]] | [la1[la2[p3'[Hss1[Hss2 ?]]]]] ]; eauto; clear Hss.
    × subst. rename Hss' into Hss.
    edestruct step_star_par_unzip' with (p2:=p2) as [lp[la3[la1[Hss_p3[Hss_p2[??]]]]]]; eauto.
    clear Hss. subst.
    rewrite <-interleaving_com in Hres; [| omega].
    edestruct Hconv as [la2[D'[D''[Hss_p2'[?[?[Hres1 Hres2]]]]]]]; clear Hconv; eauto.
    eapply res_list_count_labels_interleaving1 with (N0:=snd(split AN)) (N0':=D') in Hres; auto.
    apply res_list_count_labels_app2 in Hres.
    destruct Hres as [N'0[Hres1' Hres3]].

    apply res_list_count_labels_fun with (N'1:=N'0) (N'2:=D') in Hres1'; eauto; subst D'.

    assert (HlenD'': length D'' = length AN).
      { apply res_list_count_labels_length in Hres2. destruct Hres2 as [H4 H5].
      rewrite H5.
      rewrite split_length_l. auto. }
    assert (HlenN'0: length N'0 = length AN).
      { apply res_list_count_labels_length in Hres2. destruct Hres2 as [H4 H5].
      rewrite H4.
      rewrite split_length_l. auto. }

    assert (Hnoninter: a, (In (l_in a) la1 In (l_in a) la2) → In (l_in a) la3False).
      { clear -Hfn_in Hss_p3 Hss_p2 Hss_p2'. intros.
      apply (NameSetProps.Dec.F.empty_iff a).
      setoid_rewrite<- Hfn_in. simpl.
      apply NameSetProps.Dec.F.inter_3.
      eapply fn_in_step_star_In; eauto.
      apply NameSetProps.FM.union_2.
      destruct H.
      eapply fn_in_step_star_In; eauto.
      eapply fn_in_subset_step_star; eauto.
      eapply fn_in_step_star_In; eauto. }

    destruct (res_list_count_labels_com _ la3 N'0 (snd (split AN')) la2 D'' Hres3) as [N'''[Hres2' Hres3']].
    exact Hres2.
    intros; eapply Hnoninter; eauto.

    assert (length N''' = length AN).
      { apply res_list_count_labels_length in Hres3'; destruct Hres3'. rewrite split_length_l in H3; auto. }

    assert (step_star (p_res_list AN' (p3' p2';; p4)) []
        (p_res_list (combine (fst (split AN)) N''') (p3' p4))).
      { apply step_star_res_list' with la2; auto.
      eapply step_tsnoc.
      apply step_star_parR_seq_app.
      apply step_star_parR_step; eauto.
      apply s_parR; apply s_seq_nil.
      rewrite combine_split; auto. rewrite split_length_l; auto.
      symmetry; apply res_list_filter_In_filter; auto.
      eapply step_star_l_tau_nIn; eauto.
      repeat (rewrite combine_split; auto).
      simpl.
      rewrite HA; apply H1.
      rewrite HA; apply H.
      rewrite HA.
      rewrite combine_split; auto. rewrite split_length_l; auto. }

    destruct Hhidden as [Hhidden|Hhidden].
    + (res_list_filter (fst (split AN)) la1) (@nil lts_L) (p3'||p4)%proc
        (combine (fst (split AN)) D'')
        (combine (fst (split AN)) N'''); splits; auto.
    - rewrite app_nil_r.
    rewrite<- interleaving_com.
    apply res_list_filter_interleaving_fn2.
    eapply step_star_l_tau_nIn; eauto.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    omega.
    - rewrite combine_split; auto. rewrite split_length_l; auto.
    - left; do 2 eexists; split; eauto.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    - rewrite<- (app_nil_r (res_list_filter _ _)).
    eapply step_star_app.
    apply step_star_res_list' with (la:=la1) (AN':=combine (fst (split AN)) N'0); auto.
    apply Hss_p2.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    apply step_star_res_list with la2; auto.
    symmetry. apply res_list_filter_In_filter; auto.
    eapply step_star_l_tau_nIn; eauto.
    - apply step_star_res_list with la3; auto.
    apply step_star_parL_step; auto.
    symmetry. apply res_list_filter_In_filter; auto.
    eapply step_star_l_tau_nIn; eauto.
    intros; apply Hhidden; eapply fn_step_star_In; eauto.
    intros; apply Hhidden; eapply fn_step_star_In; eauto.
    + (@nil lts_L) (res_list_filter (fst (split AN)) la3) (p3'||p4)%proc
        (combine (fst (split AN)) D'')
        (combine (fst (split AN)) N'''); splits; auto.
    - rewrite app_nil_l.
    apply res_list_filter_interleaving_fn2.
    eapply step_star_l_tau_nIn; eauto.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    - rewrite combine_split; auto. rewrite split_length_l; auto.
    - left; do 2 eexists; split; eauto.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    - rewrite<- (app_nil_r (@nil _)).
    eapply step_star_app.
    apply step_star_res_list' with (la:=la1) (AN':=combine (fst (split AN)) N'0); auto.
    apply Hss_p2.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    symmetry. apply res_list_filter_In_filter; auto.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    rewrite combine_split; auto. rewrite split_length_l; auto.
    apply step_star_res_list with la2; auto.
    symmetry. apply res_list_filter_In_filter; auto.
    eapply step_star_l_tau_nIn; eauto.
    - apply step_star_res_list with la3; auto.
    apply step_star_parL_step; auto.

    + clear -Hss_p2 Hss_p3 Hfn_in; intros.
    apply NameSetFacts.empty_iff with (x:=a).
    rewrite<- Hfn_in.
    apply NameSetProps.Dec.F.inter_3.
    eapply fn_in_step_star_In; eauto.
    apply NameSetProps.Dec.F.union_2.
    eapply fn_in_step_star_In; eauto.

    + apply list_le_refl.

    × subst la.
    apply res_list_count_labels_app2 in Hres.
    destruct Hres as [N'1[Hres1 Hres3]].
    edestruct step_star_par_unzip' with (p2:=p2) as [lp[la12[la11[Hss_p3[Hss_p2[??]]]]]]; eauto.
    clear Hss1. subst la1.
    rewrite <-interleaving_com in Hres1; [| omega].
    edestruct Hconv as [la3[D'[_[_[_[_[Hres11 _]]]]]]]; clear Hconv; eauto.
    eapply res_list_count_labels_interleaving1 with (N0:=snd(split AN)) (N0':=D') in Hres1; auto.
    apply res_list_count_labels_app2 in Hres1.
    destruct Hres1 as [N'0[Hres11' Hres12]].
    apply res_list_count_labels_fun with (N'1:=N'0) (N'2:=D') in Hres11'; eauto; subst D'.

    assert (HlenN'0: length N'0 = length AN).
      { apply res_list_count_labels_length in Hres11. destruct Hres11 as [H2 H3].
      rewrite H3.
      rewrite split_length_l. split; auto. }

    assert ( (AN'':list (name×nat)), ( p3'' p4'', p'=p3''||p4'' AN''=AN') (p'=p_nil AN''=[]))%proc.
    { clear -Hss2 Hres3.
      edestruct step_star_par_unzip as [p3''[p4''[la4[la5[?[?[? _]]]]]]]; eauto.
      destruct H as [? | [?[??]] ]; subst; eauto.
       AN'; left; eauto. }
    destruct H as [AN'' ?].

    destruct Hhidden as [Hhidden|Hhidden].
    + (res_list_filter (fst (split AN)) la11) (res_list_filter (fst (split AN)) (la12++la2)) p'
        (combine (fst (split AN)) N'0)
        AN''; splits; auto.
    - repeat rewrite res_list_filter_app2.
    f_equal.
    rewrite<- interleaving_com.
    apply res_list_filter_interleaving_fn2.
    eapply step_star_l_tau_nIn; eauto.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    omega.
    rewrite (res_list_filter_In_filter (fst (split AN)) la12).
    reflexivity.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    - rewrite combine_split; auto. rewrite split_length_l; auto.
    - destruct H as [ [p3''[p4''[??]]] | [??] ]; subst; eauto.
    - destruct H as [ [p3''[p4''[??]]] | [??] ]; subst.
    apply step_nil.
    apply step_star_res_list_nil.
    - rewrite<- (split_combine' AN) at 1.
    apply step_star_res_list with la11; auto.
    - rewrite res_list_filter_app2.
    eapply step_star_app.
    apply step_star_res_list with la12; auto.
    apply step_star_parL_step; eauto.
    apply Hres12.
    destruct H as [ [p3''[p4''[??]]] | [??] ]; subst.
    rewrite<- (split_combine' AN').
    rewrite HA.
    apply step_star_res_list with la2; auto.
    rewrite<- (app_nil_r (res_list_filter (fst (split AN)) la2)).
    eapply step_star_app.
    apply step_star_res_list with la2; eauto.
    apply step_star_res_list_nil.

    + (@nil lts_L) (res_list_filter (fst (split AN)) (la12++la2)) p'
        (combine (fst (split AN)) N'0)
        AN''; splits; auto.
    - simpl. repeat rewrite res_list_filter_app2.
    f_equal; auto.
    apply res_list_filter_interleaving_fn2.
    eapply step_star_l_tau_nIn; eauto.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    - rewrite combine_split; auto. rewrite split_length_l; auto.
    - destruct H as [ [p3''[p4''[??]]] | [??] ]; subst; eauto.
    - destruct H as [ [p3''[p4''[??]]] | [??] ]; subst.
    apply step_nil.
    apply step_star_res_list_nil.
    - rewrite<- (split_combine' AN) at 1.
    apply step_star_res_list with la11; auto.
    symmetry; apply res_list_filter_In_filter.
    eapply step_star_l_tau_nIn; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    intros. eapply Hhidden. eapply fn_step_star_In; eauto.
    - rewrite res_list_filter_app2.
    eapply step_star_app.
    apply step_star_res_list with la12; auto.
    apply step_star_parL_step; eauto.
    apply Hres12.
    destruct H as [ [p3''[p4''[??]]] | [??] ]; subst.
    rewrite<- (split_combine' AN').
    rewrite HA.
    apply step_star_res_list with la2; auto.
    rewrite<- (app_nil_r (res_list_filter (fst (split AN)) la2)).
    eapply step_star_app.
    apply step_star_res_list with la2; eauto.
    apply step_star_res_list_nil.

    + clear -Hss_p2 Hss_p3 Hfn_in; intros.
    apply NameSetFacts.empty_iff with (x:=a).
    rewrite<- Hfn_in.
    apply NameSetProps.Dec.F.inter_3.
    eapply fn_in_step_star_In; eauto.
    apply NameSetProps.Dec.F.union_2.
    eapply fn_in_step_star_In; eauto.

    + apply list_le_refl.
  Qed.

  Lemma step_star_par_seq_converges1: AN p3 p2 p4 la AN' p',
    step_star (p3 || p2;; p4) la p'
    converges AN p2
    fst (split AN') = fst (split AN) →
    res_list_count_labels (fst (split AN)) (snd (split AN)) la (snd (split AN')) = true
    hidden_fn (fst (split AN)) p2
    NameSet.Equal (NameSet.inter (fn_in p3) (fn_in (p2;;p4))) NameSet.empty
     pp'' AN'0,
      fst (split AN'0) = fst (split AN)
      (p'=p_nilpp'' =p_nil)
      step_star (p_res_list AN' p') nil pp''
      step_star (p_res_list AN p2) nil (p_res_list AN'0 p_nil)
      step_star (p_res_list AN'0 (p3||p4)) (res_list_filter (fst (split AN)) la) pp''.
  Proof.
    intros.
    edestruct step_star_par_seq_converges as [la1[la2[pp''[AN'0[AN'1[?[?[?[?[??]]]]]]]]]]; eauto.
    replace la1 with (@nil lts_L) in ×.
    rewrite app_nil_l in ×.
    subst la2.
     (p_res_list AN'1 pp'') AN'0; splits; intros; auto.
    destruct H7 as [ [p3'[p24'[??]]] | [?[??]] ]; subst; eauto. discriminate.
    symmetry; eapply step_star_hidden; eauto.
  Qed.

  Lemma step_star_par_seq_converges2: AN p1 p3 p4 la AN' p',
    step_star (p1;; p3 || p4) la p'
    converges AN p1
    fst (split AN') = fst (split AN) →
    res_list_count_labels (fst (split AN)) (snd (split AN)) la (snd (split AN')) = true
    hidden_fn (fst (split AN)) p4
    NameSet.Equal (NameSet.inter (fn_in (p1;;p3)) (fn_in p4)) NameSet.empty
     la1 la2 pp'' AN'0,
      res_list_filter (fst (split AN)) la = la1 ++ la2
      fst (split AN'0) = fst (split AN)
      (p'=p_nilpp'' =p_nil)
      step_star (p_res_list AN' p') nil pp''
      step_star (p_res_list AN p1) la1 (p_res_list AN'0 p_nil)
      step_star (p_res_list AN'0 (p3||p4)) la2 pp''.
  Proof.
    introv Hss Hconv HA Hresla Hhidden Hnoninter.
    apply step_star_par_swap' in Hss.
    edestruct step_star_par_seq_converges as [la1[la2[pp''[AN'0[AN'1[?[?[?[?[??]]]]]]]]]]; eauto.
    setoid_rewrite NameSetProps.inter_sym; auto.
    destruct H1 as [ [p3'[p24'[??]]] | [?[??]] ].
    × destruct p'; simpl in H1; inverts H1.
    apply step_star_res_list_par_swap in H2.
    apply step_star_res_list_par_swap in H4.
     la1 la2 (p_res_list AN'1 (par_swap pp'')) AN'0; splits; auto.
    intros; discriminate.
    rewrite<- (split_length_l AN'1); rewrite<- (split_length_l AN'0); congruence.
    rewrite<- (split_length_l AN'1); rewrite<- (split_length_l AN'); congruence.
    × subst; simpl in ×.
    destruct p'; simpl in H1; try discriminate.
     la1 la2 p_nil AN'0; splits; auto.
    apply step_star_res_list_par_swap_nil'.
    simpl; auto.
  Qed.

  Lemma par_seq_inversion: AN p1 p2 p3 p4 la p',
    step_star (p_res_list AN ((p1;;p3)||(p2;;p4))) la p'
    term_impl AN p1 p2term_impl AN p2 p1
    hidden_fn (fst (split AN)) (p2;;p4) →
    NameSet.Equal (NameSet.inter (fn_in (p1;;p3)) (fn_in (p2;; p4))) NameSet.empty
    ( AN' p1' p2',
      p' = p_res_list AN' ((p1';;p3)||(p2';;p4))
      step_star (p_res_list AN (p1 || p2)) la (p_res_list AN' (p1' || p2'))
      fst (split AN) = fst (split AN'))
    ( p'',
      step_star (p_res_list AN ((p1||p2);;(p3||p4))) la p'' step_star p' nil p'').
  Proof.
    introv Hss Hterm12 Hterm21 Hhidden Hnoninter; intros. rename p' into pp'.
    edestruct step_star_res_list_inv' with (A:=fst (split AN)) (N:=snd (split AN)) as [la'[N'[p'[Hss'[?[??]]]]]]; eauto.
    rewrite split_combine'. eauto.
    rewrite split_length_l; rewrite split_length_r; reflexivity.
    clear Hss; rename Hss' into Hss.
    subst.
    edestruct step_star_par_seq_inv as [ [p1'[p2'[??]]] | [ [la1[p2'[la2[?[??]]]]] | [la1[p1'[la2[?[??]]]]] ] ]; eauto;
      clear Hss; try rename Hss' into Hss.
    × subst.
    destruct H1 as [? | [A1[A2[N1'[N2'[? _]]]]] ]; [| discriminate]. subst.
    left; do 3 eexists; splits; eauto.
    rewrite<- (split_combine' AN) at 1.
    eapply step_star_res_list; eauto.
    rewrite combine_split. reflexivity.
    apply res_list_count_labels_length in H0; destruct H0 as [??]; auto.
    × subst.
    apply res_list_count_labels_app2 in H0.
    destruct H0 as [N'0[??]].
    remember (combine (fst (split AN)) N'0) as AN'0.
    remember (combine (fst (split AN)) N') as AN'.
    assert (HA'0: fst (split AN'0) = fst (split AN)).
      subst. rewrite combine_split. reflexivity. apply res_list_count_labels_length in H0; destruct H0 as [_ H0]; auto.
    assert (HA': fst (split AN') = fst (split AN)).
      subst. rewrite combine_split. reflexivity. apply res_list_count_labels_length in H3; destruct H3 as [_ H3]; auto.
    assert (HlenAN'0: length AN'0 = length AN).
      rewrite<- split_length_l. rewrite<- split_length_l. congruence.
    assert (HlenAN': length AN' = length AN).
      rewrite<- split_length_l. rewrite<- split_length_l. congruence.
    replace N'0 with (snd (split AN'0)) in ×.
      2: subst; rewrite combine_split; [| apply res_list_count_labels_length in H0; destruct H0 as [_ H0]]; auto.
    replace N' with (snd (split AN')) in ×.
      2: subst AN'; rewrite combine_split; [| apply res_list_count_labels_length in H3; destruct H3 as [_ H3]]; auto.
    clear HeqAN' HeqAN'0.
    edestruct step_star_par_seq_converges1 with (AN:=AN'0) (AN':=AN') as [pp''[AN'1[?[?[?[??]]]]]]; eauto; try congruence.
    eapply Hterm12.
    eapply step_star_res_list'; eauto.
    congruence.
    rewrite HA'0.
    eapply hidden_fn_step_star_parL with (p2:=p2); eauto.
    eapply hidden_fn_seq1; eauto.
    apply NameSetProps.subset_antisym.
    setoid_rewrite<- Hnoninter.

    apply NameSet_Subset_inter.
    apply NameSetProps.union_subset_2.
    simpl.
    apply NameSetProps.union_subset_4.

    eapply fn_in_subset_step_star_parR; eauto.
    apply NameSetProps.subset_empty.
    assert (HlenAN'0'1: length AN'1 = length AN'0).
      rewrite<- (split_length_l AN'0). rewrite<- H4. rewrite split_length_l. reflexivity.
    right; pp''; split.
    rewrite res_list_filter_app2.
    eapply step_star_app.
    apply step_star_res_list_seq_app with (AN':=AN'0); auto.
    eapply step_star_res_list'; eauto.

    rewrite<- (app_nil_l la2). rewrite res_list_filter_app2. rewrite res_list_filter_nil2.
    eapply step_star_app.
    apply step_star_res_list_seq_app with (AN':=AN'1); eauto.
    apply step_star_res_list_parR_step; eauto.
    eapply step_tcons.
    apply s_res_list; try solve [intros; false]. apply s_seq; apply s_par_nil.
    eapply step_tcons.
    apply s_res_list; try solve [intros; false]. apply s_seq_nil.
    rewrite<- HA'0.
    apply H8.
    destruct H1 as [? | [A1[A2[N1'[N2'[?[?[?[?[??]]]]]]]]]].
    subst; auto.
    subst.
    rewrite H5; auto.
    apply step_star_res_list_nil.
    × subst.
    apply res_list_count_labels_app2 in H0.
    destruct H0 as [N'0[??]].
    remember (combine (fst (split AN)) N'0) as AN'0.
    remember (combine (fst (split AN)) N') as AN'.
    assert (HA'0: fst (split AN'0) = fst (split AN)).
      subst. rewrite combine_split. reflexivity. apply res_list_count_labels_length in H0; destruct H0 as [_ H0]; auto.
    assert (HA': fst (split AN') = fst (split AN)).
      subst. rewrite combine_split. reflexivity. apply res_list_count_labels_length in H3; destruct H3 as [_ H3]; auto.
    assert (HlenAN'0: length AN'0 = length AN).
      rewrite<- split_length_l. rewrite<- split_length_l. congruence.
    assert (HlenAN': length AN' = length AN).
      rewrite<- split_length_l. rewrite<- split_length_l. congruence.
    replace N'0 with (snd (split AN'0)) in ×.
      2: subst; rewrite combine_split; [| apply res_list_count_labels_length in H0; destruct H0 as [_ H0]]; auto.
    replace N' with (snd (split AN')) in ×.
      2: subst AN'; rewrite combine_split; [| apply res_list_count_labels_length in H3; destruct H3 as [_ H3]]; auto.
    clear HeqAN' HeqAN'0.
    edestruct step_star_par_seq_converges2 with (AN:=AN'0) (AN':=AN') as [la3[la4[pp''[AN'1[?[?[?[?[??]]]]]]]]]; eauto; try congruence.
    eapply Hterm21.
    apply step_star_res_list_par_swap'; eauto.
    eapply step_star_res_list'; eauto; congruence.
    congruence.
    rewrite HA'0.
    eapply hidden_fn_seq2; eauto.
    apply NameSetProps.subset_antisym.
    setoid_rewrite<- Hnoninter.
    apply NameSet_Subset_inter.
    simpl.
    apply NameSetProps.union_subset_4.
    eapply fn_in_subset_step_star_parL; eauto.
    apply NameSetProps.union_subset_2.
    apply NameSetProps.subset_empty.
    assert (HlenAN'0'1: length AN'1 = length AN'0).
      rewrite<- (split_length_l AN'0). rewrite<- H5. rewrite split_length_l. reflexivity.
    right; pp''; split.
    rewrite res_list_filter_app2.
    rewrite HA'0 in H4.
    rewrite H4.
    eapply step_star_app.
    apply step_star_res_list_seq_app with (AN':=AN'0); auto.
    eapply step_star_res_list'; eauto.
    eapply step_star_app.
    apply step_star_res_list_seq_app with (AN':=AN'1); eauto.
    apply step_star_res_list_parL_step; eauto.
    eapply step_tcons.
    apply s_res_list; try solve [intros; false]. apply s_seq; apply s_par_nil.
    eapply step_tcons; eauto.
    apply s_res_list; try solve [intros; false]. apply s_seq_nil.
    destruct H1 as [? | [A1[A2[N1'[N2'[?[?[?[?[??]]]]]]]]]].
    subst; auto.
    subst.
    rewrite H6; auto.
    apply step_star_res_list_nil.
  Qed.

  Lemma pbisim_seq_par: AN p1 p2 p3 p4,
    term_impl AN p1 p2term_impl AN p2 p1
    hidden_fn (fst (split AN)) (p2;; p4) →
    NameSet.Equal (NameSet.inter (fn_in (p1;; p3)) (fn_in (p2;; p4))) NameSet.empty
    par_bisimilar (p_res_list AN ((p1||p2);; (p3||p4))) (p_res_list AN ((p1;;p3) || (p2;;p4))).
  Proof.
    intros.
     (fun p q
      ( AN p1 p2,
        q = p_res_list AN ((p1;;p3)||(p2;;p4))
        p = p_res_list AN ((p1||p2);;(p3||p4))
        term_impl AN p1 p2 term_impl AN p2 p1
        hidden_fn (fst (split AN)) (p2;; p4)
        NameSet.Equal (NameSet.inter (fn_in (p1;; p3)) (fn_in (p2;; p4))) NameSet.empty)
      wbisimilar p q
    ).
    split.
    clear AN p1 p2 H H0 H1 H2.
    splits; intros p q [ [AN[p1[p2[?[?[Hterm1[Hterm2[Hhidden Hnoninterference]]]]]]]] | ? ]; subst; intros; unfold inv in ×.
    × false. repeat red in H.
    destruct AN; simpl in H. discriminate.
    destruct p; discriminate.
    × eapply halted_wbisim1; eauto.
    × false. repeat red in H.
    destruct AN; simpl in H. discriminate.
    destruct p; discriminate.
    × eapply halted_wbisim2; eauto.
    × split; intros.
    - edestruct seq_par_inversion with (l:=l_tau) as [ [AN'[p1'[p2'[?[??]]]]] | [?[??]] ]; eauto.
    apply H.
    assert (Hlen: length AN' = length AN).
      rewrite<- (split_length_l AN'); rewrite<- (split_length_l AN); congruence.
    + (p_res_list AN' ((p1';; p3) || (p2';; p4))); split.
    apply single_tstep.
    apply step_res_list_par_seq_app; auto.
    left; do 3 eexists; splits; eauto.
    eapply term_impl_step_star1 with (AN:=AN) (p1:=p1) (p2:=p2); auto.
    apply single_tstep; auto.
    eapply term_impl_step_star2; eauto.
    apply single_tstep; auto.
    subst p'.
    rewrite<- H2; intros.
    apply hidden_fn_seq3.
    eapply hidden_fn_step_star_res_list_parR with (p2:=p2); eauto.
    apply single_tstep. apply H1.
    eapply hidden_fn_seq1; eauto.
    eapply hidden_fn_seq2; eauto.
    eapply noninterference_step_star_res_list1 with (AN:=AN) (AN':=AN'); eauto.
    apply single_tstep. apply H1.
    + subst. (p_res_list AN (p3||p4)); split.
    eapply step_tcons.
    eapply s_res_list; eauto.
    intros; discriminate.
    intros; discriminate.
    apply s_parL; apply s_seq_nil.
    eapply single_tstep.
    eapply s_res_list; eauto.
    intros; discriminate.
    intros; discriminate.
    apply s_parR; apply s_seq_nil.
    right.
    eapply wbisim_res_list; eauto.
    apply wbisim_nil_seq.
    - rename a into l.
    assert ( a, l=l_in a l=l_out a).
      inverts H; eauto.
    destruct H0 as [q ?].
    edestruct seq_par_inversion with (l:=l) as [ [AN'[p1'[p2'[?[??]]]]] | [?[??]] ]; eauto.
    inverts H; eauto.
    + assert (Hlen: length AN' = length AN).
      rewrite<- (split_length_l AN'); rewrite<- (split_length_l AN); congruence.
     (p_res_list AN' ((p1';; p3) || (p2';; p4))); split.
    apply single_lstep.
    inverts H; constructor; eauto; apply step_res_list_par_seq_app; auto.
    left; do 3 eexists; splits; eauto.
    eapply term_impl_step_star1 with (AN:=AN) (p1:=p1) (p2:=p2); auto.
    apply single_lstep with (a:=l); destruct H0; subst; constructor; eauto.
    eapply term_impl_step_star2; eauto.
    apply single_lstep with (a:=l); destruct H0; subst; constructor; eauto.
    subst p'.
    rewrite<- H3; intros.
    apply hidden_fn_seq3.
    eapply hidden_fn_step_star_res_list_parR with (p2:=p2) (la:=[l]); eauto.
    destruct H0 as [?|?]; subst l; apply single_lstep; constructor; eauto.
    eapply hidden_fn_seq1; eauto.
    eapply hidden_fn_seq2; eauto.
    eapply noninterference_step_star_res_list1 with (AN:=AN) (AN':=AN') (la:=[l]); eauto.
    destruct H0 as [?|?]; subst l; apply single_lstep; constructor; eauto.
    + subst.
    edestruct step_res_list_inv'' as [l'[?[?[??]]]]; eauto.
    inverts H; eauto.
    inverts H1. inverts H8.
    destruct H0; subst.
    destruct H3 as [ [a[?[??]]] | [??] ]; false.
    destruct H3 as [ [a[?[??]]] | [??] ]; false.
    × split; intros.
    - edestruct tstep_wbisim1 as [q'[??]]; eauto.
    - edestruct lstep_wbisim1 as [q'[??]]; eauto.
    × edestruct par_seq_inversion as [ [AN'[p1'[p2'[?[??]]]]] | [p''[??]] ]; eauto.
    assert (Hlen: length AN' = length AN).
      rewrite<- (split_length_l AN'); rewrite<- (split_length_l AN); congruence.
    left; eexists; split.
    apply step_star_res_list_seq_app; eauto.
    subst p'.
    left; do 3 eexists; splits; auto.
    eapply term_impl_step_star1. 2: apply H1. auto.
    eapply term_impl_step_star2; eauto.
    rewrite<- H2; intros.
    apply hidden_fn_seq3.
    eapply hidden_fn_step_star_res_list_parR; eauto.
    eapply hidden_fn_seq1; eauto.
    eapply hidden_fn_seq2; eauto.
    eapply noninterference_step_star_res_list1; eauto.
    × edestruct step_star_wbisim2 as [q'[??]]; eauto.
    × left; do 3 eexists; splits; eauto.
  Qed.

  Corollary esim_seq_par: AN p1 p2 p3 p4,
    term_impl AN p1 p2term_impl AN p2 p1
    hidden_fn (fst (split AN)) (p2;; p4) →
    NameSet.Empty (NameSet.inter (fn_in (p1;; p3)) (fn_in (p2;; p4))) →
    eventually_similar (p_res_list AN ((p1||p2);; (p3||p4))) (p_res_list AN ((p1;;p3) || (p2;;p4))).
  Proof.
    intros.
    apply pbisim_esim.
    eapply pbisim_seq_par; eauto.
    apply NameSetProps.empty_is_empty_1; auto.
  Qed.

  Corollary csim_seq_par: AN p1 p2 p3 p4,
    term_impl AN p1 p2term_impl AN p2 p1
    hidden_fn (fst (split AN)) (p2;; p4) →
    NameSet.Empty (NameSet.inter (fn_in (p1;; p3)) (fn_in (p2;; p4))) →
    contrasimilar (p_res_list AN ((p1||p2);; (p3||p4))) (p_res_list AN ((p1;;p3) || (p2;;p4))).
  Proof.
    intros.
    apply pbisim_csim.
    eapply pbisim_seq_par; eauto.
    apply NameSetProps.empty_is_empty_1; auto.
  Qed.

  Ltac NameSet_simpl:=
    unfold NameSet.Equal, NameSet.Subset in *; intros;
    repeat first [rewrite NameSet.remove_spec in ×
      | rewrite NameSet.add_spec in ×
      | rewrite NameSet.remove_spec in ×
      | rewrite NameSet.union_spec in ×
      | rewrite NameSet.inter_spec in ×
      | rewrite NameSet.diff_spec in ×
      | rewrite NameSetFacts.empty_iff in ×
      ].

  Corollary esim_seq_par_simpl: AN p1 p2,
    converges AN p1
    (hidden_fn (fst (split AN)) p1 hidden_fn (fst (split AN)) p2) →
    NameSet.Empty (NameSet.inter (fn_in p1) (fn_in p2)) →
    eventually_similar (p_res_list AN (p1;; p2)) (p_res_list AN (p1 || p2)).
  Proof.
    intros.
    cut (eventually_similar (p_res_list AN ((p1||p_nil);; (p_nil||p2))) (p_res_list AN ((p1;; p_nil) || (p_nil;; p2)))); intros.
    × setoid_rewrite wbisim_par_nil1 in H2.
    setoid_rewrite wbisim_par_nil2 in H2.
    setoid_rewrite wbisim_nil_seq in H2.
    setoid_rewrite wbisim_seq_nil in H2.
    exact H2.
    ×
    destruct H0.
    + setoid_rewrite wbisim_par_swap.
    eapply esim_seq_par; eauto.
    apply converges_term_impl; auto.
    apply term_impl_nil.
    apply hidden_fn_seq3; auto.
    unfold hidden_fn. simpl. setoid_rewrite NameSetFacts.empty_iff. intros; false.
    simpl.
    setoid_rewrite NameSetProps.empty_union_1 with (s:= NameSet.empty).
    setoid_rewrite NameSetProps.empty_union_2 with (s:= NameSet.empty).
    setoid_rewrite NameSetProps.inter_sym.
    exact H1.
    apply NameSet.empty_spec.
    apply NameSet.empty_spec.
    + eapply esim_seq_par; eauto.
    apply term_impl_nil.
    apply converges_term_impl; auto.
    simpl.
    setoid_rewrite NameSetProps.empty_union_1 with (s:= NameSet.empty).
    setoid_rewrite NameSetProps.empty_union_2 with (s:= NameSet.empty).
    exact H1.
    apply NameSet.empty_spec.
    apply NameSet.empty_spec.
  Qed.

  Corollary csim_seq_par_simpl: AN p1 p2,
    converges AN p1
    (hidden_fn (fst (split AN)) p1 hidden_fn (fst (split AN)) p2) →
    NameSet.Empty (NameSet.inter (fn_in p1) (fn_in p2)) →
    contrasimilar (p_res_list AN (p1;; p2)) (p_res_list AN (p1 || p2)).
  Proof.
    intros.
    apply ebisim_csim.
    apply esim_ebisim1.
    apply esim_seq_par_simpl; auto.
  Qed.

End Parallelization.

Module TestParallelization.
  Import Notation.
  Import BinPos.
  Import Morphisms.

  Definition l0 := 1%positive.
  Definition l1 := 2%positive.
  Definition l2 := 3%positive.

  Let M (a b c d e f: name):= p_res_list [(e,0),(f,0)] ((p_res_list [(c,0),(d,0)] ((().c!.O + ().d!.O);; l0!.O;; (c?.e!.O + d?.f!.O)));; (e?.l1!.O + f?.l2!.O)).
  Let N (a b c d e f: name):= p_res_list [(e,0),(f,0)] ((p_res_list [(c,0),(d,0)] ( (().c!.O + ().d!.O) || (l0!.O;; (c?.e!.O + d?.f!.O))));; (e?.l1!.O + f?.l2!.O)).

  Open Scope proc_scope.

  Lemma wbisim_converges: AN p,
    ( N, length N = length ANwbisimilar (p_res_list (combine (fst (split AN)) N) p) p_nil) →
    ( a, ¬NameSet.In a (fn_in p)) →
    converges' AN p.   Proof.
    unfold converges'; intros.
    edestruct res_list_count_labels_nIn'' with (A:=fst (split AN)) (N:=snd (split AN)) (la:=la1) as [N' ?].
    intros.
    intro.
    apply fn_in_step_star_In with (a:=a) in H1; auto.
    apply H0 in H1. false.
    rewrite split_length_l, split_length_r; auto.
    eapply step_star_l_tau_nIn; eauto.
     N'; split; auto.
    edestruct res_list_count_labels_length as [??]; eauto.
    edestruct step_star_wbisim1 as [q'[??]].
    apply (H (snd (split AN))).
    rewrite H3, split_length_l; reflexivity.
    apply step_star_res_list' with (la:=la1) (AN':=combine (fst (split AN)) N'); eauto.
    rewrite combine_split; auto.
    rewrite combine_split; auto.
    rewrite combine_split; auto.
    rewrite combine_split; auto.
    rewrite combine_split in *; auto. simpl in ×.
    edestruct step_star_nil_resolve as [??]; eauto.
    subst q'.
    apply halted_wbisim2; auto.
    reflexivity.
  Qed.

  Lemma esim_M_N: a b c d e f,
    eventually_similar (M a b c d e f) (N a b c d e f).
  Proof.
    unfold M, N; intros.
    setoid_rewrite<- esim_seq_par_simpl; auto.
    × setoid_rewrite wbisim_seq_assoc.
    reflexivity.
    × apply converges_converges'_equiv.
    apply wbisim_converges; intros.
    + unfold split, fst.
    destruct N0. false. destruct N0. false.
    destruct N0.
    simpl in ×.
    setoid_rewrite wbisim_res_tau_sum.
    setoid_rewrite wbisim_res_tau_sum.
    rewrite eq_ssum.
    setoid_rewrite wbisim_res_out at 1.
    do 2 setoid_rewrite wbisim_res_nil.
    destruct (name_eq c d).
    subst.
    setoid_rewrite wbisim_res_out.
    do 2 setoid_rewrite wbisim_res_nil.
    setoid_rewrite wbisim_sum_dup.
    apply wbisim_intro_tau.
    setoid_rewrite wbisim_res_act_comm; try congruence.
    setoid_rewrite wbisim_res_out.
    do 2 setoid_rewrite wbisim_res_nil.
    setoid_rewrite wbisim_sum_dup.
    apply wbisim_intro_tau.

    false.
    + simpl.
    rewrite NameSet.union_spec in ×.
    rewrite NameSetFacts.empty_iff in ×.
    intuition.

    × left.
    simpl. red. simpl. intros.
    rewrite NameSet.union_spec in ×.
    repeat rewrite NameSet.add_spec in ×.
    rewrite NameSetFacts.empty_iff in ×.
    intuition.
  Qed.

  Lemma csim_M_N: a b c d e f,
    contrasimilar (M a b c d e f) (N a b c d e f).
  Proof. intros; apply ebisim_csim, esim_ebisim1, esim_M_N. Qed.

End TestParallelization.

Module Examples.
  Require Examples.
  Import Examples.ExampleTactics.
  Import BisimTheory.
  Import Notation.
  Import Morphisms.
  Open Scope proc_scope.

  Ltac try_constrs Nconstrs constrID := match constrID with
  | S ?constrID'
          let helper := solve
          [ cc constrID; auto
          | cc constrID; try_constrs Nconstrs Nconstrs ]
          in solve
          [ apply lstep_in; helper
          | apply lstep_out; helper
          | helper
          | try_constrs Nconstrs constrID' ]
  end.

  Ltac try_each_step prove_step_star Nconstrs max_steps constrID:= match constrID with
    | S ?constrID'solve
      [ eapply @step_tcons; [cc constrID; try_constrs Nconstrs Nconstrs | prove_step_star Nconstrs max_steps ]
      | eapply @step_lcons; [constructor; cc constrID; try_constrs Nconstrs Nconstrs | prove_step_star Nconstrs max_steps ]
      | try_each_step prove_step_star Nconstrs max_steps constrID']
    end.

  Ltac prove_step_star Nconstrs max_steps := match max_steps with
  | S ?max_steps'match goal with
    | |- @step_star _ ?S nil ?Sapply @step_nil
    | |- @step_star _ ?S1 ?la ?S2first
        [ eapply @single_tstep; try_constrs Nconstrs Nconstrs
        | eapply @single_lstep; try_constrs Nconstrs Nconstrs
        | try_each_step prove_step_star Nconstrs max_steps' Nconstrs ]
    | _auto
    end
  end.

  Ltac sstt Nconstrs max_steps RconstrID:= match RconstrID with
    | S ?RconstrID'solve
      [ split; [| cc RconstrID]; prove_step_star Nconstrs max_steps
      | split; [| cc RconstrID]; prove_step_star Nconstrs max_steps
      | sstt Nconstrs max_steps RconstrID']
    end.

  Tactic Notation "tt" constr(Nconstrs) constr(max_steps) constr(m) := eexists; sstt Nconstrs max_steps m.

  Ltac ssel Nsteps Nconstrs S:=
     S; split; [prove_step_star Nconstrs Nsteps | constructor].
  Ltac sselL Nsteps Nconstrs a := left; a; split; auto; [prove_step_star Nconstrs Nsteps|constructor].
  Ltac sselR Nsteps Nconstrs a := right; a; split; prove_step_star Nconstrs Nsteps.

  Tactic Notation "sel" constr(S) := ssel 3 14 S.
  Tactic Notation "selL" constr(a) := sselL 3 14 a.
  Tactic Notation "selR" constr(a) := sselR 3 14 a.
  Tactic Notation "tt" constr(m) := tt 14 3 m.
  Tactic Notation "ss" := prove_step_star 14 3.
  Tactic Notation "ssel" constr(S) := S; split; [| constructor].

  Ltac sinv := match goal with
  | [ H: step _ _ _ |- _ ] ⇒ inverts H
  | [ H: tstep _ _ _ |- _ ] ⇒ inverts H; sinv
  | [ H: lstep _ _ _ |- _ ] ⇒ inverts H; sinv
  | [ H: @LabeledTransitionSystems.tstep _ _ _ |- _ ] ⇒ inverts H; sinv
  | [ H: @LabeledTransitionSystems.lstep _ _ _ _ |- _ ] ⇒ inverts H; sinv
  end.

  Lemma lstep_inv: p q a pq',
    lstep (p||q) a pq'
    ( p', lstep p a p' pq'=p'||q) ( q', lstep q a q' pq'=p||q').
  Proof.
    intros. inverts H; inverts H0.
    left; p'; split; auto; constructor; auto.
    right; q'; split; auto; constructor; auto.
    left; p'; split; auto; constructor; auto.
    right; q'; split; auto; constructor; auto.
  Qed.

  Lemma step_star_par_tsum2: p q r la pqr',
    step_star (p || (().q + ().r)) la pqr'
     la1 p' la2,
      step_star p la1 p' ((pqr'=(p' || (().q + ().r))la2=nil) step_star (p' || q) la2 pqr' step_star (p' || r) la2 pqr') la=la1++la2.
  Proof.
    intros.
    remember (p || (().q + ().r)) as pqr.
    revert p q r Heqpqr.
    induction H; intros; subst; try rename p' into pqr'; try rename p'' into pqr''.
    × (@nil lts_L) p0 (@nil lts_L); splits; auto; apply step_nil.
    × edestruct lstep_inv as [ [p'[??]] | [q'[??]] ]; eauto; subst pqr'.
    edestruct IHstep_star as [la1[p''[la2[?[??]]]]]; clear IHstep_star; eauto; subst.
     (a::la1) p'' la2; splits; auto; eapply step_lcons; eauto.
    inverts H1. inverts H2; inverts H6.
    inverts H2; inverts H6.
    × inverts H.
    - edestruct IHstep_star as [la1[p''[la2[?[??]]]]]; clear IHstep_star; eauto; subst.
     la1 p'' la2; splits; auto; eapply step_tcons; eauto. apply H5.
    - inverts H5.
    + inverts H4.
     (@nil lts_L) p0 la; splits; auto. apply step_nil.
    + inverts H4.
     (@nil lts_L) p0 la; splits; auto. apply step_nil.
  Qed.

  Ltac inv := match goal with
  | _sinv
  | [ H: @step_star _ (?c _) _ _ |- _ ] ⇒ inverts H
  | [ H: @step_star _ p_nil _ _ |- _ ] ⇒ inverts H
  end.

  Ltac inv2 := match goal with
  | _sinv
  | [ H: @step_star _ (p_par ?p (p_sum (p_act l_tau ?q) (p_act l_tau ?r))) ?la ?pqr' |- _ ] ⇒ destruct (step_star_par_tsum2 p q r la pqr' H) as [?[?[?[?[ [[??]|[?|?]] ?]]]]]; clear H; try subst p; try subst q; try subst r; try subst la
  | [ H: @step_star _ (?c _) _ _ |- _ ] ⇒ inverts H
  | [ H: @step_star _ p_nil _ _ |- _ ] ⇒ inverts H
  end.

  Definition list_lts_S_list_proc (a:list lts_S) : list proc := a.
  Tactic Notation "steps" constr(lp) := let rec steps_ lp:= match lp with
    | ?p :: ?lp'solve
      [ eapply step_tcons with p; [try_constrs 14 14 | steps_ lp']
      | eapply step_lcons with p; [constructor 1; apply s_act; try_constrs 14 14 | steps_ lp']
      | eapply step_lcons with p; [constructor 2; apply s_act; try_constrs 14 14 | steps_ lp']
      ]
    | @nil procsolve
      [ apply (@step_nil lts)
      | apply single_tstep; auto; try_constrs 14 14
      | apply single_lstep; auto; apply lstep_in; try_constrs 14 14
      | apply single_lstep; auto; apply lstep_out; try_constrs 14 14
      ]
    end in
  let lp_ := eval compute in (list_lts_S_list_proc lp) in
  steps_ lp_.

Module SumSum.
  Definition p_sum1 a P Q:= a!.P + a!.Q.
  Definition p_sum2 a P Q:= a!.(().P + ().Q).

  Inductive Rd a P Q: procprocProp :=
  | Rd1: Rd a P Q (a!.P + a!.Q) (a!.(().P + ().Q))
  | Rd4: p, Rd a P Q p p.
  Tactic Notation "fix_names" ident(a) ident(P) ident(Q) := match goal with
  | |- context [ Rd ?A ?PP ?QQ _ _ ] ⇒ rename A into a; rename PP into P; rename QQ into Q
  end.

  Lemma esim_true: a P Q,
    eventually_similar (p_sum1 a P Q) (p_sum2 a P Q).
  Proof.
    unfold p_sum1, p_sum2.
    intros.
     (Rd a P Q); splits; auto; try constructor; try intros p q; intros.
    - repeat red in H0. subst p. inverts H.
    apply step_nil.
    - inverts H; try solve [false]. apply step_nil.
    - inverts H; repeat inv.
     p'; split; try constructor. apply single_tstep; auto.
    - inverts H. repeat inv.
     p'; split; try constructor. steps [().p'+().Q].
     p'; split; try constructor. steps [().P+().p'].
     p'; split; try constructor. apply single_lstep; auto.
    - inverts H. inv.
    do 2 eexists; splits; try constructor.
    inv. inv.
     P P; splits; try constructor.
    apply single_tstep; apply s_sumL; apply s_act.
    eapply single_lstep; constructor; apply s_sumL, s_act.
    repeat inv.
    repeat inv.
     p' p'; splits; try constructor.
    eapply step_lcons; eauto. constructor. apply s_sumL, s_act.
     p' p'; splits; try constructor.
    eapply step_lcons; eauto. constructor. apply s_sumR, s_act.
    repeat inv.
     p' p'; splits; try constructor. auto.
  Qed.

  Lemma csim_true: a P Q,
    contrasimilar (p_sum1 a P Q) (p_sum2 a P Q).
  Proof. intros; apply ebisim_csim, esim_ebisim1, esim_true. Qed.

  Lemma wsim_backwards_false: a b c P Q R,
    abacbc
    weak_simulation R
    R (p_sum2 a (b!.P) (c!.Q)) (p_sum1 a (b!.P) (c!.Q)) →
    False.
  Proof.
    unfold p_sum1, p_sum2.
    intros.
    edestruct weak_simulation_lstep as [p'[??]]; eauto.
    apply lstep_out; repeat constructor.
    clear H3.
    repeat inv.
    - edestruct weak_simulation_step_star with (la:=[l_out c]) as [p'[??]]; eauto; ss.
    repeat inv.
    false.
    - edestruct weak_simulation_step_star with (la:=[l_out b]) as [p'[??]]; eauto; ss.
    repeat inv.
    false.
  Qed.

  Lemma coupled_sim_false: a b c P Q,
    abacbc
    ¬coupled_similar (p_sum1 a (b!.P) (c!.Q)) (p_sum2 a (b!.P) (c!.Q)).
  Proof.
    unfold p_sum1, p_sum2.
    intros. intro.
    edestruct lstep_pcssim as [p'[??]]; eauto; [| apply cssim_pcssim2; eauto |].
    apply lstep_out; apply s_act.
    clear H2.
    repeat inv.
    - edestruct step_star_pcssim with (la:=[l_out c]) as [p'[??]]; eauto; ss.
    repeat inv.
    false.
    - edestruct step_star_pcssim with (la:=[l_out b]) as [p'[??]]; eauto; ss.
    repeat inv.
    false.
  Qed.

  Lemma wbisim_false: a b c P Q,
    abacbc
    ¬wbisimilar (p_sum1 a (b!.P) (c!.Q)) (p_sum2 a (b!.P) (c!.Q)).
  Proof.
    intros; intro.
    eapply (coupled_sim_false a b c P Q); eauto.
    apply wbisim_coupled_sim; auto.
  Qed.

End SumSum.

Module SumPar.
  Definition p_sum_par P Q R:= ().(P || Q) + ().(P || R).
  Definition p_par_sum P Q R:= P || (().Q + ().R).

  Inductive Rd P Q R: procprocProp :=
  | Rd1: Rd P Q R (().(P || Q) + ().(P || R)) (P || (().Q + ().R))
  | Rd4: p, Rd P Q R p p.
  Tactic Notation "fix_names" ident(P) ident(Q) ident(R) := match goal with
  | |- context [ Rd ?PP ?QQ ?RR _ _ ] ⇒ rename PP into P; rename QQ into Q; rename RR into R
  end.

  Lemma step_star_app': p la1 p' la2 p'' la,
    step_star p la1 p'
    step_star p' la2 p''
    la=la1++la2
    step_star p la p''.
  Proof. intros; subst; eapply step_star_app; eauto. Qed.

  Lemma esim_true: P Q R,
    eventually_similar (p_sum_par P Q R) (p_par_sum P Q R).
  Proof.
    unfold p_sum_par, p_par_sum.
    intros.
     (Rd P Q R); splits; auto; try constructor; try intros p q; intros.
    - inverts H; try solve [false]. apply step_nil.
    - inverts H; try solve [false]. apply step_nil.
    - inverts H.
    + inv; tt 4.
    + p'; split. apply single_tstep; auto. constructor.
    - inverts H.
    + repeat inv.
    + p'; split. apply single_lstep; auto. constructor.
    - inverts H.
    + edestruct step_star_par_unzip'' as [p1'[p2'[lip[la1[la2[?[?[?[??]]]]]]]]]; eauto.
    subst.
    clear H0.
    inverts H2.
    destruct H as [?|[?[??]]]; try discriminate. subst p'.
     (p1'||Q) (p1'||Q); splits; auto.
    eapply single_tstep. apply s_parR, s_sumL, s_act.
    eapply step_tcons.
    apply s_sumL, s_act.
    apply step_star_parL_step; auto. rewrite interleaving_r_nil; auto.
    constructor.
    inverts H0.
    inv; inv.
    inv; inv.
     p' p'; splits; auto.
    apply step_nil.
    inv.
    eapply step_tcons. apply s_sumL, s_act.
    destruct H as [?|[?[??]]]; subst.
    eapply step_star_par_zip'; eauto.
    eapply step_tsnoc.
    eapply step_star_par_zip'; eauto.
    apply s_par_nil.
    eapply step_tcons. apply s_sumR, s_act.
    destruct H as [?|[?[??]]]; subst.
    eapply step_star_par_zip'; eauto.
    eapply step_tsnoc.
    eapply step_star_par_zip'; eauto.
    apply s_par_nil.
    constructor.
    + p' p'; splits; auto; constructor.
  Qed.

  Lemma csim_true: P Q R,
    contrasimilar (p_sum_par P Q R) (p_par_sum P Q R).
  Proof. intros; apply ebisim_csim, esim_ebisim1, esim_true. Qed.

  Lemma wsim_backwards_false: a b c R,
    abacbc
    weak_simulation R
    R (p_par_sum (a!.O) (b!.O) (c!.O)) (p_sum_par (a!.O) (b!.O) (c!.O)) →
    False.
  Proof.
    unfold p_sum_par, p_par_sum.
    intros.
    edestruct weak_simulation_lstep as [p'[??]]; eauto.
    apply lstep_out; repeat constructor.
    clear H3.
    repeat inv.
    - edestruct weak_simulation_step_star as [p'[??]]; eauto.
    eapply step_tcons. apply s_parR, s_sumR, s_act.
    eapply step_lcons. apply lstep_out, s_parR, s_act.
    apply single_tstep, s_par_nil.
    repeat inv; congruence.
    - edestruct weak_simulation_step_star as [p'[??]]; eauto.
    eapply step_tcons. apply s_parR, s_sumR, s_act.
    eapply step_lcons. apply lstep_out, s_parR, s_act.
    apply single_tstep, s_par_nil.
    - edestruct weak_simulation_step_star as [p'[??]]; eauto.
    eapply step_tcons. apply s_parR, s_sumL, s_act.
    eapply step_lcons. apply lstep_out, s_parR, s_act.
    apply single_tstep, s_par_nil.
    repeat inv; congruence.
    - edestruct weak_simulation_step_star as [p'[??]]; eauto.
    eapply step_tcons. apply s_parR, s_sumL, s_act.
    eapply step_lcons. apply lstep_out, s_parR, s_act.
    apply single_tstep, s_par_nil.
  Qed.

  Lemma coupled_sim_false: a b c,
    abacbc
    ¬coupled_similar (p_sum_par (a!.O) (b!.O) (c!.O)) (p_par_sum (a!.O) (b!.O) (c!.O)).
  Proof.
    intros. intros [R[?[?[?[??]]]]].
    apply (wsim_backwards_false a b c R); eauto.
  Qed.

  Lemma wbisim_false: a b c,
    abacbc
    ¬wbisimilar (p_sum_par (a!.O) (b!.O) (c!.O)) (p_par_sum (a!.O) (b!.O) (c!.O)).
  Proof.
    intros; intro.
    eapply (coupled_sim_false a b c); eauto.
    apply wbisim_coupled_sim; auto.
  Qed.

  Lemma esim_true_O: a b c,
    eventually_similar (p_sum_par (a!.O) (b!.O) (c!.O)) (p_par_sum (a!.O) (b!.O) (c!.O)).
  Proof. intros; apply esim_true; auto. Qed.

  Lemma csim_true_O: a b c,
    contrasimilar (p_sum_par (a!.O) (b!.O) (c!.O)) (p_par_sum (a!.O) (b!.O) (c!.O)).
  Proof. intros; apply csim_true; auto. Qed.

End SumPar.

End Examples.