Module AlarmMon


Require Import
  Utf8 String Coqlib Util AdomLib.

Set Implicit Arguments.

Definition alarm_mon (L T:Type) : Type := (T * (list (unit -> string) * list L))%type.
Definition am_lift {L T T'} (f: TT') (m: alarm_mon L T) : alarm_mon L T' :=
  (f(fst m), snd m).

Instance block_mon_gamma L T T' `(gamma_op T T') : gamma_op (alarm_mon L T) (option T') :=
  λ t, match fst (snd t) with
       | nil => fun x => match x with Some x => x ∈ γ(fst t) | None => False end
       | _ => λ _, True
       end.

Definition am_get {L T} (x: alarm_mon L T) : option T :=
  match x with (y, (nil, _)) => Some y | _ => None end.

Instance alarm_monad L : monad (alarm_mon L) :=
  { bind A B x f :=
  let '(x, (err, log)) := x in
  let '(y, (err', log')) := f x in
  (y, (err ++ err', log ++ log'));
    ret A x := (x, (nil, nil))
  }.

Notation alarm_am s := (tt, ((fun _ => s)::nil, nil)).

Lemma bind_am_assoc {L A B C} (m: alarm_mon L A) (f: Aalarm_mon L B) (g: Balarm_mon L C) :
  bind (bind m f) g = bind ma, bind (f a) g).
Proof.
  destruct m as (a, (q,?)). simpl.
  destruct (f a) as (b, (q', ?)). simpl. destruct (g b) as (c, (q'',?)).
  rewrite <- ! app_assoc; reflexivity.
Qed.

Lemma am_bind_case {L T T'} (f: alarm_mon L T) (b: Talarm_mon L T') :
  ∀ P : alarm_mon L T' → Prop,
    (∀ x y l l', P (x, (y, l)) → P (x, (y, l'))) →
    (∀ x y z l, P (x, (y :: z, l))) →
    (∀ x l, f = (x, (nil, l)) → P (b x)) →
    P (do a <- f; b a).
Proof.
  intros P E A B.
  destruct f as (x & ([|],?)). generalize (B _ _ eq_refl); simpl; destruct (b x) as (?, (?,?)); apply E.
  simpl. destruct (b x) as (?, (?,y)). exact (E _ _ _ _ (A _ _ _ y)).
Defined.

Lemma am_get_bind {L A B} (m: alarm_mon L A) (f: Aalarm_mon L B) bs :
  am_get (do a <- m; f a) = Some bs
  ∃ x, am_get m = Some xam_get (f x) = Some bs.
Proof.
  destruct m as (a, (err,m)); simpl; intros H.
  exists a.
  destruct (f a) as (b, (err',m')); simpl.
  destruct err. destruct err'; simpl in *; eq_some_inv. intuition congruence.
  simpl in H; eq_some_inv.
Qed.

Notation am_assert b err :=
  (if b then ret tt else alarm_am err).

Lemma am_assert_spec L b err B (k: alarm_mon L B) :
  ∀ P : _Prop,
    (∀ x y l l', P (x, (y, l)) → P (x, (y, l'))) →
    (∀ x y z l, P (x, (y :: z, l))) →
    (b = trueP k) →
    P (do _ <- am_assert b err; k).
Proof.
  intros P E U V.
  apply am_bind_case; eauto.
  intros (). destruct b. auto. inversion 1.
Qed.

Lemma am_assert_inv {L} (b: bool) err :
  am_assert b err = (tt, (nil, @nil L)) →
  b = true.
Proof.
  now destruct b; intros; eq_some_inv.
Qed.

Lemma am_get_ret L A (a: A) : @am_get L A (ret a) = Some a.
Proof.
reflexivity. Qed.

Definition am_fold L A B (f: ABalarm_mon L A) (l: list B) (a: A) : alarm_mon L A :=
  fold_leftacc b, do acc' <- acc; f acc' b) l (ret a).

