Theory Stream_Processor

(*  Title:      HOL/Datatype_Examples/Stream_Processor.thy
    Author:     Dmitriy Traytel, TU Muenchen
    Author:     Andrei Popescu, TU Muenchen
    Copyright   2014

Stream processors---a syntactic representation of continuous functions on streams.
*)

section ‹Stream Processors---A Syntactic Representation of Continuous Functions on Streams›

theory Stream_Processor
imports "HOL-Library.Stream" "HOL-Library.BNF_Axiomatization"
begin

declare [[typedef_overloaded]]


section ‹Continuous Functions on Streams›

datatype ('a, 'b, 'c) spμ =
  Get "'a  ('a, 'b, 'c) spμ"
| Put "'b" "'c"

codatatype ('a, 'b) spν =
  In (out: "('a, 'b, ('a, 'b) spν) spμ")

primrec runμ :: "('a, 'b, 'c) spμ  'a stream  ('b × 'c) × 'a stream" where
  "runμ (Get f) s = runμ (f (shd s)) (stl s)"
| "runμ (Put b sp) s = ((b, sp), s)"

primcorec runν :: "('a, 'b) spν  'a stream  'b stream" where
  "runν sp s = (let ((h, sp), s) = runμ (out sp) s in h ## runν sp s)"

primcorec copy :: "('a, 'a) spν" where
  "copy = In (Get (λa. Put a copy))"

lemma runν_copy: "runν copy s = s"
  by (coinduction arbitrary: s) simp

text ‹
To use the function package for the definition of composition the
wellfoundedness of the subtree relation needs to be proved first.
›

definition "sub  {(f a, Get f) | a f. True}"

lemma subI[intro]: "(f a, Get f)  sub"
  unfolding sub_def by blast

lemma wf_sub[simp, intro]: "wf sub"
proof (rule wfUNIVI)
  fix P  :: "('a, 'b, 'c) spμ  bool" and x
  assume "x. (y. (y, x)  sub  P y)  P x"
  hence I: "x. (y. (a f. y = f a  x = Get f)  P y)  P x" unfolding sub_def by blast
  show "P x" by (induct x) (auto intro: I)
qed

lemma neq_Get [simp]: "f b  Get f"
  by (metis subI wf_asym wf_sub)


function
  spμ_comp :: "('a, 'b, 'c) spμ  ('d, 'a, ('d, 'a) spν) spμ  ('d, 'b, 'c × ('d, 'a) spν) spμ"
  (infixl "oμ" 65)
where
  "Put b sp oμ fsp = Put b (sp, In fsp)"
| "Get f oμ Put b sp = f b oμ out sp"
| "Get f oμ Get g = Get (λa. Get f oμ g a)"
by pat_completeness auto
termination
  by (relation "lex_prod sub sub") auto

primcorec spν_comp (infixl "oν" 65) where
  "out (sp oν sp') = map_spμ id (λ(sp, sp'). sp oν sp') (out sp oμ out sp')"

lemma runν_spν_comp: "runν (sp oν sp') = runν sp o runν sp'"
proof (rule ext, unfold comp_apply)
  fix s
  show "runν (sp oν sp') s = runν sp (runν sp' s)"
  proof (coinduction arbitrary: sp sp' s)
    case Eq_stream
    show ?case
    proof (induct "out sp" "out sp'" arbitrary: sp sp' s rule: spμ_comp.induct)
      case (1 b sp'')
      show ?case by (auto simp add: 1[symmetric])
    next
      case (2 f b sp'')
      from 2(1)[of "In (f b)" sp''] show ?case by (simp add: 2(2,3)[symmetric])
    next
      case (3 f h)
      from 3(1)[of _ "shd s" "In (h (shd s))", OF 3(2)] show ?case by (simp add: 3(2,3)[symmetric])
    qed
  qed
qed

text ‹Alternative definition of composition using primrec instead of function›

primrec spμ_comp2R  where
  "spμ_comp2R f (Put b sp) = f b (out sp)"
| "spμ_comp2R f (Get h) = Get (spμ_comp2R f o h)"

primrec spμ_comp2 (infixl "o*μ" 65) where
  "Put b sp o*μ fsp = Put b (sp, In fsp)"
| "Get f o*μ fsp = spμ_comp2R ((o*μ) o f) fsp"

primcorec spν_comp2 (infixl "o*ν" 65) where
  "out (sp o*ν sp') = map_spμ id (λ(sp, sp'). sp o*ν sp') (out sp o*μ out sp')"

lemma runν_spν_comp2: "runν (sp o*ν sp') = runν sp o runν sp'"
proof (rule ext, unfold comp_apply)
  fix s
  show "runν (sp o*ν sp') s = runν sp (runν sp' s)"
  proof (coinduction arbitrary: sp sp' s)
    case Eq_stream
    show ?case
    proof (induct "out sp" arbitrary: sp sp' s)
      case (Put b sp'')
      show ?case by (auto simp add: Put[symmetric])
    next
      case (Get f)
      then show ?case
      proof (induct "out sp'" arbitrary: sp sp' s)
        case (Put b sp'')
        from Put(2)[of "In (f b)" sp''] show ?case by (simp add: Put(1,3)[symmetric])
      next
        case (Get h)
        from Get(1)[OF _ Get(3,4), of "In (h (shd s))"] show ?case by (simp add: Get(2,4)[symmetric])
      qed
    qed
  qed
qed

text ‹The two definitions are equivalent›

lemma spμ_comp_spμ_comp2[simp]: "sp oμ sp' = sp o*μ sp'"
  by (induct sp sp' rule: spμ_comp.induct) auto

(*will be provided by the package*)
lemma spμ_rel_map_map[unfolded vimage2p_def, simp]:
  "rel_spμ R1 R2 (map_spμ f1 f2 sp) (map_spμ g1 g2 sp') =
  rel_spμ (BNF_Def.vimage2p f1 g1 R1) (BNF_Def.vimage2p f2 g2 R2) sp sp'"
by (tactic let
    val ks = 1 upto 2;
    val ctxt = context;
  in
    BNF_Tactics.unfold_thms_tac ctxt
      @{thms spμ.rel_compp spμ.rel_conversep spμ.rel_Grp vimage2p_Grp} THEN
    HEADGOAL (EVERY' [resolve_tac ctxt [iffI],
      resolve_tac ctxt @{thms relcomppI},
      resolve_tac ctxt @{thms GrpI},
      resolve_tac ctxt [refl],
      resolve_tac ctxt [CollectI],
      BNF_Util.CONJ_WRAP' (K (resolve_tac ctxt @{thms subset_UNIV})) ks,
      resolve_tac ctxt @{thms relcomppI}, assume_tac ctxt,
      resolve_tac ctxt @{thms conversepI},
      resolve_tac ctxt @{thms GrpI},
      resolve_tac ctxt [refl],
      resolve_tac ctxt [CollectI],
      BNF_Util.CONJ_WRAP' (K (resolve_tac ctxt @{thms subset_UNIV})) ks,
      REPEAT_DETERM o eresolve_tac ctxt @{thms relcomppE conversepE GrpE},
      hyp_subst_tac ctxt,
      assume_tac ctxt])
  end)

lemma spμ_rel_self: "(=)  R1; (=)  R2  rel_spμ R1 R2 x x"
  by (erule (1) predicate2D[OF spμ.rel_mono]) (simp only: spμ.rel_eq)

lemma spν_comp_spν_comp2: "sp oν sp' = sp o*ν sp'"
  by (coinduction arbitrary: sp sp') (auto intro!: spμ_rel_self)


section ‹Generalization to an Arbitrary BNF as Codomain›

bnf_axiomatization ('a, 'b) F for map: F

notation BNF_Def.convol ("(_,/ _)")

definition θ :: "('p,'a) F * 'b  ('p,'a * 'b) F" where
  "θ xb = F id id, λ a. (snd xb) (fst xb)"

(* The strength laws for θ: *)
lemma θ_natural: "F id (map_prod f g) o θ = θ o map_prod (F id f) g"
  unfolding θ_def F.map_comp comp_def id_apply convol_def map_prod_def split_beta fst_conv snd_conv ..

definition assl :: "'a * ('b * 'c)  ('a * 'b) * 'c" where
  "assl abc = ((fst abc, fst (snd abc)), snd (snd abc))"

lemma θ_rid: "F id fst o θ = fst"
  unfolding θ_def F.map_comp F.map_id comp_def id_apply convol_def fst_conv sym[OF id_def] ..

lemma θ_assl: "F id assl o θ = θ o map_prod θ id o assl"
  unfolding assl_def θ_def F.map_comp comp_def id_apply convol_def map_prod_def split fst_conv snd_conv ..

datatype ('a, 'b, 'c) spFμ = GetF "'a  ('a, 'b, 'c) spFμ" | PutF "('b,'c) F"
codatatype ('a, 'b) spFν = InF (outF: "('a, 'b, ('a, 'b) spFν) spFμ")

codatatype 'b JF = Ctor (dtor: "('b, 'b JF) F")

(* Definition of run for an arbitrary final coalgebra as codomain: *)

primrec
  runFμ :: "('a, 'b, ('a, 'b) spFν) spFμ  'a stream  (('b, ('a, 'b) spFν) F) × 'a stream" 
where
  "runFμ (GetF f) s = (runFμ o f) (shd s) (stl s)"
| "runFμ (PutF x) s = (x, s)"

primcorec runFν :: "('a, 'b) spFν  'a stream  'b JF" where
  "runFν sp s = (let (x, s) = runFμ (outF sp) s in Ctor (F id (λ sp. runFν sp s) x))"

end