Theory Util

section ‹Utilties›

theory Util
  imports
    Main
    "HOL-Library.Sublist"
    "HOL-Library.Multiset"

begin

abbreviation swap_events where
  "swap_events i j t  take i t @ [t ! j, t ! i] @ take (j - (i+1)) (drop (i+1) t) @ drop (j+1) t"

lemma swap_neighbors_2:
  shows
    "i+1 < length t  swap_events i (i+1) t = (t[i := t ! (i+1)])[i+1 := t ! i]"
proof (induct i arbitrary: t)
  case 0
  then show ?case 
    by (metis One_nat_def Suc_eq_plus1 add_lessD1 append.left_neutral append_Cons cancel_comm_monoid_add_class.diff_cancel drop_update_cancel length_list_update numeral_One take_0 take_Cons_numeral upd_conv_take_nth_drop zero_less_Suc)
next
  case (Suc n)
  let ?t = "tl t"
  have "t = hd t # ?t"
    by (metis Suc.prems hd_Cons_tl list.size(3) not_less_zero)
  moreover have "swap_events n (n+1) ?t = (?t[n := ?t ! (n+1)])[n+1 := ?t ! n]" 
    by (metis Suc.hyps Suc.prems Suc_eq_plus1 length_tl less_diff_conv)
  ultimately show ?case 
    by (metis Suc_eq_plus1 append_Cons diff_self_eq_0 drop_Suc_Cons list_update_code(3) nth_Cons_Suc take_Suc_Cons)
qed

lemma swap_identical_length:
  assumes
    "i < j" and
    "j < length t"
  shows
    "length t = length (swap_events i j t)"
proof -
  have "length (take i t @ [t ! j, t ! i] @ take (j - (i+1)) (drop (i+1) t))
      = length (take i t) + length [t ! j, t ! i] + length (take (j - (i+1)) (drop (i+1) t))"
    by simp
  then have "... = j+1" 
    using assms(1) assms(2) by auto
  then show ?thesis using assms(2) by auto
qed

lemma swap_identical_heads:
  assumes
    "i < j" and
    "j < length t"
  shows
    "take i t = take i (swap_events i j t)"
  using assms by auto

lemma swap_identical_tails:
  assumes
    "i < j" and
    "j < length t"
  shows
    "drop (j+1) t = drop (j+1) (swap_events i j t)"
proof -
  have "length (take i t @ [t ! j, t ! i] @ take (j - (i+1)) (drop (i+1) t))
      = length (take i t) + length [t ! j, t ! i] + length (take (j - (i+1)) (drop (i+1) t))"
    by simp
  then have "... = j+1" 
    using assms(1) assms(2) by auto
  then show ?thesis 
    by (metis length (take i t @ [t ! j, t ! i] @ take (j - (i + 1)) (drop (i + 1) t)) = length (take i t) + length [t ! j, t ! i] + length (take (j - (i + 1)) (drop (i + 1) t)) append.assoc append_eq_conv_conj)
qed

lemma swap_neighbors:
  shows
    "i+1 < length l  (l[i := l ! (i+1)])[i+1 := l ! i] = take i l @ [l ! (i+1), l ! i] @ drop (i+2) l"
proof (induct i arbitrary: l)
  case 0
  then show ?case 
    by (metis One_nat_def add.left_neutral add_lessD1 append_Cons append_Nil drop_update_cancel length_list_update one_add_one plus_1_eq_Suc take0 take_Suc_Cons upd_conv_take_nth_drop zero_less_two)
next
  case (Suc n)
  let ?l = "tl l"
  have "(l[Suc n := l ! (Suc n + 1)])[Suc n + 1 := l ! Suc n] = hd l # (?l[n := ?l ! (n+1)])[n+1 := ?l ! n]"
    by (metis Suc.prems add.commute add_less_same_cancel2 list.collapse list.size(3) list_update_code(3) not_add_less2 nth_Cons_Suc plus_1_eq_Suc)
  have "n + 1 < length ?l" using Suc.prems by auto
  then have "(?l[n := ?l ! (n+1)])[n+1 := ?l ! n] = take n ?l @ [?l ! (n+1), ?l ! n] @ drop (n+2) ?l"
    using Suc.hyps by simp
  then show ?case
    by (cases l) auto
qed

lemma swap_events_perm:
  assumes
    "i < j" and
    "j < length t"
  shows
    "mset (swap_events i j t) = mset t"
