(* Machine and Context Imports*)
  (* Import Machine DryMachine ThreadPool AsmContext HybridMachine.Concur.mySchedule. *)
  (* Machine and Context Imports*)

  Notation Sch := schedule.Notation CoarseSem := coarse_semantics.Notation threadStep := (HybridMachineSig.threadStep).Notation cmachine_step := ((corestep (AsmContext.coarse_semantics initU))).Notation fmachine_step := ((corestep (AsmContext.fine_semantics initU))).Record strong_tsim {tpc tpf : threadPool.ThreadPool.t} (mc mf : Mem.mem) {i}
         (f: memren) (pfc : containsThread tpc i)
         (pff : containsThread tpf i) (compc: mem_compatible tpc mc)
         (compf: mem_compatible tpf mf) : Prop :=
    { code_eq: ctl_inj f (getThreadC pfc) (getThreadC pff);
      obs_eq_data: mem_obs_eq f (restrPermMap ((fst (compc i pfc))))
                         (restrPermMap (fst (compf i pff)));
      obs_eq_locks: mem_obs_eq f (restrPermMap ((snd (compc i pfc))))
                         (restrPermMap (snd (compf i pff)));
    }.Record strong_tsim {tpc tpf : threadPool.ThreadPool.t} (mc mf : Mem.mem) {i}
         (f: memren) (pfc : containsThread tpc i)
         (pff : containsThread tpf i) (compc: mem_compatible tpc mc)
         (compf: mem_compatible tpf mf) : Prop :=
    { code_eq: ctl_inj f (getThreadC pfc) (getThreadC pff);
      obs_eq_data: mem_obs_eq f (restrPermMap ((fst (compc i pfc))))
                         (restrPermMap (fst (compf i pff)));
      obs_eq_locks: mem_obs_eq f (restrPermMap ((snd (compc i pfc))))
                         (restrPermMap (snd (compf i pff)));
    }.Record strong_tsim {tpc tpf : threadPool.ThreadPool.t} (mc mf : Mem.mem) {i}
         (f: memren) (pfc : containsThread tpc i)
         (pff : containsThread tpf i) (compc: mem_compatible tpc mc)
         (compf: mem_compatible tpf mf) : Prop :=
    { code_eq: ctl_inj f (getThreadC pfc) (getThreadC pff);
      obs_eq_data: mem_obs_eq f (restrPermMap ((fst (compc i pfc))))
                         (restrPermMap (fst (compf i pff)));
      obs_eq_locks: mem_obs_eq f (restrPermMap ((snd (compc i pfc))))
                         (restrPermMap (snd (compf i pff)));
    }.Record strong_tsim {tpc tpf : threadPool.ThreadPool.t} (mc mf : Mem.mem) {i}
         (f: memren) (pfc : containsThread tpc i)
         (pff : containsThread tpf i) (compc: mem_compatible tpc mc)
         (compf: mem_compatible tpf mf) : Prop :=
    { code_eq: ctl_inj f (getThreadC pfc) (getThreadC pff);
      obs_eq_data: mem_obs_eq f (restrPermMap ((fst (compc i pfc))))
                         (restrPermMap (fst (compf i pff)));
      obs_eq_locks: mem_obs_eq f (restrPermMap ((snd (compc i pfc))))
                         (restrPermMap (snd (compf i pff)));
    }.(** *** Simulation between events *)

  Definition pmap_inj f (pmap1 pmap2 : access_map) :=
    (forall b1 b2,
        f b1 = Some b2 ->
        Maps.PMap.get b1 pmap1 = Maps.PMap.get b2 pmap2) /\
    (forall b1, f b1 = None ->
           Maps.PMap.get b1 pmap1 = fun _ => None) /\
    (forall b2, (exists ofs, Maps.PMap.get b2 pmap2 ofs <> None) ->
           exists b1, f b1 = Some b2).Definition isProjection (f : memren) (deltaMap deltaMap' : delta_map) : Prop :=
    (forall b b',
      f b = Some b' ->
      Maps.PTree.get b deltaMap = Maps.PTree.get b' deltaMap').Definition delta_inj (f : memren) (deltaMap deltaMap' : delta_map) : Prop :=
    (forall b b',
        f b = Some b' ->
        match Maps.PTree.get b deltaMap, Maps.PTree.get b' deltaMap' with
        | Some d1, Some d2 => d1 = d2
        | Some d1, None => forall ofs, d1 ofs = None \/ d1 ofs = Some None
        | None, None => True
        | _, _ => False
        end) /\
    (forall b, f b = None ->
          match Maps.PTree.get b deltaMap with
          | None => True
          | Some d => forall ofs, d ofs = None \/ d ofs = Some None
          end ) /\
    (forall b', (~exists b, f b = Some b') ->
           Maps.PTree.get b' deltaMap' = None).Definition delta_content_inj (f : memren) (deltaMap deltaMap' : delta_content) : Prop :=
    (forall b b',
        f b = Some b' ->
        match Maps.PTree.get b deltaMap, Maps.PTree.get b' deltaMap' with
        | Some d1, Some d2 => forall ofs, match d1 ofs, d2 ofs with
                                    | Some mv1, Some mv2 =>
                                      memval_obs_eq f mv1 mv2
                                    | None, None => True
                                    | _, _ => False
                                    end  
        | None, None => True
        | Some d1, None => forall ofs, d1 ofs = None
        | None, Some d2 => False
        end) /\
    (forall b, f b = None ->
          match Maps.PTree.get b deltaMap with
          | None => True
          | Some d => forall ofs, d ofs = None
          end ) /\
    (forall b', (~exists b, f b = Some b') ->
           Maps.PTree.get b' deltaMap' = None).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).Inductive event_sim (f:memren) : Events.machine_event -> Events.machine_event -> Prop :=
  | ReleaseSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hdelta_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.release (b1,ofs) (Some dc1)))
                (Events.external i (Events.release (b2,ofs) (Some dc2)))
  | AcquireSim : forall i b1 b2 ofs dc1 dc2
                   (Hf: f b1 = Some b2)
                   (Hpmap1_inj: delta_content_inj f dc1 dc2),
      event_sim f (Events.external i (Events.acquire (b1,ofs) (Some dc1)))
                (Events.external i (Events.acquire (b2,ofs) (Some dc2)))
  | MkLockSim : forall i b1 b2 ofs
                   (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.mklock (b1,ofs)))
                (Events.external i (Events.mklock (b2, ofs)))
  | FreeLockSim : forall i b1 b2 ofs
                  (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.freelock (b1,ofs)))
                (Events.external i (Events.freelock (b2, ofs)))
  | SpawnSim : forall i b1 b2 ofs dc1a dc2a dc1b dc2b
                 (Hf: f b1 = Some b2)
                 (Hinj1: delta_content_inj f dc1a dc2a)
                 (Hinj2: delta_content_inj f dc1b dc2b),
      event_sim f (Events.external i (Events.spawn (b1, ofs) (Some dc1a) (Some dc1b)))
                (Events.external i (Events.spawn (b2, ofs) (Some dc2a) (Some dc2b)))
  | FAcqSim: forall i b1 b2 ofs
               (Hf: f b1 = Some b2),
      event_sim f (Events.external i (Events.failacq (b1,ofs)))
                (Events.external i (Events.failacq (b2, ofs))).(** *** Simulation between the two machines *)


  (* simStrong now maintains the extra invariant that any new blocks
      from the internal execution are owned by thread tid. This is
     needed for the suspend_sim proof. Note that it's not possible
     to prove it just by the fact that only thread tid executes because
     1. some location may be allocated and then freed in this multistep
      execution and 2. our relation only strongly relates the final state
      of the execution not in-between states. *)

  Definition fpool tpc : Type :=
    forall i (cnti: containsThread tpc i), memren.Definition max_inv mf := forall b ofs, Mem.valid_block mf b ->
                                    permission_at mf b ofs Max = Some Freeable.Notation "cnt '$' m '@'  'S'" := (getStepType cnt m Suspend) (at level 80).Definition sim_fail_def :=
    forall tpc trc tpf trf (mc mf : Mem.mem) fuelF
      (xs : Sch) (f fg : memren) (fp : fpool tpc) (i : NatTID.tid)
      (pff: ~ containsThread tpf i)
      (Hsim: sim tpc trc mc tpf trf mf xs f fg fp (S (S fuelF))),
    exists tr',
      (forall U, fmachine_step (i :: U, trf, tpf) mf (U, tr', tpf) mf) /\
      sim tpc trc mc tpf trf mf xs f fg fp (S fuelF).Notation csafe := (HybridCoarseMachine.csafe).Notation sim_fail_def := (@sim_fail_def _ _ initU).Notation internal_step := (internal_step).Notation internal_execution := (internal_execution).Notation fyield := (@yield HybridFineMachine.scheduler).Notation fdiluteMem := (@diluteMem FineDilMem).Notation sim_internal_def := (@sim_internal_def _ _ initU).Notation sim_suspend_def := (@sim_suspend_def _ _ initU).Notation sim_external_def := (@sim_external_def _ _ initU).(** Function that projects the angel through a memory injection to
    compute a new angel *)

  Definition projectAngel (f : memren) (deltaMap : delta_map) : delta_map :=
    Maps.PTree.fold (fun acc b bperm =>
                       match f b with
                       | Some b' =>
                         Maps.PTree.set b' bperm acc
                       | None =>
                         acc end)
                    deltaMap (Maps.PTree.empty _).

Lemma projectAngel_correct: forall (f:memren) deltaMap (Hf_injective: forall b1 b1' b2, f b1 = Some b2 -> f b1' = Some b2 -> b1 = b1'), isProjection f deltaMap (projectAngel f deltaMap).