Theory Fixrec

(*  Title:      HOL/HOLCF/Fixrec.thy
    Author:     Franz Regensburger
    Author:     Amber Telfer and Brian Huffman
*)

theory Fixrec
imports Cprod Sprod Ssum Up One Tr Cfun
keywords "fixrec" :: thy_defn
begin

section ‹Fixed point operator and admissibility›

subsection ‹Iteration›

primrec iterate :: "nat  ('a  'a)  ('a  'a)"
  where
    "iterate 0 = (Λ F x. x)"
  | "iterate (Suc n) = (Λ F x. F(iterate nFx))"

text ‹Derive inductive properties of iterate from primitive recursion›

lemma iterate_0 [simp]: "iterate 0Fx = x"
  by simp

lemma iterate_Suc [simp]: "iterate (Suc n)Fx = F(iterate nFx)"
  by simp

declare iterate.simps [simp del]

lemma iterate_Suc2: "iterate (Suc n)Fx = iterate nF(Fx)"
  by (induct n) simp_all

lemma iterate_iterate: "iterate mF(iterate nFx) = iterate (m + n)Fx"
  by (induct m) simp_all

text ‹The sequence of function iterations is a chain.›

lemma chain_iterate [simp]: "chain (λi. iterate iF)"
  by (rule chainI, unfold iterate_Suc2, rule monofun_cfun_arg, rule minimal)


subsection ‹Least fixed point operator›

definition "fix" :: "('a::pcpo  'a)  'a"
  where "fix = (Λ F. i. iterate iF)"

text ‹Binder syntax for termfix

abbreviation fix_syn :: "('a::pcpo  'a)  'a"  (binder μ 10)
  where "fix_syn (λx. f x)  fix(Λ x. f x)"

notation (ASCII)
  fix_syn  (binder FIX 10)

text ‹Properties of termfix

text ‹direct connection between termfix and iteration›

lemma fix_def2: "fixF = (i. iterate iF)"
  by (simp add: fix_def)

lemma iterate_below_fix: "iterate nf  fixf"
  unfolding fix_def2
  using chain_iterate by (rule is_ub_thelub)

text ‹
  Kleene's fixed point theorems for continuous functions in pointed
  omega cpo's
›

lemma fix_eq: "fixF = F(fixF)"
  apply (simp add: fix_def2)
  apply (subst lub_range_shift [of _ 1, symmetric])
   apply (rule chain_iterate)
  apply (subst contlub_cfun_arg)
   apply (rule chain_iterate)
  apply simp
  done

lemma fix_least_below: "Fx  x  fixF  x"
  apply (simp add: fix_def2)
  apply (rule lub_below)
   apply (rule chain_iterate)
  apply (induct_tac i)
   apply simp
  apply simp
  apply (erule rev_below_trans)
  apply (erule monofun_cfun_arg)
  done

lemma fix_least: "Fx = x  fixF  x"
  by (rule fix_least_below) simp

lemma fix_eqI:
  assumes fixed: "Fx = x"
    and least: "z. Fz = z  x  z"
  shows "fixF = x"
  apply (rule below_antisym)
   apply (rule fix_least [OF fixed])
  apply (rule least [OF fix_eq [symmetric]])
  done

lemma fix_eq2: "f  fixF  f = Ff"
  by (simp add: fix_eq [symmetric])

lemma fix_eq3: "f  fixF  fx = Ffx"
  by (erule fix_eq2 [THEN cfun_fun_cong])

lemma fix_eq4: "f = fixF  f = Ff"
  by (erule ssubst) (rule fix_eq)

lemma fix_eq5: "f = fixF  fx = Ffx"
  by (erule fix_eq4 [THEN cfun_fun_cong])

text ‹strictness of termfix

lemma fix_bottom_iff: "fixF =   F = "
  apply (rule iffI)
   apply (erule subst)
   apply (rule fix_eq [symmetric])
  apply (erule fix_least [THEN bottomI])
  done

lemma fix_strict: "F =   fixF = "
  by (simp add: fix_bottom_iff)

lemma fix_defined: "F    fixF  "
  by (simp add: fix_bottom_iff)