proof -
  have swap: "swap_events i j t
      = take i t @ [t ! j, t ! i] @ (take (j - (i+1)) (drop (i+1) t)) @ (drop (j+1) t)"
    by auto
  have reg: "t = take i t @ (take ((j+1) - i) (drop i t)) @ drop (j+1) t" 
    by (metis add_diff_inverse_nat add_lessD1 append.assoc append_take_drop_id assms(1) less_imp_add_positive less_not_refl take_add)
  have "mset (take i t) = mset (take i t)" by simp
  moreover have "mset (drop (j+1) t) = mset (drop (j+1) t)" by simp
  moreover have "mset ([t ! j, t ! i] @ (take (j - (i+1)) (drop (i+1) t))) = mset (take ((j+1) - i) (drop i t))"
  proof -
    let ?l = "take (j - (i+1)) (drop (i+1) t)"
    have "take ((j+1) - i) (drop i t) = t ! i # ?l @ [t ! j]"
    proof -
      have f1: "Suc (j - Suc i) = j - i"
        by (meson Suc_diff_Suc assms(1))
      have f2: "i < length t"
        using assms(1) assms(2) by linarith
      have f3: "j - (i + 1) + (i + 1) = j"
        using i < j by simp
      then have "drop (j - (i + 1)) (drop (i + 1) t) = drop j t"
        by (metis drop_drop)
      then show ?thesis
        using f3 f2 f1 by (metis (no_types) Cons_nth_drop_Suc Suc_diff_le Suc_eq_plus1 assms(1) assms(2) hd_drop_conv_nth length_drop less_diff_conv nat_less_le take_Suc_Cons take_hd_drop)
    qed
    then show ?thesis by fastforce
  qed
  ultimately show ?thesis using swap reg
    by simp (metis mset_append union_mset_add_mset_left union_mset_add_mset_right)
qed

lemma sum_eq_if_same_subterms:
  fixes
    i :: nat
  shows
    "k. i  k  k < j  f k = f' k  sum f {i..<j} = sum f' {i..<j}"
  by auto

lemma filter_neq_takeWhile:
  assumes
    "filter ((≠) a) l  takeWhile ((≠) a) l"
  shows
    "i j. i < j  j < length l  l ! i = a  l ! j  a" (is ?P)
