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 "domain_permutation A B ⊆ (A →E B) × (A →E B)"
    unfolding domain_permutation_def by auto
next
  show "refl_on (A →E B) (domain_permutation A B)"
  proof (rule refl_onI)
    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 "∀x∈A. 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› ‹∀x∈A. f x = f' (p x)› have "∀x∈A. 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 "∀x∈A. f x = f' (p x)"
      unfolding domain_permutation_def by auto
    from ‹(f', f'') ∈ _› obtain p' where "p' permutes A" and "∀x∈A. 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 "∀x∈A. f x = f'' ((p' ∘ p) x)"
      using ‹∀x∈A. f x = f' (p x)› ‹∀x∈A. 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" "∀x∈A. f x = f' (p x)"
    unfolding domain_permutation_def by auto
  have inv_p: "∀x∈A. 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: "∀x∈A. 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› ‹∀x∈A. f x = f' (p x)› have "∀x∈A. 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 "∀x∈A. 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 ‹∀x∈A. f x = f' (p x)›
    by (metis (mono_tags, opaque_lifting) comp_apply[of f' p] bij_betw_cong[of A f "f' ∘ p" B])
  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 "∀x∈A. 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 "range_permutation A B ⊆ (A →E B) × (A →E B)"
    unfolding range_permutation_def by auto
next
  show "refl_on (A →E B) (range_permutation A B)"
  proof (rule refl_onI)
    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 "∀x∈A. 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› ‹∀x∈A. f x = p (f' x)› have "∀x∈A. 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 "∀x∈A. f x = p (f' x)"
      unfolding range_permutation_def by auto
    from ‹(f', f'') ∈ _› obtain p' where "p' permutes B" and "∀x∈A. 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 "∀x∈A. f x = (p ∘ p') (f'' x)"
      using ‹∀x∈A. f x = p (f' x)› ‹∀x∈A. 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" "∀x∈A. f x = p (f' x)"
    unfolding range_permutation_def by auto
  have inv_p: "∀x∈A. 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" "∀x∈A. 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 "∀x∈A. 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 ‹∀x∈A. 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 "∀b∈B. ∃x∈A. f' x = b"
    from this show "∀b∈B. ∃x∈A. p (f' x) = b"
      using ‹p permutes B› unfolding permutes_def by metis
  next
    assume "∀b∈B. ∃x∈A. p (f' x) = b"
    from this show "∀b∈B. ∃x∈A. f' x = b"
      using ‹p permutes B› by (metis bij_betwE permutes_imp_bij permutes_inverses(2))
  qed
  also have "¬ (∀b∈B. ∃x∈A. 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 "domain_and_range_permutation A B ⊆ (A →E B) × (A →E B)"
    unfolding domain_and_range_permutation_def by auto
next
  show "refl_on (A →E B) (domain_and_range_permutation A B)"
  proof (rule refl_onI)
    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 "∀x∈A. 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 ‹∀x∈A. f x = pB (f' (pA x))› have "∀x∈A. 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 "∀x∈A. 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 "∀x∈A. 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 "∀x∈A. f x = (pB ∘ p'B) (f'' ((p'A ∘ pA) x))"
      using ‹∀x∈A. f' x = p'B (f'' (p'A x))› ‹∀x∈A. 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 "∀x∈A. 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 ‹∀x∈A. 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: ‹∀x∈A. 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 inj_on_subset)
    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 "∀x∈A. 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 ‹∀x∈A. 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 "∀x∈A. 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 ‹∀x∈A. 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" "∀x∈A. 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 ‹∀x∈A. 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