(*  Title:      HOL/Library/Finite_Lattice.thy
    Author:     Alessandro Coglio
*)

theory Finite_Lattice
imports Product_Order
begin

text \<open>A non-empty finite lattice is a complete lattice.
Since types are never empty in Isabelle/HOL,
a type of classes @{class finite} and @{class lattice}
should also have class @{class complete_lattice}.
A type class is defined
that extends classes @{class finite} and @{class lattice}
with the operators @{const bot}, @{const top}, @{const Inf}, and @{const Sup},
along with assumptions that define these operators
in terms of the ones of classes @{class finite} and @{class lattice}.
The resulting class is a subclass of @{class complete_lattice}.\<close>

class finite_lattice_complete = finite + lattice + bot + top + Inf + Sup +
  assumes bot_def: "bot = Inf_fin UNIV"
  assumes top_def: "top = Sup_fin UNIV"
  assumes Inf_def: "Inf A = Finite_Set.fold inf top A"
  assumes Sup_def: "Sup A = Finite_Set.fold sup bot A"

text \<open>The definitional assumptions
on the operators @{const bot} and @{const top}
of class @{class finite_lattice_complete}
ensure that they yield bottom and top.\<close>

lemma finite_lattice_complete_bot_least: "(bot::'a::finite_lattice_complete) \<le> x"
  by (auto simp: bot_def intro: Inf_fin.coboundedI)

instance finite_lattice_complete \<subseteq> order_bot
  by standard (auto simp: finite_lattice_complete_bot_least)

lemma finite_lattice_complete_top_greatest: "(top::'a::finite_lattice_complete) \<ge> x"
  by (auto simp: top_def Sup_fin.coboundedI)

instance finite_lattice_complete \<subseteq> order_top
  by standard (auto simp: finite_lattice_complete_top_greatest)

instance finite_lattice_complete \<subseteq> bounded_lattice ..

text \<open>The definitional assumptions
on the operators @{const Inf} and @{const Sup}
of class @{class finite_lattice_complete}
ensure that they yield infimum and supremum.\<close>

lemma finite_lattice_complete_Inf_empty: "Inf {} = (top :: 'a::finite_lattice_complete)"
  by (simp add: Inf_def)

lemma finite_lattice_complete_Sup_empty: "Sup {} = (bot :: 'a::finite_lattice_complete)"
  by (simp add: Sup_def)

lemma finite_lattice_complete_Inf_insert:
  fixes A :: "'a::finite_lattice_complete set"
  shows "Inf (insert x A) = inf x (Inf A)"
proof -
  interpret comp_fun_idem "inf :: 'a \<Rightarrow> _"
    by (fact comp_fun_idem_inf)
  show ?thesis by (simp add: Inf_def)
qed

lemma finite_lattice_complete_Sup_insert:
  fixes A :: "'a::finite_lattice_complete set"
  shows "Sup (insert x A) = sup x (Sup A)"
proof -
  interpret comp_fun_idem "sup :: 'a \<Rightarrow> _"
    by (fact comp_fun_idem_sup)
  show ?thesis by (simp add: Sup_def)
qed

lemma finite_lattice_complete_Inf_lower:
  "(x::'a::finite_lattice_complete) \<in> A \<Longrightarrow> Inf A \<le> x"
  using finite [of A]
  by (induct A) (auto simp add: finite_lattice_complete_Inf_insert intro: le_infI2)

lemma finite_lattice_complete_Inf_greatest:
  "\<forall>x::'a::finite_lattice_complete \<in> A. z \<le> x \<Longrightarrow> z \<le> Inf A"
  using finite [of A]
  by (induct A) (auto simp add: finite_lattice_complete_Inf_empty finite_lattice_complete_Inf_insert)

lemma finite_lattice_complete_Sup_upper:
  "(x::'a::finite_lattice_complete) \<in> A \<Longrightarrow> Sup A \<ge> x"
  using finite [of A]
  by (induct A) (auto simp add: finite_lattice_complete_Sup_insert intro: le_supI2)

