Theory List_Slice

theory List_Slice
  imports Main
begin

section ‹List Slices›

fun list_slice :: 
  "'a list  nat  nat  'a list"
where
  "list_slice xs i j = drop i (take j xs)"

lemma length_list_slice[simp add]:
  "length (list_slice xs i j) = (min j (length xs)) - i"
  by simp

lemma list_slice_cons:
  fixes i j :: nat
  assumes "i  j"
  assumes "i > 0"
  shows "list_slice (x # xs) i j = list_slice xs (i - 1) (j - 1)"
  using assms gr0_implies_Suc[OF order.strict_trans2]
  by (fastforce dest: gr0_implies_Suc)

lemma list_slice_append:
  fixes i j k :: nat
  assumes "i  j"
  assumes "j  k"
  shows "list_slice xs i k = list_slice xs i j @ list_slice xs j k"
using assms
proof (induct xs arbitrary: i j k)
  case (Cons a xs i j k)
  note IH = this
  show ?case
  proof (cases "i > 0")
    assume "¬ i > 0"
    hence "i = 0"
      by simp
    then show ?case
      unfolding list_slice.simps
      by (metis IH(3) append_take_drop_id drop0 min_def take_take)
  next
    assume "i > 0"
    with list_slice_cons[of i k a xs]
    have "list_slice (a # xs) i k = list_slice xs (i - 1) (k - 1)"
      using IH(2) IH(3) dual_order.trans by blast
    moreover
    from list_slice_cons[of i j a xs]
    have "list_slice (a # xs) i j = list_slice xs (i - 1) (j - 1)"
      using IH(2) i > 0 by blast
    moreover
    from list_slice_cons[of j k a xs]
    have "list_slice (a # xs) j k = list_slice xs (j - 1) (k - 1)"
      using IH(2) IH(3) 0 < i by blast
    moreover
    from IH(1)[of "i-1" "j-1" "k-1"]
    have "list_slice xs (i - 1) (k - 1) =
          list_slice xs (i - 1) (j - 1) @ list_slice xs (j - 1) (k - 1)"
      using IH(2) IH(3) diff_le_mono by blast
    ultimately show ?case
      by presburger
  qed
qed simp

lemma list_slice_0_length:
  fixes xs :: "'a list" 
  fixes n  :: nat
  assumes "length xs  n"
  shows "list_slice xs 0 n = xs"
  using assms by simp

lemma list_slice_n_n[simp add]:
  fixes xs :: "'a list" 
  fixes n  :: nat
  shows "list_slice xs n n = []"
  by simp

lemma list_slice_nth:
  fixes i s e :: nat
  fixes xs :: "'a list" 
  assumes "i < length xs"
  assumes "s  i"
  assumes "i < e"
  shows "(list_slice xs s e) ! (i - s) = xs ! i"
  using assms by simp

lemma list_slice_start_gre_length:
  fixes xs :: "'a list" 
  fixes s :: nat
  assumes "length xs  s"
  shows "list_slice xs s e = []"
  using assms by simp

lemma list_slice_end_gre_length:
  fixes xs :: "'a list" 
  fixes e :: nat
  assumes "length xs  e"
  shows "list_slice xs s e = list_slice xs s (length xs)"
  using assms by simp

lemma fold_list_slice:
  fixes i j :: nat
  fixes B :: "nat list"
  assumes "i  j"
  and "j < length B"
  and "sorted B"
  fixes T zs :: "'a list" 
  shows
   "fold (λx xs. xs @ list_slice T (B ! x) (B ! Suc x)) [i..<j] zs
    = zs @ (list_slice T (B ! i) (B ! j))"
  using assms
proof (induct j arbitrary: i)
  case 0
  then show ?case
    by (simp del: list_slice.simps)
