Theory List_Util

theory List_Util
  imports Main
begin

section ‹General Lists›

lemma list_cases_3:
  "T = []  (x. T = [x])  (a b xs. T = a # b # xs)"
proof (cases T)
  case Nil
  then show ?thesis by simp
next
  case (Cons a list)
  then show ?thesis
  proof (cases list)
    case Nil
    with T = a # list
    show ?thesis
      by simp
  next
    case (Cons a' list')
    with T = a # list
    show ?thesis
      by simp
  qed
qed

lemma length_cons_cons:
  "T = a # b # xs  n. length T = Suc (Suc n)"
  by simp

lemma length_Suc_Suc:
  "length T = Suc (Suc n)  a b xs. T = a # b # xs"
  by (metis length_Suc_conv)

lemma length_Suc_0:
  "length xs = Suc 0  x. xs = [x]"
  by (simp add: length_Suc_conv)

lemma map_eq_replicate:
  "x  set xs. f x = k  map f xs = replicate (length xs) k"
  by (metis map_eq_conv map_replicate_const)

lemma map_upt_eq_replicate:
  "x  set [i..<j]. f x = k  map f [i..<j] = replicate (j - i) k"
  by (metis length_upt map_eq_replicate)

lemma in_set_list_update:
  "x  set xs; xs ! k  x  x  set (xs[k := y])"
  by (metis in_set_conv_nth length_list_update nth_list_update_neq)

lemma Max_greD:
  "i < length s  Max (set s)  s ! i"
  by clarsimp


lemma list_neq_rc1:
  "(z zs. xs = ys @ z # zs)  xs  ys"
  by fastforce

lemma list_neq_rc2:
  "(z zs. ys = xs @ z # zs)  xs  ys"
  by fastforce

lemma list_neq_rc3:
  "(x y as bs cs. xs = as @ x # bs  ys = as @ y # cs  x  y)  xs  ys"
  by fastforce

