Theory Algorithms

(* Title:      Axioms and Algorithmic Proofs
   Author:     Walter Guttmann
   Maintainer: Walter Guttmann <walter.guttmann at canterbury.ac.nz>
*)

section ‹Axioms and Algorithmic Proofs›

text ‹
In this theory we verify the correctness of three basic graph algorithms.
We use them to constructively prove a number of graph properties.
›

theory Algorithms

imports "HOL-Hoare.Hoare_Logic" Forests

begin

context stone_kleene_relation_algebra_arc
begin

text ‹
Assuming the arc axiom we can define the function ‹choose_arc› that selects an arc in a non-empty graph.
›

definition "choose_arc x ≡ if x = bot then bot else SOME y . arc y ∧ y ≤ --x"

lemma choose_arc_below:
  "choose_arc x ≤ --x"
proof (cases "x = bot")
  case True
  thus ?thesis
    using choose_arc_def by auto
next
  let ?P = "λy . arc y ∧ y ≤ --x"
  case False
  have "?P (SOME y . ?P y)"
    apply (rule someI_ex)
    using someI_ex False arc_axiom by auto
  thus ?thesis
    using False choose_arc_def by auto
qed

lemma choose_arc_arc:
  assumes "x ≠ bot"
  shows "arc (choose_arc x)"
proof -
  let ?P = "λy . arc y ∧ y ≤ --x"
  have "?P (SOME y . ?P y)"
    apply (rule someI_ex)
    using someI_ex assms arc_axiom by auto
  thus ?thesis
    using assms choose_arc_def by auto
qed

lemma choose_arc_bot:
  "choose_arc bot = bot"
  by (metis bot_unique choose_arc_below regular_closed_bot)

lemma choose_arc_bot_iff:
  "choose_arc x = bot ⟷ x = bot"
  using covector_bot_closed inf_bot_right choose_arc_arc vector_bot_closed choose_arc_bot by fastforce

lemma choose_arc_regular:
  "regular (choose_arc x)"
proof (cases "x = bot")
  assume "x = bot"
  thus ?thesis
    by (simp add: choose_arc_bot)
next
  assume "x ≠ bot"
  thus ?thesis
    by (simp add: arc_regular choose_arc_arc)
qed

subsection ‹Constructing a spanning tree›

definition "spanning_forest f g ≡ forest f ∧ f ≤ --g ∧ components g ≤ forest_components f ∧ regular f"
definition "kruskal_spanning_invariant f g h ≡ symmetric g ∧ h = hT ∧ g ⊓ --h = h ∧ spanning_forest f (-h ⊓ g)"

lemma spanning_forest_spanning:
  "spanning_forest f g ⟹ spanning (--g) f"
  by (smt (z3) cancel_separate_1 order_trans star.circ_increasing spanning_forest_def)

lemma spanning_forest_spanning_regular:
  assumes "regular f"
      and "regular g"
  shows "spanning_forest f g ⟷ spanning g f"
  by (smt (z3) assms cancel_separate_1 components_increasing dual_order.trans forest_components_star star_isotone spanning_forest_def)

text ‹
We prove total correctness of Kruskal's spanning tree algorithm (ignoring edge weights) cite‹"Kruskal1956"›.
The algorithm and proof are adapted from the AFP theory ‹Relational_Minimum_Spanning_Trees.Kruskal› to work in Stone-Kleene relation algebras cite‹"Guttmann2017c" and "Guttmann2018c"›.
›

lemma kruskal_vc_1:
  assumes "symmetric g"
    shows "kruskal_spanning_invariant bot g g"
proof (unfold kruskal_spanning_invariant_def, intro conjI)
  show "symmetric g"
    using assms by simp
next
  show "g = gT"
    using assms by simp
next
  show "g ⊓ --g = g"
    using inf.sup_monoid.add_commute selection_closed_id by simp
next
  show "spanning_forest bot (-g ⊓ g)"
    using star.circ_transitive_equal spanning_forest_def by simp
qed

text ‹
For the remainder of this theory we assume there are finitely many regular elements.
This means that the graphs are finite and is needed for proving termination of the algorithms.
›

context
  assumes finite_regular: "finite { x . regular x }"
begin