text termfix applied to identity and constant functions›

lemma fix_id: "(μ x. x) = "
  by (simp add: fix_strict)

lemma fix_const: "(μ x. c) = c"
  by (subst fix_eq) simp


subsection ‹Fixed point induction›

lemma fix_ind: "adm P  P   (x. P x  P (Fx))  P (fixF)"
  unfolding fix_def2
  apply (erule admD)
   apply (rule chain_iterate)
  apply (rule nat_induct, simp_all)
  done

lemma cont_fix_ind: "cont F  adm P  P   (x. P x  P (F x))  P (fix(Abs_cfun F))"
  by (simp add: fix_ind)

lemma def_fix_ind: "f  fixF; adm P; P ; x. P x  P (Fx)  P f"
  by (simp add: fix_ind)

lemma fix_ind2:
  assumes adm: "adm P"
  assumes 0: "P " and 1: "P (F)"
  assumes step: "x. P x; P (Fx)  P (F(Fx))"
  shows "P (fixF)"
  unfolding fix_def2
  apply (rule admD [OF adm chain_iterate])
  apply (rule nat_less_induct)
  apply (case_tac n)
   apply (simp add: 0)
  apply (case_tac nat)
   apply (simp add: 1)
  apply (frule_tac x=nat in spec)
  apply (simp add: step)
  done

lemma parallel_fix_ind:
  assumes adm: "adm (λx. P (fst x) (snd x))"
  assumes base: "P  "
  assumes step: "x y. P x y  P (Fx) (Gy)"
  shows "P (fixF) (fixG)"