next
  case (Suc j i)
  note IH = this
  show ?case
  proof (cases "i  j")
    assume i_leq_j: "i  j"

    have "fold (λx xs. xs @ list_slice T (B ! x) (B ! Suc x)) [i..<Suc j] zs =
          fold (λx xs. xs @ list_slice T (B ! x) (B ! Suc x)) [i..<j] zs @
            list_slice T (B ! j) (B ! Suc j)"
      using i  j by force
    moreover
    from IH(1)[OF i  j _ IH(4)]
    have "fold (λx xs. xs @ list_slice T (B ! x) (B ! Suc x)) [i..<j] zs =
          zs @ list_slice T (B ! i) (B ! j)"
      using Suc.prems(2) Suc_lessD by blast
    moreover
    have "list_slice T (B ! i) (B ! Suc j) = list_slice T (B ! i) (B ! j) @
          list_slice T (B ! j) (B ! Suc j)"
      by (meson Suc.prems(2,3) Suc_lessD i_leq_j list_slice_append 
                sorted_iff_nth_Suc sorted_nth_mono)
    ultimately show ?case
      by (metis append.assoc)
  next
    assume "¬ i  j"
    then show ?case
      using Suc.prems(1) le_SucE by fastforce
  qed
qed

lemma nth_list_slice:
  fixes i s e :: nat
  fixes xs :: "'a list" 
  assumes "i < length (list_slice xs s e)"
  shows "(list_slice xs s e) ! i = xs ! (s + i)"
  using assms less_diff_conv by auto

lemma list_slice_nth_eq_iff_index_eq:
  fixes i s e j :: nat
  fixes xs :: "'a list" 
  assumes "distinct (list_slice xs s e)"
  assumes "e  length xs"
  assumes "s  i" and "i < e" 
  and     "s  j" and "j < e"
  shows   "(xs ! i = xs ! j)  (i = j)"
  using assms
  by (fastforce 
      dest: nth_eq_iff_index_eq[where i = "i - s" and j = "j - s"])

lemma distinct_list_slice:
  fixes i j :: nat
  fixes xs :: "'a list" 
  assumes "distinct xs"
  shows   "distinct (list_slice xs i j)"
  using assms by simp

lemma list_slice_nth_mem:
  fixes e :: nat
  fixes xs :: "'a list" 
  fixes s i :: nat
  assumes "s  i" and "i < e"
  assumes "e  length xs"
  shows "xs ! i  set (list_slice xs s e)"
  by (metis (no_types, opaque_lifting) assms nat_le_iff_add 
            add_diff_cancel_left' diff_less_mono nth_mem
            length_list_slice min_def nth_list_slice)

lemma nth_mem_list_slice:
  fixes x :: 'a
  fixes xs :: "'a list" 
  fixes s e :: nat
  assumes "x  set (list_slice xs s e)"
  shows "i < length xs. 
            s  i  
            i < e  
            xs ! i = x"
proof -
  from in_set_conv_nth[THEN iffD1, OF _  _]
  obtain i where
    "i < length (list_slice xs s e)"
    "(list_slice xs s e) ! i = x"
    by auto
  with nth_list_slice[of i xs s e]
  have "xs ! (s + i) = x"
    by auto
  moreover
  have "s  s + i"
    by linarith
  moreover
  have "s + i < length xs"
    using i < length (list_slice xs s e) by auto
  moreover
  have "s + i < e"
    using i < length (list_slice xs s e) by auto
  ultimately show ?thesis 
    by blast
qed

lemma list_slice_subset:
  fixes i j :: nat
  fixes xs :: "'a list" 
  shows "set (list_slice xs i j)  set xs"
  using set_drop_subset set_take_subset by force

lemma list_slice_Suc:
  fixes i j :: nat
  fixes xs :: "'a list" 
  assumes "i < length xs"
  assumes "i < j"
  shows "list_slice xs i j = xs ! i # list_slice xs (Suc i) j"
  by (metis assms Cons_nth_drop_Suc Suc_diff_Suc 
            list_slice.simps take_Suc_Cons drop_take)

lemma list_slice_update_unchanged_1:
  fixes xs :: "'a list" 
  fixes i j k :: nat
  assumes "i < j"
  shows "list_slice (xs[i := x]) j k = list_slice xs j k"
  by (simp add: assms drop_take)