lemma list_neq_rc:
  "(z zs. xs = ys @ z # zs) 
   (z zs. ys = xs @ z # zs) 
   (x y as bs cs. xs = as @ x # bs  ys = as @ y # cs  x  y) 
    xs  ys"
  by (elim disjE conjE list_neq_rc1 list_neq_rc2 list_neq_rc3)

lemma list_neq_fc:
  "xs  ys 
   (z zs. xs = ys @ z # zs) 
   (z zs. ys = xs @ z # zs) 
   (x y as bs cs. xs = as @ x # bs  ys = as @ y # cs  x  y)"
proof (induct xs arbitrary: ys)
  case Nil
  then show ?case
    by (metis append_Nil list.exhaust)
next
  case (Cons a xs ys)
  note IH = this
  then show ?case
  proof (cases ys)
    case Nil
    then show ?thesis
      by simp
  next
    case (Cons b ys')
    assume "ys = b # ys'"
    show ?thesis
    proof (cases "a = b")
      assume "a  b"
      with ys = b # ys' 
      show ?thesis
        by blast
    next
      assume "a = b"
      with IH(2) ys = b # ys'
      have "xs  ys'"
        by simp
      with IH(1)[of ys']
      show ?thesis
        by (metis Cons_eq_appendI a = b local.Cons)
    qed
  qed
qed

lemma list_neq_cases:
  "xs  ys 
   (z zs. xs = ys @ z # zs) 
   (z zs. ys = xs @ z # zs) 
   (x y as bs cs. xs = as @ x # bs  ys = as @ y # cs  x  y)"
  using list_neq_fc list_neq_rc by blast

section ‹Find›

lemma findSomeD:
  "find P xs = Some x  P x  x  set xs"
  by (induct xs) (auto split: if_split_asm)

lemma findNoneD:
  "find P xs = None  x  set xs. ¬P x"
  by (induct xs) (auto split: if_split_asm)

section ‹Filter›

lemma filter_update_nth_success:
  "P v; i < length xs 
    filter P (xs[i := v]) = (filter P (take i xs)) @ [v] @ (filter P (drop (Suc i) xs))"
  by (simp add: upd_conv_take_nth_drop)

lemma filter_update_nth_fail:
  "¬P v; i < length xs 
    filter P (xs[i := v]) = (filter P (take i xs)) @ (filter P (drop (Suc i) xs))"
  by (simp add: upd_conv_take_nth_drop)

lemma filter_take_nth_drop_success:
  "i < length xs; P (xs ! i) 
    filter P xs = (filter P (take i xs)) @ [xs ! i] @ (filter P (drop (Suc i) xs))"
  by (metis filter_update_nth_success list_update_id)

lemma filter_take_nth_drop_fail:
  "i < length xs; ¬P (xs ! i) 
    filter P xs = (filter P (take i xs)) @ (filter P (drop (Suc i) xs))"
  by (metis filter_update_nth_fail list_update_id)

lemma filter_nth_1:
  "i < length xs; P (xs ! i) 
   i'. i' < length (filter P xs)  (filter P xs) ! i' = xs ! i"
proof (induct xs arbitrary: i)
  case Nil
  then show ?case
    by simp
next
  case (Cons a xs i)
  note IH = this
  show ?case
  proof (cases i)
    case 0
    with IH(3)
    show ?thesis
      by fastforce
  next
    case (Suc n)
    with IH(1)[of n] IH(2,3)
    have "i'<length (filter P xs). filter P xs ! i' = xs ! n"
      by fastforce
    then show ?thesis
      using Suc by auto
  qed  
qed

lemma filter_nth_2:
  "i < length (filter P xs) 
   i'. i' < length xs  (filter P xs) ! i = xs ! i'"
proof (induct xs arbitrary: i)
  case Nil
  then show ?case
    by simp
next
  case (Cons a xs i)
  note IH = this
  then show ?case
  proof (cases i)
    case 0
    then show ?thesis
      using Cons.hyps Cons.prems by force
  next
    case (Suc n)
    with IH(1)[of n] IH(2)
    have "i'<length xs. filter P xs ! n = xs ! i'"
      by (metis filter.simps(2) length_Cons not_less_eq not_less_iff_gr_or_eq)
    then show ?thesis
      by (metis Cons.hyps Cons.prems Suc filter.simps(2) length_Cons not_less_eq nth_Cons_Suc)
  qed
qed

lemma filter_nth_relative_1:
  "i < length xs; P (xs ! i); j < i; P (xs ! j) 
   i' j'. i' < length (filter P xs)  j' < i'  (filter P xs) ! i' = xs ! i 
    (filter P xs) ! j' = xs ! j"
proof (induct xs arbitrary: i j)
  case Nil
  then show ?case
    by simp
next
  case (Cons a xs i j)
  note IH = this
  show ?case
  proof (cases i)
    case 0
    then show ?thesis
      using IH(4) by blast
  next
    case (Suc n)
    assume "i = Suc n"
    show ?thesis
    proof (cases j)
      case 0
      with filter_nth_1[of n xs P] IH(2,3) i = Suc n IH(4-)
      show ?thesis
        by (metis filter.simps(2) length_Cons not_less_eq nth_Cons_0 nth_Cons_Suc zero_less_Suc)
    next
      case (Suc m)
      with IH(1)[of n m] IH(2-) i = Suc n
      show ?thesis 
        by fastforce
    qed
  qed
qed

lemma filter_nth_relative_neq_1:
  assumes "i < length xs" "P (xs ! i)" "j < length xs" "P (xs ! j)" "i  j"
  shows "i' j'. i' < length (filter P xs)  j' < length (filter P xs)  (filter P xs) ! i' = xs ! i 
                 (filter P xs) ! j' = xs ! j  i'  j'"
proof (cases "i < j")
  assume "i < j"
  with filter_nth_relative_1[where P = P, OF assms(3,4) _ assms(2)]
  show ?thesis
    by (metis (no_types, lifting) nat_neq_iff order_less_trans)
next
  assume "¬ i < j"
  with assms(5)
  have "j < i"
    by auto
  with filter_nth_relative_1[where P = P, OF assms(1,2) _ assms(4)]
  show ?thesis
    using order_less_trans by blast
qed

lemma filter_nth_relative_2:
  "i < length (filter P xs); j < i 
   i' j'. i' < length xs  j' < i'  (filter P xs) ! i = xs ! i'  (filter P xs) ! j = xs ! j'"
proof (induct xs arbitrary: i j)
  case Nil
  then show ?case
    by simp
next
  case (Cons a xs i j)
  note IH = this

  let ?goal = "i' j'. i' < length (a # xs)  j' < i'  filter P (a # xs) ! i = (a # xs) ! i' 
                       filter P (a # xs) ! j = (a # xs) ! j'"
  show ?case
  proof (cases i)
    case 0
    then show ?goal
      using IH(3) by blast
  next
    case (Suc n)
    assume "i = Suc n"
    show ?thesis
    proof (cases j)
      case 0
      assume "j = 0"
      from filter_nth_2[of n P xs] IH(2)
      have "i'<length xs. filter P xs ! n = xs ! i'"
        using Suc order_less_trans by fastforce

      show ?goal
      proof (cases "P a")
        assume "P a"
        then show ?goal
          by (metis "0" Suc i'<length xs. filter P xs ! n = xs ! i' filter.simps(2)
                    length_Cons not_less_eq nth_Cons_0 nth_Cons_Suc zero_less_Suc)
      next
        assume "¬ P a"
        then show ?goal
          using Cons.hyps IH(2,3) Suc_less_eq by fastforce
        qed
    next
      case (Suc m)
      with IH(1)[of n m] IH(2-) i = Suc n
      have "i' j'. i' < length xs  j' < i'  filter P xs ! n = xs ! i' 
                    filter P xs ! m = xs ! j'"
        by (metis filter.simps(2) length_Cons not_less_eq not_less_iff_gr_or_eq)
      then obtain i' j' where
        A: "i' < length xs" "j' < i'" "filter P xs ! n = xs ! i'" "filter P xs ! m = xs ! j'"
        by blast
      show ?goal
      proof (cases "P a")
        assume "P a"
        then show ?goal
          using A Suc i = Suc n by force
      next
        assume "¬ P a"
        then show ?goal
          using Cons.hyps IH(2,3) by fastforce
      qed
    qed
  qed
qed

lemma filter_nth_relative_neq_2:
  assumes "i < length (filter P xs)" "j < length (filter P xs)" "i  j"
  shows "i' j'. i' < length xs  j' < length xs  xs ! i' = (filter P xs) ! i 
                 xs ! j' = (filter P xs) ! j  i'  j'"
proof (cases "i < j")
  assume "i < j"
  with filter_nth_relative_2[OF assms(2), of i]
  show ?thesis
    by (metis dual_order.strict_trans exists_least_iff)
next
  assume "¬ i < j"
  with assms(3)
  have "j < i"
    by auto
  with filter_nth_relative_2[OF assms(1), of j]
  show ?thesis
    by (metis nat_neq_iff order_less_trans)
qed

lemma filter_find:
  "filter P xs  []  find P xs = Some ((filter P xs) ! 0)"
  by (induct xs; auto)

lemma filter_nth_update_subset:
  "set (filter P (xs[i := v]))  {v}  set (filter P xs)"
proof
  fix x
  assume "x  set (filter P (xs[i := v]))"
  with filter_set member_filter
  have "x  set (xs[i := v])" "P x"
    by auto
  hence "k < length (xs[i := v]). (xs[i := v]) ! k = x"
    by (simp add: in_set_conv_nth)
  then obtain k where
    "k < length (xs[i := v])"
    "(xs[i := v]) ! k = x"
    by blast
  hence "k < length xs"
    by simp

  from (xs[i := v]) ! k = x k < length (xs[i := v])
  have "k = i  x = v"
    by simp
  moreover
  have "k  i  x  set (filter P xs)"
    using P x k < length xs xs[i := v] ! k = x by auto
  ultimately
  show "x  {v}  set (filter P xs)"
    by blast
qed

section ‹Upt›

lemma card_upt:
  "card {0..<n} = n"
  by simp

lemma bounded_distinct_subset_upt_length:
  "distinct xs; i<length xs. xs ! i < length xs  set xs  {0..<length xs}"
  by (intro subsetI; clarsimp simp: in_set_conv_nth)

lemma bounded_distinct_eq_upt_length:
  assumes "distinct xs" 
  assumes "i < length xs. xs ! i < length xs"
  shows "set xs = {0..<length xs}"
proof (intro card_subset_eq finite_atLeastLessThan
              bounded_distinct_subset_upt_length[OF assms] )
  from distinct_card[OF assms(1)] card_upt[of "length xs"]
  show "card (set xs) = card {0..<length xs}"
    by simp
qed

lemma set_map_nth_subset:
  assumes "n  length xs"
  shows "set (map (nth xs) [0..<n])  set xs"
  using assms by clarsimp

lemma set_map_nth_eq:
  "set (map (nth xs) [0..<length xs]) = set xs"
  by (intro equalityI set_map_nth_subset subsetI; simp add: map_nth)

lemma distinct_map_nth:
  assumes "distinct xs"
  assumes "n  length xs"
  shows "distinct (map (nth xs) [0..<n])"
  using assms by (simp add: distinct_conv_nth)
end