Theory RefineG_Domain

section ‹General Domain Theory›
theory RefineG_Domain
imports "../Refine_Misc"
begin

  subsection ‹General Order Theory Tools›
  lemma chain_f_apply: "Complete_Partial_Order.chain (fun_ord le) F
     Complete_Partial_Order.chain le {y . fF. y = f x}"
    unfolding Complete_Partial_Order.chain_def 
    by (auto simp: fun_ord_def)

  lemma ccpo_lift:
    assumes "class.ccpo lub le lt"
    shows "class.ccpo (fun_lub lub) (fun_ord le) (mk_less (fun_ord le))"
  proof -
    interpret ccpo: ccpo lub le lt by fact
    show ?thesis
      apply unfold_locales
      apply (simp_all only: mk_less_def fun_ord_def fun_lub_def)
      apply simp
      using ccpo.order_trans apply blast
      using ccpo.order.antisym apply blast
      using ccpo.ccpo_Sup_upper apply (blast intro: chain_f_apply)
      using ccpo.ccpo_Sup_least apply (blast intro: chain_f_apply)
      done
  qed
  
  (* TODO: Move *)
  lemma fun_lub_simps[simp]: 
    "fun_lub lub {} = (λx. lub {})"
    "fun_lub lub {f} = (λx. lub {f x})"
    unfolding fun_lub_def by auto

  subsection ‹Flat Ordering›
  lemma flat_ord_chain_cases: 
    assumes A: "Complete_Partial_Order.chain (flat_ord b) C"
    obtains "C={}" 
    | "C={b}" 
    | x where "xb" and "C={x}" 
    | x where "xb" and "C={b,x}"
  proof -
    have "m1 m2. C  {m1,m2}  (m1 = b  m2 = b)"
      apply (rule ccontr)
    proof clarsimp
      assume "m1 m2. C  {m1, m2}  m1b  m2b"
      then obtain m1 m2 where "m1C" "m2C" 
        "m1m2" "m1b" "m2b"
        by blast
      with A show False
        unfolding Complete_Partial_Order.chain_def flat_ord_def
        by auto
    qed
    then obtain m where "C  {m,b}" by blast
    thus ?thesis 
      apply (cases "m=b") 
      using that
      apply blast+
      done
  qed
    
  lemma flat_lub_simps[simp]:
    "flat_lub b {} = b"
    "flat_lub b {x} = x"
    "flat_lub b (insert b X) = flat_lub b X"
    unfolding flat_lub_def
    by auto

  lemma flat_ord_simps[simp]:
    "flat_ord b b x" 
    by (simp add: flat_ord_def)

  interpretation flat_ord: ccpo "flat_lub b" "flat_ord b" "mk_less (flat_ord b)"
    apply unfold_locales
    apply (simp_all only: mk_less_def flat_ord_def) apply auto [4]
    apply (erule flat_ord_chain_cases, auto) []
    apply (erule flat_ord_chain_cases, auto) []
    done

  interpretation flat_le_mono_setup: mono_setup_loc "flat_ord b"
    by standard auto

  subsubsection ‹Flat function Ordering›
  abbreviation "flatf_ord b == fun_ord (flat_ord b)"
  abbreviation "flatf_lub b == fun_lub (flat_lub b)"

  interpretation flatf_ord: ccpo "flatf_lub b" "flatf_ord b" "mk_less (flatf_ord b)"
    apply (rule ccpo_lift)
    apply unfold_locales
    done

  subsubsection ‹Fixed Points in Flat Ordering›
  text ‹
    Fixed points in a flat ordering are used to express recursion.
    The bottom element is interpreted as non-termination.
›  

  abbreviation "flat_mono b == monotone (flat_ord b) (flat_ord b)"
  abbreviation "flatf_mono b == monotone (flatf_ord b) (flatf_ord b)"
  abbreviation "flatf_fp b  flatf_ord.fixp b"

  lemma flatf_fp_mono[refine_mono]:
    ― ‹The fixed point combinator is monotonic›
    assumes "flatf_mono b f"
      and "flatf_mono b g"
      and "Z x. flat_ord b (f Z x) (g Z x)"
    shows "flat_ord b (flatf_fp b f x) (flatf_fp b g x)"
  proof -
    have "flatf_ord b (flatf_fp b f) (flatf_fp b g)"
      apply (rule flatf_ord.fixp_mono[OF assms(1,2)])
      using assms(3) by (simp add: fun_ord_def)
    thus ?thesis unfolding fun_ord_def by blast
  qed

  lemma flatf_admissible_pointwise:
    "(x. P x b)  
      ccpo.admissible (flatf_lub b) (flatf_ord b) (λg. x. P x (g x))"
    apply (intro ccpo.admissibleI allI impI)
    apply (drule_tac x=x in chain_f_apply)
    apply (erule flat_ord_chain_cases)
    apply (auto simp add: fun_lub_def) [2]
    apply (force simp add: fun_lub_def) []
    apply (auto simp add: fun_lub_def) []
    done

  text ‹
    If a property is defined pointwise, and holds for the bottom element,
    we can use fixed-point induction for it. 

    In the induction step, we can assume that the function is less or equal
    to the fixed-point.

    This rule covers refinement and transfer properties, 
    such as: Refinement of fixed-point combinators and transfer of 
    fixed-point combinators to different domains.