proof (rule ccontr)
  assume "~ ?P"
  then have asm: "i j. i < j  j < length l  l ! i  a  l ! j = a" (is ?Q) by simp
  then have "filter ((≠) a) l = takeWhile ((≠) a) l"
  proof (cases "a : set l")
    case False
    then have "i. i < length l  l ! i  a" by auto
    then show ?thesis 
      by (metis (mono_tags, lifting) False filter_True takeWhile_eq_all_conv)
  next
    case True
    then have ex_j: "j. j < length l  l ! j = a" 
      by (simp add: in_set_conv_nth)
    let ?j = "Min {j. j < length l  l ! j = a}"
    have fin_j: "finite {j. j < length l  l ! j = a}" 
      using finite_nat_set_iff_bounded by blast
    moreover have "{j. j < length l  l ! j = a}  empty" using ex_j by blast
    ultimately have "?j < length l" 
      using Min_less_iff by blast
    have tail_all_a: "j. j < length l  j  ?j  l ! j = a"
    proof (rule allI, rule impI)
      fix j
      assume "j < length l  j  ?j"
      moreover have " ?Q; j < length l  j  ?j   k. k  ?j  k  j  l ! j = a"
      proof (induct "j - ?j")
        case 0
        then have "j = ?j" using 0 by simp
        then show ?case 
          using Min_in {j. j < length l  l ! j = a}  {} fin_j by blast
      next
        case (Suc n)
        then have "k. k  ?j  k < j  l ! j = a" 
          by (metis (mono_tags, lifting) Min_in {j. j < length l  l ! j = a}  {} fin_j le_eq_less_or_eq mem_Collect_eq)
        then show ?case 
          using Suc.hyps(2) by auto
      qed
      ultimately show "l ! j = a" using asm by blast
    qed
    moreover have head_all_not_a: "i. i < ?j  l ! i  a" using asm calculation 
      by (metis (mono_tags, lifting) Min_le Min {j. j < length l  l ! j = a} < length l fin_j leD less_trans mem_Collect_eq)
    ultimately have "takeWhile ((≠) a) l = take ?j l"
    proof -
      have "length (takeWhile ((≠) a) l) = ?j" 
      proof -
        have "length (takeWhile ((≠) a) l)  ?j" (is ?S)
        proof (rule ccontr)
          assume "¬ ?S"
          then have "l ! ?j  a" 
            by (metis (mono_tags, lifting) not_le_imp_less nth_mem set_takeWhileD takeWhile_nth)
          then show False 
            using Min {j. j < length l  l ! j = a} < length l tail_all_a by blast
        qed
        moreover have "length (takeWhile ((≠) a) l)  ?j" (is ?T)
        proof (rule ccontr)
          assume "¬ ?T"
          then have "j. j < ?j  l ! j = a" 
            by (metis (mono_tags, lifting) Min {j. j < length l  l ! j = a} < length l calculation le_less_trans not_le_imp_less nth_length_takeWhile)
          then show False 
            using head_all_not_a by blast
        qed
        ultimately show ?thesis by simp
      qed
      moreover have "length (take ?j l) = ?j" 
        by (metis calculation takeWhile_eq_take)
      ultimately show ?thesis 
        by (metis takeWhile_eq_take)

    qed
    moreover have "filter ((≠) a) l = take ?j l"
    proof -
      have "filter ((≠) a) l = filter ((≠) a) (take ?j l) @ filter ((≠) a) (drop ?j l)" 
        by (metis append_take_drop_id filter_append)
      moreover have "filter ((≠) a) (take ?j l) = take ?j l" using head_all_not_a 
        by (metis takeWhile ((≠) a) l = take (Min {j. j < length l  l ! j = a}) l filter_id_conv set_takeWhileD)
      moreover have "filter ((≠) a) (drop ?j l) = []"
      proof -
        have "j. j  ?j  j < length l  l ! j = drop ?j l ! (j - ?j)" 
          by simp
        then have "j. j < length l - ?j  drop ?j l ! j = a" using tail_all_a 
          by (metis (no_types, lifting) Groups.add_ac(2) Min {j. j < length l  l ! j = a} < length l less_diff_conv less_imp_le_nat not_add_less2 not_le nth_drop)
        then show ?thesis
        proof -
          obtain aa :: "('a  bool)  'a list  'a" where
            "x0 x1. (v2. v2  set x1  x0 v2) = (aa x0 x1  set x1  x0 (aa x0 x1))"
            by moura
          then have f1: "as p. aa p as  set as  p (aa p as)  filter p as = []"
            by (metis (full_types) filter_False)
          obtain nn :: "'a list  'a  nat" where
            f2: "x0 x1. (v2<length x0. x0 ! v2 = x1) = (nn x0 x1 < length x0  x0 ! nn x0 x1 = x1)"
            by moura
          { assume "drop (Min {n. n < length l  l ! n = a}) l ! nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)) = a"
            then have "filter ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l) = []  ¬ nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)) < length (drop (Min {n. n < length l  l ! n = a}) l)  drop (Min {n. n < length l  l ! n = a}) l ! nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l))  aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)"
              using f1 by (metis (full_types)) }
          moreover
          { assume "¬ nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)) < length l - Min {n. n < length l  l ! n = a}"
            then have "¬ nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)) < length (drop (Min {n. n < length l  l ! n = a}) l)  drop (Min {n. n < length l  l ! n = a}) l ! nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l))  aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)"
              by simp }
          ultimately have "filter ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l) = []  ¬ nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)) < length (drop (Min {n. n < length l  l ! n = a}) l)  drop (Min {n. n < length l  l ! n = a}) l ! nn (drop (Min {n. n < length l  l ! n = a}) l) (aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l))  aa ((≠) a) (drop (Min {n. n < length l  l ! n = a}) l)"
            using j<length l - Min {j. j < length l  l ! j = a}. drop (Min {j. j < length l  l ! j = a}) l ! j = a by blast
          then show ?thesis
            using f2 f1 by (meson in_set_conv_nth)
        qed
      qed
      ultimately show ?thesis by simp
    qed
    ultimately show ?thesis by simp
  qed
  then show False using assms by simp
qed

lemma util_exactly_one_element:
  assumes
    "m  set l" and
    "l' = l @ [m]"
  shows
    "∃!j. j < length l'  l' ! j = m" (is ?P)
proof -
  have "j. j < length l' - 1  l' ! j  m" 
    by (metis assms(1) assms(2) butlast_snoc length_butlast nth_append nth_mem)
  then have one_j: "j. j < length l'  l' ! j = m  j = (length l' - 1)"
    by (metis (no_types, opaque_lifting) diff_Suc_1 lessE)
  show ?thesis
  proof (rule ccontr)
    assume "~ ?P"
    then obtain i j where "i  j" "i < length l'" "j < length l'"
                          "l' ! i = m" "l' ! j = m"
      using assms by auto
    then show False using one_j by blast
  qed
