theory Multiset_new
  imports "~~/src/HOL/Library/Multiset"
begin

subsection \<open>The multiset extension is cancellative for multiset union\<close>

lemma mult_cancel:
  assumes "trans s" and "irrefl s"
  shows "(X + Z, Y + Z) \<in> mult s \<longleftrightarrow> (X, Y) \<in> mult s" (is "?L \<longleftrightarrow> ?R")
proof
  assume ?L thus ?R
  proof (induct Z)
    case (add Z z)
    obtain X' Y' Z' where *: "X + Z + {#z#} = Z' + X'" "Y + Z + {#z#} = Z' + Y'" "Y' \<noteq> {#}"
      "\<forall>x \<in> set_mset X'. \<exists>y \<in> set_mset Y'. (x, y) \<in> s"
      using mult_implies_one_step[OF `trans s` add(2)] unfolding union_assoc by blast
    consider Z2 where "Z' = Z2 + {#z#}" | X2 Y2 where "X' = X2 + {#z#}" "Y' = Y2 + {#z#}"
      using *(1,2) by (metis mset_add union_iff union_single_eq_member)
    thus ?case
    proof (cases)
      case 1 thus ?thesis using * one_step_implies_mult[of Y' X' s Z2]
        by (auto simp: union_commute[of _ "{#_#}"] union_assoc intro: add(1))
    next
      case 2 then obtain y where "y \<in> set_mset Y2" "(z, y) \<in> s" using *(4) `irrefl s`
        by (auto simp: irrefl_def)
      moreover with transD[OF `trans s` _ this(2)]
      have "x' \<in> set_mset X2 \<Longrightarrow> \<exists>y \<in> set_mset Y2. (x', y) \<in> s" for x'
        using 2 *(4)[rule_format, of x'] by auto
      ultimately show ?thesis using  * one_step_implies_mult[of Y2 X2 s Z'] 2
        by (force simp: union_commute[of "{#_#}"] union_assoc[symmetric] intro: add(1))
    qed
  qed auto
next
  assume ?R then obtain I J K
    where "Y = I + J" "X = I + K" "J \<noteq> {#}" "\<forall>k \<in> set_mset K. \<exists>j \<in> set_mset J. (k, j) \<in> s"
    using mult_implies_one_step[OF `trans s`] by blast
  thus ?L using one_step_implies_mult[of J K s "I + Z"] by (auto simp: ac_simps)
qed

lemma mult_cancel_max:
  assumes "trans s" and "irrefl s"
  shows "(X, Y) \<in> mult s \<longleftrightarrow> (X - X #\<inter> Y, Y - X #\<inter> Y) \<in> mult s" (is "?L \<longleftrightarrow> ?R")
proof -
  have "X - X #\<inter> Y + X #\<inter> Y = X" "Y - X #\<inter> Y + X #\<inter> Y = Y" by (auto simp: count_inject[symmetric])
  thus ?thesis using mult_cancel[OF assms, of "X - X #\<inter> Y"  "X #\<inter> Y" "Y - X #\<inter> Y"] by auto
qed

subsection \<open>Quasi-executable version of the multiset extension\<close>

text \<open>
  Predicate variants of \<open>mult\<close> and the reflexive closure of \<open>mult\<close>, which are
  executable whenever the given predicate \<open>P\<close> is. Together with the
  standard code equations for \<open>op #\<inter>\<close> and \<open>op -\<close> this should yield
  a quadratic (with respect to calls to \<open>P\<close>) implementation of \<open>multeqp\<close>.
\<close>

definition multp :: "('a \<Rightarrow> 'a \<Rightarrow> bool) \<Rightarrow> 'a multiset \<Rightarrow> 'a multiset \<Rightarrow> bool" where
  "multp P N M =
    (let Z = M #\<inter> N; X = M - Z; Y = N - Z in
    X \<noteq> {#} \<and> (\<forall>y \<in> set_mset Y. \<exists>x \<in> set_mset X. P y x))"

definition multeqp :: "('a \<Rightarrow> 'a \<Rightarrow> bool) \<Rightarrow> 'a multiset \<Rightarrow> 'a multiset \<Rightarrow> bool" where
  "multeqp P N M =
    (let Z = M #\<inter> N; X = M - Z; Y = N - Z in
    (\<forall>y \<in> set_mset Y. \<exists>x \<in> set_mset X. P y x))"

lemma multp_iff:
  assumes "irrefl R" and "trans R" and [simp]: "\<And>x y. P x y \<longleftrightarrow> (x, y) \<in> R"
  shows "multp P N M \<longleftrightarrow> (N, M) \<in> mult R" (is "?L \<longleftrightarrow> ?R")
proof -
  have *: "M #\<inter> N + (N - M #\<inter> N) = N" "M #\<inter> N + (M - M #\<inter> N) = M"
    "(M - M #\<inter> N) #\<inter> (N - M #\<inter> N) = {#}" by (auto simp: count_inject[symmetric])
  show ?thesis
  proof
    assume ?L thus ?R
      using one_step_implies_mult[of "M - M #\<inter> N" "N - M #\<inter> N" R "M #\<inter> N"] *
      by (auto simp: multp_def Let_def)
  next
    { fix I J K :: "'a multiset" assume "(I + J) #\<inter> (I + K) = {#}"
      then have "count I x = 0" for x by (auto simp: count_inject[symmetric] dest!: fun_cong[of _ _ x])
      then have "I = {#}" by (auto simp: count_inject[symmetric])
    } note [dest!] = this
    assume ?R thus ?L
      using mult_implies_one_step[OF assms(2), of "N - M #\<inter> N" "M - M #\<inter> N"]
        mult_cancel_max[OF assms(2,1), of "N" "M"] * by (auto simp: multp_def ac_simps)
  qed
qed

lemma multeqp_iff:
  assumes "irrefl R" and "trans R" and "\<And>x y. P x y \<longleftrightarrow> (x, y) \<in> R"
  shows "multeqp P N M \<longleftrightarrow> (N, M) \<in> (mult R)\<^sup>="
proof -
  { assume "N \<noteq> M" "M - M #\<inter> N = {#}"
    then obtain y where "count N y \<noteq> count M y" by (auto simp: count_inject[symmetric])
    then have "\<exists>y. count M y < count N y" using `M - M #\<inter> N = {#}`
      by (auto simp: count_inject[symmetric] dest!: le_neq_implies_less fun_cong[of _ _ y])
  }
  then have "multeqp P N M \<longleftrightarrow> multp P N M \<or> N = M"
    by (auto simp: multeqp_def multp_def Let_def in_diff_count)
  thus ?thesis using multp_iff[OF assms] by simp
qed

subsection \<open>Monotonicity of the multiset extension\<close>

lemma mono_mult1:
  assumes "s \<subseteq> s'" shows "mult1 s \<subseteq> mult1 s'"
unfolding mult1_def using assms by blast

lemma mono_mult:
  assumes "s \<subseteq> s'" shows "mult s \<subseteq> mult s'"
unfolding mult_def using mono_mult1[OF assms] trancl_mono by blast

end