lemma kruskal_vc_2:
  assumes "kruskal_spanning_invariant f g h"
      and "h ≠ bot"
    shows "(choose_arc h ≤ -forest_components f ⟶ kruskal_spanning_invariant ((f ⊓ -(top * choose_arc h * fT⋆)) ⊔ (f ⊓ top * choose_arc h * fT⋆)T ⊔ choose_arc h) g (h ⊓ -choose_arc h ⊓ -choose_arc hT)
                                               ∧ card { x . regular x ∧ x ≤ --h ∧ x ≤ -choose_arc h ∧ x ≤ -choose_arc hT } < card { x . regular x ∧ x ≤ --h }) ∧
           (¬ choose_arc h ≤ -forest_components f ⟶ kruskal_spanning_invariant f g (h ⊓ -choose_arc h ⊓ -choose_arc hT)
                                                 ∧ card { x . regular x ∧ x ≤ --h ∧ x ≤ -choose_arc h ∧ x ≤ -choose_arc hT } < card { x . regular x ∧ x ≤ --h })"
proof -
  let ?e = "choose_arc h"
  let ?f = "(f ⊓ -(top * ?e * fT⋆)) ⊔ (f ⊓ top * ?e * fT⋆)T ⊔ ?e"
  let ?h = "h ⊓ -?e ⊓ -?eT"
  let ?F = "forest_components f"
  let ?n1 = "card { x . regular x ∧ x ≤ --h }"
  let ?n2 = "card { x . regular x ∧ x ≤ --h ∧ x ≤ -?e ∧ x ≤ -?eT }"
  have 1: "regular f ∧ regular ?e"
    by (metis assms(1) kruskal_spanning_invariant_def spanning_forest_def choose_arc_regular)
  hence 2: "regular ?f ∧ regular ?F ∧ regular (?eT)"
    using regular_closed_star regular_conv_closed regular_mult_closed by simp
  have 3: "¬ ?e ≤ -?e"
    using assms(2) inf.orderE choose_arc_bot_iff by fastforce
  have 4: "?n2 < ?n1"
    apply (rule psubset_card_mono)
    using finite_regular apply simp
    using 1 3 kruskal_spanning_invariant_def choose_arc_below by auto
  show "(?e ≤ -?F ⟶ kruskal_spanning_invariant ?f g ?h ∧ ?n2 < ?n1) ∧ (¬ ?e ≤ -?F ⟶ kruskal_spanning_invariant f g ?h ∧ ?n2 < ?n1)"
  proof (rule conjI)
    have 5: "injective ?f"
      apply (rule kruskal_injective_inv)
      using assms(1) kruskal_spanning_invariant_def spanning_forest_def apply simp
      apply (simp add: covector_mult_closed)
      apply (simp add: comp_associative comp_isotone star.right_plus_below_circ)
      apply (meson mult_left_isotone order_lesseq_imp star_outer_increasing top.extremum)
      using assms(1,2) kruskal_spanning_invariant_def kruskal_injective_inv_2 choose_arc_arc spanning_forest_def apply simp
      using assms(2) arc_injective choose_arc_arc apply blast
      using assms(1,2) kruskal_spanning_invariant_def kruskal_injective_inv_3 choose_arc_arc spanning_forest_def by simp
    show "?e ≤ -?F ⟶ kruskal_spanning_invariant ?f g ?h ∧ ?n2 < ?n1"
    proof
      assume 6: "?e ≤ -?F"
      have 7: "equivalence ?F"
        using assms(1) kruskal_spanning_invariant_def forest_components_equivalence spanning_forest_def by simp
      have "?eT * top * ?eT = ?eT"
        using assms(2) by (simp add: arc_top_arc choose_arc_arc)
      hence "?eT * top * ?eT ≤ -?F"
        using 6 7 conv_complement conv_isotone by fastforce
      hence 8: "?e * ?F * ?e = bot"
        using le_bot triple_schroeder_p by simp
      show "kruskal_spanning_invariant ?f g ?h ∧ ?n2 < ?n1"
      proof (unfold kruskal_spanning_invariant_def, intro conjI)
        show "symmetric g"
          using assms(1) kruskal_spanning_invariant_def by simp
      next
        show "?h = ?hT"
          using assms(1) by (simp add: conv_complement conv_dist_inf inf_commute inf_left_commute kruskal_spanning_invariant_def)
      next
        show "g ⊓ --?h = ?h"
          using 1 2 by (metis assms(1) kruskal_spanning_invariant_def inf_assoc pp_dist_inf)
      next
        show "spanning_forest ?f (-?h ⊓ g)"
        proof (unfold spanning_forest_def, intro conjI)
          show "injective ?f"
            using 5 by simp
        next
          show "acyclic ?f"
            apply (rule kruskal_acyclic_inv)
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def apply simp
            apply (simp add: covector_mult_closed)
            using 8 assms(1) kruskal_spanning_invariant_def spanning_forest_def kruskal_acyclic_inv_1 apply simp
            using 8 apply (metis comp_associative mult_left_sub_dist_sup_left star.circ_loop_fixpoint sup_commute le_bot)
            using 6 by (simp add: p_antitone_iff)
        next
          show "?f ≤ --(-?h ⊓ g)"
            apply (rule kruskal_subgraph_inv)
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def apply simp
            using assms(1) apply (metis kruskal_spanning_invariant_def choose_arc_below order.trans pp_isotone_inf)
            using assms(1) kruskal_spanning_invariant_def apply simp
            using assms(1) kruskal_spanning_invariant_def by simp
        next
          show "components (-?h ⊓ g) ≤ forest_components ?f"
            apply (rule kruskal_spanning_inv)
            using 5 apply simp
            using 1 regular_closed_star regular_conv_closed regular_mult_closed apply simp
            using 1 apply simp
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def by simp
        next
          show "regular ?f"
            using 2 by simp
        qed
      next
        show "?n2 < ?n1"
          using 4 by simp
      qed
    qed
  next
    show "¬ ?e ≤ -?F ⟶ kruskal_spanning_invariant f g ?h ∧ ?n2 < ?n1"
    proof
      assume "¬ ?e ≤ -?F"
      hence 9: "?e ≤ ?F"
        using 2 assms(2) arc_in_partition choose_arc_arc by fastforce
      show "kruskal_spanning_invariant f g ?h ∧ ?n2 < ?n1"
      proof (unfold kruskal_spanning_invariant_def, intro conjI)
        show "symmetric g"
          using assms(1) kruskal_spanning_invariant_def by simp
      next
        show "?h = ?hT"
          using assms(1) by (simp add: conv_complement conv_dist_inf inf_commute inf_left_commute kruskal_spanning_invariant_def)
      next
        show "g ⊓ --?h = ?h"
          using 1 2 by (metis assms(1) kruskal_spanning_invariant_def inf_assoc pp_dist_inf)
      next
        show "spanning_forest f (-?h ⊓ g)"
        proof (unfold spanning_forest_def, intro conjI)
          show "injective f"
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def by simp
        next
          show "acyclic f"
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def by simp
        next
          have "f ≤ --(-h ⊓ g)"
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def by simp
          also have "... ≤ --(-?h ⊓ g)"
            using comp_inf.mult_right_isotone inf.sup_monoid.add_commute inf_left_commute p_antitone_inf pp_isotone by auto
          finally show "f ≤ --(-?h ⊓ g)"
            by simp
        next
          show "components (-?h ⊓ g) ≤ ?F"
            apply (rule kruskal_spanning_inv_1)
            using 9 apply simp
            using 1 apply simp
            using assms(1) kruskal_spanning_invariant_def spanning_forest_def apply simp
            using assms(1) kruskal_spanning_invariant_def forest_components_equivalence spanning_forest_def by simp
        next
          show "regular f"
            using 1 by simp
        qed
      next
        show "?n2 < ?n1"
          using 4 by simp
      qed
    qed
  qed