proof -
  from adm have adm': "adm (case_prod P)"
    unfolding split_def .
  have "P (iterate iF) (iterate iG)" for i
    by (induct i) (simp add: base, simp add: step)
  then have "i. case_prod P (iterate iF, iterate iG)"
    by simp
  then have "case_prod P (i. (iterate iF, iterate iG))"
    by - (rule admD [OF adm'], simp, assumption)
  then have "case_prod P (i. iterate iF, i. iterate iG)"
    by (simp add: lub_Pair)
  then have "P (i. iterate iF) (i. iterate iG)"
    by simp
  then show "P (fixF) (fixG)"
    by (simp add: fix_def2)
qed

lemma cont_parallel_fix_ind:
  assumes "cont F" and "cont G"
  assumes "adm (λx. P (fst x) (snd x))"
  assumes "P  "
  assumes "x y. P x y  P (F x) (G y)"
  shows "P (fix(Abs_cfun F)) (fix(Abs_cfun G))"
  by (rule parallel_fix_ind) (simp_all add: assms)


subsection ‹Fixed-points on product types›

text ‹
  Bekic's Theorem: Simultaneous fixed points over pairs
  can be written in terms of separate fixed points.
›

lemma fix_cprod:
  fixes F :: "'a::pcpo × 'b::pcpo  'a × 'b"
  shows
    "fixF =
     (μ x. fst (F(x, μ y. snd (F(x, y)))),
      μ y. snd (F(μ x. fst (F(x, μ y. snd (F(x, y)))), y)))"
  (is "fixF = (?x, ?y)")
proof (rule fix_eqI)
  have *: "fst (F(?x, ?y)) = ?x"
    by (rule trans [symmetric, OF fix_eq], simp)
  have "snd (F(?x, ?y)) = ?y"
    by (rule trans [symmetric, OF fix_eq], simp)
  with * show "F(?x, ?y) = (?x, ?y)"
    by (simp add: prod_eq_iff)
next
  fix z
  assume F_z: "Fz = z"
  obtain x y where z: "z = (x, y)" by (rule prod.exhaust)
  from F_z z have F_x: "fst (F(x, y)) = x" by simp
  from F_z z have F_y: "snd (F(x, y)) = y" by simp
  let ?y1 = "μ y. snd (F(x, y))"
  have "?y1  y"
    by (rule fix_least) (simp add: F_y)
  then have "fst (F(x, ?y1))  fst (F(x, y))"
    by (simp add: fst_monofun monofun_cfun)
  with F_x have "fst (F(x, ?y1))  x"
    by simp
  then have *: "?x  x"
    by (simp add: fix_least_below)
  then have "snd (F(?x, y))  snd (F(x, y))"
    by (simp add: snd_monofun monofun_cfun)
  with F_y have "snd (F(?x, y))  y"
    by simp
  then have "?y  y"
    by (simp add: fix_least_below)
  with z * show "(?x, ?y)  z"
    by simp
qed


section "Package for defining recursive functions in HOLCF"

subsection ‹Pattern-match monad›

pcpodef 'a match = "UNIV::(one ++ 'a u) set"
by simp_all

definition
  fail :: "'a match" where
  "fail = Abs_match (sinlONE)"

definition
  succeed :: "'a  'a match" where
  "succeed = (Λ x. Abs_match (sinr(upx)))"

lemma matchE [case_names bottom fail succeed, cases type: match]:
  "p =   Q; p = fail  Q; x. p = succeedx  Q  Q"
unfolding fail_def succeed_def
apply (cases p, rename_tac r)
apply (rule_tac p=r in ssumE, simp add: Abs_match_strict)
apply (rule_tac p=x in oneE, simp, simp)
apply (rule_tac p=y in upE, simp, simp add: cont_Abs_match)
done

lemma succeed_defined [simp]: "succeedx  "
by (simp add: succeed_def cont_Abs_match Abs_match_bottom_iff)

lemma fail_defined [simp]: "fail  "
by (simp add: fail_def Abs_match_bottom_iff)

lemma succeed_eq [simp]: "(succeedx = succeedy) = (x = y)"
by (simp add: succeed_def cont_Abs_match Abs_match_inject)

lemma succeed_neq_fail [simp]:
  "succeedx  fail" "fail  succeedx"
by (simp_all add: succeed_def fail_def cont_Abs_match Abs_match_inject)


subsubsection ‹Run operator›

definition
  run :: "'a match  'a::pcpo" where
  "run = (Λ m. sscase(fupID)(Rep_match m))"

text ‹rewrite rules for run›

lemma run_strict [simp]: "run = "
unfolding run_def
by (simp add: cont_Rep_match Rep_match_strict)

lemma run_fail [simp]: "runfail = "
unfolding run_def fail_def
by (simp add: cont_Rep_match Abs_match_inverse)

lemma run_succeed [simp]: "run(succeedx) = x"
unfolding run_def succeed_def
by (simp add: cont_Rep_match cont_Abs_match Abs_match_inverse)


subsubsection ‹Monad plus operator›

definition
  mplus :: "'a match  'a match  'a match" where
  "mplus = (Λ m1 m2. sscase(Λ _. m2)(Λ _. m1)(Rep_match m1))"

abbreviation
  mplus_syn :: "['a match, 'a match]  'a match"  (infixr +++ 65)  where
  "m1 +++ m2 == mplusm1m2"

text ‹rewrite rules for mplus›

lemma mplus_strict [simp]: " +++ m = "
unfolding mplus_def
by (simp add: cont_Rep_match Rep_match_strict)

lemma mplus_fail [simp]: "fail +++ m = m"
unfolding mplus_def fail_def
by (simp add: cont_Rep_match Abs_match_inverse)

lemma mplus_succeed [simp]: "succeedx +++ m = succeedx"
unfolding mplus_def succeed_def
by (simp add: cont_Rep_match cont_Abs_match Abs_match_inverse)

lemma mplus_fail2 [simp]: "m +++ fail = m"
by (cases m, simp_all)

lemma mplus_assoc: "(x +++ y) +++ z = x +++ (y +++ z)"
by (cases x, simp_all)


subsection ‹Match functions for built-in types›

definition
  match_bottom :: "'a::pcpo  'c match  'c match"
where
  "match_bottom = (Λ x k. seqxfail)"

definition
  match_Pair :: "'a × 'b  ('a  'b  'c match)  'c match"
where
  "match_Pair = (Λ x k. csplitkx)"

definition
  match_spair :: "'a::pcpo  'b::pcpo  ('a  'b  'c match)  'c::pcpo match"
where
  "match_spair = (Λ x k. ssplitkx)"

definition
  match_sinl :: "'a::pcpo  'b::pcpo  ('a  'c::pcpo match)  'c match"
where
  "match_sinl = (Λ x k. sscasek(Λ b. fail)x)"

definition
  match_sinr :: "'a::pcpo  'b::pcpo  ('b  'c::pcpo match)  'c match"
where
  "match_sinr = (Λ x k. sscase(Λ a. fail)kx)"

definition
  match_up :: "'a u  ('a  'c::pcpo match)  'c match"
where
  "match_up = (Λ x k. fupkx)"

definition
  match_ONE :: "one  'c::pcpo match  'c match"
where
  "match_ONE = (Λ ONE k. k)"

definition
  match_TT :: "tr  'c::pcpo match  'c match"
where
  "match_TT = (Λ x k. If x then k else fail)"

definition
  match_FF :: "tr  'c::pcpo match  'c match"
where
  "match_FF = (Λ x k. If x then fail else k)"

lemma match_bottom_simps [simp]:
  "match_bottomxk = (if x =  then  else fail)"
by (simp add: match_bottom_def)

lemma match_Pair_simps [simp]:
  "match_Pair(x, y)k = kxy"
by (simp_all add: match_Pair_def)

lemma match_spair_simps [simp]:
  "x  ; y    match_spair(:x, y:)k = kxy"
  "match_spairk = "
by (simp_all add: match_spair_def)

lemma match_sinl_simps [simp]:
  "x    match_sinl(sinlx)k = kx"
  "y    match_sinl(sinry)k = fail"
  "match_sinlk = "
by (simp_all add: match_sinl_def)

lemma match_sinr_simps [simp]:
  "x    match_sinr(sinlx)k = fail"
  "y    match_sinr(sinry)k = ky"
  "match_sinrk = "
by (simp_all add: match_sinr_def)

lemma match_up_simps [simp]:
  "match_up(upx)k = kx"
  "match_upk = "
by (simp_all add: match_up_def)

lemma match_ONE_simps [simp]:
  "match_ONEONEk = k"
  "match_ONEk = "
by (simp_all add: match_ONE_def)

lemma match_TT_simps [simp]:
  "match_TTTTk = k"
  "match_TTFFk = fail"
  "match_TTk = "
by (simp_all add: match_TT_def)

lemma match_FF_simps [simp]:
  "match_FFFFk = k"
  "match_FFTTk = fail"
  "match_FFk = "
by (simp_all add: match_FF_def)


subsection ‹Mutual recursion›

text ‹
  The following rules are used to prove unfolding theorems from
  fixed-point definitions of mutually recursive functions.
›

lemma Pair_equalI: "x  fst p; y  snd p  (x, y)  p"
by simp

lemma Pair_eqD1: "(x, y) = (x', y')  x = x'"
by simp

lemma Pair_eqD2: "(x, y) = (x', y')  y = y'"
by simp

lemma def_cont_fix_eq:
  "f  fix(Abs_cfun F); cont F  f = F f"
by (simp, subst fix_eq, simp)

lemma def_cont_fix_ind:
  "f  fix(Abs_cfun F); cont F; adm P; P ; x. P x  P (F x)  P f"
by (simp add: fix_ind)

text ‹lemma for proving rewrite rules›

lemma ssubst_lhs: "t = s; P s = Q  P t = Q"
by simp


subsection ‹Initializing the fixrec package›

ML_file ‹Tools/holcf_library.ML›
ML_file ‹Tools/fixrec.ML›

method_setup fixrec_simp = Scan.succeed (SIMPLE_METHOD' o Fixrec.fixrec_simp_tac) "pattern prover for fixrec constants"

setup Fixrec.add_matchers
    [ (const_nameup, const_namematch_up),
      (const_namesinl, const_namematch_sinl),
      (const_namesinr, const_namematch_sinr),
      (const_namespair, const_namematch_spair),
      (const_namePair, const_namematch_Pair),
      (const_nameONE, const_namematch_ONE),
      (const_nameTT, const_namematch_TT),
      (const_nameFF, const_namematch_FF),
      (const_namebottom, const_namematch_bottom) ]

hide_const (open) succeed fail run

end