Lemma am_fold_app L A B f l l' a :
  @am_fold L A B f (l ++ l') a = do a' <- am_fold f l a; am_fold f l' a'.
Proof.
  revert l' l a. induction l' as [|x l' IH] using rev_ind.
  intros. rewrite app_nil_r. destruct (am_fold f l a) as (?,(?,?)). simpl. rewrite !app_nil_r. destruct l0; reflexivity.
  intros l a. unfold am_fold at 1. rewrite app_assoc. rewrite fold_left_app.
  fold (am_fold f (l ++ l') a). rewrite IH. clear IH. simpl. rewrite bind_am_assoc.
  destruct (am_fold f l a) as (a', (q, m)). simpl.
  unfold am_fold at 2. rewrite fold_left_app. reflexivity.
Qed.

Definition am_rev_map L A B (f: Balarm_mon L A) (l: list B) : alarm_mon L (list A) :=
  am_foldacc b, do a <- f b; ret (a :: acc)) l nil.

Inductive am_map_t (A: Type) : Type :=
| Result (a: A)
| Errorc (a: option A) (msg: unit -> string).
Notation Error a msg := (Errorc a (fun _ => msg)).
Lemma Result_Error {A} (a: A) a' m : Result a = Errorc a' m → ∀ P : Prop, P.
ProofE, match E in _ = t return match t return Prop with Result _ => True | _ => _ end with eq_refl => I end).
Lemma Result_eq_inv {A} (a a': A) : Result a = Result a' → a = a'.
ProofE, match E in _ = t return match t with Result x => a = x | _ => False end with eq_refl => eq_refl end).
Ltac result_eq_inv :=
  repeat match goal with
  | H : Result _ = Errorc _ _ |- _ => exact (Result_Error H _)
  | H : Errorc _ _ = Result _ |- _ => exact (Result_Error (eq_sym H) _)
  | H : Result _ = Result _ |- _ => apply Result_eq_inv in H
  end.

Definition am_map' L A B (f: Bam_map_t A) (l: list B) : alarm_mon L (list A) :=
  am_foldacc b, match f b with
                      | Result a => ret (a :: acc)
                      | Errorc a msg =>
                        do _ <- (tt, (msg::nil, nil));
                        ret match a with
                                 | None => acc
                                 | Some a => a :: acc
                               end
                    end) l nil.

Lemma am_rev_map_correct L A B (f: Balarm_mon L A) :
  ∀ l,
    match am_get (am_rev_map f l) with
    | Some m =>
      (∀ y, In y m → ∃ x, am_get (f x) = Some yIn x l)
    ∧ (∀ x, In x l → ∃ y, am_get (f x) = Some yIn y m)
    | None => ∃ x, In x lam_get (f x) = None
    end.
Proof.
  induction l using rev_ind.
+ split; intros ? ().
+ unfold am_rev_map, am_fold. rewrite fold_left_app. simpl.
  change (fold_leftacc b, do acc' <- acc; do a <- f b; (a :: acc', (nil, nil))) l (nil, (nil, nil)))
  with (am_rev_map f l).
  destruct (am_rev_map f l) as (acc', ([|], ?)).
  destruct IHl as (IH & HI).
  destruct (f x) as (a, ([|],?)) eqn: FX.
  simpl. split.
  - intros y [->|IN].
    exists x; split. rewrite FX. reflexivity. rewrite in_app. right. left. reflexivity.
    destruct (IH _ IN) as (? & ? & ?).
    eexists; split; eauto. rewrite in_app. simpl. eauto.
  - intros x' IN. rewrite in_app in IN. destruct IN as [IN|[->|()]].
    destruct (HI x' IN) as (? & ? & H). eauto.
    rewrite FX. eexists. split. reflexivity. auto.
  - exists x. split. rewrite in_app. right. left. reflexivity.
    rewrite FX. reflexivity.
  - destruct IHl as (x' & ? & ?).
    simpl. destruct (do a <- f x; (a::acc', (nil, nil))) as (?, (?, ?)).
    exists x'. intuition.
Qed.

Lemma am_rev_map_inv L A B (f: Balarm_mon L A) :
  ∀ l,
    match am_get (am_rev_map f l) with
    | Some m =>
      m = rev (map (fstf) l)
    | None => True
    end.
Proof.
  induction l using rev_ind.
+ split; intros ? ().
+ unfold am_rev_map, am_fold. rewrite fold_left_app. simpl.
  change (fold_leftacc b, do acc' <- acc; do a <- f b; (a :: acc', (nil, nil))) l (nil, (nil, nil)))
  with (am_rev_map f l).
  destruct (am_rev_map f l) as (acc', ([|], ?)).
  destruct (f x) as (a, ([|], ?)) eqn: FX.
  simpl. rewrite map_app, rev_app_distr, IHl. simpl.
  rewrite FX. simpl.
  simpl. reflexivity.
  exact I.
  simpl. destruct (do a <- f x; (a::acc', (nil, nil))) as (?, (?,?)). exact I.
Qed.

Lemma am_map'_correct L A B (f: Bam_map_t A) :
  ∀ l,
    match am_get (am_map' L f l) with
    | Some m =>
      (∀ y, In y m → ∃ x, f x = Result yIn x l)
    ∧ (∀ x, In x l → ∃ y, f x = Result yIn y m)
    | None => True
    end.
Proof.
  induction l using rev_ind.
+ split; intros ? ().
+ unfold am_map', am_fold. rewrite fold_left_app. simpl.
  apply am_bind_case. easy. intros; exact I.
  intros al log.
  intros AL.
  destruct (f x) eqn: FX. 2: exact I.
  unfold am_map', am_fold in IHl. simpl in IHl.
  rewrite AL in IHl.
  destruct IHl as (IH & HI).
  simpl; split.
  - intros y [->|IN].
    exists x; split. rewrite FX. reflexivity. rewrite in_app. right. left. reflexivity.
    destruct (IH _ IN) as (? & ? & ?).
    eexists; split; eauto. rewrite in_app. simpl. eauto.
  - intros x' IN. rewrite in_app in IN. destruct IN as [IN|[->|()]].
    destruct (HI x' IN) as (? & ? & H). eauto.
    rewrite FX. eexists. split. reflexivity. auto.
Qed.

Ltac with_am_map' :=
  match goal with
  |- appcontext[ am_map' ?L ?f ?l ] =>
  let map_f := fresh "map_f" in
  remember f as map_f;
  generalize (am_map'_correct L map_f l);
    destruct (am_map' L map_f l) as (? & ([|], ?));
    [subst map_f| easy ]
  end.