qed

theorem kruskal_spanning:
  "VARS e f h
  [ symmetric g ]
  f := bot;
  h := g;
  WHILE h ≠ bot
    INV { kruskal_spanning_invariant f g h }
    VAR { card { x . regular x ∧ x ≤ --h } }
     DO e := choose_arc h;
        IF e ≤ -forest_components f THEN
          f := (f ⊓ -(top * e * fT⋆)) ⊔ (f ⊓ top * e * fT⋆)T ⊔ e
        ELSE
          SKIP
        FI;
        h := h ⊓ -e ⊓ -eT
     OD
  [ spanning_forest f g ]"
  apply vcg_tc_simp
  using kruskal_vc_1 apply simp
  using kruskal_vc_2 apply simp
  using kruskal_spanning_invariant_def by auto

lemma kruskal_exists_spanning:
  "symmetric g ⟹ ∃f . spanning_forest f g"
  using tc_extract_function kruskal_spanning by blast

text ‹Theorem 16›

lemma symmetric_spannable:
  "symmetric g ⟹ spannable (--g)"
  using kruskal_exists_spanning spanning_forest_spanning spannable_def by blast

subsection ‹Breadth-first search›

text ‹
We prove total correctness of a simple breadth-first search algorithm.
It is a variant of an algorithm discussed in cite‹"Berghammer1999"›.
›