lemma finite_lattice_complete_Sup_least:
  "\<forall>x::'a::finite_lattice_complete \<in> A. z \<ge> x \<Longrightarrow> z \<ge> Sup A"
  using finite [of A]
  by (induct A) (auto simp add: finite_lattice_complete_Sup_empty finite_lattice_complete_Sup_insert)

instance finite_lattice_complete \<subseteq> complete_lattice
proof
qed (auto simp:
  finite_lattice_complete_Inf_lower
  finite_lattice_complete_Inf_greatest
  finite_lattice_complete_Sup_upper
  finite_lattice_complete_Sup_least
  finite_lattice_complete_Inf_empty
  finite_lattice_complete_Sup_empty)

text \<open>The product of two finite lattices is already a finite lattice.\<close>

lemma finite_bot_prod:
  "(bot :: ('a::finite_lattice_complete \<times> 'b::finite_lattice_complete)) =
    Inf_fin UNIV"
  by (metis Inf_fin.coboundedI UNIV_I bot.extremum_uniqueI finite_UNIV)

lemma finite_top_prod:
  "(top :: ('a::finite_lattice_complete \<times> 'b::finite_lattice_complete)) =
    Sup_fin UNIV"
  by (metis Sup_fin.coboundedI UNIV_I top.extremum_uniqueI finite_UNIV)

lemma finite_Inf_prod:
  "Inf(A :: ('a::finite_lattice_complete \<times> 'b::finite_lattice_complete) set) =
    Finite_Set.fold inf top A"
  by (metis Inf_fold_inf finite)

lemma finite_Sup_prod:
  "Sup (A :: ('a::finite_lattice_complete \<times> 'b::finite_lattice_complete) set) =
    Finite_Set.fold sup bot A"
  by (metis Sup_fold_sup finite)

instance prod :: (finite_lattice_complete, finite_lattice_complete) finite_lattice_complete
  by standard (auto simp: finite_bot_prod finite_top_prod finite_Inf_prod finite_Sup_prod)

text \<open>Functions with a finite domain and with a finite lattice as codomain
already form a finite lattice.\<close>

lemma finite_bot_fun: "(bot :: ('a::finite \<Rightarrow> 'b::finite_lattice_complete)) = Inf_fin UNIV"
  by (metis Inf_UNIV Inf_fin_Inf empty_not_UNIV finite)

lemma finite_top_fun: "(top :: ('a::finite \<Rightarrow> 'b::finite_lattice_complete)) = Sup_fin UNIV"
  by (metis Sup_UNIV Sup_fin_Sup empty_not_UNIV finite)

lemma finite_Inf_fun:
  "Inf (A::('a::finite \<Rightarrow> 'b::finite_lattice_complete) set) =
    Finite_Set.fold inf top A"
  by (metis Inf_fold_inf finite)

lemma finite_Sup_fun:
  "Sup (A::('a::finite \<Rightarrow> 'b::finite_lattice_complete) set) =
    Finite_Set.fold sup bot A"
  by (metis Sup_fold_sup finite)

instance "fun" :: (finite, finite_lattice_complete) finite_lattice_complete
  by standard (auto simp: finite_bot_fun finite_top_fun finite_Inf_fun finite_Sup_fun)

subsection \<open>Finite Distributive Lattices\<close>

text \<open>A finite distributive lattice is a complete lattice
whose @{const inf} and @{const sup} operators
distribute over @{const Sup} and @{const Inf}.\<close>

class finite_distrib_lattice_complete =
  distrib_lattice + finite_lattice_complete

lemma finite_distrib_lattice_complete_sup_Inf:
  "sup (x::'a::finite_distrib_lattice_complete) (Inf A) = (INF y:A. sup x y)"
  using finite
  by (induct A rule: finite_induct) (simp_all add: sup_inf_distrib1)

