Library proofs

Require Import FunctionalExtensionality ZArith List.
Require Import lang Vtac combine_results.

Set Implicit Arguments.

Reflexive transitive closure

Inductive ss_star (e: exp) : exp -> Prop :=
| ss_starR : ss_star e e
| ss_starT e2 e3: step e e2 -> ss_star e2 e3 -> ss_star e e3.

Lemma ss_star_one: forall e1 e2, step e1 e2 -> ss_star e1 e2.
Proof.
  intros.
  apply ss_starT with e2.
  auto.
  apply ss_starR.
Qed.

Lemma ss_star_trans: forall e1 e2 e3, ss_star e1 e2 -> ss_star e2 e3 -> ss_star e1 e3.
Proof.
  induction 1; intros; auto.
  apply ss_starT with e2; auto.
Qed.

Lemma letFirst : forall e v e2 x, ss_star e (Eval v) -> ss_star (Elet x e e2) (Elet x (Eval v) e2).
Proof.
  intros; induction H; eauto using step, ss_star.
Qed.

Soundness of small step w.r.t. big step


Theorem soundness_helper_ss: forall e e' (H: step e e') v, reduce e' v -> reduce e v.
Proof.
  induction 1; inversion 1; subst; eauto using reduce.
Qed.

Theorem soundness_ss: forall e v, ss_star e (Eval v) -> reduce e v.
Proof.
  intros; remember (Eval v) as e'; revert v Heqe'.
  induction H; intros; subst; eauto using reduce, soundness_helper_ss.
Qed.

Completeness of small step w.r.t. big step


Lemma pairFirst : forall e e2 v, ss_star e (Eval v) -> ss_star (Epair e e2) (Epair (Eval v) e2).
Proof.
  intros; induction H; eauto using step, ss_star.
Qed.

Lemma pairSecond : forall e e1 v, ss_star e (Eval v) -> ss_star (Epair e1 e) (Epair e1 (Eval v)).
Proof.
  intros; induction H; eauto using step, ss_star.
Qed.

Theorem completeness_ss: forall e v, reduce e v -> ss_star e (Eval v).
Proof.
  intros; induction H; subst; try (assert (ss_star (Elet x e1 e2) (Elet x (Eval v1) e2)) by eauto using letFirst); eauto using step, ss_star, ss_star_one, ss_star_trans.
  eauto 6 using pairFirst, pairSecond, ss_star_trans, step, ss_star_one.
Qed.

Inductive recover_star (r:res) : res -> Prop :=
| recover_starR: recover_star r r
| recover_starT r2 r3: recover_star r r2 -> recover r2 r3 -> recover_star r r3
.

Fault prone evaluation to a value


Lemma fp_implies_ff: forall e v, reducef e (Rval v) -> reduce e v.
Proof.
  intros.
  remember (Rval v) as x. revert v Heqx.
  induction H; intros; clarify; eauto using reduce.
  assert (exists v1, exists v2, r1 = (Rval v1) /\ r2 = (Rval v2) /\ v = (Vpair v1 v2)) by apply (res_combine_values r1 r2 v Heqx).
  des. subst. eauto using reduce.
Qed.

Correctness of fault recovery


Lemma fp_recover_transitivity : forall e r1 r2, reducef e r1 -> recover r1 r2 -> reducef e r2.
Proof.
  intros.
  revert r2 H0.
  induction H; intros; try now inv H0; eauto using reducef.
    inv H1. eauto using reducef.
    remember (r1 # r2) as r; symmetry in Heqr; revert r1 r2 H H0 H1 IHreducef1 IHreducef2 Heqr.
    induction r; intros; try by exfalso; eauto using res_combine_bot, res_combine_rletl, res_combine_rletr.
    by eapply res_combine_values in Heqr; desf; ins; inv H1; eauto using reducef, reducef_tuple.
    by eapply res_combine_ptuple in Heqr; desf; inv H1; eauto using reducef, reducef_tuple.
    induction r2; try now inv H1; eauto using reducef.
    induction r; inv H2;
    try now eauto using reducef;
    try now eauto using reducef; inv H4; eauto using reducef.
Qed.

Lemma recover_implies_fp: forall e r, recover_star (Rbot e) r -> reducef e r.
Proof.
  intros.
  remember (Rbot e) as x.
  revert e Heqx.
  induction H; intros; subst; eauto using reducef.
  assert (recover_star (Rbot e) r3) by eauto using recover_star.
  assert (reducef e r2).
  apply (IHrecover_star e); reflexivity.
  eauto using fp_recover_transitivity.
Qed.

Theorem Correctness: forall e v, recover_star (Rbot e) (Rval v) -> reduce e v.
Proof.
  intros; eauto using recover_implies_fp, fp_implies_ff.
Qed.

Completeness of fault recovery


Lemma completeness_one_step: forall e v, reduce e v -> recover (Rbot e) (Rval v).
Proof.
  intros. constructor.
    induction H; eauto using reducef, reducef_tuple.
Qed.

Theorem Completeness: forall e v, reduce e v -> recover_star (Rbot e) (Rval v).
Proof.
  eauto using recover_star, completeness_one_step.
Qed.

A partial order on the success of the results


Inductive geq : res -> res -> Prop :=
| Geq_Bot r e: geq r (Rbot e)
| Geq_Val v r: match r with Rval v' => v' = v | _ => True end -> geq (Rval v) r
| Geq_Ptuple r11 r12 r21 r22:
  geq r11 r21 ->
  geq r12 r22 ->
  geq (Rptuple r11 r12) (Rptuple r21 r22)
| Geq_letl r1 r2 x e:
  geq r1 r2 ->
  geq (Rletl r1 x e) (Rletl r2 x e)
| Geq_letr1 r1 r2 x e:
  geq (Rletr r1) (Rletl r2 x e)
| Geq_letr2 r1 r2:
  geq r1 r2 ->
  geq (Rletr r1) (Rletr r2).

Pseudo equality between results


Inductive pseq : res -> res -> Prop :=
| PSeq_Bot e' e: pseq (Rbot e') (Rbot e)
| PSeq_Val v: pseq (Rval v) (Rval v)
| PSeq_Ptuple r11 r12 r21 r22:
  pseq r11 r21 ->
  pseq r12 r22 ->
  pseq (Rptuple r11 r12) (Rptuple r21 r22)
| PSeq_letl r1 r2 x e:
  pseq r1 r2 ->
  pseq (Rletl r1 x e) (Rletl r2 x e)
| PSeq_letr2 r1 r2:
  pseq r1 r2 ->
  pseq (Rletr r1) (Rletr r2).

Proof that geq is a partial order


reflexivity
Lemma geq_reflexive: forall r, geq r r.
Proof.
  intros; induction r; eauto using geq.
Qed.

anti-symmetry assuming pseudo equality
Lemma geq_impl_pseq: forall r1 r2, geq r1 r2 -> geq r2 r1 -> pseq r1 r2.
Proof.
  intros r1 r2 A B; induction A; inv B; eauto 8 using pseq.
Qed.

transitivity
Lemma geq_transitive: forall r1 r2 r3, geq r1 r2 -> geq r2 r3 -> geq r1 r3.
Proof.
  intros r1 r2 r3 A B; revert r3 B.
  induction A; intros; inversion B; subst; eauto using geq.
    constructor.
    rewrite <- H. assumption.