theorem bfs_reachability:
  "VARS p q t
  [ regular r ∧ regular s ∧ vector s ]
  t := bot;
  q := s;
  p := -s ⊓ rT * s;
  WHILE p ≠ bot
    INV { regular r ∧ regular q ∧ vector q ∧ asymmetric t ∧ t ≤ r ∧ t ≤ q ∧ q = tT⋆ * s ∧ p = -q ⊓ rT * q }
    VAR { card { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) } }
     DO t := t ⊔ (r ⊓ q * pT);
        q := q ⊔ p;
        p := -q ⊓ rT * p
     OD
  [ asymmetric t ∧ t ≤ r ∧ q = tT⋆ * s ∧ q = rT⋆ * s ]"
proof vcg_tc
  fix p q t
  assume "regular r ∧ regular s ∧ vector s"
  thus "regular r ∧ regular s ∧ vector s ∧ asymmetric bot ∧ bot ≤ r ∧ bot ≤ s ∧ s = botT⋆ * s ∧ -s ⊓ rT * s = -s ⊓ rT * s"
    by (simp add: star.circ_zero)
next
  fix p q t
  assume 1: "(regular r ∧ regular q ∧ vector q ∧ asymmetric t ∧ t ≤ r ∧ t ≤ q ∧ q = tT⋆ * s ∧ p = -q ⊓ rT * q) ∧ ¬ p ≠ bot"
  have "q = rT⋆ * s"
    apply (rule order.antisym)
    using 1 conv_order mult_left_isotone star_isotone apply simp
    using 1 by (metis inf.sup_monoid.add_commute mult_1_left mult_left_isotone mult_right_isotone order_lesseq_imp pseudo_complement star.circ_reflexive star_left_induct_mult)
  thus "asymmetric t ∧ t ≤ r ∧ q = tT⋆ * s ∧ q = rT⋆ * s"
    using 1 by simp
