Module MoreSemantics

Require Import Behaviors.
Require Import Util.
Import Utf8.
Import Coqlib.
Import Smallstep.

Lemma forall_exists {T: Type} (P : TProp) : (∀ t : T, ¬ P t) ∨ ∃ t : T, P t.
Proof.
destruct (Classical_Prop.classic (ex P)); eauto. Qed.

Section SEM.

Context (sem: semantics).

Definition reachable (s: state sem) : Prop :=
  ∃ i tr, initial_state sem istar (step sem) (globalenv sem) i tr s.

Lemma reachable_initial_state { s } :
  sinitial_state sem
  sreachable.
Proof.
  exists s, nil. vauto.
Qed.

Lemma reachable_step {s tr s'} :
  step sem (globalenv sem) s tr s' →
  reachable s
  reachable s'.
Proof.
  intros STEP (i & tr' & INIT & STAR).
  exists i, (tr' ++ tr). split. exact INIT.
  clear INIT.
  induction STAR. eright. exact STEP. left. symmetry. apply Events.E0_right.
  subst. rewrite Events.Eapp_assoc. vauto.
Qed.

Lemma reachable_star {s tr s'} :
  Star sem s tr s' →
  reachable s
  reachable s'.
Proof.
  induction 1. exact id.
  eauto using reachable_step.
Qed.

Lemma reachable_plus {s tr s'} :
  Plus sem s tr s' →
  reachable s
  reachable s'.
Proof.
  intros (). eauto using reachable_star, reachable_step.
Qed.

Definition invariant (P: state semProp) : Prop :=
  reachableP.

Definition invariant_weaken (P Q: state semProp) :
  (PQ) → invariant Pinvariant Q :=
  λ w p s r, w s (p s r).

Definition safe : Prop :=
  program_behaves semnot_wrong.

Section INVARIANT.

Context (P: state semProp).

Hypothesis invariant_init : initial_state semP.

Hypothesis invariant_step :
  ∀ s, reachable sP s → ∀ tr, step sem (globalenv sem) s trP.

Theorem invariant_ind: invariant P.
Proof.
  intros s (i & tr & INIT & STAR).
  pose proof (reachable_initial_state INIT) as REACH.
  apply invariant_init in INIT.
  induction STAR. auto. eauto using reachable_step.
Qed.

End INVARIANT.

Context P (Hinv: invariant P).

Definition sem_with_strong_local_invariant : semantics :=
  {|
    state := state sem;
    genvtype := genvtype sem;
    step := λ ge s tr s', step sem ge s tr s' ∧ P s';
    initial_state := initial_state semP;
    final_state := final_state sem;
    globalenv := globalenv sem;
    symbolenv := symbolenv sem
  |}.

Theorem global_invariant_strong_local :
  forward_simulation sem sem_with_strong_local_invariant.
Proof.
  refine (forward_simulation_step
            sem sem_with_strong_local_invariant
            (λ id, eq_refl)
            (λ s s', s' = sreachable s)
            _ _ _).
  - intros s INIT. exists s.
    assert (reachable s) as R.
    exists s, nil; vauto.
    vauto.
  - intros s ? r ( -> & _ ). exact id.
  - intros s1 t s1' H s2 ( -> & R ).
    exists s1'.
    assert (reachable s1') as R'.
    eauto using reachable_step.
    vauto.
Defined.

Definition sem_with_local_invariant : semantics :=
  {|
    state := state sem;
    genvtype := genvtype sem;
    step := λ ge s tr s', step sem ge s tr s' ∧ P s;
    initial_state := initial_state sem;
    final_state := final_state sem;
    globalenv := globalenv sem;
    symbolenv := symbolenv sem
  |}.

Theorem global_invariant_local :
  forward_simulation sem sem_with_local_invariant.
Proof.
  refine (forward_simulation_step
            sem sem_with_local_invariant
            (λ id, eq_refl)
            (λ s s', s' = sreachable s)
            _ _ _).
  - intros s INIT. exists s.
    assert (reachable s) as R.
    exists s, nil; vauto.
    vauto.
  - intros s ? r ( -> & _ ). exact id.
  - intros s1 t s1' H s2 ( -> & R ).
    exists s1'.
    assert (reachable s1') as R'.
    eauto using reachable_step.
    vauto.
Defined.

Lemma blocking_determinate :
  determinate sem
  determinate sem_with_local_invariant.
Proof.
  intros DET.
  constructor; try apply DET; simpl.
  - intros s t1 s1 t2 s2 [Hs1 p] [Hs2 _]. eapply DET; eassumption.
  - intros s t s' [H p]. eapply DET; eassumption.
  - intros s r H tr s' [K p]. eapply DET; eassumption.
Qed.

End SEM.

Definition behavior_inclusion_preserves_safety (L1 L2: semantics) :
  program_behaves L2program_behaves L1
  safe L1safe L2 :=
  λ i s b H, s b (i b H).

Lemma strong_local_invariant_global' sem P :
  invariant (sem_with_strong_local_invariant sem P) P.
Proof.
  apply invariant_ind.
  - intros s (); auto.
  - intros s H H0 tr a (); auto.
Qed.

Lemma safe_progress {sem} :
  safe sem
  ∀ {s}, reachable sem s
       (∃ tr s', step sem (globalenv sem) s tr s') ∨ (∃ r, final_state sem s r).
Proof.
  intros SAFE s REACH.
  case (forall_exists (final_state sem s)). 2: auto.
  intros NotFinal. left.
  case (forall_existstr_s', let '(tr, s') := tr_s' in step sem (globalenv sem) s tr s')).
  + intros X. specializetr s', X (tr, s')). simpl in X.
    exfalso.
    destruct REACH as (i & itr & Hi & STEPS).
    exact (SAFE _ (program_runs Hi (state_goes_wrong sem STEPS X NotFinal))).
  + intros ((tr & s') & H). vauto.
Qed.

Lemma local_invariant_global_1 sem P :
  safe (sem_with_local_invariant sem P) →
  invariant (sem_with_local_invariant sem P) (P ∪ λ s, ∃ r, final_state sem s r).
Proof.
  intros SAFE.
  apply invariant_ind.
  - intros s Hs.
    case (safe_progress SAFE (reachable_initial_state _ Hs)).
    + intros (tr & s' & STEP & Hs'). auto.
    + auto.
  - intros s REACH _ tr s' STEP.
    case (safe_progress SAFE (reachable_step (sem_with_local_invariant sem P) STEP REACH)).
    + intros (tr' & s'' & STEP' & Hs'). auto.
    + auto.
Qed.

Lemma local_invariant_global_2 sem P :
  (∀ r s, final_state sem s rNostep sem s) →
  safe (sem_with_local_invariant sem P) →
  invariant sem (reachable (sem_with_local_invariant sem P)).
Proof.
  intros DET SAFE.
  apply invariant_ind.
  - intros s Hs.
    apply reachable_initial_state, Hs.
  - intros s REACH REACH' tr s' STEP.
    generalize (local_invariant_global_1 _ _ SAFE _ REACH').
    intros [ Hs | (r & FINAL) ].
    2: elim (DET r s FINAL _ _ STEP).
    assert (Step (sem_with_local_invariant sem P) s tr s') as STEP'.
    vauto.
    eapply reachable_step; eauto.
Qed.

Theorem local_invariant_global sem P :
  (∀ r s, final_state sem s rNostep sem s) →
  safe (sem_with_local_invariant sem P) →
  invariant sem (P ∪ λ s, ∃ r, final_state sem s r).
Proof.
  intros DET SAFE s REACH.
  apply local_invariant_global_1. assumption.
  apply local_invariant_global_2; assumption.
Qed.

Section SIM.

Context {L1 L2: semantics} (sim: forward_simulation L1 L2).

Lemma reachable_sim s1 :
  reachable L1 s1
  ∃ idx s2,
    reachable L2 s2fsim_match_states sim idx s1 s2.
Proof.
  intros (i1 & tr & INIT1 & STAR1).
  destruct (fsim_match_initial_states sim _ INIT1) as (idx & i2 & INIT2 & SIM).
  destruct (simulation_star sim STAR1 _ _ SIM) as (idx' & s2' & STAR2 & SIM').
  exists idx', s2'.
  vauto.
Qed.

End SIM.

Section BSIM.

Context {L1 L2: semantics} (sim: backward_simulation L1 L2).
Context (DET: ∀ r s, final_state L2 s rNostep L2 s).

Context (P1: state L1Prop) (P2: state L2Prop).

Hypothesis L1_safe : safe L1.

Hypothesis inv1 : invariant L1 P1.

Hypothesis inv2_from_sim :
  ∀ idx s1 s2,
    sim idx s1 s2
    s1P1
    s2P2.

Lemma invariant_from_match_state: invariant L2 (P2 ∪ λ s, ∃ r, final_state L2 s r).
Proof.
  apply local_invariant_global. assumption.
  refine (behavior_inclusion_preserves_safety _ _
           (backward_simulation_same_safe_behavior _ L1_safe) L1_safe).
  eapply (Backward_simulation
            (sem_with_local_invariant L2 P2)
            (bsim_order_wf sim)
            (λ idx s1 s2,
             bsim_match_states sim idx s1 s2reachable L1 s1
         ));
    try apply sim;
    simpl.
  - intros s1 s2 Hs1 Hs2.
    generalize (bsim_match_initial_states sim s1 s2 Hs1 Hs2).
    intros (idx & s1' & Hs1' & SIM).
    pose proof (reachable_initial_state L1 Hs1'). vauto.
  - intros idx s1 s2 r (SIM & Hs1).
    exact (bsim_match_final_states sim idx s2 r SIM).
  - intros idx s1 s2 (SIM & Hs1) Safe1.
    generalize (bsim_progress sim idx s2 SIM Safe1).
    intros [ F | (tr & s2' & STEP) ]. auto. right. vauto.
  - intros s2 tr s2' (STEP & Hs2) idx s1 (SIM & Hs1) Safe1.
    generalize (bsim_simulation sim s2 tr s2' STEP idx SIM Safe1).
    intros (idx' & s1' & STEPS & SIM').
    exists idx', s1'.
    split. exact STEPS.
    split. exact SIM'.
    destruct STEPS as [ PLUS | (STAR & LT) ].
    eauto using reachable_plus.
    eauto using reachable_star.
Qed.

End BSIM.