chapter \<open>Homework 2\<close>
theory Homework2
imports Main
begin

  (*
    This file is intended to be viewed within the Isabelle/jEdit IDE.
    In a standard text-editor, it is pretty unreadable!
  *)

  (*
    HOMEWORK #2
    RELEASED: Tue, Aug 19 2017
    DUE:      Tue, Aug 26, 2017, 11:59pm

    To be submitted via canvas.
  *)

section \<open>General Hints\<close>
(*
  The best way to work on this homework is to fill in the missing gaps in this file.

  Try to go for simple function definitions, as simple definitions mean simple proofs!
  In particular, do not go for a more complicated definition only b/c it may be more efficient!

  The proofs should work with induction (structural/computations),
  and some generalizations, followed by auto to solve the subgoals.
  You may have to add some simp-lemmas to auto, which we will hint at.

  We indicate were auxiliary lemmas are likely to be needed.
  However, as proofs depend on your definitions, we cannot predict every corner
  case that you may run into. Changing the definition to something simpler might
  help to get a simpler proof.

*)

section \<open>For all elements in list\<close>
  (* Define a function that checks wether a predicate P holds for all elements in a list.
     Note: A predicate is just a function of type "'a \<Rightarrow> bool". It must evaluate to True for all elements in the list.
  *)

  fun listall :: "('a \<Rightarrow> bool) \<Rightarrow> 'a list \<Rightarrow> bool" where
    "listall P [] \<longleftrightarrow> True" (* Return True for the empty list. *)
    (* Add your equation(s) here *)

  (* Show: P holds for all elements of xs@ys, if and only if it holds for all elements of xs and for all elements of ys: *)
  lemma "listall P (xs@ys) \<longleftrightarrow> listall P xs \<and> listall P ys"
    oops

  (* Show: When checking P for a  mapped list, we can also apply the function before we evaluate P *)
  lemma "listall P (map f xs) = listall (\<lambda>x. P (f x)) xs"
    oops

  (* Specify and show: If we filter the list with P, all elements in the result will satisfy P.
    Note: In this exercise, you have to come up with both, the formula and the proof!
  *)
  lemma "add your formula here and prove it" oops

section \<open>Sum of elements in a list\<close>