next
  fix n p q t
  assume 2: "((regular r ∧ regular q ∧ vector q ∧ asymmetric t ∧ t ≤ r ∧ t ≤ q ∧ q = tT⋆ * s ∧ p = -q ⊓ rT * q) ∧ p ≠ bot) ∧ card { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) } = n"
  hence 3: "vector p"
    using vector_complement_closed vector_inf_closed vector_mult_closed by blast
  show "(-(q ⊔ p) ⊓ rT * p, q ⊔ p, t ⊔ (r ⊓ q * pT))
        ∈ { trip . (case trip of (p, q, t) ⇒ regular r ∧ regular q ∧ vector q ∧ asymmetric t ∧ t ≤ r ∧ t ≤ q ∧ q = tT⋆ * s ∧ p = -q ⊓ rT * q) ∧
                   (case trip of (p, q, t) ⇒ card { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) }) < n }"
    apply (rule CollectI, rule conjI)
    subgoal proof (intro case_prodI, intro conjI)
      show "regular r"
        using 2 by blast
      show "regular (q ⊔ p)"
        using 2 regular_conv_closed regular_mult_closed by force
      show "vector (q ⊔ p)"
        using 2 vector_complement_closed vector_inf_closed vector_mult_closed vector_sup_closed by force
      show "asymmetric (t ⊔ (r ⊓ q * pT))"
      proof -
        have "t ⊓ (r ⊓ q * pT)T ≤ t ⊓ p * qT"
          by (metis comp_inf.mult_right_isotone conv_dist_comp conv_involutive conv_order inf.cobounded2)
        also have "... ≤ t ⊓ p"
          using 3 by (metis comp_inf.mult_right_isotone comp_inf.star.circ_sup_sub_sup_one_1 inf.boundedE le_sup_iff mult_right_isotone)
        finally have 4: "t ⊓ (r ⊓ q * pT)T = bot"
          using 2 by (metis order.antisym bot_least inf.sup_monoid.add_assoc pseudo_complement)
        hence 5: "r ⊓ q * pT ⊓ tT = bot"
          using conv_inf_bot_iff inf_commute by force
        have "r ⊓ q * pT ⊓ (r ⊓ q * pT)T ≤ q * pT ⊓ p * qT"
          by (metis comp_inf.comp_isotone conv_dist_comp conv_involutive conv_order inf.cobounded2)
        also have "... ≤ q ⊓ p"
          using 2 3 by (metis comp_inf.comp_isotone inf.cobounded1 vector_covector)
        finally have 6: "r ⊓ q * pT ⊓ (r ⊓ q * pT)T = bot"
          using 2 by (metis inf.cobounded1 inf.sup_monoid.add_commute le_bot pseudo_complement)
        have "(t ⊔ (r ⊓ q * pT)) ⊓ (t ⊔ (r ⊓ q * pT))T = (t ⊓ tT) ⊔ (t ⊓ (r ⊓ q * pT)T) ⊔ (r ⊓ q * pT ⊓ tT) ⊔ (r ⊓ q * pT ⊓ (r ⊓ q * pT)T)"
          by (simp add: sup.commute sup.left_commute conv_dist_sup inf_sup_distrib1 inf_sup_distrib2)
        also have "... = bot"
          using 2 4 5 6 by auto
        finally show ?thesis
          .
      qed
      show "t ⊔ (r ⊓ q * pT) ≤ r"
        using 2 by (meson inf.cobounded1 le_supI)
      show "t ⊔ (r ⊓ q * pT) ≤ q ⊔ p"
        using 2 by (metis comp_inf.star.circ_sup_sub_sup_one_1 inf.absorb2 inf.coboundedI2 inf.sup_monoid.add_commute le_sup_iff mult_right_isotone sup_left_divisibility)
      show "q ⊔ p = (t ⊔ (r ⊓ q * pT))T⋆ * s"
      proof (rule order.antisym)
        have 7: "q ≤ (t ⊔ (r ⊓ q * pT))T⋆ * s"
          using 2 by (metis conv_order mult_left_isotone star_isotone sup_left_divisibility)
        have "-q ⊓ (r ⊓ q * pT)T * q ≤ (t ⊔ (r ⊓ q * pT))T * tT⋆ * s"
          using 2 comp_associative conv_dist_sup inf.coboundedI2 mult_right_sub_dist_sup_right by auto
        also have "... ≤ (t ⊔ (r ⊓ q * pT))T⋆ * s"
          by (simp add: conv_dist_sup mult_left_isotone star.circ_increasing star.circ_mult_upper_bound star.circ_sub_dist)
        finally have 8: "-q ⊓ (r ⊓ q * pT)T * q ≤ (t ⊔ (r ⊓ q * pT))T⋆ * s"
          .
        have 9: "(r ⊓ -q)T * q = bot"
          using 2 by (metis conv_dist_inf covector_inf_comp_3 pp_inf_p semiring.mult_not_zero vector_complement_closed)
        have "-q ⊓ (r ⊓ -(q * pT))T * q = -q ⊓ (r ⊓ (-q ⊔ -pT))T * q"
          using 2 3 by (metis p_dist_inf vector_covector)
        also have "... = (-q ⊓ (r ⊓ -q)T * q) ⊔ (-q ⊓ (r ⊓ -pT)T * q)"
          by (simp add: conv_dist_sup inf_sup_distrib1 mult_right_dist_sup)
        also have "... = -q ⊓ (r ⊓ -pT)T * q"
          using 9 by simp
        also have "... = -q ⊓ -p ⊓ rT * q"
          using 3 by (metis conv_complement conv_dist_inf conv_involutive inf.sup_monoid.add_assoc inf_vector_comp vector_complement_closed)
        finally have 10: "-q ⊓ (r ⊓ -(q * pT))T * q = bot"
          using 2 inf_import_p pseudo_complement by auto
        have "r = (r ⊓ q * pT) ⊔ (r ⊓ -(q * pT))"
          using 2 by (smt (z3) maddux_3_11_pp pp_dist_comp regular_closed_inf regular_conv_closed)
        hence "p = -q ⊓ ((r ⊓ q * pT) ⊔ (r ⊓ -(q * pT)))T * q"
          using 2 by auto
        also have "... = (-q ⊓ (r ⊓ q * pT)T * q) ⊔ (-q ⊓ (r ⊓ -(q * pT))T * q)"
          by (simp add: conv_dist_sup inf_sup_distrib1 semiring.distrib_right)
        also have "... ≤ (t ⊔ (r ⊓ q * pT))T⋆ * s"
          using 8 10 le_sup_iff bot_least by blast
        finally show "q ⊔ p ≤ (t ⊔ (r ⊓ q * pT))T⋆ * s"
          using 7 by simp
        have 11: "tT * q ≤ rT * q"
          using 2 conv_order mult_left_isotone by auto
        have "tT * p ≤ tT * top"
          by (simp add: mult_right_isotone)
        also have "... = tT * q ⊔ tT * -q"
          using 2 regular_complement_top semiring.distrib_left by force
        also have "... = tT * q"
        proof -
          have "tT * -q = bot"
            using 2 by (metis bot_least conv_complement_sub conv_dist_comp conv_involutive mult_right_isotone regular_closed_bot stone sup.absorb2 sup_commute)
          thus ?thesis
            by simp
        qed
        finally have 12: "tT * p ≤ rT * q"
          using 11 order.trans by blast
        have 13: "(r ⊓ q * pT)T * q ≤ rT * q"
          by (simp add: conv_dist_inf mult_left_isotone)
        have "(r ⊓ q * pT)T * p ≤ p"
          using 3 by (metis conv_dist_comp conv_dist_inf conv_involutive inf.coboundedI2 mult_isotone mult_right_isotone top.extremum)
        hence 14: "(r ⊓ q * pT)T * p ≤ rT * q"
          using 2 le_infE by blast
        have "(t ⊔ (r ⊓ q * pT))T * (q ⊔ p) = tT * q ⊔ tT * p ⊔ (r ⊓ q * pT)T * q ⊔ (r ⊓ q * pT)T * p"
          by (metis conv_dist_sup semiring.distrib_left semiring.distrib_right sup_assoc)
        also have "... ≤ rT * q"
          using 11 12 13 14 by simp
        finally have "(t ⊔ (r ⊓ q * pT))T * (q ⊔ p) ≤ q ⊔ p"
          using 2 by (metis maddux_3_21_pp sup.boundedE sup_right_divisibility)
        thus "(t ⊔ (r ⊓ q * pT))T⋆ * s ≤ q ⊔ p"
          using 2 by (smt (verit, ccfv_SIG) star.circ_loop_fixpoint star_left_induct sup.bounded_iff sup_left_divisibility)
      qed
      show "-(q ⊔ p) ⊓ rT * p = -(q ⊔ p) ⊓ rT * (q ⊔ p)"
      proof (rule order.antisym)
        show "-(q ⊔ p) ⊓ rT * p ≤ -(q ⊔ p) ⊓ rT * (q ⊔ p)"
          using inf.sup_right_isotone mult_left_sub_dist_sup_right by blast
        have 15: "- (q ⊔ p) ⊓ rT * (q ⊔ p) = - (q ⊔ p) ⊓ rT * q ⊔ - (q ⊔ p) ⊓ rT * p"
          by (simp add: comp_inf.semiring.distrib_left mult_left_dist_sup)
        have "- (q ⊔ p) ⊓ rT * q ≤ - (q ⊔ p) ⊓ rT * p"
          using 2 by (metis bot_least p_dist_inf p_dist_sup p_inf_sup_below pseudo_complement)
        thus "- (q ⊔ p) ⊓ rT * (q ⊔ p) ≤ - (q ⊔ p) ⊓ rT * p"
          using 15 sup.absorb2 by force
      qed
    qed
    subgoal proof clarsimp
      have "card { x . regular x ∧ x ≤ -q ∧ x ≤ -p ∧ x ≤ --(rT⋆ * s) } < card { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) }"
      proof (rule psubset_card_mono)
        show "finite { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) }"
          using finite_regular by simp
        show "{ x . regular x ∧ x ≤ -q ∧ x ≤ -p ∧ x ≤ --(rT⋆ * s) } ⊂ { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) }"
        proof -
          have "∀x . x ≤ -q ∧ x ≤ --(rT⋆ * s) ⟶ x ≤ --(-q ⊓ rT⋆ * s)"
            by auto
          hence 16: "{ x . regular x ∧ x ≤ -q ∧ x ≤ -p ∧ x ≤ --(rT⋆ * s) } ⊆ { x . regular x ∧ x ≤ --(-q ⊓ rT⋆ * s) }"
            by blast
          have 17: "regular p"
            using 2 regular_conv_closed regular_mult_closed by force
          hence 18: "¬ p ≤ -p"
            using 2 by (metis inf.absorb1 pp_inf_p)
          have 19: "p ≤ -q"
            using 2 by simp
          have "rT * q ≤ rT⋆ * s"
            using 2 by (metis (no_types, lifting) comp_associative conv_dist_sup mult_left_isotone star.circ_increasing star.circ_mult_upper_bound star.circ_sub_dist sup_left_divisibility)
          hence 20: "p ≤ --(rT⋆ * s)"
            using 2 le_infI2 order_lesseq_imp pp_increasing by blast
          hence 21: "p ≤ --(-q ⊓ rT⋆ * s)"
            using 2 by simp
          show ?thesis
            using 16 17 18 19 20 21 by blast
        qed
      qed
      thus "card { x . regular x ∧ x ≤ -q ∧ x ≤ -p ∧ x ≤ --(rT⋆ * s) } < n"
        using 2 by auto
    qed
    done