lemma finite_distrib_lattice_complete_inf_Sup:
  "inf (x::'a::finite_distrib_lattice_complete) (Sup A) = (SUP y:A. inf x y)"
  using finite [of A] by induct (simp_all add: inf_sup_distrib1)

instance finite_distrib_lattice_complete \<subseteq> complete_distrib_lattice
proof
qed (auto simp:
  finite_distrib_lattice_complete_sup_Inf
  finite_distrib_lattice_complete_inf_Sup)

text \<open>The product of two finite distributive lattices
is already a finite distributive lattice.\<close>

instance prod ::
  (finite_distrib_lattice_complete, finite_distrib_lattice_complete)
  finite_distrib_lattice_complete
  ..

text \<open>Functions with a finite domain
and with a finite distributive lattice as codomain
already form a finite distributive lattice.\<close>

instance "fun" ::
  (finite, finite_distrib_lattice_complete) finite_distrib_lattice_complete
  ..

subsection \<open>Linear Orders\<close>

text \<open>A linear order is a distributive lattice.
A type class is defined
that extends class @{class linorder}
with the operators @{const inf} and @{const sup},
along with assumptions that define these operators
in terms of the ones of class @{class linorder}.
The resulting class is a subclass of @{class distrib_lattice}.\<close>

class linorder_lattice = linorder + inf + sup +
  assumes inf_def: "inf x y = (if x \<le> y then x else y)"
  assumes sup_def: "sup x y = (if x \<ge> y then x else y)"

text \<open>The definitional assumptions
on the operators @{const inf} and @{const sup}
of class @{class linorder_lattice}
ensure that they yield infimum and supremum
and that they distribute over each other.\<close>

lemma linorder_lattice_inf_le1: "inf (x::'a::linorder_lattice) y \<le> x"
  unfolding inf_def by (metis (full_types) linorder_linear)

lemma linorder_lattice_inf_le2: "inf (x::'a::linorder_lattice) y \<le> y"
  unfolding inf_def by (metis (full_types) linorder_linear)

lemma linorder_lattice_inf_greatest:
  "(x::'a::linorder_lattice) \<le> y \<Longrightarrow> x \<le> z \<Longrightarrow> x \<le> inf y z"
  unfolding inf_def by (metis (full_types))

lemma linorder_lattice_sup_ge1: "sup (x::'a::linorder_lattice) y \<ge> x"
  unfolding sup_def by (metis (full_types) linorder_linear)

lemma linorder_lattice_sup_ge2: "sup (x::'a::linorder_lattice) y \<ge> y"
  unfolding sup_def by (metis (full_types) linorder_linear)

lemma linorder_lattice_sup_least:
  "(x::'a::linorder_lattice) \<ge> y \<Longrightarrow> x \<ge> z \<Longrightarrow> x \<ge> sup y z"
  by (auto simp: sup_def)

lemma linorder_lattice_sup_inf_distrib1:
  "sup (x::'a::linorder_lattice) (inf y z) = inf (sup x y) (sup x z)"
  by (auto simp: inf_def sup_def)

instance linorder_lattice \<subseteq> distrib_lattice
proof
qed (auto simp:
  linorder_lattice_inf_le1
  linorder_lattice_inf_le2
  linorder_lattice_inf_greatest
  linorder_lattice_sup_ge1
  linorder_lattice_sup_ge2
  linorder_lattice_sup_least
  linorder_lattice_sup_inf_distrib1)

subsection \<open>Finite Linear Orders\<close>

text \<open>A (non-empty) finite linear order is a complete linear order.\<close>

class finite_linorder_complete = linorder_lattice + finite_lattice_complete

instance finite_linorder_complete \<subseteq> complete_linorder ..

text \<open>A (non-empty) finite linear order is a complete lattice
whose @{const inf} and @{const sup} operators
distribute over @{const Sup} and @{const Inf}.\<close>

instance finite_linorder_complete \<subseteq> finite_distrib_lattice_complete ..

end


(*<*)theory Axioms imports Overloading "../Setup" begin(*>*)

subsection \<open>Axioms\<close>