qed

lemma exists_one_iff_filter_one:
  shows
    "(∃!j. j < length l  l ! j = a)  length (filter ((=) a) l) = 1"
proof (rule iffI)
  assume "∃!j. j < length l  l ! j = a"
  then obtain j where "j < length l" "l ! j = a"
    by blast
  moreover have "k. k  j  k < length l  l ! k  a" 
    using ∃!j. j < length l  l ! j = a j < length l l ! j = a by blast
  moreover have "l = take j l @ [l ! j] @ drop (j+1) l" 
    by (metis Cons_eq_appendI Cons_nth_drop_Suc Suc_eq_plus1 append_self_conv2 append_take_drop_id calculation(1) calculation(2))
  moreover have "filter ((=) a) (take j l) = []"
  proof -
    have "k. k < length (take j l)  (take j l) ! k  a" 
      using calculation(3) by auto
    then show ?thesis 
      by (metis (full_types) filter_False in_set_conv_nth)
  qed
  moreover have "filter ((=) a) (drop (j+1) l) = []"
  proof -
    have "k. k < length (drop (j+1) l)  (drop (j+1) l) ! k  a" 
      using calculation(3) by auto
    then show ?thesis
      by (metis (full_types) filter_False in_set_conv_nth)
  qed
  ultimately show "length (filter ((=) a) l) = 1" 
    by (metis (mono_tags, lifting) One_nat_def Suc_eq_plus1 append_Cons append_self_conv2 filter.simps(2) filter_append list.size(3) list.size(4))
next
  assume asm: "length (filter ((=) a) l) = 1"
  then have "filter ((=) a) l = [a]"
  proof -
    let ?xs = "filter ((=) a) l"
    have "length ?xs = 1"
      using asm by blast
    then show ?thesis 
      by (metis (full_types) Cons_eq_filterD One_nat_def length_0_conv length_Suc_conv)
  qed
  then have "j. j < length l  l ! j = a" 
    by (metis (full_types) filter_False in_set_conv_nth list.discI)
  then obtain j where j: "j < length l" "l ! j = a" by blast
  moreover have "k. k < length l  k  j  l ! k  a"
  proof (rule allI, rule impI)
    fix k
    assume assm: "k < length l  k  j"
    then have k < length l ..
    show "l ! k  a"
    proof (rule ccontr)
      assume lka: "¬ l ! k  a"
      then have l ! k = a
        by simp
      show False
      proof (cases "k < j")
        let ?xs = "take (k+1) l"
        let ?ys = "drop (k+1) l"
        case True
        then have "length (filter ((=) a) ?xs) > 0"
          using k < length l l ! k = a by (auto simp add: filter_empty_conv in_set_conv_nth)
        moreover have "length (filter ((=) a) ?ys) > 0"
        proof -
          have "?ys ! (j - (k+1)) = l ! j" 
            using True assm by auto
          moreover have "j - (k+1) < length ?ys" 
            using True j < length l by auto
          ultimately show ?thesis 
            by (metis (full_types) l ! j = a filter_empty_conv length_greater_0_conv nth_mem)
        qed
        moreover have "?xs @ ?ys = l" 
          using append_take_drop_id by blast
        ultimately have "length (filter ((=) a) l) > 1" 
          by (metis (no_types, lifting) One_nat_def Suc_eq_plus1 asm filter_append length_append less_add_eq_less less_one nat_neq_iff)
        then show False using asm by simp
      next
        let ?xs = "take (j+1) l"
        let ?ys = "drop (j+1) l"
        case False
        then have "length (filter ((=) a) ?xs) > 0" 
          using k < length l l ! j = a by (auto simp add: filter_empty_conv in_set_conv_nth)
        moreover have "length (filter ((=) a) ?ys) > 0"
        proof -
          have "?ys ! (k - (j+1)) = l ! k" 
            using False assm by auto
          moreover have "k - (j+1) < length ?ys" 
            using False assm by auto
          ultimately show ?thesis 
            by (metis (full_types) filter_empty_conv length_greater_0_conv lka nth_mem)
        qed
        moreover have "?xs @ ?ys = l" 
          using append_take_drop_id by blast
        ultimately have "length (filter ((=) a) l) > 1" 
          by (metis (no_types, lifting) One_nat_def Suc_eq_plus1 asm filter_append length_append less_add_eq_less less_one nat_neq_iff)
        then show False using asm by simp
      qed
    qed
  qed
  ultimately show "∃!j. j < length l  l ! j = a" by blast
qed

end