›
  lemma flatf_fp_induct_pointwise:
    ― ‹Fixed-point induction for pointwise properties›
    fixes a :: 'a
    assumes cond_bot: "a x. pre a x  post a x b"
    assumes MONO: "flatf_mono b B"
    assumes PRE0: "pre a x"
    assumes STEP: "f a x. 
      a' x'. pre a' x'  post a' x' (f x'); pre a x; 
        flatf_ord b f (flatf_fp b B)  
       post a x (B f x)"
    shows "post a x (flatf_fp b B x)"
  proof -
    define ub where "ub = flatf_fp b B"

    have "(x a. pre a x  post a x (flatf_fp b B x)) 
       flatf_ord b (flatf_fp b B) ub"
      apply (rule flatf_ord.fixp_induct[OF _ MONO])
      apply (rule admissible_conj)
      apply (rule flatf_admissible_pointwise)
      apply (blast intro: cond_bot)
      apply (rule ccpo.admissibleI)
      apply (blast intro: flatf_ord.ccpo_Sup_least)

      apply (auto intro: cond_bot simp: fun_ord_def flat_ord_def) []

      apply (rule conjI)
      unfolding ub_def

      apply (blast intro: STEP)

      apply (subst flatf_ord.fixp_unfold[OF MONO])
      apply (blast intro: monotoneD[OF MONO])
      done
    with PRE0 show ?thesis by blast
  qed
  
  text ‹
    The next rule covers transfer between fixed points.
    It allows to lift a pointwise transfer condition 
    P x y ⟶ tr (f x) (f y)› to fixed points.
    Note that one of the fixed points may be an arbitrary fixed point.
›
  lemma flatf_fixp_transfer:
    ― ‹Transfer rule for fixed points›
    assumes TR_BOT[simp]: "x'. tr b x'"
    assumes MONO: "flatf_mono b B"
    assumes FP': "fp' = B' fp'"
    assumes R0: "P x x'"
    assumes RS: "f f' x x'.
       x x'. P x x'  tr (f x) (f' x'); P x x'; fp' = f'
        tr (B f x) (B' f' x')"
    shows "tr (flatf_fp b B x) (fp' x')"
    apply (rule flatf_fp_induct_pointwise[where pre="λx y. P y x", OF _ MONO])
    apply simp
    apply (rule R0)
    apply (subst FP')
    apply (blast intro: RS)
    done

  subsubsection ‹Relation of Flat Ordering to Complete Lattices›
  text ‹
    In this section, we establish the relation between flat orderings 
    and complete lattices. This relation is exploited to show properties
    of fixed points wrt. a refinement ordering.
›

  abbreviation "flat_le  flat_ord bot"
  abbreviation "flat_ge  flat_ord top"
  abbreviation "flatf_le  flatf_ord bot"
  abbreviation "flatf_ge  flatf_ord top"

  text ‹The flat ordering implies the lattice ordering›
  lemma flat_ord_compat: 
    fixes x y :: "'a :: complete_lattice"
    shows 
    "flat_le x y  x  y"
    "flat_ge x y  x  y"
    unfolding flat_ord_def by auto

  lemma flatf_ord_compat: 
    fixes x y :: "'a  ('b :: complete_lattice)"
    shows 
    "flatf_le x y  x  y"
    "flatf_ge x y  x  y"
    by (auto simp: flat_ord_compat le_fun_def fun_ord_def)
  
  abbreviation "flat_mono_le  flat_mono bot"
  abbreviation "flat_mono_ge  flat_mono top"

  abbreviation "flatf_mono_le  flatf_mono bot"
  abbreviation "flatf_mono_ge  flatf_mono top"

  abbreviation "flatf_gfp  flatf_ord.fixp top"
  abbreviation "flatf_lfp  flatf_ord.fixp bot"

  text ‹If a functor is monotonic wrt. both the flat and the 
    lattice ordering, the fixed points wrt. these orderings coincide. 
›
  lemma lfp_eq_flatf_lfp:
    assumes FM: "flatf_mono_le B" and M: "mono B"
    shows "lfp B = flatf_lfp B"
  proof -
    from lfp_unfold[OF M, symmetric] have "B (lfp B) = lfp B" .
    hence "flatf_le (B (lfp B)) (lfp B)" by simp
    with flatf_ord.fixp_lowerbound[OF FM] have "flatf_le (flatf_lfp B) (lfp B)" .
    with flatf_ord_compat have "flatf_lfp B  lfp B" by blast
    also
    from flatf_ord.fixp_unfold[OF FM, symmetric] have "B (flatf_lfp B) = flatf_lfp B" .
    hence "B (flatf_lfp B)  flatf_lfp B" by simp
    with lfp_lowerbound[where A="flatf_lfp B"] have "lfp B  flatf_lfp B" .
    finally show "lfp B = flatf_lfp B" ..
  qed

  lemma gfp_eq_flatf_gfp:
    assumes FM: "flatf_mono_ge B" and M: "mono B"
    shows "gfp B = flatf_gfp B"
  proof -
    from gfp_unfold[OF M, symmetric] have "B (gfp B) = gfp B" .
    hence "flatf_ge (B (gfp B)) (gfp B)" by simp
    with flatf_ord.fixp_lowerbound[OF FM] have "flatf_ge (flatf_gfp B) (gfp B)" .
    with flatf_ord_compat have "gfp B  flatf_gfp B" by blast
    also
    from flatf_ord.fixp_unfold[OF FM, symmetric] have "B (flatf_gfp B) = flatf_gfp B" .
    hence "flatf_gfp B  B (flatf_gfp B)" by simp
    with gfp_upperbound[where X="flatf_gfp B"] have "flatf_gfp B  gfp B" .
    finally show "gfp B = flatf_gfp B" .
  qed
  

  (* TODO: This belongs to "General Recursion"*)
  text ‹
    The following lemma provides a well-founded induction scheme for arbitrary 
    fixed point combinators.
›
  lemma wf_fixp_induct:
    ― ‹Well-Founded induction for arbitrary fixed points›
    fixes a :: 'a
    assumes fixp_unfold: "fp B = B (fp B)"
    assumes WF: "wf V"
    assumes P0: "pre a x"
    assumes STEP: "f a x.  
      a' x'.  pre a' x'; (x',x)V   post a' x' (f x'); fp B = f; pre a x 
      post a x (B f x)"
    shows "post a x (fp B x)"
  proof -
    have "a. pre a x  post a x (fp B x)"
      using WF
      apply (induct x rule: wf_induct_rule)
      apply (intro allI impI)
      apply (subst fixp_unfold)
      apply (rule STEP)
      apply (simp)
      apply (simp)
      apply (simp)
      done
    with P0 show ?thesis by blast
  qed


  lemma flatf_lfp_transfer:
    ― ‹Transfer rule for least fixed points›
    fixes B::"(_  'a::order_bot)  _"
    assumes TR_BOT[simp]: "x. tr bot x"
    assumes MONO: "flatf_mono_le B"
    assumes MONO': "flatf_mono_le B'"
    assumes R0: "P x x'"
    assumes RS: "f f' x x'.
       x x'. P x x'  tr (f x) (f' x'); P x x'; flatf_lfp B' = f'
        tr (B f x) (B' f' x')"
    shows "tr (flatf_lfp B x) (flatf_lfp B' x')"
    apply (rule flatf_fixp_transfer[where tr=tr and fp'="flatf_lfp B'" and P=P])
    apply (fact|rule flatf_ord.fixp_unfold[OF MONO'])+
    done

  lemma flatf_gfp_transfer:
    ― ‹Transfer rule for greatest fixed points›
    fixes B::"(_  'a::order_top)  _"
    assumes TR_TOP[simp]: "x. tr x top"
    assumes MONO: "flatf_mono_ge B"
    assumes MONO': "flatf_mono_ge B'"
    assumes R0: "P x x'"
    assumes RS: "f f' x x'.
       x x'. P x x'  tr (f x) (f' x'); P x x'; flatf_gfp B = f
        tr (B f x) (B' f' x')"
    shows "tr (flatf_gfp B x) (flatf_gfp B' x')"
    apply (rule flatf_fixp_transfer[where 
        tr="λx y. tr y x" and fp'="flatf_gfp B" and P="λx y. P y x"])
    apply (fact|assumption|rule flatf_ord.fixp_unfold[OF MONO] RS)+
    done

  lemma meta_le_everything_if_top: "(m=top)  (x. x  (m::'a::order_top))"
    by auto

  lemmas flatf_lfp_refine = flatf_lfp_transfer[
    where tr = "λa b. a  cf b" for cf, OF bot_least]
  lemmas flatf_gfp_refine = flatf_gfp_transfer[
    where tr = "λa b. a  cf b" for cf, OF meta_le_everything_if_top]


  lemma flat_ge_sup_mono[refine_mono]: "a a'::'a::complete_lattice. 
    flat_ge a a'  flat_ge b b'  flat_ge (sup a b) (sup a' b')"
    by (auto simp: flat_ord_def)
  
  declare sup_mono[refine_mono]


end