qed

text ‹Theorem 18›

lemma bfs_reachability_exists:
  "regular r ∧ regular s ∧ vector s ⟹ ∃t . asymmetric t ∧ t ≤ r ∧ tT⋆ * s = rT⋆ * s"
  using tc_extract_function bfs_reachability by blast

text ‹Theorem 17›

lemma orientable_path:
  "arc x ⟹ x ≤ --y⋆ ⟹ ∃z . z ≤ y ∧ asymmetric z ∧ x ≤ --z⋆"
proof -
  assume 1: "arc x" and 2: "x ≤ --y⋆"
  hence "regular (--y) ∧ regular (x * top) ∧ vector (x * top)"
    using bijective_regular mult_assoc by auto
  from this obtain t where 3: "asymmetric t ∧ t ≤ --y ∧ tT⋆ * (x * top) = (--y)T⋆ * (x * top)"
    using bfs_reachability_exists by blast
  let ?z = "t ⊓ y"
  have "xT * top * xT ≤ (--y)T⋆"
    using 1 2 by (metis arc_top_arc conv_complement conv_isotone conv_star_commute arc_conv_closed pp_dist_star)
  hence "xT ≤ (--y)T⋆ * x * top"
    using 1 comp_associative conv_dist_comp shunt_bijective by force
  also have "... = tT⋆ * x * top"
    using 3 mult_assoc by force
  finally have "xT * top * xT ≤ tT⋆"
    using 1 comp_associative conv_dist_comp shunt_bijective by force
  hence "xT ≤ tT⋆"
    using 1 by (metis arc_top_arc arc_conv_closed)
  also have "... ≤ (--?z)T⋆"
    using 3 by (metis conv_order inf.orderE inf_pp_semi_commute star_isotone)
  finally have "x ≤ --?z⋆"
    using conv_order conv_star_commute pp_dist_star by fastforce
  thus "∃z . z ≤ y ∧ asymmetric z ∧ x ≤ --z⋆"
    using 3 asymmetric_inf_closed inf.cobounded2 by blast