lemma list_slice_update_unchanged_2:
  fixes i j k :: nat
  fixes xs :: "'a list" 
  assumes " k  i"
  shows   "list_slice (xs[i := x]) j k = list_slice xs j k"
  by (metis assms list_slice.simps take_update_cancel)

lemma list_slice_update_changed:
  assumes "i < length xs"
  assumes  "j  i" 
  assumes  "i < k"
  shows "list_slice (xs[i := x]) j k = (list_slice xs j k)[i - j := x]"
  using assms
  by (fastforce 
         intro: list_eq_iff_nth_eq[THEN iffD2]
         simp:  nth_list_slice  nth_list_update)

lemma list_slice_map_nth_upt:
  assumes "j < length xs"
  shows "list_slice xs i j = map (nth xs) [i..<j]"
  using assms 
  by (fastforce intro: list_eq_iff_nth_eq[THEN iffD2])


lemma map_list_slice:
  "map f (list_slice xs i j) = list_slice (map f xs) i j"
  by (simp add: drop_map take_map)

section ‹Sorted List Slice›

lemma (in linorder) sorted_list_slice:
  assumes "sorted xs"
  shows "sorted (list_slice xs i j)"
  by (simp add: assms sorted_wrt_drop sorted_wrt_take)

lemma (in linorder) sorted_map_list_slice:
  assumes "sorted (map f xs)" 
  shows "sorted (map f (list_slice xs i j))"
  by (metis assms drop_map list_slice.simps local.sorted_list_slice take_map)

lemma (in linorder) sorted_map_filter_list_slice:
  assumes "sorted (map f (filter P xs))"
  shows "sorted (map f (filter P (list_slice xs i j)))"
proof -
  have "i  j  j < i"
    using linorder_class.le_less_linear by blast
  moreover
  have "j < i  ?thesis"
  proof -
    assume "j < i"
    hence "list_slice xs i j = []"
      by (simp add: drop_take)
    then show ?thesis
      by simp
  qed
  moreover
  have "i  j  ?thesis"
  proof -
    let ?as = "list_slice xs 0 i" and
        ?bs = "list_slice xs i j" and
        ?cs = "list_slice xs j (length xs)"
    assume "i  j"
    hence "xs = ?as @ ?bs @ ?cs"
      by (metis le0 linorder_class.linear list_slice_0_length list_slice_append
                list_slice_start_gre_length)
    hence "filter P xs = filter P ?as @ filter P ?bs @ filter P ?cs"
      by (metis filter_append)
    hence "map f (filter P xs)
            = (map f (filter P ?as)) @ (map f (filter P ?bs)) @ (map f (filter P ?cs))"
      by simp
    with sorted (map f (filter P xs))
    show ?thesis
      by (simp add: local.sorted_append)
  qed
  ultimately show ?thesis
    by blast
qed

lemma (in linorder) list_slice_sorted_nth_mono:
  assumes "sorted (list_slice xs s e)"
  and     "s  i"
  and     "i  j"
  and     "j < e"
  and     "j < length xs"
shows "xs ! i  xs ! j"
proof -
  have "i'. i = s + i'"
    using assms(2) nat_le_iff_add by blast
  then obtain i' where
    "i = s + i'"
    by blast

  have "j'. j = s + j'"
    using assms(2) assms(3) nat_le_iff_add by auto
  then obtain j' where
    "j = s + j'"
    by blast

  have "i'  j'"
    using i = s + i' j = s + j' assms(3) by auto

  have "j' < length (list_slice xs s e)"
    using j = s + j' assms(4) assms(5) by auto
  with sorted_nth_mono[OF assms(1) `i'  j'`]
  have "list_slice xs s e ! i'  list_slice xs s e ! j'" .
  moreover
  have "xs ! i = list_slice xs s e ! i'"
    using i = s + i' assms(3-5) by force
  moreover
  have "xs ! j = list_slice xs s e ! j'"
    using j = s + j' j' < length (list_slice xs s e) nth_list_slice by force
  ultimately show ?thesis
    by simp
qed
end