(* Specify a function to sum up all elements in a list of integers. The empty list has sum 0. *)

  fun listsum :: "int list \<Rightarrow> int" where
    "listsum _ = undefined"
    (* Replace by your equation(s) *)

  (* Show the following lemmas: *)

  (* Instead of summing over xs@ys, we can also add the results from summing over xs and summing over ys *)
  lemma listsum_append: "listsum (xs@ys) = listsum xs + listsum ys"
    oops

  (* Filtering out zeroes does not affect the sum *)
  lemma listsum_filter_z: "listsum (filter (op\<noteq>0) l) = listsum l"
    oops

  (* Reversing a list does not affect the sum. HINT: You'll need an auxiliary lemma. (that you already have proved) *)
  lemma listsum_rev: "listsum (rev xs) = listsum xs"
    oops

  (* Specify and prove: If summing up a list with only non-negative numbers, the result will be non-negative: *)

  lemma "add formula and prove it" oops

section \<open>Bonus: Delta-Encoding\<close>
  (*
    We want to encode a list of integers as follows:
      The first element is unchanged, and every next element
      only indicates the difference to its predecessor.

      For example: (Hint: Use this as test cases for your spec!)
        enc [1,2,4,8] = [1,1,2,4]
        enc [3,4,5] = [3,1,1]
        enc [5] = [5]
        enc [] = []

      Background: This algorithm may be used in lossless data compression,
        when the difference between two adjacent values is expected to be
        small, e.g., audio data, image data, sensor data.

        It typically requires much less space to store the small deltas, than
        the absolute values.

        Disadvantage: If the stream gets corrupted, recovery is only possible
          when the next absolute value is transmitted. For this reason, in
          practice, one will submit the current absolute value from time to
          time. (This is not modeled in this exercise!)

  *)

  (* Specify a function to encode a list with delta-encoding.
    The first argument is used to represent the previous value, and can be initialized to 0.
  *)
  fun denc :: "int \<Rightarrow> int list \<Rightarrow> int list" where
    "denc _ _ = undefined"
    (* Replace by your equation(s) *)

  (* Specify the decoder. Again, the first argument represents the previous
      decoded value, and can be initialized to 0. *)
  fun ddec :: "int \<Rightarrow> int list \<Rightarrow> int list" where
    "ddec _ _ = undefined"
    (* Replace by your equation(s) *)

  (* Show that encoding and then decoding yields the same list. HINT: The lemma will need generalization. *)
  lemma "ddec 0 (denc 0 l) = l"
    oops

end


section \<open>Formalization of Bit Vectors\<close>

theory BitVector imports Main begin

type_synonym bit_vector = "bool list"

fun bv_leqs :: "bit_vector \<Rightarrow> bit_vector \<Rightarrow> bool" ("_ \<preceq>\<^sub>b _" 99)
  where bv_Nils:"[] \<preceq>\<^sub>b [] = True"
  | bv_Cons:"(x#xs) \<preceq>\<^sub>b (y#ys) = ((x \<longrightarrow> y) \<and> xs \<preceq>\<^sub>b ys)"
  | bv_rest:"xs \<preceq>\<^sub>b ys = False"

subsection \<open>Some basic properties\<close>

lemma bv_length: "xs \<preceq>\<^sub>b ys \<Longrightarrow> length xs = length ys"
by(induct rule:bv_leqs.induct)auto

lemma [dest!]: "xs \<preceq>\<^sub>b [] \<Longrightarrow> xs = []"
by(induct xs) auto

lemma bv_leqs_AppendI:
  "\<lbrakk>xs \<preceq>\<^sub>b ys; xs' \<preceq>\<^sub>b ys'\<rbrakk> \<Longrightarrow> (xs@xs') \<preceq>\<^sub>b (ys@ys')"
by(induct xs ys rule:bv_leqs.induct,auto)

lemma bv_leqs_AppendD:
  "\<lbrakk>(xs@xs') \<preceq>\<^sub>b (ys@ys'); length xs = length ys\<rbrakk>
  \<Longrightarrow> xs \<preceq>\<^sub>b ys \<and> xs' \<preceq>\<^sub>b ys'"
by(induct xs ys rule:bv_leqs.induct,auto)

lemma bv_leqs_eq:
  "xs \<preceq>\<^sub>b ys = ((\<forall>i < length xs. xs ! i \<longrightarrow> ys ! i) \<and> length xs = length ys)"
proof(induct xs ys rule:bv_leqs.induct)
  case (2 x xs y ys)
  note eq = \<open>xs \<preceq>\<^sub>b ys =
    ((\<forall>i < length xs. xs ! i \<longrightarrow> ys ! i) \<and> length xs = length ys)\<close>
  show ?case
  proof
    assume leqs:"x#xs \<preceq>\<^sub>b y#ys"
    with eq have "x \<longrightarrow> y" and "\<forall>i < length xs. xs ! i \<longrightarrow> ys ! i"
      and "length xs = length ys" by simp_all
    from \<open>x \<longrightarrow> y\<close> have "(x#xs) ! 0 \<longrightarrow> (y#ys) ! 0" by simp
    { fix i assume "i > 0" and "i < length (x#xs)"
      then obtain j where "i = Suc j" and "j < length xs" by(cases i) auto
      with \<open>\<forall>i < length xs. xs ! i \<longrightarrow> ys ! i\<close>
      have "(x#xs) ! i \<longrightarrow> (y#ys) ! i" by auto }
    hence "\<forall>i < length (x#xs). i > 0 \<longrightarrow> (x#xs) ! i \<longrightarrow> (y#ys) ! i" by simp
    with \<open>(x#xs) ! 0 \<longrightarrow> (y#ys) ! 0\<close> \<open>length xs = length ys\<close>
    show "(\<forall>i < length (x#xs). (x#xs) ! i \<longrightarrow> (y#ys) ! i) \<and>
      length (x#xs) = length (y#ys)"
      by clarsimp(case_tac "i>0",auto)
  next
    assume "(\<forall>i < length (x#xs). (x#xs) ! i \<longrightarrow> (y#ys) ! i) \<and>
      length (x#xs) = length (y#ys)"
    hence "\<forall>i < length (x#xs). (x#xs) ! i \<longrightarrow> (y#ys) ! i"
      and "length (x#xs) = length (y#ys)" by simp_all
    from \<open>\<forall>i < length (x#xs). (x#xs) ! i \<longrightarrow> (y#ys) ! i\<close>
    have "\<forall>i < length xs. xs ! i \<longrightarrow> ys ! i"
      by clarsimp(erule_tac x="Suc i" in allE,auto)
    with eq \<open>length (x#xs) = length (y#ys)\<close> have "xs \<preceq>\<^sub>b ys" by simp
    from \<open>\<forall>i < length (x#xs). (x#xs) ! i \<longrightarrow> (y#ys) ! i\<close>
    have "x \<longrightarrow> y" by(erule_tac x="0" in allE) simp
    with \<open>xs \<preceq>\<^sub>b ys\<close> show "x#xs \<preceq>\<^sub>b y#ys" by simp
  qed
qed simp_all

subsection \<open>$\preceq_b$ is an order on bit vectors with minimal and
  maximal element\<close>

lemma minimal_element:
  "replicate (length xs) False \<preceq>\<^sub>b xs"
by(induct xs) auto

lemma maximal_element:
  "xs \<preceq>\<^sub>b replicate (length xs) True"
by(induct xs) auto

lemma bv_leqs_refl:"xs \<preceq>\<^sub>b xs"
  by(induct xs) auto

lemma bv_leqs_trans:"\<lbrakk>xs \<preceq>\<^sub>b ys; ys \<preceq>\<^sub>b zs\<rbrakk> \<Longrightarrow> xs \<preceq>\<^sub>b zs"
proof(induct xs ys arbitrary:zs rule:bv_leqs.induct)
  case (2 x xs y ys)
  note IH = \<open>\<And>zs. \<lbrakk>xs \<preceq>\<^sub>b ys; ys \<preceq>\<^sub>b zs\<rbrakk> \<Longrightarrow> xs \<preceq>\<^sub>b zs\<close>
  from \<open>(x#xs) \<preceq>\<^sub>b (y#ys)\<close> have "xs \<preceq>\<^sub>b ys" and "x \<longrightarrow> y" by simp_all
  from \<open>(y#ys) \<preceq>\<^sub>b zs\<close> obtain z zs' where "zs = z#zs'" by(cases zs) auto
  with \<open>(y#ys) \<preceq>\<^sub>b zs\<close> have "ys \<preceq>\<^sub>b zs'" and "y \<longrightarrow> z" by simp_all
  from IH[OF \<open>xs \<preceq>\<^sub>b ys\<close> \<open>ys \<preceq>\<^sub>b zs'\<close>] have "xs \<preceq>\<^sub>b zs'" .
  with \<open>x \<longrightarrow> y\<close> \<open>y \<longrightarrow> z\<close> \<open>zs = z#zs'\<close> show ?case by simp
qed simp_all

lemma bv_leqs_antisym:"\<lbrakk>xs \<preceq>\<^sub>b ys; ys \<preceq>\<^sub>b xs\<rbrakk> \<Longrightarrow> xs = ys"
  by(induct xs ys rule:bv_leqs.induct)auto

definition bv_less :: "bit_vector \<Rightarrow> bit_vector \<Rightarrow> bool" ("_ \<prec>\<^sub>b _" 99)
  where "xs \<prec>\<^sub>b ys \<equiv> xs \<preceq>\<^sub>b ys \<and> xs \<noteq> ys"

interpretation order "bv_leqs" "bv_less"
by(unfold_locales,
   auto intro:bv_leqs_refl bv_leqs_trans bv_leqs_antisym simp:bv_less_def)

end


theory Lecture8
imports Main
begin
text{* An "abstract" notion of a finite graph *}

locale finitegraph =
  fixes edges :: "('a\<times>'a)set" and vertices::"'a set"
  assumes finite_vertex_set : "finite vertices"
  and is_graph : "(u, v) \<in> edges \<Longrightarrow>  u \<in> vertices \<and> v \<in> vertices"
begin

(* Note that we can tell Isabelle's simplifier about 2 of the rules using [simp].
   More on this later in the course. *)
inductive walk:: "'a list \<Rightarrow> bool" where
Nil[simp] : "walk []"
|Singleton [simp] : "v \<in> vertices \<Longrightarrow> walk [v]"
|Cons : "(v,w)\<in>edges \<Longrightarrow> walk(w#vs) \<Longrightarrow> walk(v#w#vs)"

lemma walk_edge: assumes "(v,w) \<in> edges" shows "walk [v,w]"
proof -
  have "w \<in> vertices"
    using assms is_graph by presburger
  then show ?thesis
    by (simp add: assms walk.Cons)
qed

definition connected :: "'a \<Rightarrow> 'a \<Rightarrow> bool" (infixl "\<rightarrow>\<^sup>*" 60) where
  "connected v w \<equiv> \<exists>xs. walk xs \<and> xs \<noteq> Nil \<and> hd xs = v \<and> last xs = w"

end

(* We can examine the definition of the locale and any lemma proven (whether explicitly or implicitly)
   inside it. For example: *)

thm finitegraph_def
thm "finitegraph.walk_edge"
thm "finitegraph.walk.Singleton"
(* An induction principle was proven automatically for our finite graphs*)
thm "finitegraph.walk.induct"

text{* Extending the finite graphs locale to one about weighted finite graphs*}

locale weighted_finitegraph = finitegraph +
  fixes weight :: "('a \<times> 'a) \<Rightarrow> nat"
  assumes edges_weighted: "\<forall>e\<in>edges. \<exists>w. weight e = w"

thm finitegraph_def
thm weighted_finitegraph_def [unfolded weighted_finitegraph_axioms_def]

text{* We can define a concrete graph and show that it is an instance of our finite graph. In this
   case, we just go for the graph with a single vertex "1" and an edge "(1,1)" to itself.
*}
interpretation singleton_finitegraph: finitegraph "{(1,1)}" "{1}"
proof
 show "finite {1}" by simp
 next fix u v assume "(u, v) \<in> {(1::'a, 1::'a)}" then show "u \<in> {1} \<and> v \<in> {1}" by blast
qed

(* We can "peek" into our instance: *)
term singleton_finitegraph.connected
term singleton_finitegraph.walk

(* The definitions and theorems are now  available for the locale instance. For example: *)
thm singleton_finitegraph.connected_def
thm singleton_finitegraph.walk_edge
thm singleton_finitegraph.walk.Singleton
thm singleton_finitegraph.walk.induct

text{* We can make the concrete singleton_finitegraph an instance of the weighted_finitegraph locale
   by providing a weight function e.g. the one that associates a weight "1" to any edge.
*}

interpretation singleton_finitegraph: weighted_finitegraph "{(1,1)}" "{1}" "\<lambda>(u,v). 1"
by (unfold_locales) simp

(* The walk_edge theorem is still available *)
thm singleton_finitegraph.walk_edge
(* The weighted edges assumption instantiated with our single edge graph (yielding a trivial property)*)
thm singleton_finitegraph.edges_weighted