qed

subsection ‹Extending partial orders to linear orders›

text ‹
We prove total correctness of Szpilrajn's algorithm cite‹"Szpilrajn1930"›.
A partial-correctness proof using Prover9 is given in cite‹"BerghammerStruth2010"›.
›

theorem szpilrajn:
  "VARS e t
  [ order p ∧ regular p ]
  t := p;
  WHILE t ⊔ tT ≠ top
    INV { order t ∧ regular t ∧ p ≤ t }
    VAR { card { x . regular x ∧ x ≤ -(t ⊔ tT) } }
     DO e := choose_arc (-(t ⊔ tT));
        t := t ⊔ t * e * t
     OD
  [ linear_order t ∧ p ≤ t ]"
proof vcg_tc_simp
  fix t
  let ?e = "choose_arc (-t ⊓ -tT)"
  let ?tet = "t * ?e * t"
  let ?t = "t ⊔ ?tet"
  let ?s1 = "{ x . regular x ∧ x ≤ -t ∧ x ≤ -?tet ∧ x ≤ -?tT }"
  let ?s2 = "{ x . regular x ∧ x ≤ -t ∧ x ≤ -tT }"
  assume 1: "reflexive t ∧ transitive t ∧ antisymmetric t ∧ regular t ∧ p ≤ t ∧ ¬ linear t"
  show "reflexive ?t ∧
        transitive ?t ∧
        antisymmetric ?t ∧
        ?t = t ⊔ --?tet ∧
        p ≤ ?t ∧
        card ?s1 < card ?s2"
  proof (intro conjI)
    show "reflexive ?t"
      using 1 by (simp add: sup.coboundedI1)
    have "-t ⊓ -tT ≠ bot"
      using 1 regular_closed_top regular_conv_closed by force
    hence 2: "arc ?e"
      using choose_arc_arc by blast
    have "?t * ?t = t * t ⊔ t * ?tet ⊔ ?tet * t ⊔ ?tet * ?tet"
      by (smt (z3) mult_left_dist_sup mult_right_dist_sup sup_assoc)
    also have "... ≤ ?t"
    proof (intro sup_least)
      show "t * t ≤ ?t"
        using 1 sup.coboundedI1 by blast
      show "t * ?tet ≤ ?t"
        using 1 by (metis le_supI2 mult_left_isotone mult_assoc)
      show "?tet * t ≤ ?t"
        using 1 mult_right_isotone sup.coboundedI2 mult_assoc by auto
      have "?e * t * t * ?e ≤ ?e"
        using 2 by (smt (z3) arc_top_arc mult_assoc mult_right_isotone mult_left_isotone top_greatest)
      hence "transitive ?tet"
        by (smt (z3) mult_assoc mult_right_isotone mult_left_isotone)
      thus "?tet * ?tet ≤ ?t"
        using le_supI2 by auto
    qed
    finally show "transitive ?t"
      .
    have 3: "?e ≤ -tT"
      by (metis choose_arc_below inf.cobounded2 order_lesseq_imp p_dist_sup regular_closed_p)
    have 4: "?e ≤ -t"
      by (metis choose_arc_below inf.cobounded1 order_trans regular_closed_inf regular_closed_p)
    have "?t ⊓ ?tT = (t ⊓ tT) ⊔ (t ⊓ ?tetT) ⊔ (?tet ⊓ tT) ⊔ (?tet ⊓ ?tetT)"
      by (smt (z3) conv_dist_sup inf_sup_distrib1 inf_sup_distrib2 sup_monoid.add_assoc)
    also have "... ≤ 1"
    proof (intro sup_least)
      show "antisymmetric t"
        using 1 by simp
      have "t * t * t = t"
        using 1 preorder_idempotent by fastforce
      also have "... ≤ -?eT"
        using 3 by (metis p_antitone_iff conv_complement conv_order conv_involutive)
      finally have "tT * ?eT * tT ≤ -t"
        using triple_schroeder_p by blast
      hence "t ⊓ ?tetT = bot"
        by (simp add: comp_associative conv_dist_comp p_antitone pseudo_complement_pp)
      thus "t ⊓ ?tetT ≤ 1"
        by simp
      thus "?tet ⊓ tT ≤ 1"
        by (smt (z3) conv_isotone inf_commute conv_one conv_dist_inf conv_involutive)
      have "?e * t * ?e ≤ ?e"
        using 2 by (smt (z3) arc_top_arc mult_assoc mult_right_isotone mult_left_isotone top_greatest)
      also have "... ≤ -tT"
        using 3 by simp
      finally have "?tet ≤ -?eT"
        by (metis conv_dist_comp schroeder_3_p triple_schroeder_p)
      hence "t * t * ?e * t * t ≤ -?eT"
        using 1 by (metis preorder_idempotent mult_assoc)
      hence "tT * ?eT * tT ≤ -?tet"
        using triple_schroeder_p mult_assoc by auto
      hence "?tet ⊓ ?tetT = bot"
        by (simp add: conv_dist_comp p_antitone pseudo_complement_pp mult_assoc)
      thus "antisymmetric ?tet"
        by simp
    qed
    finally show "antisymmetric ?t"
      .
    show "?t = t ⊔ --?tet"
      using 1 choose_arc_regular regular_mult_closed by auto
    show "p ≤ ?t"
      using 1 by (simp add: le_supI1)
    show "card ?s1 < card ?s2"
    proof (rule psubset_card_mono)
      show "finite { x . regular x ∧ x ≤ -t ∧ x ≤ -tT }"
        using finite_regular by simp
      show "{ x . regular x ∧ x ≤ -t ∧ x ≤ -?tet ∧ x ≤ -?tT } ⊂ { x . regular x ∧ x ≤ -t ∧ x ≤ -tT }"
      proof -
        have "∀x . regular x ∧ x ≤ -t ∧ x ≤ -?tet ∧ x ≤ -?tT ⟶ regular x ∧ x ≤ -t ∧ x ≤ -tT"
          using conv_dist_sup by auto
        hence 5: "{ x . regular x ∧ x ≤ -t ∧ x ≤ -?tet ∧ x ≤ -?tT } ⊆ { x . regular x ∧ x ≤ -t ∧ x ≤ -tT }"
          by blast
        have 6: "regular ?e ∧ ?e ≤ -t ∧ ?e ≤ -tT"
          using 2 3 4 choose_arc_regular by blast
        have "¬ ?e ≤ -?tet"
        proof
          assume 7: "?e ≤ -?tet"
          have "?e ≤ ?e * t"
            using 1 by (meson mult_right_isotone mult_sub_right_one order.trans) 
          also have "?e * t ≤ -(tT * ?e)"
            using 7 p_antitone_iff schroeder_3_p mult_assoc by auto
          also have "... ≤ -(1T * ?e)"
            using 1 conv_isotone mult_left_isotone p_antitone by blast
          also have "... = -?e"
            by simp
          finally show False
            using 1 2 by (smt (z3) bot_least eq_refl inf.absorb1 pseudo_complement semiring.mult_not_zero top_le)
        qed
        thus ?thesis
          using 5 6 by blast
      qed
    qed
  qed
qed

text ‹Theorem 15›

lemma szpilrajn_exists:
  "order p ∧ regular p ⟹ ∃t . linear_order t ∧ p ≤ t"
  using tc_extract_function szpilrajn by blast

lemma complement_one_transitively_orientable:
  "transitively_orientable (-1)"
proof -
  have "∃t . linear_order t"
    using szpilrajn_exists bijective_one_closed bijective_regular order_one_closed by blast
  thus ?thesis
    using exists_split_characterisations(4) by blast
qed

end

end

end