text \<open>Attaching axioms to our classes lets us reason on the level of
classes.  The results will be applicable to all types in a class, just
as in axiomatic mathematics.

\begin{warn}
Proofs in this section use structured \emph{Isar} proofs, which are not
covered in this tutorial; but see @{cite "Nipkow-TYPES02"}.%
\end{warn}\<close>

subsubsection \<open>Semigroups\<close>

text\<open>We specify \emph{semigroups} as subclass of \<^class>\<open>plus\<close>:\<close>

class semigroup = plus +
  assumes assoc: "(x \<oplus> y) \<oplus> z = x \<oplus> (y \<oplus> z)"

text \<open>\noindent This @{command class} specification requires that
all instances of \<^class>\<open>semigroup\<close> obey @{fact "assoc:"}~@{prop
[source] "\<And>x y z :: 'a::semigroup. (x \<oplus> y) \<oplus> z = x \<oplus> (y \<oplus> z)"}.

We can use this class axiom to derive further abstract theorems
relative to class \<^class>\<open>semigroup\<close>:\<close>

lemma assoc_left:
  fixes x y z :: "'a::semigroup"
  shows "x \<oplus> (y \<oplus> z) = (x \<oplus> y) \<oplus> z"
  using assoc by (rule sym)

text \<open>\noindent The \<^class>\<open>semigroup\<close> constraint on type \<^typ>\<open>'a\<close> restricts instantiations of \<^typ>\<open>'a\<close> to types of class
\<^class>\<open>semigroup\<close> and during the proof enables us to use the fact
@{fact assoc} whose type parameter is itself constrained to class
\<^class>\<open>semigroup\<close>.  The main advantage of classes is that theorems
can be proved in the abstract and freely reused for each instance.

On instantiation, we have to give a proof that the given operations
obey the class axioms:\<close>

instantiation nat :: semigroup
begin

instance proof

txt \<open>\noindent The proof opens with a default proof step, which for
instance judgements invokes method @{method intro_classes}.\<close>

  fix m n q :: nat
  show "(m \<oplus> n) \<oplus> q = m \<oplus> (n \<oplus> q)"
    by (induct m) simp_all
qed

end

text \<open>\noindent Again, the interesting things enter the stage with
parametric types:\<close>

instantiation prod :: (semigroup, semigroup) semigroup
begin

instance proof
  fix p\<^sub>1 p\<^sub>2 p\<^sub>3 :: "'a::semigroup \<times> 'b::semigroup"
  show "p\<^sub>1 \<oplus> p\<^sub>2 \<oplus> p\<^sub>3 = p\<^sub>1 \<oplus> (p\<^sub>2 \<oplus> p\<^sub>3)"
    by (cases p\<^sub>1, cases p\<^sub>2, cases p\<^sub>3) (simp add: assoc)

txt \<open>\noindent Associativity of product semigroups is established
using the hypothetical associativity @{fact assoc} of the type
components, which holds due to the \<^class>\<open>semigroup\<close> constraints
imposed on the type components by the @{command instance} proposition.
Indeed, this pattern often occurs with parametric types and type
classes.\<close>

qed

end

subsubsection \<open>Monoids\<close>

text \<open>We define a subclass \<open>monoidl\<close> (a semigroup with a
left-hand neutral) by extending \<^class>\<open>semigroup\<close> with one additional
parameter \<open>neutral\<close> together with its property:\<close>

class monoidl = semigroup +
  fixes neutral :: "'a" ("\<zero>")
  assumes neutl: "\<zero> \<oplus> x = x"

text \<open>\noindent Again, we prove some instances, by providing
suitable parameter definitions and proofs for the additional
specifications.\<close>

instantiation nat :: monoidl
begin

definition
  neutral_nat_def: "\<zero> = (0::nat)"

instance proof
  fix n :: nat
  show "\<zero> \<oplus> n = n"
    unfolding neutral_nat_def by simp
qed

end

text \<open>\noindent In contrast to the examples above, we here have both
specification of class operations and a non-trivial instance proof.

