Theory Transitive_Closure_Ext

(*  Title:       Safe OCL
    Author:      Denis Nikiforov, March 2019
    Maintainer:  Denis Nikiforov <denis.nikif at gmail.com>
    License:     LGPL
*)
section ‹Transitive Closures›
theory Transitive_Closure_Ext
  imports "HOL-Library.FuncSet"
begin

(*** Basic Properties *******************************************************)

subsection ‹Basic Properties›

text ‹
  @{text "R++"} is a transitive closure of a relation @{text R}.
  @{text "R**"} is a reflexive transitive closure of a relation @{text R}.›

text ‹
  A function @{text f} is surjective on @{text "R++"} iff for any
  two elements in the range of @{text f}, related through @{text "R++"},
  all their intermediate elements belong to the range of @{text f}.›

abbreviation "surj_on_trancl R f ≡
  (∀x y z. R++ (f x) y ⟶ R y (f z) ⟶ y ∈ range f)"

text ‹
  A function @{text f} is bijective on @{text "R++"} iff
  it is injective and surjective on @{text "R++"}.›

abbreviation "bij_on_trancl R f ≡ inj f ∧ surj_on_trancl R f"

(*** Helper Lemmas **********************************************************)

subsection ‹Helper Lemmas›

lemma rtranclp_eq_rtranclp [iff]:
  "(λx y. P x y ∨ x = y)** = P**"
proof (intro ext iffI)
  fix x y
  have "(λx y. P x y ∨ x = y)** x y ⟶ P==** x y"
    by (rule mono_rtranclp) simp
  thus "(λx y. P x y ∨ x = y)** x y ⟹ P** x y"
    by simp
  show "P** x y ⟹ (λx y. P x y ∨ x = y)** x y"
    by (metis (no_types, lifting) mono_rtranclp)
qed

lemma tranclp_eq_rtranclp [iff]:
  "(λx y. P x y ∨ x = y)++ = P**"
proof (intro ext iffI)
  fix x y
  have "(λx y. P x y ∨ x = y)** x y ⟶ P==** x y"
    by (rule mono_rtranclp) simp
  thus "(λx y. P x y ∨ x = y)++ x y ⟹ P** x y"
    using tranclp_into_rtranclp by force
  show "P** x y ⟹ (λx y. P x y ∨ x = y)++ x y"
    by (metis (mono_tags, lifting) mono_rtranclp rtranclpD tranclp.r_into_trancl)
qed

lemma rtranclp_eq_rtranclp' [iff]:
  "(λx y. P x y ∧ x ≠ y)** = P**"
proof (intro ext iffI)
  fix x y
  show "(λx y. P x y ∧ x ≠ y)** x y ⟹ P** x y"
    by (metis (no_types, lifting) mono_rtranclp)
  assume "P** x y"
  hence "(inf P (≠))** x y"
    by (simp add: rtranclp_r_diff_Id)
  also have "(inf P (≠))** x y ⟶ (λx y. P x y ∧ x ≠ y)** x y"
    by (rule mono_rtranclp) simp
  finally show "P** x y ⟹ (λx y. P x y ∧ x ≠ y)** x y" by simp
qed

lemma tranclp_tranclp_to_tranclp_r:
  assumes "(⋀x y z. R++ x y ⟹ R y z ⟹ P x ⟹ P z ⟹ P y)"
  assumes "R++ x y" and "R++ y z"
  assumes "P x" and "P z"
  shows "P y"
proof -
  have "(⋀x y z. R++ x y ⟹ R y z ⟹ P x ⟹ P z ⟹ P y) ⟹
         R++ y z ⟹ R++ x y ⟹ P x ⟶ P z ⟶ P y"
    by (erule tranclp_induct, auto) (meson tranclp_trans)
  thus ?thesis using assms by auto
qed

(*** Transitive Closure Preservation & Reflection ***************************)

subsection ‹Transitive Closure Preservation›

text ‹
  A function @{text f} preserves @{text "R++"} if it preserves @{text R}.›

text ‹
  The proof was derived from the accepted answer on the website
  Stack Overflow that is available at
  @{url "https://stackoverflow.com/a/52573551/632199"}
  and provided with the permission of the author of the answer.›

