Theory Equiv_Relations_on_Functions

(*  Author: Lukas Bulwahn <lukas.bulwahn-at-gmail.com> *)

section ‹Definition of Equivalence Classes›

theory Equiv_Relations_on_Functions
imports
  Preliminaries
  Twelvefold_Way_Core
begin

subsection ‹Permutation on the Domain›

definition domain_permutation
where
  "domain_permutation A B = {(f, f')  (A E B) × (A E B). p. p permutes A  (x  A. f x = f' (p x))}"

lemma equiv_domain_permutation:
  "equiv (A E B) (domain_permutation A B)"
proof (rule equivI)
  show "refl_on (A E B) (domain_permutation A B)"
  proof (rule refl_onI)
    show "domain_permutation A B  (A E B) × (A E B)"
      unfolding domain_permutation_def by auto
  next
    fix f
    assume "f  A E B"
    from this show "(f, f)  domain_permutation A B"
      using permutes_id unfolding domain_permutation_def by fastforce
  qed
next
  show "sym (domain_permutation A B)"
  proof (rule symI)
    fix f f'
    assume "(f, f')  domain_permutation A B"
    from this obtain p where "p permutes A" and "xA. f x = f' (p x)"
      unfolding domain_permutation_def by auto
    from (f, f')  domain_permutation A B have "f  A E B" "f'  A E B"
      unfolding domain_permutation_def by auto
    moreover from p permutes A have "inv p permutes A"
      by (simp add: permutes_inv)
    moreover from p permutes A xA. f x = f' (p x) have "xA. f' x = f (inv p x)"
      using permutes_in_image permutes_inverses(1) by (metis (mono_tags, opaque_lifting))
    ultimately show "(f', f)  domain_permutation A B"
      unfolding domain_permutation_def by auto
  qed
next
  show "trans (domain_permutation A B)"
  proof (rule transI)
    fix f f' f''
    assume "(f, f')  domain_permutation A B" "(f', f'')  domain_permutation A B"
    from (f, f')  _ obtain p where "p permutes A" and "xA. f x = f' (p x)"
      unfolding domain_permutation_def by auto
    from (f', f'')  _ obtain p' where "p' permutes A" and "xA. f' x = f'' (p' x)"
      unfolding domain_permutation_def by auto
    from (f, f')  domain_permutation A B have "f  A E B"
      unfolding domain_permutation_def by auto
    moreover from (f', f'')  domain_permutation A B have "f''  A E B"
      unfolding domain_permutation_def by auto
    moreover from p permutes A p' permutes A have "(p'  p) permutes A"
      by (simp add: permutes_compose)
    moreover have "xA. f x = f'' ((p'  p) x)"
      using xA. f x = f' (p x) xA. f' x = f'' (p' x) p permutes A
      by (simp add: permutes_in_image)
    ultimately show "(f, f'')  domain_permutation A B"
      unfolding domain_permutation_def by auto
  qed
qed

subsubsection ‹Respecting Functions›

lemma inj_on_respects_domain_permutation:
  "(λf. inj_on f A) respects domain_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_permutation A B"
  from this obtain p where p: "p permutes A" "xA. f x = f' (p x)"
    unfolding domain_permutation_def by auto
  have inv_p: "xA. f' x = f (inv p x)"
    using p by (metis permutes_inverses(1) permutes_not_in)
  show "inj_on f A  inj_on f' A"
  proof
    assume "inj_on f A"
    show "inj_on f' A"
    proof (rule inj_onI)
      fix a a'
      assume "a  A" "a'  A" "f' a = f' a'"
      from this p permutes A have "inv p a  A" "inv p a'  A"
        by (simp add:  permutes_in_image permutes_inv)+
      have "f (inv p a) = f (inv p a')"
        using f' a = f' a' a  A a'  A inv_p by auto
      from inj_on f A this inv p a  A inv p a'  A have "inv p a = inv p a'"
        using inj_on_contraD by fastforce
      from this show "a = a'"
        by (metis p permutes A permutes_inverses(1))
    qed
  next
    assume "inj_on f' A"
    from this p show "inj_on f A"
      unfolding inj_on_def
      by (metis inj_on_contraD permutes_in_image permutes_inj_on)
  qed
qed

lemma image_respects_domain_permutation:
  "(λf. f ` A) respects (domain_permutation A B)"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_permutation A B"
  from this obtain p where p: "p permutes A" and f_eq: "xA. f x = f' (p x)"
    unfolding domain_permutation_def by auto
  show "f ` A = f' ` A"
  proof
    from p f_eq show "f ` A  f' ` A"
      by (auto simp add: permutes_in_image)
  next
    from p permutes A xA. f x = f' (p x) have "xA. f' x = f (inv p x)"
      using permutes_in_image permutes_inverses(1) by (metis (mono_tags, opaque_lifting))
    from this show "f' ` A  f ` A"
      using p permutes A by (auto simp add: permutes_inv permutes_in_image)
  qed
qed

lemma surjective_respects_domain_permutation:
  "(λf. f ` A = B) respects domain_permutation A B"
by (metis image_respects_domain_permutation congruentD congruentI)

lemma bij_betw_respects_domain_permutation:
  "(λf. bij_betw f A B) respects domain_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_permutation A B"
  from this obtain p where "p permutes A" and "xA. f x = f' (p x)"
    unfolding domain_permutation_def by auto
  have "bij_betw f A B  bij_betw (f' o p) A B"
    using xA. f x = f' (p x)
    by (metis (mono_tags, opaque_lifting) bij_betw_cong comp_apply)
  also have "...  bij_betw f' A B"
    using p permutes A
    by (auto intro!: bij_betw_comp_iff[symmetric] permutes_imp_bij)
  finally show "bij_betw f A B  bij_betw f' A B" .
qed

lemma image_mset_respects_domain_permutation:
  shows "(λf. image_mset f (mset_set A)) respects (domain_permutation A B)"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_permutation A B"
  from this obtain p where "p permutes A" and "xA. f x = f' (p x)"
    unfolding domain_permutation_def by auto
  from this show "image_mset f (mset_set A) = image_mset f' (mset_set A)"
    using permutes_implies_image_mset_eq by fastforce
qed

subsection ‹Permutation on the Range›

definition range_permutation
where
  "range_permutation A B = {(f, f')  (A E B) × (A E B). p. p permutes B  (x  A. f x = p (f' x))}"

lemma equiv_range_permutation:
  "equiv (A E B) (range_permutation A B)"
proof (rule equivI)
  show "refl_on (A E B) (range_permutation A B)"
  proof (rule refl_onI)
    show "range_permutation A B  (A E B) × (A E B)"
      unfolding range_permutation_def by auto
  next
    fix f
    assume "f  A E B"
    from this show "(f, f)  range_permutation A B"
      using permutes_id unfolding range_permutation_def by fastforce
  qed
next
  show "sym (range_permutation A B)"
  proof (rule symI)
    fix f f'
    assume "(f, f')  range_permutation A B"
    from this obtain p where "p permutes B" and "xA. f x = p (f' x)"
      unfolding range_permutation_def by auto
    from (f, f')  range_permutation A B have "f  A E B" "f'  A E B"
      unfolding range_permutation_def by auto
    moreover from p permutes B have "inv p permutes B"
      by (simp add: permutes_inv)
    moreover from p permutes B xA. f x = p (f' x) have "xA. f' x = inv p (f x)"
      by (simp add: permutes_inverses(2))
    ultimately show "(f', f)  range_permutation A B"
      unfolding range_permutation_def by auto
  qed
next
  show "trans (range_permutation A B)"
  proof (rule transI)
    fix f f' f''
    assume "(f, f')  range_permutation A B" "(f', f'')  range_permutation A B"
    from (f, f')  _ obtain p where "p permutes B" and "xA. f x = p (f' x)"
      unfolding range_permutation_def by auto
    from (f', f'')  _ obtain p' where "p' permutes B" and "xA. f' x = p' (f'' x)"
      unfolding range_permutation_def by auto
    from (f, f')  range_permutation A B have "f  A E B"
      unfolding range_permutation_def by auto
    moreover from (f', f'')  range_permutation A B have "f''  A E B"
      unfolding range_permutation_def by auto
    moreover from p permutes B p' permutes B have "(p  p') permutes B"
      by (simp add: permutes_compose)
    moreover have "xA. f x = (p  p') (f'' x)"
      using xA. f x = p (f' x) xA. f' x = p' (f'' x) by auto
    ultimately show "(f, f'')  range_permutation A B"
      unfolding range_permutation_def by auto
  qed
qed

subsubsection ‹Respecting Functions›

lemma inj_on_respects_range_permutation:
  "(λf. inj_on f A) respects range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  range_permutation A B"
  from this obtain p where p: "p permutes B" "xA. f x = p (f' x)"
    unfolding range_permutation_def by auto
  have inv_p: "xA. f' x = inv p (f x)"
    using p by (simp add: permutes_inverses(2))
  show "inj_on f A  inj_on f' A"
  proof
    assume "inj_on f A"
    from this p show "inj_on f' A"
      unfolding inj_on_def by auto
  next
    assume "inj_on f' A"
    from this inv_p show "inj_on f A"
      unfolding inj_on_def by auto
  qed
qed

lemma surj_on_respects_range_permutation:
  "(λf. f ` A = B) respects range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume a: "(f, f')  range_permutation A B"
  from this have "f  A E B" "f'  A E B"
    unfolding range_permutation_def by auto
  from a obtain p where p: "p permutes B" "xA. f x = p (f' x)"
    unfolding range_permutation_def by auto
  have 1: "f ` A = (λx. p (f' x)) ` A"
    using p by (meson image_cong)
  have 2: "inv p ` ((λx. p (f' x)) ` A) = f' ` A"
    using p by (simp add: image_image image_inv_f_f permutes_inj)
  show "(f ` A = B) = (f' ` A = B)"
  proof
    assume "f ` A = B"
    from this 1 2 show "f' ` A = B"
      using p by (simp add: permutes_image permutes_inv)
  next
    assume "f' ` A = B"
    from this 1 2 show "f ` A = B"
      using p by (metis image_image permutes_image)
  qed
qed

lemma bij_betw_respects_range_permutation:
  "(λf. bij_betw f A B) respects range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  range_permutation A B"
  from this obtain p where "p permutes B" and "xA. f x = p (f' x)"
    and "f'  A E B"
    unfolding range_permutation_def by auto
  have "bij_betw f A B  bij_betw (p o f') A B"
    using xA. f x = p (f' x)
    by (metis (mono_tags, opaque_lifting) bij_betw_cong comp_apply)
  also have "...  bij_betw f' A B"
    using f'  A E B p permutes B
    by (auto intro!: bij_betw_comp_iff2[symmetric] permutes_imp_bij)
  finally show "bij_betw f A B  bij_betw f' A B" .
qed

lemma domain_partitions_respects_range_permutation:
  "(λf. (λb. {x  A. f x = b}) ` B - {{}}) respects range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  range_permutation A B"
  from this obtain p where p: "p permutes B" "x  A. f x = p (f' x)"
    unfolding range_permutation_def by blast
  have "{}  (λb. {x  A. f' x = b}) ` B  ¬ (b  B. x  A. f' x = b)" by auto
  also have "(b  B. x  A. f' x = b)  (b  B. x  A. p (f' x) = b)"
  proof
    assume "bB. xA. f' x = b"
    from this show "bB. xA. p (f' x) = b"
      using p permutes B unfolding permutes_def by metis
  next
    assume "bB. xA. p (f' x) = b"
    from this show "bB. xA. f' x = b"
      using p permutes B by (metis bij_betwE permutes_imp_bij permutes_inverses(2))
  qed
  also have "¬ (bB. xA. p (f' x) = b)  {}  (λb. {x  A. p (f' x) = b}) ` B" by auto
  finally have "{}  (λb. {x  A. f' x = b}) ` B  {}  (λb. {x  A. p (f' x) = b}) ` B" .
  moreover have "(λb. {x  A. f' x = b}) ` B = (λb. {x  A. p (f' x) = b}) ` B"
    using p permutes B permutes_implies_inv_image_on_eq by blast
  ultimately have "(λb. {x  A. f' x = b}) ` B - {{}} = (λb. {x  A. p (f' x) = b}) ` B - {{}}" by auto
  also have " = (λb. {x  A. f x = b}) ` B - {{}}"
    using x  A. f x = p (f' x) Collect_cong image_cong by auto
  finally show "(λb. {x  A. f x = b}) ` B - {{}} = (λb. {x  A. f' x = b}) ` B - {{}}" ..
qed

subsection ‹Permutation on the Domain and the Range›

definition domain_and_range_permutation
where
  "domain_and_range_permutation A B = {(f, f')  (A E B) × (A E B).
    pA pB. pA permutes A  pB permutes B  (x  A. f x = pB (f' (pA x)))}"

lemma equiv_domain_and_range_permutation:
  "equiv (A E B) (domain_and_range_permutation A B)"
proof (rule equivI)
  show "refl_on (A E B) (domain_and_range_permutation A B)"
  proof (rule refl_onI)
    show "domain_and_range_permutation A B  (A E B) × (A E B)"
      unfolding domain_and_range_permutation_def by auto
  next
    fix f
    assume "f  A E B"
    from this show "(f, f)  domain_and_range_permutation A B"
      using permutes_id[of A] permutes_id[of B]
      unfolding domain_and_range_permutation_def by fastforce
  qed
next
  show "sym (domain_and_range_permutation A B)"
  proof (rule symI)
    fix f f'
    assume "(f, f')  domain_and_range_permutation A B"
    from this obtain pA pB where "pA permutes A" "pB permutes B" and "xA. f x = pB (f' (pA x))"
      unfolding domain_and_range_permutation_def by auto
    from (f, f')  domain_and_range_permutation A B have f: "f  A E B" "f'  A E B"
      unfolding domain_and_range_permutation_def by auto
    moreover from pA permutes A pB permutes B have "inv pA permutes A" "inv pB permutes B"
      by (auto simp add: permutes_inv)
    moreover from xA. f x = pB (f' (pA x)) have "xA. f' x = inv pB (f (inv pA x))"
      using pA permutes A pB permutes B inv pA permutes A inv pB permutes B
      by (metis (no_types, lifting) bij_betwE bij_inv_eq_iff permutes_bij permutes_imp_bij)
    ultimately show "(f', f)  domain_and_range_permutation A B"
      unfolding domain_and_range_permutation_def by auto
  qed
next
  show "trans (domain_and_range_permutation A B)"
  proof (rule transI)
    fix f f' f''
    assume "(f, f')  domain_and_range_permutation A B"
    assume "(f', f'')  domain_and_range_permutation A B"
    from (f, f')  _ obtain pA pB where
      "pA permutes A" "pB permutes B" and "xA. f x = pB (f' (pA x))"
      unfolding domain_and_range_permutation_def by auto
    from (f', f'')  _ obtain p'A p'B where
      "p'A permutes A" "p'B permutes B" and "xA. f' x = p'B (f'' (p'A x))"
      unfolding domain_and_range_permutation_def by auto
    from (f, f')  domain_and_range_permutation A B have "f  A E B"
      unfolding domain_and_range_permutation_def by auto
    moreover from (f', f'')  domain_and_range_permutation A B have "f''  A E B"
      unfolding domain_and_range_permutation_def by auto
    moreover from pA permutes A p'A permutes A have "(p'A  pA) permutes A"
      by (simp add: permutes_compose)
    moreover from pB permutes B p'B permutes B have "(pB  p'B) permutes B"
      by (simp add: permutes_compose)
    moreover have "xA. f x = (pB  p'B) (f'' ((p'A  pA) x))"
      using xA. f' x = p'B (f'' (p'A x)) xA. f x = pB (f' (pA x)) pA permutes A
      by (simp add: permutes_in_image)
    ultimately show "(f, f'')  domain_and_range_permutation A B"
      unfolding domain_and_range_permutation_def by fastforce
  qed
qed

subsubsection ‹Respecting Functions›

lemma inj_on_respects_domain_and_range_permutation:
  "(λf. inj_on f A) respects domain_and_range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_and_range_permutation A B"
  from this obtain pA pB where "pA permutes A" "pB permutes B" and "xA. f x = pB (f' (pA x))"
    unfolding domain_and_range_permutation_def by auto
  from (f, f')  domain_and_range_permutation A B have "f' ` A  B"
    unfolding domain_and_range_permutation_def by auto
  from pA permutes A have "pA ` A = A" by (auto simp add: permutes_image)
  from pA permutes A have "inj_on pA A"
    using bij_betw_imp_inj_on permutes_imp_bij by blast
  from pB permutes B have "inj_on pB B"
    using bij_betw_imp_inj_on permutes_imp_bij by blast
  show "inj_on f A  inj_on f' A"
  proof -
    have "inj_on f A  inj_on (λx. pB (f' (pA x))) A"
      using xA. f x = pB (f' (pA x)) inj_on_cong comp_apply by fastforce
    have "inj_on f A  inj_on (pB o f' o pA) A"
      by (simp add: xA. f x = pB (f' (pA x)) inj_on_def)
    also have "inj_on (pB o f' o pA) A  inj_on (pB o f') A"
      using inj_on pA A pA ` A = A
      by (auto dest: inj_on_imageI intro: comp_inj_on)
    also have "inj_on (pB o f') A  inj_on f' A"
      using inj_on pB B f' ` A  B
      by (auto dest: inj_on_imageI2 intro: comp_inj_on subset_inj_on)
    finally show ?thesis .
  qed
qed

lemma surjective_respects_domain_and_range_permutation:
  "(λf. f ` A = B) respects domain_and_range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_and_range_permutation A B"
  from this obtain pA pB where
    permutes: "pA permutes A" "pB permutes B" and "xA. f x = pB (f' (pA x))"
    unfolding domain_and_range_permutation_def by auto
  from permutes have "pA ` A = A" "pB ` B = B" by (auto simp add: permutes_image)
  from pB permutes B have "inj pB" by (simp add: permutes_inj)
  show "(f ` A = B)  (f' ` A = B)"
  proof -
    have "f ` A = B  (λx. pB (f' (pA x))) ` A = B"
      using xA. f x = pB (f' (pA x)) by (metis (mono_tags, lifting) image_cong)
    also have "(λx. pB (f' (pA x))) ` A = B  (λx. pB (f' x)) ` A = B"
      using pA ` A = A by (metis image_image)
    also have "(λx. pB (f' x)) ` A = B  (f' ` A = B)"
      using pB ` B = B inj pB by (metis image_image image_inv_f_f)
    finally show ?thesis .
  qed
qed

lemma bij_betw_respects_domain_and_range_permutation:
  "(λf. bij_betw f A B) respects domain_and_range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_and_range_permutation A B"
  from this obtain pA pB where "pA permutes A" "pB permutes B"
    and "xA. f x = pB (f' (pA x))" and "f'  A E B"
    unfolding domain_and_range_permutation_def by auto
  have "bij_betw f A B  bij_betw (pB o f' o pA) A B"
    using xA. f x = pB (f' (pA x)) bij_betw_congI by fastforce
  also have "...  bij_betw (pB o f')  A B"
    using pA permutes A
    by (auto intro!: bij_betw_comp_iff[symmetric] permutes_imp_bij)
  also have "...  bij_betw f' A B"
    using f'  A E B pB permutes B
    by (auto intro!: bij_betw_comp_iff2[symmetric] permutes_imp_bij)
  finally show "bij_betw f A B  bij_betw f' A B" .
qed

lemma count_image_mset':
  "count (image_mset f A) x = sum (count A) {x'  set_mset A. f x' = x}"
proof -
  have "count (image_mset f A) x = sum (count A) (f -` {x}  set_mset A)"
    unfolding count_image_mset ..
  also have " = sum (count A) {x'  set_mset A. f x' = x}"
  proof -
    have "(f -` {x}  set_mset A) = {x'  set_mset A. f x' = x}" by blast
    from this show ?thesis by simp
  qed
  finally show ?thesis .
qed

lemma multiset_of_partition_cards_respects_domain_and_range_permutation:
  assumes "finite B"
  shows "(λf. image_mset (λX. card X) (mset_set (((λb. {x  A. f x = b})) ` B - {{}}))) respects domain_and_range_permutation A B"
proof (rule congruentI)
  fix f f'
  assume "(f, f')  domain_and_range_permutation A B"
  from this obtain pA pB where "pA permutes A" "pB permutes B" "xA. f x = pB (f' (pA x))"
    unfolding domain_and_range_permutation_def by auto
  have "(λb. {x  A. f x = b}) ` B = (λb. {x  A. pB (f' (pA x)) = b}) ` B"
    using xA. f x = pB (f' (pA x)) by auto
  from this have "image_mset card (mset_set ((λb. {x  A. f x = b}) ` B - {{}})) =
    image_mset card (mset_set ((λb. {x  A. pB (f' (pA x)) = b}) ` B - {{}}))" by simp
  also have "image_mset card (mset_set ((λb. {x  A. pB (f' (pA x)) = b}) ` B - {{}})) =
    image_mset card (mset_set ((λb. {x  A. f' (pA x) = b}) ` B - {{}}))"
    using permutes_implies_inv_image_on_eq[OF pB permutes B, of A] by metis
  also have "image_mset card (mset_set ((λb. {x  A. f' (pA x) = b}) ` B - {{}})) =
    image_mset card (mset_set ((λb. {x  A. f' x = b}) ` B - {{}}))"
  proof (rule multiset_eqI)
    fix n
    have "bij_betw (λX. pA ` X) {X  (λb. {x  A. f' (pA x) = b}) ` B - {{}}. card X = n} {X  (λb. {x  A. f' x = b}) ` B - {{}}. card X = n}"
    proof (rule bij_betw_byWitness)
      show "X{X  (λb. {x  A. f' (pA x) = b}) ` B - {{}}. card X = n}. inv pA ` pA ` X = X"
        by (meson pA permutes A image_inv_f_f permutes_inj)
      show "X{X  (λb. {x  A. f' x = b}) ` B - {{}}. card X = n}. pA ` inv pA ` X = X"
        by (meson pA permutes A image_f_inv_f permutes_surj)
      show "(λX. pA ` X) ` {X  (λb. {x  A. f' (pA x) = b}) ` B - {{}}. card X = n}  {X  (λb. {x  A. f' x = b}) ` B - {{}}. card X = n}"
      proof -
        have "card (pA ` {x  A. f' (pA x) = b}) = card {x  A. f' (pA x) = b}" for b
        proof -
          have "inj_on pA {x  A. f' (pA x) = b}"
            by (metis (no_types, lifting) pA permutes A injD inj_onI permutes_inj)
          from this show ?thesis by (simp add: card_image)
        qed
        moreover have "pA ` {x  A. f' (pA x) = b} = {x  A. f' x = b}" for b
        proof
          show "pA ` {x  A. f' (pA x) = b}  {x  A. f' x = b}"
            by (auto simp add: pA permutes A permutes_in_image)
          show "{x  A. f' x = b}  pA ` {x  A. f' (pA x) = b}"
          proof
            fix x
            assume "x  {x  A. f' x = b}"
            moreover have "pA (inv pA x) = x"
              using pA permutes A permutes_inverses(1) by fastforce
            moreover from x  {x  A. f' x = b} have "inv pA x  A"
              by (simp add: pA permutes A permutes_in_image permutes_inv)
            ultimately show "x  pA ` {x  A. f' (pA x) = b}"
              by (auto intro: image_eqI[where x="inv pA x"])
          qed
        qed
        ultimately show ?thesis by auto
      qed
      show "(λX. inv pA ` X) ` {X  (λb. {x  A. f' x = b}) ` B - {{}}. card X = n}  {X  (λb. {x  A. f' (pA x) = b}) ` B - {{}}. card X = n}"
      proof -
        have "card (inv pA ` {x  A. f' x = b}) = card {x  A. f' x = b}" for b
        proof -
          have "inj_on (inv pA) {x  A. f' x = b}"
            by (metis (no_types, lifting) pA permutes A injD inj_onI permutes_surj surj_imp_inj_inv)
          from this show ?thesis by (simp add: card_image)
        qed
        moreover have "inv pA ` {x  A. f' x = b} = {x  A. f' (pA x) = b}" for b
        proof
          show "inv pA ` {x  A. f' x = b}  {x  A. f' (pA x) = b}"
            using pA permutes A
            by (auto simp add: permutes_in_image permutes_inv permutes_inverses(1))
          show "{x  A. f' (pA x) = b}  inv pA ` {x  A. f' x = b}"
          proof
            fix x
            assume "x  {x  A. f' (pA x) = b}"
            moreover have "inv pA (pA x) = x"
              by (meson pA permutes A permutes_inverses(2))
            moreover from x  {x  A. f' (pA x) = b} have "pA x  A"
              by (simp add: pA permutes A permutes_in_image)
            ultimately show "x  inv pA ` {x  A. f' x = b}"
              by (auto intro: image_eqI[where x="pA x"])
          qed
        qed
        ultimately show ?thesis by auto
      qed
    qed
    from this have "card {x'  (λb. {x  A. f' (pA x) = b}) ` B - {{}}. card x' = n} = card {x'  (λb. {x  A. f' x = b}) ` B - {{}}. card x' = n}"
      by (rule bij_betw_same_card)
    from this show "count (image_mset card (mset_set ((λb. {x  A. f' (pA x) = b}) ` B - {{}}))) n =
      count (image_mset card (mset_set ((λb. {x  A. f' x = b}) ` B - {{}}))) n"
      using finite B by (simp add: count_image_mset')
  qed
  finally show "image_mset card (mset_set ((λb. {x  A. f x = b}) ` B - {{}})) =
    image_mset card (mset_set ((λb. {x  A. f' x = b}) ` B - {{}}))" .
qed

end