This covers products as well:
\<close>

instantiation prod :: (monoidl, monoidl) monoidl
begin

definition
  neutral_prod_def: "\<zero> = (\<zero>, \<zero>)"

instance proof
  fix p :: "'a::monoidl \<times> 'b::monoidl"
  show "\<zero> \<oplus> p = p"
    by (cases p) (simp add: neutral_prod_def neutl)
qed

end

text \<open>\noindent Fully-fledged monoids are modelled by another
subclass which does not add new parameters but tightens the
specification:\<close>

class monoid = monoidl +
  assumes neutr: "x \<oplus> \<zero> = x"

text \<open>\noindent Corresponding instances for \<^typ>\<open>nat\<close> and products
are left as an exercise to the reader.\<close>

subsubsection \<open>Groups\<close>

text \<open>\noindent To finish our small algebra example, we add a \<open>group\<close> class:\<close>

class group = monoidl +
  fixes inv :: "'a \<Rightarrow> 'a" ("\<div> _" [81] 80)
  assumes invl: "\<div> x \<oplus> x = \<zero>"

text \<open>\noindent We continue with a further example for abstract
proofs relative to type classes:\<close>

lemma left_cancel:
  fixes x y z :: "'a::group"
  shows "x \<oplus> y = x \<oplus> z \<longleftrightarrow> y = z"
proof
  assume "x \<oplus> y = x \<oplus> z"
  then have "\<div> x \<oplus> (x \<oplus> y) = \<div> x \<oplus> (x \<oplus> z)" by simp
  then have "(\<div> x \<oplus> x) \<oplus> y = (\<div> x \<oplus> x) \<oplus> z" by (simp add: assoc)
  then show "y = z" by (simp add: invl neutl)
next
  assume "y = z"
  then show "x \<oplus> y = x \<oplus> z" by simp
qed

text \<open>\noindent Any \<open>group\<close> is also a \<open>monoid\<close>; this
can be made explicit by claiming an additional subclass relation,
together with a proof of the logical difference:\<close>

instance group \<subseteq> monoid
proof
  fix x
  from invl have "\<div> x \<oplus> x = \<zero>" .
  then have "\<div> x \<oplus> (x \<oplus> \<zero>) = \<div> x \<oplus> x"
    by (simp add: neutl invl assoc [symmetric])
  then show "x \<oplus> \<zero> = x" by (simp add: left_cancel)
qed

text \<open>\noindent The proof result is propagated to the type system,
making \<open>group\<close> an instance of \<open>monoid\<close> by adding an
additional edge to the graph of subclass relation; see also
Figure~\ref{fig:subclass}.

\begin{figure}[htbp]
 \begin{center}
   \small
   \unitlength 0.6mm
   \begin{picture}(40,60)(0,0)
     \put(20,60){\makebox(0,0){\<open>semigroup\<close>}}
     \put(20,40){\makebox(0,0){\<open>monoidl\<close>}}
     \put(00,20){\makebox(0,0){\<open>monoid\<close>}}
     \put(40,00){\makebox(0,0){\<open>group\<close>}}
     \put(20,55){\vector(0,-1){10}}
     \put(15,35){\vector(-1,-1){10}}
     \put(25,35){\vector(1,-3){10}}
   \end{picture}
   \hspace{8em}
   \begin{picture}(40,60)(0,0)
     \put(20,60){\makebox(0,0){\<open>semigroup\<close>}}
     \put(20,40){\makebox(0,0){\<open>monoidl\<close>}}
     \put(00,20){\makebox(0,0){\<open>monoid\<close>}}
     \put(40,00){\makebox(0,0){\<open>group\<close>}}
     \put(20,55){\vector(0,-1){10}}
     \put(15,35){\vector(-1,-1){10}}
     \put(05,15){\vector(3,-1){30}}
   \end{picture}
   \caption{Subclass relationship of monoids and groups:
      before and after establishing the relationship
      \<open>group \<subseteq> monoid\<close>;  transitive edges are left out.}
   \label{fig:subclass}
 \end{center}