Qed.

Proof for the progress of recovery


Theorem Progress: forall r1 r2, recover r1 r2 -> geq r2 r1.
Proof.
  intros.
  induction H; try constructor; eauto.
  unfold res_combine in *.
  induction r1'; induction r2'; subst; eauto using geq.
Qed.

Theorem ff_deterministic: forall e v1 v2 (RED1: reduce e v1) (RED2: reduce e v2), v1 = v2.
Proof.
  intros.
  revert v2 RED2.
  induction RED1; intros; inv RED2; eauto using reduce.
  apply (IHRED1_2 v0).
  assert (v1 = v4) by eauto; subst; eauto.
  f_equal; eauto.
Qed.

Theorem fp_deterministic: forall e v1 v2 (RED1: reducef e (Rval v1)) (RED2: reducef e (Rval v2)), v1 = v2.
Proof.
  intros; eauto using fp_implies_ff, ff_deterministic.
Qed.

HIGH LEVEL vs. LOW LEVEL


Reflexive transitive closure of multiprocessor step


Inductive mpstep_star : proc_map -> result_map -> proc_map -> result_map -> Prop :=
| mpstep_starR pm rm : mpstep_star pm rm pm rm
| mpstep_starT pm rm pm' rm' pm'' rm'': mpstep pm rm pm' rm' -> mpstep_star pm' rm' pm'' rm'' -> mpstep_star pm rm pm'' rm''.

Lemma mpstep_star_one: forall pm rm pm' rm', mpstep pm rm pm' rm' -> mpstep_star pm rm pm' rm'.
Proof.
  intros.
    apply mpstep_starT with pm' rm'. auto.
    apply mpstep_starR.
Qed.

Lemma mpstep_star_trans: forall pm rm pm' rm' pm'' rm'', mpstep_star pm rm pm' rm' -> mpstep_star pm' rm' pm'' rm'' -> mpstep_star pm rm pm'' rm''.
Proof.
  induction 1; intros.
    auto.
    apply mpstep_starT with pm' rm'; auto.
Qed.

rus = read_update_same
Lemma rm_rus B rm id (b: B): projT1 (ff_update rm id b) id = Some b.
Proof.
  unfold ff_update; simpl; desf.
Qed.

ruo = read_update_other
Lemma rm_ruo B rm id (b: B) id' (NEQ: id <> id'): projT1 (ff_update rm id b) id' = projT1 rm id'.
Proof.
  unfold ff_update; simpl; desf.
Qed.

Small step implies multiprocessor step


Lemma ss_impl_mpstep_s: forall e e' (S: ss_star e e') s es ss d pm pid rm,
  projT1 pm pid = Some (PCrun e s es ss, d) ->
  exists pm',
    mpstep_star pm rm pm' rm
    /\ projT1 pm' pid = Some (PCrun e' s es ss, d).
Proof.
  induction 1; intros; eauto using mpstep_star.
  edestruct IHS as (pm' & ? & ?); [eapply rm_rus|].
  eexists,; [eapply mpstep_starT|]; eauto; eauto using mpstep, spstep.
Qed.

Lemma ss_impl_mpstep_s_start: forall e e' s d pm pid rm,
  ss_star e e' ->
  projT1 pm pid = Some (PCstart e s, d) ->
  exists pm', mpstep_star pm rm pm' rm /\ projT1 pm' pid = Some (PCrun e' s e s, d).
Proof.
  ins; eapply ss_impl_mpstep_s with (pm := ff_update pm _ _) in H; eauto using rm_rus.
  des; eexists,; [eapply mpstep_starT|]; vauto.
Qed.

Stack related definitions and lemmas


Fixpoint stack_app lc s :=
  match lc with
    | nil => s
    | cons C l => S_cons C (stack_app l s)
  end.

Fixpoint exp_app lc e :=
  match lc with
    | nil => e
    | cons C l => exp_app l (appctx_context_exp C e)
  end.

Fixpoint stack_last (s:stack) : stack :=
match s with
| S_cons C s' => stack_last s'
| _ => s
end.

Fixpoint stack_conts (s:stack) :=
  match s with
    | S_cons C s' => C :: stack_conts s'
    | _ => nil
  end.

Fixpoint app_stack (s:stack) (e:exp) : exp :=
match s with
| S_cons C s' => app_stack s' (appctx_context_exp C e)
| _ => e
end.

Lemma exp_app_app l1 l2 : forall e,
  exp_app (l1 ++ l2) e = exp_app l2 (exp_app l1 e).
Proof.
  induction l1; ins.
Qed.

Lemma app_stack1 s e: app_stack s e = exp_app (stack_conts s) e.
Proof.
  revert e; induction s; ins; rewrite exp_app_app, IHs; ins.
Qed.

Lemma rm_ru :
  forall B rm id (b : B) id' b',
  projT1 (ff_update rm id' b') id = Some b <->
    id' = id /\ b' = b
  \/ projT1 rm id = Some b /\ id' <> id.
Proof.
  simpl; unfold ff_update; intros; desf; intuition congruence.
Qed.

Lemma rm_run :
  forall B rm id id' (b : B),
  projT1 (ff_update rm id b) id' = None <-> projT1 rm id' = None /\ id <> id'.
Proof.
  simpl; unfold ff_update; intros; desf; intuition congruence.
Qed.

Lemma nat_eq (pid1 pid2 : nat) : { pid1 = pid2 } + { pid1 <> pid2 }.
Proof.
  repeat decide equality.
Qed.

Determination of task ids from deques and stacks


Fixpoint deque_taskids d :=
  match d with
    | nil => nil
    | t :: d =>
        match t with
          | T_right id _ => TIDright id
          
        end :: deque_taskids d
  end.

Fixpoint stack_taskids s :=
  match s with
    | S_left id => TIDleft id :: nil
    | S_right id => TIDright id :: nil
    | S_cons _ s => stack_taskids s
  end.

Definition taskids pcdo :=
  match pcdo with
    | Some (pc, d) =>
      (match pc with
         | PCstart es ss => stack_taskids ss
         | PCrun _ _ es ss => stack_taskids ss
         | PCfail es ss => stack_taskids ss
         | _ => nil
       end) ++ deque_taskids d
    | None => nil
  end.

Definition rm_taskids x :=
  match x with
    | None => nil
    | Some (B_neither s) => stack_taskids s
    | Some (B_left _ s) => stack_taskids s
    | Some (B_right _ s) => stack_taskids s
    | Some (B_finished _) => nil
  end.

Definition allowed_taskid (rm : ffun _) tid :=
  match tid with
    | TIDleft id => match projT1 rm id with
                      | Some (B_neither s) | Some (B_right _ s) => True
                      | _ => False
                    end
    | TIDright id => match projT1 rm id with
                       | Some (B_neither s) | Some (B_left _ s) => True
                       | _ => False
                     end
  end.

Fixpoint count_rec B (f : nat -> option B) (g: B -> Z) m n :=
  match n with 0 => 0%Z | S n =>
  Zplus match f (m + n) with None => 0 | Some k => g k end (count_rec f g m n)
  end.

Definition count B (f : ffun B) g :=
  count_rec (projT1 f) g 0 (projT1 (projT2 f)).

Lemma count_helper B (f: ffun B) g n :
  count f g = count_rec (projT1 f) g 0 (max (projT1 (projT2 f)) n).
