Theory Utilities

section ‹Utilities›

theory Utilities
  imports
    "Finite-Map-Extras.Finite_Map_Extras"
begin

subsection ‹Utilities for lists›

fun foldr1 :: "('a  'a  'a)  'a list  'a" where
  "foldr1 f [x] = x"
| "foldr1 f (x # xs) = f x (foldr1 f xs)"
| "foldr1 f [] = undefined f"

abbreviation lset where "lset  List.set"

lemma rev_induct2 [consumes 1, case_names Nil snoc]:
  assumes "length xs = length ys"
  and "P [] []"
  and "x xs y ys. length xs = length ys  P xs ys  P (xs @ [x]) (ys @ [y])"
  shows "P xs ys"
using assms proof (induction xs arbitrary: ys rule: rev_induct)
  case (snoc x xs)
  then show ?case by (cases ys rule: rev_cases) simp_all
qed simp

subsection ‹Utilities for finite maps›

no_syntax
  "_fmaplet" :: "['a, 'a]  fmaplet" (‹_ /$$:=/ _›)
  "_fmaplets" :: "['a, 'a]  fmaplet" (‹_ /[$$:=]/ _›)

syntax
  "_fmaplet" :: "['a, 'a]  fmaplet" (‹_ // _›)
  "_fmaplets" :: "['a, 'a]  fmaplet" (‹_ /[↣]/ _›)

lemma fmdom'_fmap_of_list [simp]:
  shows "fmdom' (fmap_of_list ps) = lset (map fst ps)"
  by (induction ps) force+

lemma fmran'_singleton [simp]:
  shows "fmran' {k  v} = {v}"
proof -
  have "v'  fmran' {k  v}  v' = v" for v'
  proof -
    assume "v'  fmran' {k  v}"
    fix k'
    have "fmdom' {k  v} = {k}"
      by simp
    then show "v' = v"
    proof (cases "k' = k")
      case True
      with v'  fmran' {k  v} show ?thesis
        using fmdom'I by fastforce
    next
      case False
      with fmdom' {k  v} = {k} and v'  fmran' {k  v} show ?thesis
        using fmdom'I by fastforce
    qed
  qed
  moreover have "v  fmran' {k  v}"
    by (simp add: fmran'I)
  ultimately show ?thesis
    by blast
qed

lemma fmran'_fmupd [simp]:
  assumes "m $$ x = None"
  shows "fmran' (m(x  y)) = {y}  fmran' m"
using assms proof (intro subset_antisym subsetI)
  fix x'
  assume "m $$ x = None" and "x'  fmran' (m(x  y))"
  then show "x'  {y}  fmran' m"
    by (auto simp add: fmlookup_ran'_iff, metis option.inject)
next
  fix x'
  assume "m $$ x = None" and "x'  {y}  fmran' m"
  then show "x'  fmran' (m(x  y))"
    by (force simp add: fmlookup_ran'_iff)
qed

lemma fmran'_fmadd [simp]:
  assumes "fmdom' m  fmdom' m' = {}"
  shows "fmran' (m ++f m') = fmran' m  fmran' m'"
using assms proof (intro subset_antisym subsetI)
  fix x
  assume "fmdom' m  fmdom' m' = {}" and "x  fmran' (m ++f m')"
  then show "x  fmran' m  fmran' m'"
    by (auto simp add: fmlookup_ran'_iff) meson
next
  fix x
  assume "fmdom' m  fmdom' m' = {}" and "x  fmran' m  fmran' m'"
  then show "x  fmran' (m ++f m')"
    using fmap_disj_comm and fmlookup_ran'_iff by fastforce
qed

lemma finite_fmran':
  shows "finite (fmran' m)"
  by (simp add: fmran'_alt_def)

lemma fmap_of_zipped_list_range:
  assumes "length ks = length vs"
  and "m = fmap_of_list (zip ks vs)"
  and "k  fmdom' m"
  shows "m $$! k  lset vs"
  using assms by (induction arbitrary: m rule: list_induct2) auto

lemma fmap_of_zip_nth [simp]:
  assumes "length ks = length vs"
  and "distinct ks"
  and "i < length ks"
  shows "fmap_of_list (zip ks vs) $$! (ks ! i) = vs ! i"
  using assms by (simp add: fmap_of_list.rep_eq map_of_zip_nth)

lemma fmap_of_zipped_list_fmran' [simp]:
  assumes "distinct (map fst ps)"
  shows "fmran' (fmap_of_list ps) = lset (map snd ps)"
using assms proof (induction ps)
  case Nil
  then show ?case
    by auto
next
  case (Cons p ps)
  then show ?case
  proof (cases "p  lset ps")
    case True
    then show ?thesis
      using Cons.prems by auto
  next
    case False
    obtain k and v where "p = (k, v)"
      by fastforce
    with Cons.prems have "k  fmdom' (fmap_of_list ps)"
      by auto
    then have "fmap_of_list (p # ps) = {k  v} ++f fmap_of_list ps"
      using p = (k, v) and fmap_singleton_comm by fastforce
    with Cons.prems have "fmran' (fmap_of_list (p # ps)) = {v}  fmran' (fmap_of_list ps)"
      by (simp add: p = (k, v))
    then have "fmran' (fmap_of_list (p # ps)) = {v}  lset (map snd ps)"
      using Cons.IH and Cons.prems by force
    then show ?thesis
      by (simp add: p = (k, v))
  qed
qed

lemma fmap_of_list_nth [simp]:
  assumes "distinct (map fst ps)"
    and "j < length ps"
  shows "fmap_of_list ps $$ ((map fst ps) ! j) = Some (map snd ps ! j)"
  using assms by (induction j) (simp_all add: fmap_of_list.rep_eq)

lemma fmap_of_list_nth_split [simp]:
  assumes "distinct xs"
    and "j < length xs"
    and "length ys = length xs" and "length zs = length xs"
  shows "fmap_of_list (zip xs (take k ys @ drop k zs)) $$ (xs ! j) =
          (if j < k then Some (take k ys ! j) else Some (drop k zs ! (j - k)))"
using assms proof (induction k arbitrary: xs ys zs j)
  case 0
  then show ?case
    by (simp add: fmap_of_list.rep_eq map_of_zip_nth)
next
  case (Suc k)
  then show ?case
  proof (cases xs)
    case Nil
    with Suc.prems(2) show ?thesis
      by auto
  next
    case (Cons x xs')
    let ?ps = "zip xs (take (Suc k) ys @ drop (Suc k) zs)"
    from Cons and Suc.prems(3,4) obtain y and z and ys' and zs'
      where "ys = y # ys'" and "zs = z # zs'"
      by (metis length_0_conv neq_Nil_conv)
    let ?ps' = "zip xs' (take k ys' @ drop k zs')"
    from Cons have *: "fmap_of_list ?ps = fmap_of_list ((x, y) # ?ps')"
      using ys = y # ys' and zs = z # zs' by fastforce
    also have " = {x  y} ++f fmap_of_list ?ps'"
    proof -
      from ys = y # ys' and zs = z # zs' have "fmap_of_list ?ps' $$ x = None"
        using Cons and Suc.prems(1,3,4) by (simp add: fmdom'_notD)
      then show ?thesis
        using fmap_singleton_comm by fastforce
    qed
    finally have "fmap_of_list ?ps = {x  y} ++f fmap_of_list ?ps'" .
    then show ?thesis
    proof (cases "j = 0")
      case True
      with ys = y # ys' and Cons show ?thesis
        by simp
    next
      case False
      then have "xs ! j = xs' ! (j - 1)"
        by (simp add: Cons)
      moreover from ys = y # ys' and zs = z # zs' have "fmdom' (fmap_of_list ?ps') = lset xs'"
        using Cons and Suc.prems(3,4) by force
      moreover from False and Suc.prems(2) and Cons have "j - 1 < length xs'"
        using le_simps(2) by auto
      ultimately have "fmap_of_list ?ps $$ (xs ! j) = fmap_of_list ?ps' $$ (xs' ! (j - 1))"
        using Cons and * and Suc.prems(1) by auto
      with Suc.IH and Suc.prems(1,3,4) and Cons have **: "fmap_of_list ?ps $$ (xs ! j) =
        (if j - 1 < k then Some (take k ys' ! (j - 1)) else Some (drop k zs' ! ((j - 1) - k)))"
        using j - 1 < length xs' and ys = y # ys' and zs = z # zs' by simp
      then show ?thesis
      proof (cases "j - 1 < k")
        case True
        with False and ** show ?thesis
          using ys = y # ys' by auto
      next
        case False
        from Suc.prems(1) and Cons and j - 1 < length xs' and xs ! j = xs' ! (j - 1) have "j > 0"
          using nth_non_equal_first_eq by fastforce
        with False have "j  Suc k"
          by simp
        moreover have "fmap_of_list ?ps $$ (xs ! j) = Some (drop (Suc k) zs ! (j - Suc k))"
          using ** and False and zs = z # zs' by fastforce
        ultimately show ?thesis
          by simp
      qed
    qed
  qed
qed

lemma fmadd_drop_cancellation [simp]:
  assumes "m $$ k = Some v"
  shows "{k  v} ++f fmdrop k m = m"
using assms proof (induction m)
  case fmempty
  then show ?case
    by simp
next
  case (fmupd k' v' m')
  then show ?case
  proof (cases "k' = k")
    case True
    with fmupd.prems have "v = v'"
      by fastforce
    have "fmdrop k' (m'(k'  v')) = m'"
      unfolding fmdrop_fmupd_same using fmdrop_idle'[OF fmdom'_notI[OF fmupd.hyps]] by (unfold True)
    then have "{k  v} ++f fmdrop k' (m'(k'  v')) = {k  v} ++f m'"
      by simp
    then show ?thesis
      using fmap_singleton_comm[OF fmupd.hyps] by (simp add: True v = v')
  next
    case False
    with fmupd.prems have "m' $$ k = Some v"
      by force
    from False have "{k  v} ++f fmdrop k (m'(k'  v')) = {k  v} ++f (fmdrop k m')(k'  v')"
      by (simp add: fmdrop_fmupd)
    also have " = ({k  v} ++f fmdrop k m')(k'  v')"
      by fastforce
    also from fmupd.prems and fmupd.IH[OF m' $$ k = Some v] have " = m'(k'  v')"
      by force
    finally show ?thesis .
  qed
qed

lemma fmap_of_list_fmmap [simp]:
  shows "fmap_of_list (map2 (λv' A'. (v', f A')) xs ys) = fmmap f (fmap_of_list (zip xs ys))"
  unfolding fmmap_of_list
  using cond_case_prod_eta
    [where f = "λv' A'.(v', f A')" and g = "apsnd f", unfolded apsnd_conv, simplified]
  by (rule arg_cong)

end