\end{figure}
\<close>

subsubsection \<open>Inconsistencies\<close>

text \<open>The reader may be wondering what happens if we attach an
inconsistent set of axioms to a class. So far we have always avoided
to add new axioms to HOL for fear of inconsistencies and suddenly it
seems that we are throwing all caution to the wind. So why is there no
problem?

The point is that by construction, all type variables in the axioms of
a \isacommand{class} are automatically constrained with the class
being defined (as shown for axiom @{thm[source]refl} above). These
constraints are always carried around and Isabelle takes care that
they are never lost, unless the type variable is instantiated with a
type that has been shown to belong to that class. Thus you may be able
to prove \<^prop>\<open>False\<close> from your axioms, but Isabelle will remind you
that this theorem has the hidden hypothesis that the class is
non-empty.

Even if each individual class is consistent, intersections of
(unrelated) classes readily become inconsistent in practice. Now we
know this need not worry us.\<close>

subsubsection\<open>Syntactic Classes and Predefined Overloading\<close>

text \<open>In our algebra example, we have started with a \emph{syntactic
class} \<^class>\<open>plus\<close> which only specifies operations but no axioms; it
would have been also possible to start immediately with class \<^class>\<open>semigroup\<close>, specifying the \<open>\<oplus>\<close> operation and associativity at
the same time.

Which approach is more appropriate depends.  Usually it is more
convenient to introduce operations and axioms in the same class: then
the type checker will automatically insert the corresponding class
constraints whenever the operations occur, reducing the need of manual
annotations.  However, when operations are decorated with popular
syntax, syntactic classes can be an option to re-use the syntax in
different contexts; this is indeed the way most overloaded constants
in HOL are introduced, of which the most important are listed in
Table~\ref{tab:overloading} in the appendix.  Section
\ref{sec:numeric-classes} covers a range of corresponding classes
\emph{with} axioms.

Further note that classes may contain axioms but \emph{no} operations.
An example is class \<^class>\<open>finite\<close> from theory \<^theory>\<open>HOL.Finite_Set\<close>
which specifies a type to be finite: @{lemma [source] "finite (UNIV :: 'a::finite
set)" by (fact finite_UNIV)}.\<close>

(*<*)end(*>*)


theory Weighted_Graph
  imports
    "HOL-Library.Extended_Real"
    Walk
begin

type_synonym 'a cost_fun = "'a set \<Rightarrow> real"

definition walk_edges_cost :: "'a cost_fun \<Rightarrow> 'a set list \<Rightarrow> real" where
  "walk_edges_cost f l = sum_list (map f (l))"

definition walk_cost :: "'a cost_fun \<Rightarrow> 'a list \<Rightarrow> real" where
  "walk_cost f p = walk_edges_cost f (walk_edges p)"

section \<open>Walks\<close>

subsection \<open>Basic Lemmas\<close>

lemma walk_edges_cost_Nil [simp]:
  shows "walk_edges_cost f [] = 0"
  by (simp add: walk_edges_cost_def)

lemma walk_cost_Nil [simp]:
  shows "walk_cost f [] = 0"
  by (simp add: walk_cost_def)

lemma walk_edges_cost_Cons [simp]:
  shows "walk_edges_cost f (x # xs) = f x + walk_edges_cost f xs"
  by (simp add: walk_edges_cost_def)

lemma walk_cost_non_negative_if_cost_non_negative:
  assumes "\<And>e. e \<in> edges G \<Longrightarrow> 0 \<le> f e"
  assumes "walk G u p w"
  shows "0 \<le> walk_cost f p"
  using assms
  by (induction p arbitrary: u rule: walk_induct) (auto simp add: walk_def walk_cost_def)

subsection \<open>Appending walks\<close>

lemma walk_edges_cost_append [simp]:
  shows "walk_edges_cost f (xs @ ys) = walk_edges_cost f xs + walk_edges_cost f ys"
  by (simp add: walk_edges_cost_def)

