Theory Util

theory Util
  imports Main
begin

section‹Utility Theorems›

text‹
  This theory contains general facts that we use in our proof but which do not depend on our
  development.
›

text@{const list_all} and @{const list_ex} are dual›
lemma not_list_all:
  "(¬ list_all P xs) = list_ex (λx. ¬ P x) xs"
  by (metis Ball_set Bex_set)
lemma not_list_ex:
  "(¬ list_ex P xs) = list_all (λx. ¬ P x) xs"
  by (metis Ball_set Bex_set)

text‹A list of length more than one starts with two elements›
lemma list_obtain_2:
  assumes "1 < length xs"
  obtains v vb vc where "xs = v # vb # vc"
  using assms by (cases xs rule: remdups_adj.cases) simp_all

text‹Generalise the theorem @{thm Nat.less_add_eq_less}
lemma less_add_eq_less_general:
    fixes k l m n :: "'a :: {comm_monoid_add, ordered_cancel_ab_semigroup_add, linorder}"
  assumes "k < l"
      and "m + l = k + n"
    shows "m < n"
  using assms by (metis add.commute add_strict_left_mono linorder_not_less nless_le)

text‹
  Consider a list of elements and two functions, one of which is always at less-than or equal to the
  other on elements of that list.
  If for one element of that list the first function is strictly less than the other, then summing
  the list with the first function is also strictly less summing it with the second function.
›
lemma sum_list_mono_one_strict:
  fixes f g :: "'a  'b :: {comm_monoid_add, ordered_cancel_ab_semigroup_add, linorder}"
  assumes "x. x  set xs  f x  g x"
      and "x  set xs"
      and "f x < g x"
    shows "sum_list (map f xs) < sum_list (map g xs)"
proof -
  have "sum_list (map f xs)  sum_list (map g xs)"
    using assms sum_list_mono by blast
  moreover have "sum_list (map f xs)  sum_list (map g xs)"
  proof
    assume "sum_list (map f xs) = sum_list (map g xs)"
    then have "sum_list (map f (remove1 x xs)) > sum_list (map g (remove1 x xs))"
      by (metis add.commute assms(2,3) less_add_eq_less_general sum_list_map_remove1)
    then show False
      by (metis assms(1) leD notin_set_remove1 sum_list_mono)
  qed
  ultimately show ?thesis
    by simp
qed

text‹Generalise @{thm Groups_List.sum_list_mono} to allow for different lists›
lemma sum_list_mono_list_all2:
    fixes f g :: "'a  'b::{monoid_add, ordered_ab_semigroup_add}"
  assumes "list_all2 (λx y. f x  g y) xs ys"
    shows "(xxs. f x)  (xys. g x)"
  using assms
proof (induct xs arbitrary: ys)
  case Nil
  then show ?case by simp
next
  case (Cons a as)
  moreover obtain b bs where "ys = b # bs"
    using Cons by (meson list_all2_Cons1)
  ultimately show ?case
    by (simp add: add_mono)
qed

text‹Generalise @{thm sum_list_mono_one_strict} to allow for different lists›
lemma sum_list_mono_one_strict_list_all2:
    fixes f g :: "'a  'b :: {comm_monoid_add, ordered_cancel_ab_semigroup_add, linorder}"
  assumes "list_all2 (λx y. f x  g y) xs ys"
      and "(x, y)  set (zip xs ys)"
      and "f x < g y"
    shows "sum_list (map f xs) < sum_list (map g ys)"
proof -
  note len = list_all2_lengthD[OF assms(1)]

  have "sum_list (map f xs) = (xzip xs ys. f (fst x))"
  proof -
    have "map f xs = map f (map fst (zip xs ys))"
      using len by simp
    then have "map f xs = map (λx. f (fst x)) (zip xs ys)"
      by simp
    then show ?thesis
      by metis
  qed
  moreover have "sum_list (map g ys) = (xzip xs ys. g (snd x))"
  proof -
    have "map g ys = map g (map snd (zip xs ys))"
      using len by simp
    then have "map g ys = map (λx. g (snd x)) (zip xs ys)"
      by simp
    then show ?thesis
      by metis
  qed
  moreover have "x  set (zip xs ys)  f (fst x)  g (snd x)" for x
    using assms(1) by (fastforce simp add: in_set_zip list_all2_conv_all_nth)
  ultimately show ?thesis
    using assms(2,3) by (simp add: sum_list_mono_one_strict)
qed

text‹Define a function to count the number of list elements satisfying a predicate›
primrec count_if :: "('a  bool)  'a list  nat"
  where
    "count_if P [] = 0"
  | "count_if P (x#xs) = (if P x then Suc (count_if P xs) else count_if P xs)"

lemma count_if_append [simp]:
  "count_if P (xs @ ys) = count_if P xs + count_if P ys"
  by (induct xs) simp_all

lemma count_if_0_conv:
  "(count_if P xs = 0) = (¬ list_ex P xs)"
  by (induct xs) simp_all

text‹Intersection of sets that are the same is any of those sets›
lemma Inter_all_same:
  assumes "x y. x  A; y  A  f x = f y"
      and "x  A"
    shows "(x  A. f x) = f x"
  using assms by blast

end