Theory Transitive_Closure_Ext
section ‹Transitive Closures›
theory Transitive_Closure_Ext
  imports "HOL-Library.FuncSet"
begin
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"
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
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