Module IndexedStreams

From Velus Require Import Common.
From Velus Require Import Operators.
From Velus Require Import FunctionalEnvironment.
From Velus Require Import Clocks.

From Coq Require Import Setoid.
From Coq Require Import Morphisms.
From Coq Require Import Arith.EqNat.
From Coq Require Import List.
Import List.ListNotations.
Open Scope list_scope.
From Coq Require Import Logic.FunctionalExtensionality.

Extensional model of synchronous streams

Our model is extensional in the sense that it encodes a coinductive, infinite datastructure (streams) with a function of domain nat. To reason about this object, we shall use functional extensionality Logic.FunctionalExtensionality. This axiom is believed to be consistent with Coq.

       (Import Ids : IDS)
       (Import Op : OPERATORS)
       (Import OpAux : OPERATORS_AUX Ids Op)
       (Import Clocks : CLOCKS Ids Op OpAux).


A stream is represented by its characteristic function:

  Definition stream A := nat -> A.

An indexed stream of lists is well-formed when the length of the lists is uniform over time.
  Definition wf_streams {A} (xss: stream (list A)) :=
    forall k' k, length (xss k) = length (xss k').

  Definition eq_str {A} (xs xs': stream A) := forall n, xs n = xs' n.
  Infix "≈" := eq_str (at level 70, no associativity).

  Lemma eq_str_refl:
    forall {A} (xs: stream A),
    intros * n; reflexivity.

  Lemma eq_str_sym:
    forall {A} (xs xs': stream A),
      xsxs' -> xs' ≈ xs.
    intros * E n; auto.

  Lemma eq_str_trans:
    forall {A} (xs ys zs: stream A),
      xsys -> yszs -> xszs.
    intros * E1 E2 n; auto.
    rewrite E1; auto.

  Add Parametric Relation A : (stream A) (@eq_str A)
      reflexivity proved by (@eq_str_refl A)
      symmetry proved by (@eq_str_sym A)
      transitivity proved by (@eq_str_trans A)
        as eq_str_rel.

A synchronous stream thus maps time to synchronous values:

  Notation vstream := (stream svalue).
  Implicit Type vs : vstream.

A clock is a stream that returns true if the clocked stream contains a value (present c) at the corresponding instant, false if the clocked stream is absent at the corresponding instant.

  Notation cstream := (stream bool).
  Implicit Type cs : cstream.

Synchronous functions

  Lemma present_injection:
    forall (x y : svalue), x = y <-> present x = present y.
    split; intro H; [rewrite H|injection H]; auto.

  Lemma not_absent_present:
    forall (x : svalue), x <> absent <-> exists c, x = present c.
    intros x.
    split; intro HH.
    destruct x; [intuition|eauto].
    destruct HH as [c HH]; rewrite HH.
    intro; discriminate.

  Definition absent_list (xs: list svalue): Prop :=
    Forall (fun v => v = absent) xs.

  Definition present_list (xs: list svalue): Prop :=
    Forall (fun v => v <> absent) xs.

  Definition all_absent {A} (l: list A) : list svalue :=
    map (fun _ => absent) l.

  Remark all_absent_spec:
    forall A (l: list A),
      absent_list (all_absent l).
    induction l; simpl; constructor; auto.

  Remark nth_all_absent:
    forall (xs: list svalue) n,
      nth n (all_absent xs) absent = absent.
    induction xs, n; simpl; auto.

  Lemma absent_list_spec_b:
    forall xs,
      absent_list xs <-> forallb (fun x => x ==b absent) xs = true.
    induction xs; simpl; split; auto.
    - constructor.
    - inversion_clear 1; subst; apply andb_true_intro; intuition.
    - intro H; apply andb_prop in H as (E & Hforall).
      rewrite equiv_decb_equiv in E; constructor; auto.
      apply IHxs; auto.

Count the number of resets ticks seen at n so far.
  Fixpoint count (rs: cstream) (n: nat) : nat :=
    let c := match n with 0 => 0 | S n => count rs n end in
    if rs n then S c else c.

mask k rs xss is the list of streams which clips the list of streams xss between the kth and the (k+1)th reset, outputting absent everywhere else.
  Definition mask (k: nat) (rs: cstream) (xss: stream (list svalue))
    : stream (list svalue) :=
    fun n => if k =? (count rs n) then xss n else all_absent (xss 0).


  Lemma count_le:
    forall r n,
      count r n <= count r (S n).
    intros; simpl.
    destruct (r (S n)); lia.

  Lemma count_le':
    forall r n' n,
      n' < n ->
      count r n' <= count r n.
    induction 1.
    - apply count_le.
    - simpl; destruct (r (S m)); lia.

  Lemma count_true_ge_1:
    forall n r,
      r n = true ->
      1 <= count r n.
    induction n; simpl; intros * E; rewrite E; auto.
    apply Nat.succ_le_mono; lia.

  Lemma count_reset_now:
    forall rs i,
      rs i = true ->
      count rs i = S (match i with 0 => 0 | S m => count rs m end).
    destruct i; intro Hr; simpl; now rewrite Hr.

  Lemma count_reset_gt:
    forall rs i n,
      rs i = true ->
      n < i ->
      count rs n < count rs i.
    intros * Hrs Hn.
    rewrite count_reset_now with (1:=Hrs).
    destruct i; [now inv Hn|].
    inv Hn; auto.
    take (S n <= i) and rewrite Nat.le_succ_l in it.
    now apply Nat.lt_succ_r, count_le'.

  Lemma mask_opaque:
    forall xss k r n,
      count r n <> k ->
      mask k r xss n = all_absent (xss 0).
    intros * E.
    unfold mask.
    assert ((k =? count r n) = false) as ->
        by (apply Nat.eqb_neq; lia); auto.

  Lemma mask_transparent:
    forall xss k r n,
      count r n = k ->
      mask k r xss n = xss n.
    intros * E.
    unfold mask.
    assert ((k =? count r n) = true) as ->
        by (apply Nat.eqb_eq; lia); auto.

  Lemma memory_masked_alt_cond:
    forall rs k n,
      count rs n = (if rs n then S k else k)
      k = (match n with 0 => 0 | S m => count rs m end).
    induction n; simpl.
    - destruct (rs 0); intuition.
    - destruct (rs (S n)); [|now intuition].
      split; inversion 1; subst; auto.

  Add Parametric Morphism : count
      with signature eq_str ==> eq ==> eq
        as count_eq_str.
    intros * E n.
    induction n; simpl; rewrite E; auto.
    now rewrite IHn.

  Add Parametric Morphism k : (mask k)
      with signature eq_str ==> eq_str ==> eq_str
        as mask_eq_str.
    intros * E1 ? ? E2 n; unfold mask.
    now rewrite E1, 2 E2.

  Lemma mask_length:
    forall k xss r n,
      wf_streams xss ->
      length (mask k r xss n) = length (xss n).
    intros; unfold mask.
    destruct (k =? count r n); auto.
    unfold all_absent; rewrite map_length.
    induction n; auto.

If all masks ar well-formed then the underlying stream of lists is well-formed.
  Lemma wf_streams_mask:
    forall xss r,
      (forall n, wf_streams (mask n r xss)) ->
      wf_streams xss.
    unfold wf_streams, mask; intros * WF k k'.
    pose proof (WF (count r k) k' k) as WFk;
      pose proof (WF (count r k') k' k) as WFk'.
    rewrite Nat.eqb_refl in WFk, WFk'.
    rewrite Nat.eqb_sym in WFk'.
    destruct (count r k =? count r k'); auto.
    now rewrite WFk, <-WFk'.

Presence and absence in non-empty lists

  Lemma not_absent_present_list:
    forall xs,
      0 < length xs ->
      present_list xs ->
      ~ absent_list xs.
    intros * Hnz Hpres Habs.
    unfold present_list in Hpres.
    unfold absent_list in Habs.
    destruct xs; [now inversion Hnz|].
    now inv Hpres; inv Habs; auto.

  Lemma absent_list_mask:
    forall xss k r n,
      absent_list (xss n) ->
      absent_list (mask k r xss n).
    intros; unfold mask.
    destruct (k =? count r n); auto using all_absent_spec.

Restrictions of FEnvironments
  Section HistoryRestriction.

    Definition env := @FEnv.t ident svalue.
    Definition history' := @FEnv.t ident (stream svalue).

    Definition restr_hist (H : history') (n: nat): env := (fun xs => xs n) H.

    Hint Unfold restr_hist : fenv.

    Lemma FEnv_find_restr_hist:
      forall H x i,
        (restr_hist H i) x = option_map (fun xs => xs i) (H x).
      unfold restr_hist. reflexivity.

    Lemma FEnv_add_restr_hist:
      forall H x s i,
        FEnv.Equiv eq (restr_hist (FEnv.add x s H) i) (FEnv.add x (s i) (restr_hist H i)).
      intros H x s i x'. simpl_fenv.
      destruct (ident_eq_dec x' x).
      - subst. now setoid_rewrite FEnv.gss.
      - now setoid_rewrite FEnv.gso.

    Lemma FEnv_In_restr_hist:
      forall H x i,
        FEnv.In x H <-> FEnv.In x (restr_hist H i).
      intros. unfold restr_hist.
      now rewrite FEnv.map_in_iff.

    Lemma FEnv_dom_restr_hist:
      forall H xs i,
        FEnv.dom H xs <-> FEnv.dom (restr_hist H i) xs.
      split; intros HH x; specialize (HH x);
        now rewrite <-HH, FEnv_In_restr_hist.

  End HistoryRestriction.

Instantaneous semantics

  Section InstantSemantics.

    Definition history := stream env.

    Variable base: bool.
    Variable R: env.

    Definition sem_var_instant (x: ident) (v: svalue) : Prop :=
      R x = Some v.

    Inductive sem_clock_instant: clock -> bool -> Prop :=
    | Sbase:
        sem_clock_instant Cbase base
    | Son:
        forall ck x t b,
          sem_clock_instant ck true ->
          sem_var_instant x (present (Venum b)) ->
          sem_clock_instant (Con ck x (t, b)) true
    | Son_abs1:
        forall ck x c,
          sem_clock_instant ck false ->
          sem_var_instant x absent ->
          sem_clock_instant (Con ck x c) false
    | Son_abs2:
        forall ck x t b b',
          sem_clock_instant ck true ->
          sem_var_instant x (present (Venum b')) ->
          b <> b' ->
          sem_clock_instant (Con ck x (t, b)) false.

  End InstantSemantics.

  Global Hint Constructors sem_clock_instant : indexedstreams.

  Add Parametric Morphism : sem_var_instant
      with signature FEnv.Equiv eq ==> @eq _ ==> @eq _ ==> Basics.impl
        as sem_var_instant_morph.
    intros H H' EH x v Hvar.
    specialize (EH x). rewrite Hvar in EH. inv EH.
    symmetry in H1. eapply H1.

  Add Parametric Morphism : sem_clock_instant
      with signature eq ==> FEnv.Equiv eq ==> eq ==> eq ==> Basics.impl
        as sem_clock_instant_morph.
    intros b H H' EH ck. revert b.
    induction ck; intros b b' Sem; inv Sem.
    - constructor; auto.
    - apply Son; auto. apply IHck; auto.
      rewrite <-EH; auto.
    - apply Son_abs1; auto. apply IHck; auto.
      rewrite <-EH; auto.
    - eapply Son_abs2; eauto. apply IHck; auto.
      rewrite <-EH; auto.

  Fact sem_clock_instant_true_inv : forall b H ck,
      sem_clock_instant b H ck true ->
      b = true.
    intros * Hsem.
    induction ck; inv Hsem; auto.

  Section LiftSemantics.

    Variable bk : stream bool.
    Variable H : history.

    Definition lift {A B} (sem: bool -> env -> A -> B -> Prop)
               x (ys: stream B): Prop :=
      forall n, sem (bk n) (H n) x (ys n).

    Definition lift' {A B} (sem: env -> A -> B -> Prop) x (ys: stream B): Prop :=
      forall n, sem (H n) x (ys n).

    Definition sem_var (x: ident) (xs: stream svalue): Prop :=
      lift' sem_var_instant x xs.

    Definition sem_clock (ck: clock) (xs: stream bool): Prop :=
      lift sem_clock_instant ck xs.

  End LiftSemantics.

  Global Hint Unfold lift lift' : indexedstreams.

Determinism of the semantics

Instantaneous semantics

  Section InstantDeterminism.

    Variable base: bool.
    Variable R: env.

    Lemma sem_var_instant_det:
      forall x v1 v2,
        sem_var_instant R x v1
        -> sem_var_instant R x v2
        -> v1 = v2.

    Lemma sem_clock_instant_det:
      forall ck v1 v2,
        sem_clock_instant base R ck v1
        -> sem_clock_instant base R ck v2
        -> v1 = v2.
      induction ck; repeat inversion 1; subst; intuition;
        try repeat progress match goal with
                            | H1: sem_clock_instant ?bk ?R ?ck ?l,
                                  H2: sem_clock_instant ?bk ?R ?ck ?r |- _ =>
                              apply IHck with (1:=H1) in H2; discriminate
                            | H1: sem_var_instant ?R ?i (present ?l),
                                  H2: sem_var_instant ?R ?i (present ?r) |- _ =>
                              apply sem_var_instant_det with (1:=H1) in H2;
                                injection H2; intro; subst
      1,2:exfalso; auto.

  End InstantDeterminism.

  Section LiftDeterminism.

    Variable bk : stream bool.
    Variable H : history.

    Lemma lift_det:
      forall {A B} (P: bool -> env -> A -> B -> Prop) (bk: stream bool)
        x (xs1 xs2 : stream B),
        (forall b R v1 v2, P b R x v1 -> P b R x v2 -> v1 = v2) ->
        lift bk H P x xs1 -> lift bk H P x xs2 -> xs1 = xs2.
      intros * Hpoint H1 H2.
      extensionality n. specialize (H1 n). specialize (H2 n).
      eapply Hpoint; eassumption.

    Lemma lift'_det:
      forall {A B} (P: env -> A -> B -> Prop)
        x (xs1 xs2 : stream B),
        (forall R v1 v2, P R x v1 -> P R x v2 -> v1 = v2) ->
        lift' H P x xs1 -> lift' H P x xs2 -> xs1 = xs2.
      intros * Hpoint H1 H2.
      extensionality n. specialize (H1 n). specialize (H2 n).
      eapply Hpoint; eassumption.

    Ltac apply_lift sem_det :=
      intros; eapply lift_det; try eassumption;
      compute; intros; eapply sem_det; eauto.

    Ltac apply_lift' sem_det :=
      intros; eapply lift'_det; try eassumption;
      compute; intros; eapply sem_det; eauto.

    Lemma sem_var_det:
      forall x xs1 xs2,
        sem_var H x xs1 -> sem_var H x xs2 -> xs1 = xs2.
      apply_lift' sem_var_instant_det.

    Lemma sem_clock_det:
      forall ck bs1 bs2,
        sem_clock bk H ck bs1 -> sem_clock bk H ck bs2 -> bs1 = bs2.
      apply_lift sem_clock_instant_det.

  End LiftDeterminism.

  Ltac sem_det :=
    match goal with
    | H1: sem_clock_instant ?bk ?H ?C ?X,
          H2: sem_clock_instant ?bk ?H ?C ?Y |- ?X = ?Y =>
      eapply sem_clock_instant_det; eexact H1 || eexact H2
    | H1: sem_clock ?bk ?H ?C ?X,
          H2: sem_clock ?bk ?H ?C ?Y |- ?X = ?Y =>
      eapply sem_clock_det; eexact H1 || eexact H2
    | H1: sem_var_instant ?H ?C ?X,
          H2: sem_var_instant ?H ?C ?Y |- ?X = ?Y =>
      eapply sem_var_instant_det; eexact H1 || eexact H2
    | H1: sem_var ?H ?C ?X,
          H2: sem_var ?H ?C ?Y |- ?X = ?Y =>
      eapply sem_var_det; eexact H1 || eexact H2

  Ltac by_sem_det :=
    repeat match goal with
           | H: exists _, _ |- _ => destruct H
    match goal with
    | H1: sem_clock_instant ?bk ?H ?C ?X,
          H2: sem_clock_instant ?bk ?H ?C ?Y |- _ =>
      assert (X = Y) by (eapply sem_clock_instant_det; eexact H1 || eexact H2)
    | H1: sem_var_instant ?H ?C ?X,
          H2: sem_var_instant ?H ?C ?Y |- _ =>
      assert (X = Y) by (eapply sem_var_instant_det; eexact H1 || eexact H2)
    end; discriminate.


  Section InstantInterpreter.

    Variable base : bool.
    Variable R: env.

    Definition interp_var_instant (x: ident): svalue :=
      match R x with
      | Some v => v
      | None => absent

    Lemma interp_var_instant_complete:
      forall x v,
        sem_var_instant R x v ->
        v = interp_var_instant x.
      unfold interp_var_instant; now intros * ->.

    Fixpoint interp_clock_instant (c: clock): bool :=
      match c with
      | Cbase =>
      | Con c x (t, b) =>
        let cb := interp_clock_instant c in
        andb cb (interp_var_instant x ==b present (Venum b))

    Ltac rw_exp_helper :=
      repeat match goal with
             | _: sem_var_instant R ?x ?v |- context[interp_var_instant ?x] =>
               erewrite <-(interp_var_instant_complete x v); eauto; simpl
             | H: sem_unop ?op ?c ?t = _ |- context[sem_unop ?op ?c ?t] =>
               rewrite H
             | H: sem_binop ?op ?c1 ?t1 ?c2 ?t2 = _ |- context[sem_binop ?op ?c1 ?t1 ?c2 ?t2] =>
               rewrite H

    Lemma interp_clock_instant_complete:
      forall c b,
        sem_clock_instant base R c b ->
        b = interp_clock_instant c.
      induction 1; auto; simpl; rw_exp_helper;
        rewrite <-IHsem_clock_instant; simpl; auto.
      - symmetry. apply equiv_decb_refl.
      - destruct c; auto.
      - symmetry. rewrite <-Bool.not_true_iff_false, svalue_eqb_eq.
        intro contra; inv contra; auto.

  End InstantInterpreter.

Liftings of instantaneous semantics

  Section LiftInterpreter.

    Variable bk : stream bool.
    Variable H: history.

    Definition lift_interp {A B} (interp: bool -> env -> A -> B) x: stream B :=
      fun n => interp (bk n) (H n) x.

    Definition lift_interp' {A B} (interp: env -> A -> B) x: stream B :=
      fun n => interp (H n) x.

    Definition interp_clock (ck: clock): stream bool :=
      lift_interp interp_clock_instant ck.

    Definition interp_var (x: ident): stream svalue :=
      lift_interp' interp_var_instant x.

    Lemma lift_complete:
      forall {A B} (sem: bool -> env -> A -> B -> Prop) interp x xs,
        (forall b R x v, sem b R x v -> v = interp b R x) ->
        lift bk H sem x xs ->
        xslift_interp interp x.
      intros * Instant Sem n.
      specialize (Sem n); unfold lift_interp; auto.

    Lemma lift'_complete:
      forall {A B} (sem: env -> A -> B -> Prop) interp x xs,
        (forall R x v, sem R x v -> v = interp R x) ->
        lift' H sem x xs ->
        xslift_interp' interp x.
      intros * Instant Sem n.
      specialize (Sem n); unfold lift_interp'; auto.

    Corollary interp_clock_complete:
      forall ck bs,
        sem_clock bk H ck bs ->
        bsinterp_clock ck.
      intros; eapply lift_complete; eauto;
        apply interp_clock_instant_complete.

    Corollary interp_var_complete:
      forall x vs,
        sem_var H x vs ->
        vsinterp_var x.
      intros; eapply lift'_complete; eauto;
        apply interp_var_instant_complete.

  End LiftInterpreter.


Module IndexedStreamsFun
       (Ids : IDS)
       (Op : OPERATORS)
       (OpAux : OPERATORS_AUX Ids Op)
       (Clocks : CLOCKS Ids Op OpAux)
<: INDEXEDSTREAMS Ids Op OpAux Clocks.
  Include INDEXEDSTREAMS Ids Op OpAux Clocks.
End IndexedStreamsFun.