lemma walk_cost_append:
  assumes "p \<noteq> []"
  shows "walk_cost f (p @ q) = walk_cost f p + walk_cost f ([last p] @ q)"
  using assms
  by (simp add: walk_cost_def walk_edges_append)

lemma walk_cost_append_2:
  assumes "p \<noteq> []"
  assumes "q \<noteq> []"
  assumes "last p = hd q"
  shows "walk_cost f (p @ tl q) = walk_cost f p + walk_cost f q"
  using assms
  by (simp add: walk_cost_append)

lemma walk_cost_append_append:
  assumes "p \<noteq> []"
  assumes "Suc 0 < length q"
  assumes "r \<noteq> []"
  assumes "last p = hd q"
  assumes "last q = hd r"
  shows "walk_cost f (p @ tl q @ tl r) = walk_cost f p + walk_cost f q + walk_cost f r"
proof -
  have "walk_cost f (p @ tl q @ tl r) = walk_cost f (p @ tl q) + walk_cost f r"
    using assms(2, 3, 5) walk_cost_append[where ?p = "p @ tl q"]
    by (simp add: tl_non_empty_conv last_tl)
  also have "... = walk_cost f p + walk_cost f q + walk_cost f r"
    using assms(1, 2, 4)
    by (auto intro: walk_cost_append_2)
  finally show ?thesis
    .
qed

subsection \<open>Reversing walks\<close>

lemma walk_edges_cost_rev [simp]:
  shows "walk_edges_cost f (rev l) = walk_edges_cost f l"
  by (simp add: walk_edges_cost_def rev_map[symmetric])

lemma walk_cost_rev [simp]:
  shows "walk_cost f (rev p) = walk_cost f p"
  by (simp add: walk_cost_def walk_edges_rev[symmetric])

subsection \<open>Decomposing walks\<close>

lemma (in graph) walk_cost_closed_walk_decomp:
  assumes "walk G u p v"
  assumes "\<not> distinct p"
  assumes "walk_closed_walk_decomp G p = (q, r, s)"
  shows "walk_cost f p = walk_cost f q + walk_cost f r + walk_cost f s"
proof -
  obtain
    "p = q @ tl r @ tl s"
    "\<exists>w. walk G u q w \<and> closed_walk G w r \<and> walk G w s v"
    using assms
    by (elim walk_closed_walk_decompE_2)
  thus ?thesis
    by (auto simp add: walk_def intro: walk_cost_append_append)
qed

section \<open>Paths\<close>

lemma (in graph) walk_cost_ge_walk_to_path_cost:
  assumes f_non_negative: "\<And>e. e \<in> edges G \<Longrightarrow> 0 \<le> f e"
  assumes "walk G u p v"
  shows "walk_cost f (walk_to_path p) \<le> walk_cost f p"
  using assms(2)
proof (induction rule: walk_to_path_induct)
  case path
  thus ?case by simp
next
  case (decomp p q r s)
  have walks: "\<exists>w. walk G u q w \<and> closed_walk G w r \<and> walk G w s v"
    using decomp.hyps
    by (elim walk_closed_walk_decompE_2)

  have "walk_cost f (walk_to_path p) = walk_cost f (walk_to_path (q @ tl s))"
    using decomp.hyps(1, 2)
    by (auto simp add: decomp.hyps(3))
  also have "... \<le> walk_cost f (q @ tl s)"
    using decomp.IH
    .
  also have "... = walk_cost f q + walk_cost f s"
    using walks
    by (auto simp add: walk_def intro: walk_cost_append_2)
  also have "... \<le> walk_cost f q + walk_cost f r + walk_cost f s"
    using f_non_negative walks
    by (auto intro: walk_cost_non_negative_if_cost_non_negative)
  also have "... = walk_cost f p"
    using decomp.hyps
    by (rule walk_cost_closed_walk_decomp[symmetric])
  finally show ?case
    .
qed

end