lemma preserve_tranclp:
  assumes "⋀x y. R x y ⟹ S (f x) (f y)"
  assumes "R++ x y"
  shows "S++ (f x) (f y)"
proof -
  define P where P: "P = (λx y. S++ (f x) (f y))" 
  define r where r: "r = (λx y. S (f x) (f y))"
  have "r++ x y" by (insert assms r; erule tranclp_trans_induct; auto)
  moreover have "⋀x y. r x y ⟹ P x y" unfolding P r by simp
  moreover have "⋀x y z. r++ x y ⟹ P x y ⟹ r++ y z ⟹ P y z ⟹ P x z"
    unfolding P by auto
  ultimately have "P x y" by (rule tranclp_trans_induct)
  with P show ?thesis by simp
qed

text ‹
  A function @{text f} preserves @{text "R**"} if it preserves @{text R}.›

lemma preserve_rtranclp:
  "(⋀x y. R x y ⟹ S (f x) (f y)) ⟹
   R** x y ⟹ S** (f x) (f y)"
  unfolding Nitpick.rtranclp_unfold
  by (metis preserve_tranclp)

text ‹
  If one needs to prove that @{text "(f x)"} and @{text "(g y)"}
  are related through @{text "S**"} then one can use the previous
  lemma and add a one more step from @{text "(f y)"} to @{text "(g y)"}.›

lemma preserve_rtranclp':
  "(⋀x y. R x y ⟹ S (f x) (f y)) ⟹
   (⋀y. S (f y) (g y)) ⟹
   R** x y ⟹ S** (f x) (g y)"
  by (metis preserve_rtranclp rtranclp.rtrancl_into_rtrancl)

lemma preserve_rtranclp'':
  "(⋀x y. R x y ⟹ S (f x) (f y)) ⟹
   (⋀y. S (f y) (g y)) ⟹
   R** x y ⟹ S++ (f x) (g y)"
  apply (rule_tac ?b="f y" in rtranclp_into_tranclp1, auto)
  by (rule preserve_rtranclp, auto)

subsection ‹Transitive Closure Reflection›

text ‹
  A function @{text f} reflects @{text "S++"} if it reflects
  @{text S} and is bijective on @{text "S++"}.›

text ‹
  The proof was derived from the accepted answer on the website
  Stack Overflow that is available at
  @{url "https://stackoverflow.com/a/52573551/632199"}
  and provided with the permission of the author of the answer.›

lemma reflect_tranclp:
  assumes refl_f: "⋀x y. S (f x) (f y) ⟹ R x y"
  assumes bij_f: "bij_on_trancl S f"
  assumes prem: "S++ (f x) (f y)"
  shows "R++ x y"
proof -
  define B where B: "B = range f"
  define g where g: "g = the_inv_into UNIV f"
  define gr where gr: "gr = restrict g B"
  define P where P: "P = (λx y. x ∈ B ⟶ y ∈ B ⟶ R++ (gr x) (gr y))"
  from prem have major: "S++ (f x) (f y)" by blast
  from refl_f bij_f have cases_1: "⋀x y. S x y ⟹ P x y"
    unfolding B P g gr
    by (simp add: f_the_inv_into_f tranclp.r_into_trancl)
  from refl_f bij_f
  have "(⋀x y z. S++ x y ⟹ S++ y z ⟹ x ∈ B ⟹ z ∈ B ⟹ y ∈ B)"
    unfolding B
    by (rule_tac ?z="z" in tranclp_tranclp_to_tranclp_r, auto, blast)
  with P have cases_2:
    "⋀x y z. S++ x y ⟹ P x y ⟹ S++ y z ⟹ P y z ⟹ P x z"
    unfolding B
    by auto
  from major cases_1 cases_2 have "P (f x) (f y)"
    by (rule tranclp_trans_induct)
  with bij_f show ?thesis unfolding P B g gr by (simp add: the_inv_f_f)
qed

text ‹
  A function @{text f} reflects @{text "S**"} if it reflects
  @{text S} and is bijective on @{text "S++"}.›

lemma reflect_rtranclp:
  "(⋀x y. S (f x) (f y) ⟹ R x y) ⟹
   bij_on_trancl S f ⟹
   S** (f x) (f y) ⟹ R** x y"
  unfolding Nitpick.rtranclp_unfold
  by (metis (full_types) injD reflect_tranclp)

end