Theory FOR_Preliminaries

section‹Preliminaries›
text‹This theory contains some auxiliary results previously located in Auxx.Util of IsaFoR.›
theory FOR_Preliminaries
  imports
    Polynomial_Factorization.Missing_List 
begin

lemma in_set_idx: "a  set as  i. i < length as  a = as ! i"
  unfolding set_conv_nth by auto

lemma finite_card_eq_imp_bij_betw:
  assumes "finite A"
    and "card (f ` A) = card A"
  shows "bij_betw f A (f ` A)"
  using card (f ` A) = card A
  unfolding inj_on_iff_eq_card [OF finite A, symmetric]
  by (rule inj_on_imp_bij_betw)

text ‹Every bijective function between two finite subsets of a set S› can be turned
  into a compatible renaming (with finite domain) on S›.›
lemma bij_betw_extend:
  assumes *: "bij_betw f A B"
    and "A  S"
    and "B  S"
    and "finite A"
  shows "g. finite {x. g x  x} 
    (xUNIV - (A  B). g x = x) 
    (xA. g x = f x) 
    bij_betw g S S"
proof -
  have "finite B" using assms by (metis bij_betw_finite)
  have [simp]: "card A = card B" by (metis * bij_betw_same_card)
  have "card (A - B) = card (B - A)"
  proof -
    have "card (A - B) = card A - card (A  B)"
      by (metis finite A card_Diff_subset_Int finite_Int)
    moreover have "card (B - A) = card B - card (A  B)"
      by (metis finite A card_Diff_subset_Int finite_Int inf_commute)
    ultimately show ?thesis by simp
  qed
  then obtain g where **: "bij_betw g (B - A) (A - B)"
    by (metis finite A finite B bij_betw_iff_card finite_Diff)
  define h where "h = (λx. if x  A then f x else if x  B - A then g x else x)"
  have "bij_betw h A B"
    by (metis (full_types) * bij_betw_cong h_def)
  moreover have "bij_betw h (S - (A  B)) (S - (A  B))"
    by (auto simp: bij_betw_def h_def inj_on_def)
  moreover have "B  (S - (A  B)) = {}" by blast
  ultimately have "bij_betw h (A  (S - (A  B))) (B  (S - (A  B)))"
    by (rule bij_betw_combine)
  moreover have "A  (S - (A  B)) = S - (B - A)"
    and "B  (S - (A  B)) = S - (A - B)"
    using A  S and B  S by blast+
  ultimately have "bij_betw h (S - (B - A)) (S - (A - B))" by simp
  moreover have "bij_betw h (B - A) (A - B)"
    using ** by (auto simp: bij_betw_def h_def inj_on_def)
  moreover have "(S - (A - B))  (A - B) = {}" by blast
  ultimately have "bij_betw h ((S - (B - A))  (B - A)) ((S - (A - B))  (A - B))"
    by (rule bij_betw_combine)
  moreover have "(S - (B - A))  (B - A) = S"
    and "(S - (A - B))  (A - B) = S"
    using A  S and B  S by auto
  ultimately have "bij_betw h S S" by simp
  moreover have "xA. h x = f x" by (auto simp: h_def)
  moreover have "finite {x. h x  x}"
  proof -
    have "finite (A  (B - A))" using finite A and finite B by auto
    moreover have "{x. h x  x}  (A  (B - A))" by (auto simp: h_def)
    ultimately show ?thesis by (metis finite_subset)
  qed
  moreover have "xUNIV - (A  B). h x = x" by (simp add: h_def)
  ultimately show ?thesis by blast
qed

lemma concat_nth:
  assumes "m < length xs" and "n < length (xs ! m)"
    and "i = sum_list (map length (take m xs)) + n"
  shows "concat xs ! i = xs ! m ! n" 
  using assms
proof (induct xs arbitrary: m n i)
  case (Cons x xs)
  show ?case
  proof (cases m)
    case 0
    then show ?thesis using Cons by (simp add: nth_append)
  next
    case (Suc k)
    with Cons(1) [of k n "i - length x"] and Cons(2-)
    show ?thesis by (simp_all add: nth_append)
  qed
qed simp

lemma concat_nth_length:
  "i < length uss  j < length (uss ! i) 
    sum_list (map length (take i uss)) + j < length (concat uss)"
proof (induct uss arbitrary: i j)
  case (Cons u uss i j)
  thus ?case by (cases i, auto)
qed auto

lemma less_length_concat:
  assumes "i < length (concat xs)"
  shows "m n.
    i = sum_list (map length (take m xs)) + n 
    m < length xs  n < length (xs ! m)  concat xs ! i = xs ! m ! n"
  using assms
proof (induct xs arbitrary: i rule: length_induct)
  case (1 xs)
  then show ?case
  proof (cases xs)
    case (Cons y ys)
    note [simp] = this
    { assume *: "i < length y"
      with 1 obtain n where "i = n" and "n < length y"
        and "y ! i = y ! n" by simp
      then have "i = sum_list (map length (take 0 xs)) + n"
        and "0 < length xs" and "n < length (xs ! 0)"
        and "concat xs ! i = xs ! 0 ! n"
        using * by (auto simp: nth_append)
      then have ?thesis by blast }
    moreover
    { assume *: "i  length y"
      define j where "j = i - length y"
      then have "length ys < length xs" "j < length (concat ys)"
        using * and "1.prems" by auto
      with 1 obtain m n where "j = sum_list (map length (take m ys)) + n"
        and "m < length ys" and "n < length (ys ! m)"
        and "concat ys ! j = ys ! m ! n" by blast
      then have "i = sum_list (map length (take (Suc m) xs)) + n"
        and "Suc m < length xs" and "n < length (xs ! Suc m)"
        and "concat xs ! i = xs ! Suc m ! n"
        using * by (simp_all add: j_def nth_append)
      then have ?thesis by blast }
    ultimately show ?thesis by force
  qed simp
qed

lemma concat_remove_nth:
  assumes "i < length sss"
    and "j < length (sss ! i)"
  defines "k  sum_list (map length (take i sss)) + j"
  shows "concat (take i sss @ remove_nth j (sss ! i) # drop (Suc i) sss) = remove_nth k (concat sss)"
  using assms
  unfolding remove_nth_def
proof (induct sss rule: List.rev_induct)
  case Nil then show ?case by auto
next
  case (snoc ss sss)
  then have "i = length sss  i < length sss" by auto 
  then show ?case
  proof
    assume i:"i = length sss"
    have "sum_list (map length sss) = length (concat sss)" by (simp add: length_concat)
    with snoc i show ?thesis by simp
  next
    assume i:"i < length sss"
    then have nth:"(sss @ [ss]) ! i = sss ! i" by (simp add: nth_append)
    from i have drop:"drop (Suc i) (sss @ [ss]) = drop (Suc i) sss @ [ss]" by auto
    from i have take:"take i (sss @ [ss]) = take i sss" by auto
    from snoc(1)[OF i] snoc(2-) have 1:"concat (take i (sss @ [ss]) @ 
      (take j ((sss @ [ss]) ! i) @ drop (Suc j) ((sss @ [ss]) ! i)) # drop (Suc i) (sss @ [ss])) = 
      take k (concat sss) @ drop (Suc k) (concat sss) @ ss" unfolding take nth drop by simp
    from snoc(4) take have k:"k = sum_list (map length (take i sss)) + j" by auto
    from nth snoc(3) have j: "j < length (sss ! i)" by auto
    have takek:"take (sum_list (map length (take i sss)) + j) (concat (sss @ [ss])) = 
      take (sum_list (map length (take i sss)) + j) (concat sss)"
      using concat_nth_length[OF i j] by auto
    have dropk:"drop (Suc (sum_list (map length (take i sss)) + j)) (concat sss) @ ss = 
      drop (Suc (sum_list (map length (take i sss)) + j)) (concat (sss @ [ss]))"
      using concat_nth_length[OF i j] by auto
    have "take k (concat sss) @ drop (Suc k) (concat sss) @ ss = 
      take k (concat (sss @ [ss])) @ drop (Suc k) (concat (sss @ [ss]))"
      unfolding k takek dropk ..
    with 1 show ?thesis by auto
  qed
qed

lemma nth_append_Cons: "(xs @ y # zs) ! i =
  (if i < length xs then xs ! i else if i = length xs then y else zs ! (i - Suc (length xs)))"
  by (cases i "length xs" rule: linorder_cases, auto simp: nth_append)

lemma finite_imp_eq [simp]:
  "finite {x. P x  Q x}  finite {x. ¬ P x}  finite {x. Q x}"
  by (auto simp: Collect_imp_eq Collect_neg_eq)

lemma sum_list_take_eq:
  fixes xs :: "nat list"
  shows "k < i  i < length xs  sum_list (take i xs) =
    sum_list (take k xs) + xs ! k + sum_list (take (i - Suc k) (drop (Suc k) xs))"
  by (subst id_take_nth_drop [of k]) (auto simp: min_def drop_take)

lemma nth_equalityE:
  "xs = ys  (length xs = length ys  (i. i < length xs  xs ! i = ys ! i)  P)  P"
  by simp

fun fold_map :: "('a  'b  'c × 'b)  'a list  'b  'c list × 'b" where
  "fold_map f [] y = ([], y)" 
| "fold_map f (x#xs) y = (case f x y of
      (x', y')  case fold_map f xs y' of
      (xs', y'')  (x' # xs', y''))"

lemma fold_map_cong [fundef_cong]:
  assumes "a = b" and "xs = ys"
    and "x. x  set xs  f x = g x"
  shows "fold_map f xs a = fold_map g ys b"
  using assms by (induct ys arbitrary: a b xs) simp_all

lemma fold_map_map_conv:
  assumes "x ys. x  set xs  f (g x) (g' x @ ys) = (h x, ys)"
  shows "fold_map f (map g xs) (concat (map g' xs) @ ys) = (map h xs, ys)"
  using assms by (induct xs) simp_all

lemma map_fst_fold_map:
  "map f (fst (fold_map g xs y)) = fst (fold_map (λa b. apfst f (g a b)) xs y)"
  by (induct xs arbitrary: y) (auto split: prod.splits, metis fst_conv)

lemma not_Nil_imp_last: "xs  []  ys y. xs = ys@[y]"
proof (induct xs)
  case Nil then show ?case by simp
next
  case (Cons x xs) show ?case
  proof (cases xs)
    assume "xs = []" with Cons show ?thesis by simp
  next
    fix x' xs' assume "xs = x'#xs'"
    then have "xs  []" by simp
    with Cons obtain ys y where "xs = ys@[y]" by auto
    then have "x#xs = x#(ys@[y])" by simp
    then have "x#xs = (x#ys)@[y]" by simp
    then show ?thesis by auto
  qed
qed

lemma Nil_or_last: "xs = []  (ys y. xs = ys@[y])"
  using not_Nil_imp_last by blast

subsection ‹Combinators›

definition const :: "'a  'b  'a" where
  "const  (λx y. x)"

definition flip :: "('a  'b  'c)  ('b  'a  'c)" where
  "flip f  (λx y. f y x)"
declare flip_def[simp]

lemma const_apply[simp]: "const x y = x"
  by (simp add: const_def)

lemma foldr_Cons_append_conv [simp]:
  "foldr (#) xs ys = xs @ ys"
  by (induct xs) simp_all

lemma foldl_flip_Cons[simp]:
  "foldl (flip (#)) xs ys = rev ys @ xs"
  by (induct ys arbitrary: xs) simp_all

text ‹already present as @{thm [source] foldr_conv_foldl}, but
  direction seems odd›
lemma foldr_flip_rev[simp]:
  "foldr (flip f) (rev xs) a = foldl f a xs"
  by (simp add: foldr_conv_foldl)

text ‹already present as @{thm [source] foldl_conv_foldr}, but
  direction seems odd›
lemma foldl_flip_rev[simp]:
  "foldl (flip f) a (rev xs) = foldr f xs a"
  by (simp add: foldl_conv_foldr)

fun debug :: "(String.literal  String.literal)  String.literal  'a  'a" where "debug i t x = x"

subsection ‹Distinct Lists and Partitions›

text ‹This theory provides some auxiliary lemmas related to lists with distinct elements and
partitions. This is mainly used for dealing with \emph{linear} terms.›

lemma distinct_alt:
  assumes " x. length (filter ((=) x) xs)  1"
  shows "distinct xs"
  using assms proof(induct xs)
  case (Cons x xs)
  then have IH:"distinct xs"
    by (metis dual_order.trans filter.simps(2) impossible_Cons nle_le) 
  from Cons(2) have "length (filter ((=) x) xs) = 0"
    by (metis (mono_tags) One_nat_def add.right_neutral add_Suc_right filter.simps(2) le_less length_0_conv less_Suc0 list.simps(3) list.size(4) nat.inject)
  then have "x  set (xs)"
    by (metis (full_types) filter_empty_conv length_0_conv) 
  with IH show ?case
    by simp
qed simp

lemma distinct_filter2:
  assumes "i < size xs.  j < size xs. i  j  f (xs!i)  f (xs!j)  xs!i  xs!j" 
  shows "distinct (filter f xs)"
  using assms proof(induct xs)
  case (Cons x xs)
  {fix i j assume "i < length xs" "j < length xs" "i  j" "f (xs!i)" "f (xs!j)" 
    with Cons(2) have "xs!i  xs!j"  
      by (metis not_less_eq nth_Cons_Suc Suc_inject length_Cons) 
  }
  with Cons(1) have IH:"distinct (filter f xs)"
    by presburger 
  show ?case proof(cases "f x")
    case True
    with Cons(2) have "j<length xs. f (xs ! j)  x  xs ! j" by fastforce 
    then have "x  set (filter f xs)" by (metis filter_set in_set_conv_nth member_filter)
    then show ?thesis unfolding filter.simps using True IH by simp
  next
    case False
    then show ?thesis unfolding filter.simps using IH by presburger
  qed
qed simp

lemma distinct_is_partition:
  assumes "distinct xs"
  shows "is_partition (map (λx. {x}) xs)"
  using assms proof(induct xs)
  case (Cons x xs)
  then show ?case unfolding list.map(2) is_partition_Cons by force
qed (simp add: is_partition_Nil)

lemma is_partition_append:
  assumes "is_partition xs" and "is_partition zs" 
    and "i < length xs. xs!i   (set zs) = {}"
  shows "is_partition (xs@zs)"
  by (smt (verit, del_insts) add_diff_inverse_nat assms(1) assms(2) assms(3) disjoint_iff is_partition_alt is_partition_alt_def length_append mem_simps(9) nat_add_left_cancel_less nth_append nth_mem)

lemma distinct_is_partitition_sets:
  assumes "distinct xs"
    and "xs = concat ys"
  shows "is_partition (map set ys)" 
  using assms proof(induct ys arbitrary:xs)
  case (Cons y ys)
  have "is_partition (map set ys)" proof-
    from Cons(2,3) have "distinct (concat ys)"
      unfolding concat.simps by simp
    with Cons(1) show ?thesis by simp 
  qed
  moreover from Cons(2,3) have "set y   (set (map set ys)) = {}"
    using distinct_append[of y "concat ys"]by simp
  ultimately show ?case 
    unfolding is_partition_Cons list.map by simp
qed (simp add: is_partition_Nil)

end