Proof.
  destruct f as (f & n' & PF); unfold count; simpl.
  destruct (Max.max_spec n' n) as [[? ->]|[? ->]]; try done.
  replace n with ((n - n') + n').
    induction (n - n'); ins; rewrite PF; ins; omega.
  apply NPeano.Nat.sub_add; auto with arith.
Qed.

Lemma count_split B (f: nat -> option B) g m n1 n2 :
  count_rec f g m (n1 + n2) =
  Zplus (count_rec f g m n1)
        (count_rec f g (m + n1) n2).
Proof.
  induction n2; ins; rewrite ?NPeano.Nat.add_0_r, ?NPeano.Nat.add_succ_r; ins;
     try omega.
  rewrite Plus.plus_assoc; desf; omega.
Qed.

Lemma count_rec_upd :
  forall B f g k (b: B) m n,
    k < m \/ k >= m + n ->
    count_rec (fun y => if Peano_dec.eq_nat_dec k y then Some b else f y) g m n =
    count_rec f g m n.
Proof.
  induction n; ins; destruct (Peano_dec.eq_nat_dec); omega.
Qed.

Lemma count_update B (f: ffun B) g id v :
  count (ff_update f id v) g =
    (count f g + g v - match projT1 f id with None => 0 | Some x => g x end)%Z.
Proof.
  rewrite (count_helper f g (S id)).
  destruct f as (f & n & PF); unfold count; simpl.
  replace (max n (S id)) with (id + 1 + (n - S id)).
    2: pose proof (Max.max_spec n (S id)); omega.
  rewrite !count_split; simpl.
  rewrite !count_rec_upd, !Plus.plus_0_r; try omega; simpl.
  desf; ins; omega.
Qed.

Fixpoint stack_istid tid s :=
  (match s, tid with
    | S_cons _ s', _ => stack_istid tid s'
    | S_left id, TIDleft id' => if eq_nat_dec id id' then 1 else 0
    | S_right id, TIDright id' => if eq_nat_dec id id' then 1 else 0
    | _, _ => 0
  end)%Z.

Definition tid_eq : forall x y : taskid, { x = y } + { x <> y }.
Proof.
  decide equality; apply eq_nat_dec.
Qed.

Fixpoint count_list x l :=
  match l with
    | nil => 0%Z
    | y :: l' => if tid_eq x y then (1 + count_list x l')%Z else count_list x l'
  end.

Arguments Zplus : simpl never.

Lemma count_list_app x l1 l2 :
  count_list x (l1 ++ l2) = (count_list x l1 + count_list x l2)%Z.
Proof.
  induction l1; ins; desf; omega.
Qed.

Definition task_istid tid t :=
  (match t, tid with
    | T_right id _, TIDright id' => if eq_nat_dec id id' then 1 else 0
    
    | _, _ => 0
  end)%Z.

Fixpoint list_sum A f (l: list A) :=
  (match l with
    | nil => 0
    | x :: l => f x + list_sum f l
  end)%Z.

Lemma list_sum_app A f (l1 l2 : list A):
  (list_sum f (l1 ++ l2) = list_sum f l1 + list_sum f l2)%Z.
Proof.
  induction l1; ins; omega.
Qed.

Definition pm_istid tid pcd :=
  let '(pc,d) := pcd in
  (match pc with
     | PCstart es ss => stack_istid tid ss
     | PCrun _ _ es ss => stack_istid tid ss
     | PCfail es ss => stack_istid tid ss
     | _ => 0
   end + list_sum (task_istid tid) d)%Z.

Definition rm_istid tid x :=
  match x with
    | (B_neither s)
    | (B_left _ s)
    | (B_right _ s) => stack_istid tid s
    | (B_finished _) => 0%Z
  end.

Well Formedness


Definition well_formed_nd (pm:proc_map) (rm:result_map) :=
  forall id,
   (count pm (pm_istid (TIDleft id)) +
    count rm (rm_istid (TIDleft id)) =
      match projT1 rm id with
        | Some (B_neither _) | Some (B_right _ _) => 1
        | _ => 0
      end /\
    count pm (pm_istid (TIDright id)) +
    count rm (rm_istid (TIDright id)) =
      match projT1 rm id with
        | Some (B_neither _) | Some (B_left _ _) => 1
        | _ => 0
      end )%Z.

Definition well_formed_nd_alt (pm:proc_map) (rm:result_map) :=

  << PPND: forall pid1 pid2 (NEQ: pid1 <> pid2),
             NoDup (taskids (projT1 pm pid1) ++ taskids (projT1 pm pid2)) >> /\

  << RRND : forall id1 id2 (NEQ: id1 <> id2),
             NoDup (rm_taskids (projT1 rm id1) ++ rm_taskids (projT1 rm id2)) >> /\
  << PRND : forall pid id, NoDup (taskids (projT1 pm pid) ++ rm_taskids (projT1 rm id)) >> /\
  << PALL : forall pid x (IN: In x (taskids (projT1 pm pid))), allowed_taskid rm x >> /\
  << RALL : forall id x (IN: In x (rm_taskids (projT1 rm id))), allowed_taskid rm x >>.

Lemma wf_preserved_nd pm rm pm' rm'
      (WF: well_formed_nd pm rm)
      (SEQ: forall pid e s es ss d (PEQ: projT1 pm pid = Some (PCrun e s es ss, d)) tid,
              stack_istid tid s = stack_istid tid ss)
      (STEP: mpstep pm rm pm' rm') :
  well_formed_nd pm' rm'.
Proof.
  unfold well_formed_nd in *; ins.
  generalize (WF id); clear WF; inv STEP;
    try match goal with H : spstep _ _ |- _ => inv H end;
    simpl; rewrite ?count_update; simpl;
  repeat destruct eq_nat_dec; clarify;
  repeat match goal with
      H : _ = _ |- _ => rewrite H; simpl
    | H : projT1 pm _ = Some (PCrun _ _ _ _, _) |- _ => rewrite (SEQ _ _ _ _ _ _ H)
    end;
   rewrite ?list_sum_app; simpl;
  repeat match goal with
      H : projT1 pm _ = Some (PCrun _ _ _ _, _) |- _ =>
      rewrite <- (SEQ _ _ _ _ _ _ H); simpl
    end;
  repeat destruct eq_nat_dec; clarify; omega.
Qed.

Lemma in_count x l : In x l <-> (0 < count_list x l)%Z.
Proof.
  induction l; ins; rewrite IHl; clear; split; ins; desf; vauto.
  by clear; induction l; ins; desf; omega.
  by clear; induction l; ins; desf; omega.
Qed.

Lemma nodup_count l (H: forall x, (count_list x l <= 1)%Z): NoDup l.
Proof.
  induction l; econstructor.
    specialize (H a); ins; desf; rewrite in_count; omega.
  eapply IHl; intros; specialize (H x); ins; desf; omega.
Qed.

Lemma count1_int B (f : nat -> option B) g (POS: forall y:B, (0 <= g y)%Z) n x b:
  x < n ->
  f x = Some b ->
  (0 <= g b <= count_rec f g 0 n)%Z.
Proof.
  split; auto; induction n; ins; [omega|].
  destruct (eq_nat_dec x n).
    desf; clear - POS.
    induction n; ins; desf; try specialize (POS b0); omega.
  assert (X: x < n) by omega.
  specialize (IHn X); desf; try specialize (POS b0); omega.
Qed.

Lemma count1 B (f: ffun B) g (POS: forall y, (0 <= g y)%Z) x:
  match (projT1 f x) with
    | None => True
    | Some y => (0 <= g y <= count f g)%Z
  end.
Proof.
  desf; unfold count; destruct f as (f & n & H); ins.
  destruct (lt_dec x n); [|rewrite H in *; try done; omega].
  eauto using count1_int.
Qed.

Lemma count2 B (f: ffun B) g (POS: forall y, (0 <= g y)%Z) x y (NEQ: x <> y) :
  match projT1 f x, projT1 f y with
    | Some x, Some y => (0 <= g x + g y <= count f g)%Z
    | None, Some x
    | Some x, None => (0 <= g x <= count f g)%Z
    | None, None => True
  end.
Proof.
  desf; unfold count in *; destruct f as (f & n & H); ins;
    try (destruct (lt_dec x n); [|rewrite H in Heq; try done; omega]);
    try (destruct (lt_dec y n); [clear H|rewrite H in Heq0; try done; omega]);
    eauto using count1_int.

  induction n; ins; [omega|].
  destruct (eq_nat_dec x n), (eq_nat_dec y n); desf;
    try assert (X: x < n) by omega;
    try assert (Y: y < n) by omega;
    try specialize (POS b1); try omega; try tauto.
  eapply count1_int in Heq0; try split; eauto using Z.add_nonneg_nonneg; try omega.
  eapply count1_int in Heq; try split; eauto using Z.add_nonneg_nonneg; try omega.
  intuition.
Qed.

Lemma count_list_stack x s:
  count_list x (stack_taskids s) = stack_istid x s.
Proof.
  induction s; ins; desf.
Qed.

Lemma count_list_deque x d:
  count_list x (deque_taskids d) = list_sum (task_istid x) d.
Proof.
  induction d; ins; desf; ins; desf; omega.
Qed.

Lemma stack_istid_nonneg x s: (0 <= stack_istid x s)%Z.
Proof.
  induction s; ins; desf.
Qed.
Hint Resolve stack_istid_nonneg : zarith.

Lemma rm_istid_nonneg x y : (0 <= rm_istid x y)%Z.
Proof.
  destruct x, y; ins; auto with zarith.
Qed.
Hint Resolve rm_istid_nonneg : zarith.

Lemma task_istid_nonneg x y : (0 <= task_istid x y)%Z.
Proof.
  destruct x, y; ins; desf.
Qed.
Hint Resolve task_istid_nonneg : zarith.

Lemma list_sum_nonneg A f l (H: forall x:A, (0 <= f x)%Z) : (0 <= list_sum f l)%Z.
Proof.
  induction l; ins; desf; auto with zarith.
Qed.
Hint Resolve list_sum_nonneg : zarith.

Lemma pm_istid_nonneg x y : (0 <= pm_istid x y)%Z.
Proof.
  destruct x, y; ins; desf; auto with zarith.
Qed.
Hint Resolve pm_istid_nonneg : zarith.

Lemma count_nonneg B f g (H: forall y : B, Zle 0 (g y)) : Zle 0 (count f g).
Proof.
  unfold count; destruct f as (f & n & M); ins; clear M.
  induction n; ins; desf; auto with zarith.
Qed.
Hint Resolve count_nonneg : zarith.

Well Formedness related lemmas


Lemma wf_ppnd pm rm (WF: well_formed_nd pm rm):
  forall pid1 pid2 (NEQ: pid1 <> pid2),
    NoDup (taskids (projT1 pm pid1) ++ taskids (projT1 pm pid2)).
Proof.
  intros; eapply nodup_count; ins; rewrite count_list_app;
  eapply (count2 pm (pm_istid x)) in NEQ; auto with zarith.
  assert (count pm (pm_istid x) <= 1)%Z.
    clear - WF; pose proof (count_nonneg rm _ (rm_istid_nonneg x));
    destruct x; specialize (WF id); [apply proj1 in WF | apply proj2 in WF];
    desf; omega.
  do 2 (desf; ins); rewrite ?count_list_app, ?count_list_stack, ?count_list_deque; omega.
Qed.

Lemma wf_rrnd pm rm (WF: well_formed_nd pm rm):
  forall id1 id2 (NEQ: id1 <> id2),
    NoDup (rm_taskids (projT1 rm id1) ++ rm_taskids (projT1 rm id2)).
Proof.
  intros; eapply nodup_count; ins; rewrite count_list_app;
  eapply (count2 rm (rm_istid x)) in NEQ; auto with zarith.
  assert (count rm (rm_istid x) <= 1)%Z.
    clear - WF; pose proof (count_nonneg pm _ (pm_istid_nonneg x));
    destruct x; specialize (WF id); [apply proj1 in WF | apply proj2 in WF];
    desf; omega.
  do 2 (desf; ins); rewrite ?count_list_app, ?count_list_stack, ?count_list_deque; omega.
Qed.

Lemma wf_prnd pm rm (WF: well_formed_nd pm rm):
  forall id1 id2,
    NoDup (taskids (projT1 pm id1) ++ rm_taskids (projT1 rm id2)).
Proof.
  intros; eapply nodup_count; ins; rewrite count_list_app;
  exploit (count1 pm (pm_istid x)); auto with zarith; instantiate (1 := id1).
  exploit (count1 rm (rm_istid x)); auto with zarith; instantiate (1 := id2).
  pose proof (count_nonneg pm _ (pm_istid_nonneg x));
  pose proof (count_nonneg rm _ (rm_istid_nonneg x)).
  assert (count pm (pm_istid x) + count rm (rm_istid x) <= 1)%Z.
    clear - WF; destruct x; specialize (WF id); [apply proj1 in WF | apply proj2 in WF];
    desf; omega.
  do 2 (desf; ins); rewrite ?count_list_app, ?count_list_stack, ?count_list_deque; omega.
Qed.

Lemma wf_nd_altI pm rm (WF: well_formed_nd pm rm) :
  well_formed_nd_alt pm rm.
Proof.
  repeat split; unnw; eauto using wf_ppnd, wf_rrnd, wf_prnd.
Case 1.
  ins; eapply in_count in IN.
  exploit (count1 pm (pm_istid x)); auto with zarith; instantiate (1 := pid).
  pose proof (count_nonneg pm _ (pm_istid_nonneg x));
  pose proof (count_nonneg rm _ (rm_istid_nonneg x)).
  destruct x; specialize (WF id); [apply proj1 in WF|apply proj2 in WF];
  do 2 (desf; ins); rewrite ?count_list_app, ?count_list_stack, ?count_list_deque in *; omega.
Case 2.
  ins; eapply in_count in IN.
  exploit (count1 rm (rm_istid x)); auto with zarith; instantiate (1 := id).
  pose proof (count_nonneg pm _ (pm_istid_nonneg x));
  pose proof (count_nonneg rm _ (rm_istid_nonneg x)).
  destruct x; specialize (WF id0); [apply proj1 in WF|apply proj2 in WF];
  do 2 (desf; ins); rewrite ?count_list_app, ?count_list_stack, ?count_list_deque in *; omega.
Qed.

General Definition of well-formedness


Definition well_formed (pm:proc_map) (rm:result_map) :=
  (forall pid e s es ss d (PEQ: projT1 pm pid = Some (PCrun e s es ss, d)),
    << WFRo: stack_last s = stack_last ss >> /\
    << WFR: ss_star (app_stack ss es) (app_stack s e) >>) /\
  well_formed_nd pm rm.

Lemma step_app_stack e s e' (S: step e e') : step (app_stack s e) (app_stack s e').
Proof.
  revert e e' S; induction s; ins; destruct C; ins; eauto using step.
Qed.

Lemma step_exp_app e s e' (S: step e e') : step (exp_app s e) (exp_app s e').
Proof.
  revert e e' S; induction s as [|[]]; ins; eauto using step.
Qed.

Lemma NoDup_app A (l1 l2 : list A) :
  NoDup (l1 ++ l2) <-> NoDup l1 /\ NoDup l2 /\ (forall x, In x l1 -> In x l2 -> False).
Proof.
  split; induction l1; ins; intuition; vauto;
  try by inv H; try rewrite in_app_iff in *; try constructor; try tauto.
  by inv H; intuition; try constructor; eauto.
  by inv H0; intuition; eauto; constructor; try rewrite in_app_iff in *; eauto; intro; des; eauto.
Qed.

Lemma deque_taskids_app l l' :
  deque_taskids (l ++ l') = deque_taskids l ++ deque_taskids l'.
Proof.
  by induction l; ins; desf; ins; f_equal; eauto.
Qed.

Lemma allowed_taskid_upd :
  forall rm x id b,
    allowed_taskid rm x ->
    match b with
      | B_neither _ => True
      | B_left _ _ => x <> TIDleft id
      | B_right _ _ => x <> TIDright id
      | B_finished _ => x <> TIDleft id /\ x <> TIDright id
    end ->
    allowed_taskid (ff_update rm id b) x.
Proof.
  ins; destruct x; ins; desf; desf.
Qed.

Preservation of well-formedness in a multiprocessor step


Lemma wf_preserved pm rm pm' rm' (WF: well_formed pm rm) (STEP: mpstep pm rm pm' rm') :
  well_formed pm' rm'.
Proof.
  destruct WF; split; eauto using wf_preserved_nd; unnw; intros.
  inv STEP; rewrite !rm_ru in *; desf; eauto.
  inv H1; simpl; eauto using ss_starR;
   eapply H in H2; desf; eauto using ss_star_trans, ss_star_one, step_app_stack.
  eapply wf_preserved_nd; eauto; intros; exploit H; eauto.
  clear; intros [A _]; revert e es ss A; induction s; ins; clarify; eauto;
   induction ss; ins; clarify; eauto.
Qed.

Definition of related expression


Inductive cexp_rel_left (pm : proc_map) (rm: result_map) (id : identifier) (e : exp) : Prop :=
  | cexp_rel_left_Start pid d e' lc
      (PM: projT1 pm pid = Some (PCstart e' (stack_app lc (S_left id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_left_Run pid ce s d e' lc
      (PM: projT1 pm pid = Some (PCrun ce s e' (stack_app lc (S_left id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_left_Fail pid d e' lc
      (PM: projT1 pm pid = Some (PCfail e' (stack_app lc (S_left id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_left_Finished v
      (RM: (exists s, projT1 rm id = Some (B_left v s)) \/
           (exists v', projT1 rm id = Some (B_finished (Vpair v v'))))
      (EQ: e = Eval v)
  | cexp_rel_left_Neither id' lc e1 e2
       (RM: projT1 rm id' = Some (B_neither (stack_app lc (S_left id))))
       (LEFT: cexp_rel_left pm rm id' e1)
       (RIGHT: cexp_rel_right pm rm id' e2)
       (EQ: e = exp_app lc (Epair e1 e2))
  | cexp_rel_left_Left id' lc v1 e2
       (RM: projT1 rm id' = Some (B_left v1 (stack_app lc (S_left id))))
       (RIGHT: cexp_rel_right pm rm id' e2)
       (EQ: e = exp_app lc (Epair (Eval v1) e2))
  | cexp_rel_left_Right id' lc e1 v2
       (RM: projT1 rm id' = Some (B_right v2 (stack_app lc (S_left id))))
       (LEFT: cexp_rel_left pm rm id' e1)
       (EQ: e = exp_app lc (Epair e1 (Eval v2)))

with cexp_rel_right (pm : proc_map) (rm : result_map) (id : identifier) (e : exp) : Prop :=
  | cexp_rel_right_Thunk pid pc d
      (PM: projT1 pm pid = Some (pc, d))
      (IN: In (T_right id e) d)
  | cexp_rel_right_Start pid d e' lc
      (PM: projT1 pm pid = Some (PCstart e' (stack_app lc (S_right id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_right_Run pid ce s d e' lc
      (PM: projT1 pm pid = Some (PCrun ce s e' (stack_app lc (S_right id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_right_Fail pid d e' lc
      (PM: projT1 pm pid = Some (PCfail e' (stack_app lc (S_right id)), d))
      (EQ: e = exp_app lc e')
  | cexp_rel_right_Finished v
      (RM: (exists s, projT1 rm id = Some (B_right v s)) \/
           (exists v', projT1 rm id = Some (B_finished (Vpair v' v))))
      (EQ: e = Eval v)
  | cexp_rel_right_Neither id' lc e1 e2
       (RM: projT1 rm id' = Some (B_neither (stack_app lc (S_right id))))
       (LEFT: cexp_rel_left pm rm id' e1)
       (RIGHT: cexp_rel_right pm rm id' e2)
       (EQ: e = exp_app lc (Epair e1 e2))
  | cexp_rel_right_Left id' lc v1 e2
       (RM: projT1 rm id' = Some (B_left v1 (stack_app lc (S_right id))))
       (RIGHT: cexp_rel_right pm rm id' e2)
       (EQ: e = exp_app lc (Epair (Eval v1) e2))
  | cexp_rel_right_Right id' lc e1 v2
       (RM: projT1 rm id' = Some (B_right v2 (stack_app lc (S_right id))))
       (LEFT: cexp_rel_left pm rm id' e1)
       (EQ: e = exp_app lc (Epair e1 (Eval v2)))
.

Scheme cexp_rel_left_ind1 := Induction for cexp_rel_left Sort Prop

with cexp_rel_right_ind1 := Induction for cexp_rel_right Sort Prop.

Combined Scheme cexp_rel_ind from cexp_rel_left_ind1, cexp_rel_right_ind1.

A finihed execution is stable


Lemma finished_stable pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') id v
  (RM : projT1 rm id = Some (B_finished v)) :
  projT1 rm' id = Some (B_finished v).
Proof.
  inv STEP; ins; desf; vauto.
Qed.

Lemma mpstep_neither pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') id s
  (RM : projT1 rm id = Some (B_neither s)) :
  projT1 rm' id = Some (B_neither s) \/
  (exists v pid d es ss,
    projT1 rm' id = Some (B_left v s) /\
    projT1 pm pid = Some (PCrun (Eval v) (S_left id) es ss, d) /\
    projT1 pm' pid = Some (PCfinish, d) \/
    projT1 rm' id = Some (B_right v s) /\
    projT1 pm pid = Some (PCrun (Eval v) (S_right id) es ss, d) /\
    projT1 pm' pid = Some (PCfinish, d)).
Proof.
  inv STEP; rewrite ?rm_ru; try destruct (nat_eq id0 id); vauto;
  try by right; repeat eexists; eauto using rm_rus.
Qed.

Lemma mpstep_left pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') id v s
  (RM : projT1 rm id = Some (B_left v s)) :
  projT1 rm' id = Some (B_left v s) \/
  (exists v' pid d es ss,
    projT1 rm' id = Some (B_finished (Vpair v v')) /\
    projT1 pm pid = Some (PCrun (Eval v') (S_right id) es ss, d) /\
    projT1 pm' pid = Some (PCstart (Eval (Vpair v v')) s, d)).
Proof.
  inv STEP; rewrite ?rm_ru; try destruct (nat_eq id0 id); vauto;
  try by right; repeat eexists; eauto using rm_rus.
Qed.

Lemma mpstep_right pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') id v s
  (RM : projT1 rm id = Some (B_right v s)) :
  projT1 rm' id = Some (B_right v s) \/
  (exists v' pid d es ss,
    projT1 rm' id = Some (B_finished (Vpair v' v)) /\
    projT1 pm pid = Some (PCrun (Eval v') (S_left id) es ss, d) /\
    projT1 pm' pid = Some (PCstart (Eval (Vpair v' v)) s, d)).
Proof.
  inv STEP; rewrite ?rm_ru; try destruct (nat_eq id0 id); vauto;
  try by right; repeat eexists; eauto using rm_rus.
Qed.

Lemma ss_star_exp_app e s e' (S: ss_star e e') : ss_star (exp_app s e) (exp_app s e').
Proof.
  induction S; eauto using step_exp_app, ss_star.
Qed.

Lemma ss_star_pair :
  forall e1 e1' (S1: ss_star e1 e1') e2 e2' (S2: ss_star e2 e2'),
    ss_star (Epair e1 e2) (Epair e1' e2').
Proof.
  induction 1; induction 1; eauto using step, ss_star.
Qed.

Lemma start_step pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') pid e s d
  (PM : projT1 pm pid = Some (PCstart e s, d)) :
  exists d',
  projT1 pm' pid = Some (PCstart e s, d') \/
  projT1 pm' pid = Some (PCrun e s e s, d').
Proof.
  inv STEP; eauto;
    destruct (nat_eq pid1 pid); try destruct (nat_eq pid2 pid); subst;
      repeat first [rewrite rm_rus | rewrite rm_ruo; [|done]]; clarify; eauto.
  inv H; eauto.
Qed.

Lemma run_step pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') pid e s es ss d
  (PM : projT1 pm pid = Some (PCrun e s es ss, d)) :
  (exists e' s' d', projT1 pm' pid = Some (PCrun e' s' es ss, d')) \/
  (projT1 pm' pid = Some (PCfail es ss, d)) \/
  (exists e1 e2 id,
    e = Epair e1 e2 /\
    projT1 rm id = None /\
    pm' = ff_update pm pid (PCstart e1 (S_left id), T_right id e2 :: d) /\
    rm' = ff_update rm id (B_neither s)) \/
  (exists v id s',
    e = Eval v /\
    s = S_left id /\
    projT1 rm id = Some (B_neither s') /\
    pm' = ff_update pm pid (PCfinish, d) /\
    rm' = ff_update rm id (B_left v s')) \/
  (exists v1 v2 id s',
    e = Eval v1 /\
    s = S_left id /\
    projT1 rm id = Some (B_right v2 s') /\
    pm' = ff_update pm pid (PCstart (Eval (Vpair v1 v2)) s', d) /\
    rm' = ff_update rm id (B_finished (Vpair v1 v2))) \/
  (exists v id s',
    e = Eval v /\
    s = S_right id /\
    projT1 rm id = Some (B_neither s') /\
    pm' = ff_update pm pid (PCfinish, d) /\
    rm' = ff_update rm id (B_right v s')) \/
  (exists v1 v2 id s',
    e = Eval v2 /\
    s = S_right id /\
    projT1 rm id = Some (B_left v1 s') /\
    pm' = ff_update pm pid (PCstart (Eval (Vpair v1 v2)) s' , d) /\
    rm' = ff_update rm id (B_finished (Vpair v1 v2))).
Proof.
  inv STEP;
    destruct (nat_eq pid1 pid); try destruct (nat_eq pid2 pid); subst;
      repeat first [rewrite rm_rus | rewrite rm_ruo; [|done]]; clarify;
    try (by left; repeat eexists; eauto).
  by do 2 right; left; repeat eexists; eauto.
  by inv H; eauto; try by left; repeat eexists; eauto.
  by do 3 right; left; repeat eexists; eauto.
  by do 4 right; left; repeat eexists; eauto.
  by do 5 right; left; repeat eexists; eauto.
  by do 6 right; repeat eexists; eauto.
Qed.

Lemma fail_step pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') pid es ss d
  (PM : projT1 pm pid = Some (PCfail es ss, d)) :
  (exists d', projT1 pm' pid = Some (PCfail es ss, d')) \/
  (exists pid' d', projT1 pm' pid' = Some (PCstart es ss, d')).
Proof.
  inv STEP;
    destruct (nat_eq pid1 pid); try destruct (nat_eq pid2 pid); subst;
      repeat first [rewrite rm_rus | rewrite rm_ruo; [|done]]; clarify;
    try (by left; repeat eexists; eauto).
  by right; exists pid2; eauto using rm_rus.
  by inv H.
Qed.

Lemma indeq_step pm rm pm' rm'
  (STEP : mpstep pm rm pm' rm') pid pc d
  (PM : projT1 pm pid = Some (pc, d)) x
  (IN : In x d):
  (exists pid' pc' d', projT1 pm' pid' = Some (pc', d') /\ In x d') \/
  (exists id e d, x = T_right id e /\ projT1 pm' pid = Some (PCstart e (S_right id), d)).
Proof.
  inv STEP;
   destruct (nat_eq pid1 pid); try destruct (nat_eq pid2 pid); subst;
      repeat first [rewrite rm_rus | rewrite rm_ruo; [|done]]; clarify;
    try (match goal with H : spstep _ _ _ _ |- _ => inv H end);
    try (eapply in_app_or in IN); simpl in IN; des; clarify; eauto 6;
    try (by left; repeat eexists; try eapply rm_ru; eauto using rm_rus, in_cons, in_eq);
    try (by left; repeat eexists; try eapply rm_ru; eauto);
    try (by clear H H0; left; repeat eexists; [eapply rm_ru; right; split; [eapply rm_ru|]|]; eauto).
Qed.

Lemma stack_taskids_app lc s:
  stack_taskids (stack_app lc s) = stack_taskids s.
Proof.
  induction lc; eauto.
Qed.

Lemma stack_app_det_left: forall lc lc' id,
  stack_app lc (S_left id) = stack_app lc' (S_left id) ->
  lc = lc'.
Proof.
  induction lc; destruct lc'; ins; clarify; f_equal; eauto.
Qed.

Lemma stack_app_det_right: forall lc lc' id,
  stack_app lc (S_right id) = stack_app lc' (S_right id) ->
  lc = lc'.
Proof.
  induction lc; destruct lc'; ins; clarify; f_equal; eauto.
Qed.

Lemma In_right_deq id e d:
  In (T_right id e) d -> In (TIDright id) (deque_taskids d).
Proof.
  induction d; ins; intuition; desf; ins; desf; eauto.
Qed.

Related expression is deterministic


Lemma cexp_rel_left_det pm rm (WF: well_formed_nd pm rm) id e1
  (REL1 : cexp_rel_left pm rm id e1) :
  forall e2 (REL2 : cexp_rel_left pm rm id e2),
  e1 = e2

with cexp_rel_right_det pm rm (WF: well_formed_nd pm rm) id e1
  (REL1 : cexp_rel_right pm rm id e1) :
  forall e2 (REL2 : cexp_rel_right pm rm id e2),
  e1 = e2.
Proof.
  specialize (cexp_rel_left_det _ _ WF).
  specialize (cexp_rel_right_det _ _ WF).
  eapply wf_nd_altI in WF; red in WF; des; destruct REL1; intros; inv REL2; subst; eauto;
  try destruct (nat_eq pid pid0) as [|NEQ]; clarify;
  try destruct (nat_eq id' id'0) as [|NEQ]; clarify;
  try (by eapply PPND in NEQ; rewrite !NoDup_app, PM, PM0 in *; ins;
         try rewrite ?stack_taskids_app in *; ins; desf; exfalso; eauto);
  try (specialize (PRND pid id'); rewrite PM, RM in *; ins;
         try rewrite ?stack_taskids_app in *; ins; desf; exfalso; eauto;
         try by inv PRND; rewrite in_app_iff in *; ins; eauto);
  try (by eapply RRND in NEQ; rewrite !NoDup_app, RM, RM0 in *; ins;
         try rewrite ?stack_taskids_app in *; ins; desf; exfalso; eauto);
  try (by des; generalize (PALL pid); rewrite PM; simpl;
         rewrite ?stack_taskids_app;
         intro X; specialize (X _ (or_introl eq_refl)); revert X; simpl; rewrite RM);
  try (by des; generalize (RALL id'); rewrite ?RM, ?RM0; simpl;
         rewrite ?stack_taskids_app; simpl;
         intro X; specialize (X _ (or_introl eq_refl)); revert X; simpl; rewrite ?RM, ?RM0).

  by eapply stack_app_det_left in H1; clarify.
  by eapply stack_app_det_left in H1; clarify.
  by eapply stack_app_det_left in H1; clarify.
  by des; clarify.
  by eapply stack_app_det_left in H0; clarify; repeat f_equal; eauto.
  by eapply stack_app_det_left in H0; clarify; repeat f_equal; eauto.
  by eapply stack_app_det_left in H0; clarify; repeat f_equal; eauto.

  specialize (cexp_rel_left_det _ _ WF).
  specialize (cexp_rel_right_det _ _ WF).
  eapply wf_nd_altI in WF; red in WF; des; destruct REL1; intros; inv REL2; subst; eauto;
  try destruct (nat_eq pid pid0) as [|NEQ]; clarify;
  try destruct (nat_eq id' id'0) as [|NEQ]; clarify;
  try (by try eapply In_right_deq in IN; try eapply In_right_deq in IN0;
         eapply PPND in NEQ; rewrite !NoDup_app, PM, PM0 in *; ins;
         try rewrite ?stack_taskids_app in *; ins; desf; exfalso; eauto 6 using in_or_app);
  try (specialize (PRND pid0 id); rewrite PM in *; ins;
         try rewrite ?stack_taskids_app in *; ins; try by desf; exfalso; eauto;
         try by inv PRND; try eapply In_right_deq in IN; rewrite in_app_iff in *; ins; eauto);
  try (specialize (PRND pid id'); rewrite PM, ?RM in *; ins;
         try rewrite ?stack_taskids_app in *; ins; try by desf; exfalso; eauto;
         try solve [by inv PRND; try eapply In_right_deq in IN; rewrite in_app_iff in *; ins; eauto
            | by rewrite NoDup_app in *; des; ins; eapply In_right_deq in IN;
                 exfalso; eauto using in_or_app ]);
  try (by eapply RRND in NEQ; rewrite !NoDup_app, RM, RM0 in *; ins;
         try rewrite ?stack_taskids_app in *; ins; desf; exfalso; eauto);
  try (by des; generalize (PALL pid); rewrite PM; simpl;
         rewrite ?stack_taskids_app;
         intro X; specialize (X _ (or_introl eq_refl)); revert X; simpl; rewrite RM);
  try (by des; generalize (RALL id'); rewrite ?RM, ?RM0; simpl;
         rewrite ?stack_taskids_app; simpl;
         intro X; specialize (X _ (or_introl eq_refl)); revert X; simpl; rewrite ?RM, ?RM0);
  try (by eapply In_right_deq in IN; des; generalize (PALL pid); rewrite PM; simpl;
         rewrite ?stack_taskids_app;
         intro X; specialize (X _ (in_or_app _ _ _ (or_intror IN))); revert X; simpl; rewrite RM).

  by repeat rewrite NoDup_app in *; des; clear -IN0 IN PRND2;
  induction d0; ins; desf; try inv PRND2; eauto; exfalso; eauto using In_right_deq.

  by eapply stack_app_det_right in H1; clarify.
  by eapply stack_app_det_right in H1; clarify.
  by eapply stack_app_det_right in H1; clarify.
  by des; clarify.
  by eapply stack_app_det_right in H0; clarify; repeat f_equal; eauto.
  by eapply stack_app_det_right in H0; clarify; repeat f_equal; eauto.
  by eapply stack_app_det_right in H0; clarify; repeat f_equal; eauto.
Qed.

Lemma stack_last_app l s: stack_last (stack_app l s) = stack_last s.
Proof.
  induction l; ins.
Qed.

Lemma stack_decomp s: stack_app (stack_conts s) (stack_last s) = s.
Proof.
  induction s; ins; f_equal; eauto.
Qed.

Lemma app_stack_app : forall lc s e,
  app_stack (stack_app lc s) e = app_stack s (exp_app lc e).
Proof.
  induction lc; ins.
Qed.

Relation of expressions is preserved in multiprocessor step


Lemma cexp_rel_preserve pm rm (WF: well_formed pm rm) id e :
  (forall
    (REL: cexp_rel_left pm rm id e)
    pm' rm' (STEP: mpstep pm rm pm' rm'),
    exists e', ss_star e e' /\ cexp_rel_left pm' rm' id e') /\
  (forall (REL: cexp_rel_right pm rm id e)
    pm' rm' (STEP: mpstep pm rm pm' rm'),
    exists e', ss_star e e' /\ cexp_rel_right pm' rm' id e').
Proof.
  eapply (cexp_rel_ind
    (fun id e c => forall pm' rm' (STEP: mpstep pm rm pm' rm'),
      exists e', ss_star e e' /\ cexp_rel_left pm' rm' id e')
    (fun id e c => forall pm' rm' (STEP: mpstep pm rm pm' rm'),
      exists e', ss_star e e' /\ cexp_rel_right pm' rm' id e')); clear id e; ins; subst.

    by eapply start_step in STEP; eauto; instantiate; clear - STEP; des; eexists,; vauto.

  Case 1.
   eapply run_step in STEP; eauto; desf; try (by eexists,;vauto);
     eapply WF in PM; des; ins;
     rewrite stack_last_app, app_stack_app in *; ins; clarify;
     eauto 6 using cexp_rel_left, rm_rus.
   eexists,; eauto.
   eapply cexp_rel_left_Neither with (lc:=stack_conts s);
     try rewrite <- WFRo, stack_decomp; eauto using rm_rus.
   eapply cexp_rel_left_Start with (lc:=nil); eauto using rm_rus.
   eapply cexp_rel_right_Thunk; eauto using rm_rus, in_eq.
   by simpl; rewrite app_stack1.

  Case 2.
  by eapply fail_step in STEP; des; eauto using ss_starR, cexp_rel_left.

  Case 3.
  by des; [eapply mpstep_left in STEP; des|eapply finished_stable in STEP];
     eauto 6 using ss_starR, cexp_rel_left.

  Case 0.
  specialize (H _ _ STEP); specialize (H0 _ _ STEP); des.
  exploit mpstep_neither; eauto; intro; des.
  by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app.

  eexists,; [|by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_pair; eauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e1 = app_stack ss es); [|congruence].
  eapply cexp_rel_left_det; eauto.
  eapply cexp_rel_left_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  eexists,; [|by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_pair; eauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e2 = app_stack ss es); [|congruence].
  eapply cexp_rel_right_det; eauto.
  eapply cexp_rel_right_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  Case 1.
  specialize (H _ _ STEP); des; eauto.
  exploit mpstep_left; eauto; intro; des.
    by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app, ss_starR.
  eexists,; [|by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_trans, ss_star_one; [eapply ss_star_pair|vauto]; vauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e2 = app_stack ss es); [|congruence].
  eapply cexp_rel_right_det; eauto.
  eapply cexp_rel_right_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  Case 2.
  specialize (H _ _ STEP); des; eauto.
  exploit mpstep_right; eauto; intro; des.
    by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app, ss_starR.
  eexists,; [|by eauto 6 using cexp_rel_left, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_trans, ss_star_one; [eapply ss_star_pair|vauto]; vauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e1 = app_stack ss es); [|congruence].
  eapply cexp_rel_left_det; eauto.
  eapply cexp_rel_left_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.


  by eapply indeq_step in STEP; des; eauto; clarify; eexists,;vauto;
     eapply cexp_rel_right_Start with (lc:=nil); eauto.

  by eapply start_step in STEP; eauto; instantiate; clear - STEP; des; eexists,; vauto.

  Case 1.
   eapply run_step in STEP; eauto; desf; try (by eexists,;vauto);
     eapply WF in PM; des; ins;
     rewrite stack_last_app, app_stack_app in *; ins; clarify;
     eauto 6 using cexp_rel_right, rm_rus.
   eexists,; eauto.
   eapply cexp_rel_right_Neither with (lc:=stack_conts s);
     try rewrite <- WFRo, stack_decomp; eauto using rm_rus.
   eapply cexp_rel_left_Start with (lc:=nil); eauto using rm_rus.
   eapply cexp_rel_right_Thunk; eauto using rm_rus, in_eq.
   by simpl; rewrite app_stack1.

  Case 2.
  by eapply fail_step in STEP; des; eauto using ss_starR, cexp_rel_right.

  Case 3.
  by des; [eapply mpstep_right in STEP; des|eapply finished_stable in STEP];
     eauto 6 using ss_starR, cexp_rel_right.

  Case 0.
  specialize (H _ _ STEP); specialize (H0 _ _ STEP); des.
  exploit mpstep_neither; eauto; intro; des.
    by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app.

  eexists,; [|by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_pair; eauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e1 = app_stack ss es); [|congruence].
  eapply cexp_rel_left_det; eauto.
  eapply cexp_rel_left_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  eexists,; [|by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_pair; eauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e2 = app_stack ss es); [|congruence].
  eapply cexp_rel_right_det; eauto.
  eapply cexp_rel_right_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  Case 1.
  specialize (H _ _ STEP); des.
  exploit mpstep_left; eauto; intro; des.
  by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app, ss_starR.

  eexists,; [|by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_trans, ss_star_one; [eapply ss_star_pair|vauto]; vauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e2 = app_stack ss es); [|congruence].
  eapply cexp_rel_right_det; eauto.
  eapply cexp_rel_right_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.

  Case 2.
  specialize (H _ _ STEP); des.
  exploit mpstep_right; eauto; intro; des.
  by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app, ss_starR.

  eexists,; [|by eauto 6 using cexp_rel_right, ss_star_pair, ss_star_exp_app].
  eapply ss_star_exp_app, ss_star_trans, ss_star_one; [eapply ss_star_pair|vauto]; vauto.
  red in WF; des; pose proof x1 as xxx; eapply WF in xxx; des; ins.
  assert (e1 = app_stack ss es); [|congruence].
  eapply cexp_rel_left_det; eauto.
  eapply cexp_rel_left_Run; simpl; eauto using app_stack1.
  eby rewrite WFRo, stack_decomp.
Qed.

Lemma cexp_rel_right_mult pm rm id e
  (REL: cexp_rel_right pm rm id e) : forall
  (WF: well_formed pm rm)
  pm' rm' (STEP: mpstep_star pm rm pm' rm'),
  exists e', ss_star e e' /\ cexp_rel_right pm' rm' id e' /\ well_formed_nd pm' rm'.
Proof.
  intros; revert e REL WF; induction STEP; ins.
    by destruct WF; eauto using ss_starR.
  edestruct cexp_rel_preserve as [_ X]; eauto.
  exploit X; eauto; clear X; intro X; desf.
  edestruct IHSTEP; des; eauto using ss_star_trans, wf_preserved.
Qed.

Lemma count_empty B g: count (empty_map B) g = 0%Z.
Proof.
  done.
Qed.

Soundness of multiprocessor evaluation


Theorem mpstep_soundness: forall pid e id pm' rm' v s,
  mpstep_star (ff_update (empty_map _) pid (PCstart e (S_right id), nil))
              (ff_update (empty_map _) id (B_neither (S_left id))) pm' rm' ->
  projT1 rm' id = Some (B_right v s) ->
  reduce e v.
Proof.
  intros; eapply cexp_rel_right_mult in H; try eapply cexp_rel_right_Start with (lc := nil);
    eauto using rm_rus; des; try eapply soundness_ss.
    by replace (Eval v) with e'; eauto using cexp_rel_right, cexp_rel_right_det.
  split; [by ins; desf|].
  by split; rewrite ?count_update, ?count_empty; simpl; desf.
Qed.

Completeness of multiprocessor evaluation


Theorem mpstep_completeness: forall e v pid id s,
  reduce e v ->
  exists pm' rm',
    mpstep_star
      (ff_update (empty_map _) pid (PCstart e (S_right id), nil))
      (ff_update (empty_map _) id (B_neither s)) pm' rm'
    /\ projT1 rm' id = Some (B_right v s).
Proof.
  intros; eapply completeness_ss in H.
  eapply ss_impl_mpstep_s_start with (pid := pid) in H; eauto using rm_rus; des.
  assert (X: forall rm, projT1 rm id = Some (B_neither s) ->
             exists pm'', mpstep pm' rm pm'' (ff_update rm id (B_right v s))).
    by eexists; eapply mpstep_right_first; eauto.
  edestruct X; eauto 6 using mpstep_star_trans, mpstep_star_one, rm_rus.
Qed.