Theory Min_Max_Least_Greatest.Min_Max_Least_Greatest_Multiset

theory Min_Max_Least_Greatest_Multiset
  imports
    Relation_Reachability
    Min_Max_Least_Greatest_Set
    "HOL-Library.Multiset"
    "HOL-Library.Multiset_Order"
begin

section ‹Minimal and maximal elements›

definition is_minimal_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R 
    is_minimal_in_mset_wrt R X = is_minimal_in_set_wrt R (set_mset X)"

definition is_maximal_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R 
    is_maximal_in_mset_wrt R X = is_maximal_in_set_wrt R (set_mset X)"

definition is_strictly_minimal_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R 
    is_strictly_minimal_in_mset_wrt R X x  x ∈# X  (y ∈# X - {# x #}. ¬ (R== y x))"

definition is_strictly_maximal_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R 
    is_strictly_maximal_in_mset_wrt R X x  x ∈# X  (y ∈# X - {# x #}. ¬ (R== x y))"

context
  fixes X R
  assumes
    trans: "transp_on (set_mset X) R" and
    asym: "asymp_on (set_mset X) R"
begin


subsection ‹Conversions›

lemma is_minimal_in_mset_wrt_iff:
  "is_minimal_in_mset_wrt R X x  x ∈# X  (y ∈# X. y  x  ¬ R y x)"
  using is_minimal_in_set_wrt_iff[OF trans asym]
  using is_minimal_in_mset_wrt_def[OF trans asym]
  by simp

lemma "is_minimal_in_mset_wrt R X x  x ∈# X  (y ∈# X. ¬ R y x)"
  unfolding is_minimal_in_mset_wrt_iff
proof (rule refl_conj_eq, rule ball_cong)
  show "set_mset X = set_mset X" ..
next
  show "y. y ∈# X  (y  x  ¬ R y x) = (¬ R y x)"
    using asym[THEN asymp_onD] by metis
qed

lemma is_maximal_in_mset_wrt_iff:
  "is_maximal_in_mset_wrt R X x  x ∈# X  (y ∈# X. y  x  ¬ R x y)"
  using is_maximal_in_set_wrt_iff[OF trans asym]
  using is_maximal_in_mset_wrt_def[OF trans asym]
  by simp

lemma "is_maximal_in_mset_wrt R X x  x ∈# X  (y ∈# X. ¬ R x y)"
  unfolding is_maximal_in_mset_wrt_iff
proof (rule refl_conj_eq, rule ball_cong)
  show "set_mset X = set_mset X" ..
next
  show "y. y ∈# X  (y  x  ¬ R x y) = (¬ R x y)"
    using asym[THEN asymp_onD] by metis
qed

lemma is_strictly_minimal_in_mset_wrt_iff:
  "is_strictly_minimal_in_mset_wrt R X x  x ∈# X  (y ∈# X- {# x #}. ¬ R== y x)"
  unfolding is_strictly_minimal_in_mset_wrt_def[OF trans asym]
  by(rule refl)

lemma is_strictly_maximal_in_mset_wrt_iff:
  "is_strictly_maximal_in_mset_wrt R X x  x ∈# X  (y ∈# X- {# x #}. ¬ R== x y)"
  unfolding is_strictly_maximal_in_mset_wrt_def[OF trans asym]
  by(rule refl)

lemma is_minimal_in_mset_wrt_if_is_strictly_minimal_in_mset_wrt: 
  "is_strictly_minimal_in_mset_wrt R X x  is_minimal_in_mset_wrt R X x"
  unfolding is_minimal_in_mset_wrt_iff is_strictly_minimal_in_mset_wrt_iff
  using multi_member_split by fastforce 

lemma is_maximal_in_mset_wrt_if_is_strictly_maximal_in_mset_wrt:
  "is_strictly_maximal_in_mset_wrt R X x  is_maximal_in_mset_wrt R X x"
  unfolding is_maximal_in_mset_wrt_iff is_strictly_maximal_in_mset_wrt_iff
  using multi_member_split by fastforce


subsection ‹Existence›

lemma ex_minimal_in_mset_wrt:
  "X  {#}  m. is_minimal_in_mset_wrt R X m"
  using trans asym ex_minimal_in_set_wrt[of "set_mset X" R] is_minimal_in_mset_wrt_def
  by (metis finite_set_mset set_mset_eq_empty_iff)

lemma ex_maximal_in_mset_wrt:
  "X  {#}  m. is_maximal_in_mset_wrt R X m"
  using trans asym ex_maximal_in_set_wrt[of "set_mset X" R] is_maximal_in_mset_wrt_def
  by (metis finite_set_mset set_mset_eq_empty_iff)


subsection ‹Miscellaneous›

lemma explode_maximal_in_mset_wrt:
  assumes max: "is_maximal_in_mset_wrt R X x"
  obtains n :: nat where "replicate_mset (Suc n) x + {#y ∈# X. y  x#} = X"
  using max[unfolded is_maximal_in_mset_wrt_iff]
  by (metis filter_eq_replicate_mset in_countE multiset_partition)

lemma explode_strictly_maximal_in_mset_wrt:
  assumes max: "is_strictly_maximal_in_mset_wrt R X x"
  shows "add_mset x {#y ∈# X. y  x#} = X"
proof -
  have "x ∈# X" and "y ∈# X - {#x#}. x  y"
    using max unfolding is_strictly_maximal_in_mset_wrt_iff by simp_all

  have "add_mset x (X - {#x#}) = X"
    using x ∈# X by (metis insert_DiffM)

  moreover have "{#y ∈# X. y  x#} = X - {#x#}"
    using y ∈# X - {#x#}. x  y
    by (smt (verit, best) x ∈# X add_diff_cancel_left' diff_subset_eq_self filter_mset_eq_conv
        insert_DiffM2 set_mset_add_mset_insert set_mset_empty singletonD)

  ultimately show ?thesis
    by (simp only:)
qed

end

lemma is_minimal_in_filter_mset_wrt_iff:
  assumes
    tran: "transp_on (set_mset (filter_mset P X)) R" and
    asym: "asymp_on (set_mset (filter_mset P X)) R"
  shows "is_minimal_in_mset_wrt R (filter_mset P X) x 
    (x ∈# X  P x  (y ∈# X - {#x#}. P y  ¬ R y x))"
proof -
  have "is_minimal_in_mset_wrt R (filter_mset P X) x 
    is_minimal_in_set_wrt R ({y  set_mset X. P y}) x"
    using is_minimal_in_mset_wrt_iff[OF tran asym]
    using is_minimal_in_set_wrt_iff[OF tran asym]
    by auto
  also have "  x ∈# X  P x  (y  set_mset X - {x}. P y  ¬ R y x)"
  proof (rule is_minimal_in_set_wrt_filter_iff)
    show "transp_on {y. y ∈# X  P y} R"
      using tran by simp
  next
    show "asymp_on {y. y ∈# X  P y} R"
      using asym by simp
  qed
  finally show ?thesis
    by (metis (no_types, lifting) DiffD1 asym asymp_onD at_most_one_mset_mset_diff insertE
        insert_Diff is_minimal_in_mset_wrt_iff more_than_one_mset_mset_diff tran)
qed

lemma multp_if_maximal_of_lhs_is_less:
  assumes
    trans: "transp R" and
    asym: "asymp_on (set_mset M1) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    "x1 ∈# M1" and "x2 ∈# M2" and
    "is_maximal_in_mset_wrt R M1 x1" and "R x1 x2"
  shows "multp R M1 M2"
proof (rule one_step_implies_multp[of _ _ _ "{#}", simplified])
  show "M2  {#}"
    using x2 ∈# M2 by auto
next
  show "k∈#M1. j∈#M2. R k j"
    using assms
    using is_maximal_in_mset_wrt_iff[OF transp_on_subset[OF trans subset_UNIV] asym]
    by (metis Un_iff totalp_onD transpE)
qed


subsection ‹Nonuniqueness›

lemma
  fixes x :: 'a and y :: 'a
  assumes "x  y"
  shows
    not_Uniq_is_minimal_in_mset_if_two_distinct_elements:
      "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
        transp_on (set_mset X) R  asymp_on (set_mset X) R 
        (1x. is_minimal_in_mset_wrt R X x))" and
    not_Uniq_is_maximal_in_mset_wrt_if_two_distinct_elements:
      "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
        transp_on (set_mset X) R  asymp_on (set_mset X) R 
        (1x. is_maximal_in_mset_wrt R X x))" and
    not_Uniq_is_strictly_minimal_in_mset_if_two_distinct_elements:
      "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
        transp_on (set_mset X) R  asymp_on (set_mset X) R 
        (1x. is_strictly_minimal_in_mset_wrt R X x))" and
    not_Uniq_is_strictly_maximal_in_mset_wrt_if_two_distinct_elements:
      "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
        transp_on (set_mset X) R  asymp_on (set_mset X) R 
        (1x. is_strictly_maximal_in_mset_wrt R X x))"
proof -
  let ?R = "λ_ _. False"
  let ?X = "{#x, y#}"

  have trans: "transp_on (set_mset ?X) ?R" and asym: "asymp_on (set_mset ?X) ?R"
    by (simp_all add: transp_onI asymp_onI)

  have "is_minimal_in_mset_wrt ?R ?X x" and "is_minimal_in_mset_wrt ?R ?X y"
    using is_minimal_in_mset_wrt_iff[OF trans asym] by simp_all

  thus "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
    transp_on (set_mset X) R  asymp_on (set_mset X) R 
    (1x. is_minimal_in_mset_wrt R X x))"
    using x  y trans asym
    by (metis Uniq_D)

  have "is_maximal_in_mset_wrt ?R ?X x" and "is_maximal_in_mset_wrt ?R ?X y"
    using is_maximal_in_mset_wrt_iff[OF trans asym] by simp_all

  thus "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
    transp_on (set_mset X) R  asymp_on (set_mset X) R 
    (1x. is_maximal_in_mset_wrt R X x))"
    using x  y trans asym
    by (metis Uniq_D)

  have "is_strictly_minimal_in_mset_wrt ?R ?X x" and "is_strictly_minimal_in_mset_wrt ?R ?X y"
    using x  y is_strictly_minimal_in_mset_wrt_iff[OF trans asym] by simp_all

  thus "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
    transp_on (set_mset X) R  asymp_on (set_mset X) R 
    (1x. is_strictly_minimal_in_mset_wrt R X x))"
    using x  y trans asym
    by (metis Uniq_D)

  have "is_strictly_maximal_in_mset_wrt ?R ?X x" and "is_strictly_maximal_in_mset_wrt ?R ?X y"
    using x  y is_strictly_maximal_in_mset_wrt_iff[OF trans asym] by simp_all

  thus "¬ ((R :: 'a  'a  bool) (X :: 'a multiset).
    transp_on (set_mset X) R  asymp_on (set_mset X) R 
    (1x. is_strictly_maximal_in_mset_wrt R X x))"
    using x  y trans asym
    by (metis Uniq_D)
qed


section ‹Least and greatest elements›

definition is_least_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R  totalp_on (set_mset X) R 
    is_least_in_mset_wrt R X x  x ∈# X  (y ∈# X - {#x#}. R x y)"

definition is_greatest_in_mset_wrt :: "('a  'a  bool)  'a multiset  'a  bool" where
  "transp_on (set_mset X) R  asymp_on (set_mset X) R  totalp_on (set_mset X) R 
    is_greatest_in_mset_wrt R X x  x ∈# X  (y ∈# X - {#x#}. R y x)"

context
  fixes X R
  assumes
    trans: "transp_on (set_mset X) R" and
    asym: "asymp_on (set_mset X) R" and
    tot: "totalp_on (set_mset X) R"
begin

subsection ‹Conversions›

lemma is_least_in_mset_wrt_iff:
  "is_least_in_mset_wrt R X x  x ∈# X  (y ∈# X - {#x#}. R x y)"
  using is_least_in_mset_wrt_def[OF trans asym tot] .

lemma is_greatest_in_mset_wrt_iff:
  "is_greatest_in_mset_wrt R X x  x ∈# X  (y ∈# X - {#x#}. R y x)"
  using is_greatest_in_mset_wrt_def[OF trans asym tot] .

lemma is_minimal_in_mset_wrt_if_is_least_in_mset_wrt[intro]:
  "is_least_in_mset_wrt R X x  is_minimal_in_mset_wrt R X x"
  unfolding is_minimal_in_mset_wrt_iff[OF trans asym]
  unfolding is_least_in_mset_wrt_def[OF trans asym tot]
  by (metis add_mset_remove_trivial_eq asym asymp_onD insert_noteq_member)

lemma is_maximal_in_mset_wrt_if_is_greatest_in_mset_wrt[intro]:
  "is_greatest_in_mset_wrt R X x  is_maximal_in_mset_wrt R X x"
  unfolding is_maximal_in_mset_wrt_iff[OF trans asym]
  unfolding is_greatest_in_mset_wrt_def[OF trans asym tot]
  by (metis add_mset_remove_trivial_eq asym asymp_onD insert_noteq_member)

lemma is_strictly_minimal_in_mset_wrt_iff_is_least_in_mset_wrt: 
  "is_strictly_minimal_in_mset_wrt R X = is_least_in_mset_wrt R X"
  unfolding is_strictly_minimal_in_mset_wrt_iff[OF trans asym] is_least_in_mset_wrt_iff 
proof(intro ext iffI)
  fix x
  show "x ∈# X  (y∈#X - {#x#}. ¬ R== y x)  x ∈# X  (y ∈# X - {#x#}. R x y)"
    by (metis (mono_tags, lifting) in_diffD sup2CI tot totalp_onD)
next 
  fix x
  show "x ∈# X  (y ∈# X - {#x#}. R x y)  x ∈# X  (y∈#X - {#x#}. ¬ R== y x)"
    by (metis (full_types) asym asymp_onD in_diffD sup2E)
qed

lemma is_strictly_maximal_in_mset_wrt_iff_is_greatest_in_mset_wrt: 
  "is_strictly_maximal_in_mset_wrt R X = is_greatest_in_mset_wrt R X"
  unfolding is_strictly_maximal_in_mset_wrt_iff[OF trans asym] is_greatest_in_mset_wrt_iff 
proof(intro ext iffI)
  fix x
  show "x ∈# X  (y∈#X - {#x#}. ¬ R== x y)  x ∈# X  (y∈#X - {#x#}. R y x)"
    by (metis (mono_tags, lifting) in_diffD sup2CI tot totalp_onD)
next 
  fix x
  show "x ∈# X  (y∈#X - {#x#}. R y x)  x ∈# X  (y∈#X - {#x#}. ¬ R== x y)"
    by (metis (full_types) asym asymp_onD in_diffD sup2E)
qed

subsection ‹Uniqueness›

lemma Uniq_is_minimal_in_mset_wrt[intro]:
  "1x. is_minimal_in_mset_wrt R X x"
  unfolding is_minimal_in_mset_wrt_iff[OF trans asym]
  by (smt (verit, best) Uniq_I tot totalp_onD)

lemma Uniq_is_maximal_in_mset_wrt[intro]:
  "1x. is_maximal_in_mset_wrt R X x"
  unfolding is_maximal_in_mset_wrt_iff[OF trans asym]
  by (smt (verit, best) Uniq_I tot totalp_onD)

lemma Uniq_is_least_in_mset_wrt[intro]:
  "1x. is_least_in_mset_wrt R X x"
  using is_least_in_mset_wrt_iff
  by (smt (verit, best) Uniq_I asym asymp_onD insert_DiffM insert_noteq_member)

lemma Uniq_is_greatest_in_mset_wrt[intro]:
  "1x. is_greatest_in_mset_wrt R X x"
  unfolding is_greatest_in_mset_wrt_iff
  by (smt (verit, best) Uniq_I asym asymp_onD insert_DiffM insert_noteq_member)

lemma Uniq_is_strictly_minimal_in_mset_wrt[intro]: contributor ‹Balazs Toth›
  "1x. is_strictly_minimal_in_mset_wrt R X x"
  using Uniq_is_least_in_mset_wrt
  unfolding is_strictly_minimal_in_mset_wrt_iff_is_least_in_mset_wrt.

lemma Uniq_is_strictly_maximal_in_mset_wrt[intro]: contributor ‹Balazs Toth›
  "1x. is_strictly_maximal_in_mset_wrt R X x"
  using Uniq_is_greatest_in_mset_wrt
  unfolding is_strictly_maximal_in_mset_wrt_iff_is_greatest_in_mset_wrt.

subsection ‹Miscellaneous›

lemma is_least_in_mset_wrt_iff_is_minimal_and_count_eq_one:
  "is_least_in_mset_wrt R X x  is_minimal_in_mset_wrt R X x  count X x = 1"
proof (rule iffI)
  assume "is_least_in_mset_wrt R X x"
  thus "is_minimal_in_mset_wrt R X x  count X x = 1"
    unfolding is_least_in_mset_wrt_iff is_minimal_in_mset_wrt_iff[OF trans asym]
    by (metis One_nat_def add_mset_remove_trivial_eq asym asymp_onD count_add_mset not_in_iff)
next
  assume "is_minimal_in_mset_wrt R X x  count X x = 1"
  then show "is_least_in_mset_wrt R X x"
    unfolding is_least_in_mset_wrt_iff is_minimal_in_mset_wrt_iff[OF trans asym]
    by (metis count_single in_diffD in_diff_count nat_less_le tot totalp_onD)
qed

lemma is_greatest_in_mset_wrt_iff_is_maximal_and_count_eq_one:
  "is_greatest_in_mset_wrt R X x  is_maximal_in_mset_wrt R X x  count X x = 1"
proof (rule iffI)
  assume "is_greatest_in_mset_wrt R X x"
  thus "is_maximal_in_mset_wrt R X x  count X x = 1"
    unfolding is_greatest_in_mset_wrt_iff is_maximal_in_mset_wrt_iff[OF trans asym]
    by (metis One_nat_def add_mset_remove_trivial_eq asym asymp_onD count_add_mset not_in_iff)
next
  assume "is_maximal_in_mset_wrt R X x  count X x = 1"
  then show "is_greatest_in_mset_wrt R X x"
    unfolding is_greatest_in_mset_wrt_iff is_maximal_in_mset_wrt_iff[OF trans asym]
    by (metis count_single in_diffD in_diff_count nat_less_le tot totalp_onD)
qed

lemma count_ge_2_if_minimal_in_mset_wrt_and_not_least_in_mset_wrt:
  assumes "is_minimal_in_mset_wrt R X x" and "¬ is_least_in_mset_wrt R X x"
  shows "count X x  2"
  using assms
  unfolding is_least_in_mset_wrt_iff_is_minimal_and_count_eq_one
  by (metis Suc_1 Suc_le_eq antisym_conv1 asym count_greater_eq_one_iff is_minimal_in_mset_wrt_iff
      trans)

lemma count_ge_2_if_maximal_in_mset_wrt_and_not_greatest_in_mset_wrt:
  assumes "is_maximal_in_mset_wrt R X x" and "¬ is_greatest_in_mset_wrt R X x"
  shows "count X x  2"
  using assms
  unfolding is_greatest_in_mset_wrt_iff_is_maximal_and_count_eq_one
  by (metis Suc_1 Suc_le_eq antisym_conv1 asym count_greater_eq_one_iff is_maximal_in_mset_wrt_iff
      trans)

lemma explode_greatest_in_mset_wrt:
  assumes max: "is_greatest_in_mset_wrt R X x"
  shows "add_mset x {#y ∈# X. y  x#} = X"
  using max[folded is_strictly_maximal_in_mset_wrt_iff_is_greatest_in_mset_wrt]
  using explode_strictly_maximal_in_mset_wrt[OF trans asym]
  by metis

end


lemma multpHO_if_maximal_wrt_less_that_maximal_wrt:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M1  set_mset M2) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    x1_maximal: "is_maximal_in_mset_wrt R M1 x1" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x2" and
    "R x1 x2"
  shows "multpHO R M1 M2"
proof -
  have
    trans1: "transp_on (set_mset M1) R" and trans2: "transp_on (set_mset M2) R" and
    asym1: "asymp_on (set_mset M1) R" and asym2: "asymp_on (set_mset M2) R" and
    tot1: "totalp_on (set_mset M1) R" and tot2: "totalp_on (set_mset M2) R"
    using trans[THEN transp_on_subset] asym[THEN asymp_on_subset] tot[THEN totalp_on_subset]
    by simp_all

  have x1_in: "x1 ∈# M1" and x1_gr: "y∈#M1. y  x1  ¬ R x1 y"
    using x1_maximal[unfolded is_maximal_in_mset_wrt_iff[OF trans1 asym1]] by argo+

  have x2_in: "x2 ∈# M2" and x2_gr: "y∈#M2. y  x2  ¬ R x2 y"
    using x2_maximal[unfolded is_maximal_in_mset_wrt_iff[OF trans2 asym2]] by argo+

  show "multpHO R M1 M2"
    unfolding multpHO_def
  proof (intro conjI)
    show "M1  M2"
      using x1_in x2_in x1_gr
      by (metis R x1 x2 asym2 asymp_onD)
  next
    show "y. count M2 y < count M1 y  (x. R y x  count M1 x < count M2 x)"
      using x1_in x2_in x1_gr x2_gr
      by (smt (verit, best) assms(6) asym1 asymp_onD count_greater_zero_iff count_inI
          dual_order.strict_trans local.trans subsetD sup_ge1 sup_ge2 tot1 totalp_onD transp_onD)
  qed
qed

lemma multpDM_if_maximal_wrt_less_that_maximal_wrt:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M1  set_mset M2) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    x1_maximal: "is_maximal_in_mset_wrt R M1 x1" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x2" and
    "R x1 x2"
  shows "multpDM R M1 M2"
  using multpHO_if_maximal_wrt_less_that_maximal_wrt[OF assms, THEN multpHO_imp_multpDM] .

lemma multp_if_maximal_wrt_less_that_maximal_wrt:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M1  set_mset M2) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    x1_maximal: "is_maximal_in_mset_wrt R M1 x1" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x2" and
    "R x1 x2"
  shows "multp R M1 M2"
  using multpDM_if_maximal_wrt_less_that_maximal_wrt[OF assms, THEN multpDM_imp_multp] .


lemma multpHO_if_same_maximal_wrt_and_count_lt:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M1  set_mset M2) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    x1_maximal: "is_maximal_in_mset_wrt R M1 x" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x" and
    "count M1 x < count M2 x"
  shows "multpHO R M1 M2"
  by (metis assms(6) asym asymp_on_subset count_inI is_greatest_in_set_wrt_iff
      is_maximal_in_mset_wrt_def is_maximal_in_set_wrt_eq_is_greatest_in_set_wrt less_zeroE
      local.trans multpHO_def order_less_imp_not_less sup_ge1 tot totalp_on_subset transp_on_subset
      x1_maximal)

lemma multp_if_same_maximal_wrt_and_count_lt:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M1  set_mset M2) R" and
    tot: "totalp_on (set_mset M1  set_mset M2) R" and
    x1_maximal: "is_maximal_in_mset_wrt R M1 x" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x" and
    "count M1 x < count M2 x"
  shows "multp R M1 M2"
  using multpHO_if_same_maximal_wrt_and_count_lt[
      OF assms, THEN multpHO_imp_multpDM, THEN multpDM_imp_multp] .

lemma less_than_maximal_wrt_if_multpHO:
  assumes
    trans: "transp_on (set_mset M1  set_mset M2) R" and
    asym: "asymp_on (set_mset M2) R" and
    tot: "totalp_on (set_mset M2) R" and
    x2_maximal: "is_maximal_in_mset_wrt R M2 x2" and
    "multpHO R M1 M2" and
    "x1 ∈# M1"
  shows "R== x1 x2"
proof -
  have
    trans2: "transp_on (set_mset M2) R" and
    asym2: "asymp_on (set_mset M2) R" and
    tot2: "totalp_on (set_mset M2) R"
    using trans[THEN transp_on_subset] asym[THEN asymp_on_subset] tot[THEN totalp_on_subset]
    by simp_all

  have x2_in: "x2 ∈# M2" and x2_gr: "y∈#M2. y  x2  ¬ R x2 y"
    using x2_maximal[unfolded is_maximal_in_mset_wrt_iff[OF trans2 asym2]] by argo+

  show ?thesis
  proof (cases "x1 ∈# M2")
    case True
    thus ?thesis
      using x2_gr by (metis (mono_tags) sup2CI tot2 totalp_onD x2_in)
  next
    case False

    hence "x1 ∈# M1 - M2"
      using x1 ∈# M1 by (simp add: in_diff_count not_in_iff)

    moreover have "k∈#M1 - M2. x∈#M2 - M1. R k x"
      using multpHO_implies_one_step_strong(2)[OF multpHO R M1 M2] .

    ultimately obtain x where "x ∈# M2 - M1" and "R x1 x"
      by metis

    hence "x  x2  ¬ R x2 x"
      using x2_gr by (metis in_diffD)

    hence "x  x2  R x x2"
      by (metis x ∈# M2 - M1 in_diffD tot2 totalp_onD x2_in)

    thus ?thesis
      using R x1 x
      by (meson Un_iff x ∈# M2 - M1 assms(6) in_diffD local.trans sup2I1 transp_onD x2_in)
  qed
qed


section ‹Examples of duplicate handling in set and multiset definitions›

lemma
  fixes M :: "nat multiset"
  defines "M  {#0, 0, 1, 1, 2, 2#}"
  shows
    "is_minimal_in_set_wrt (<) (set_mset M) 0"
    "is_minimal_in_mset_wrt (<) M 0"
    "is_least_in_set_wrt (<) (set_mset M) 0"
    "y. is_least_in_mset_wrt (<) M y"
  by (auto simp: M_def is_minimal_in_set_wrt_iff is_minimal_in_mset_wrt_def
      is_least_in_set_wrt_iff is_least_in_mset_wrt_def)

lemma
  fixes x y :: 'a and M :: "'a multiset"
  defines "M  {#x, y, y#}"
  defines "R  λ_ _. False"
  assumes "x  y"
  shows
    "is_maximal_in_mset_wrt R M x"
    "is_maximal_in_mset_wrt R M y"
    "is_strictly_maximal_in_mset_wrt R M x"
    "¬ is_strictly_maximal_in_mset_wrt R M y"
proof -
  have transp_on_False[simp]: "A. transp_on A (λ_ _. False)"
    by (simp add: transp_onI)

  have asymp_on_False[simp]: "A. asymp_on A (λ_ _. False)"
    by (simp add: asymp_onI)

  show
    "is_maximal_in_mset_wrt R M x"
    "is_maximal_in_mset_wrt R M y"
    "is_strictly_maximal_in_mset_wrt R M x"
    "¬ is_strictly_maximal_in_mset_wrt R M y"
    unfolding is_maximal_in_mset_wrt_iff[of M R, unfolded R_def, simplified, folded R_def]
    unfolding is_strictly_maximal_in_mset_wrt_iff[of M R, unfolded R_def, simplified, folded R_def]
    unfolding atomize_conj
    using x  y
    by (simp add: M_def)
qed


section ‹Hide stuff›

text ‹We restrict the public interface to ease future internal changes.›

hide_fact is_minimal_in_mset_wrt_def is_maximal_in_mset_wrt_def
hide_fact is_strictly_minimal_in_mset_wrt_def is_strictly_maximal_in_mset_wrt_def
hide_fact is_least_in_mset_wrt_def is_greatest_in_mset_wrt_def


section ‹Integration in type classes›

abbreviation (in order) is_minimal_in_mset where
  "is_minimal_in_mset  is_minimal_in_mset_wrt (<)"

abbreviation (in order) is_maximal_in_mset where
  "is_maximal_in_mset  is_maximal_in_mset_wrt (<)"

abbreviation (in order) is_strictly_minimal_in_mset where contributor ‹Balazs Toth›
  "is_strictly_minimal_in_mset  is_strictly_minimal_in_mset_wrt (<)"

abbreviation (in order) is_strictly_maximal_in_mset where contributor ‹Balazs Toth›
  "is_strictly_maximal_in_mset  is_strictly_maximal_in_mset_wrt (<)"

lemmas (in order) is_minimal_in_mset_iff =
  is_minimal_in_mset_wrt_iff[OF transp_on_less asymp_on_less]

lemmas (in order) is_maximal_in_mset_iff =
  is_maximal_in_mset_wrt_iff[OF transp_on_less asymp_on_less]

lemmas (in order) is_strictly_minimal_in_mset_iff contributor ‹Balazs Toth› =
  is_strictly_minimal_in_mset_wrt_iff[OF transp_on_less asymp_on_less]

lemmas (in order) is_strictly_maximal_in_mset_iff contributor ‹Balazs Toth› =
  is_strictly_maximal_in_mset_wrt_iff[OF transp_on_less asymp_on_less]

lemmas (in order) is_minimal_in_mset_if_is_strictly_minimal_in_mset[intro] contributor ‹Balazs Toth› =
  is_minimal_in_mset_wrt_if_is_strictly_minimal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) is_maximal_in_mset_if_is_strictly_maximal_in_mset[intro] contributor ‹Balazs Toth› =
  is_maximal_in_mset_wrt_if_is_strictly_maximal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) ex_minimal_in_mset =
  ex_minimal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) ex_maximal_in_mset =
  ex_maximal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) explode_maximal_in_mset =
  explode_maximal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) explode_strictly_maximal_in_mset =
  explode_strictly_maximal_in_mset_wrt[OF transp_on_less asymp_on_less]

lemmas (in order) is_minimal_in_filter_mset_iff =
  is_minimal_in_filter_mset_wrt_iff[OF transp_on_less asymp_on_less]


abbreviation (in linorder) is_least_in_mset where
  "is_least_in_mset  is_least_in_mset_wrt (<)"

abbreviation (in linorder) is_greatest_in_mset where
  "is_greatest_in_mset  is_greatest_in_mset_wrt (<)"

lemmas (in linorder) is_least_in_mset_iff =
  is_least_in_mset_wrt_iff[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) is_greatest_in_mset_iff =
  is_greatest_in_mset_wrt_iff[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) is_minimal_in_mset_if_is_least_in_mset[intro] =
  is_minimal_in_mset_wrt_if_is_least_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) is_maximal_in_mset_if_is_greatest_in_mset[intro] =
  is_maximal_in_mset_wrt_if_is_greatest_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_minimal_in_mset[intro] =
  Uniq_is_minimal_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_maximal_in_mset[intro] =
  Uniq_is_maximal_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_least_in_mset[intro] =
  Uniq_is_least_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_greatest_in_mset[intro] =
  Uniq_is_greatest_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_strictly_minimal_in_mset[intro] contributor ‹Balazs Toth› =
  Uniq_is_strictly_minimal_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) Uniq_is_strictly_maximal_in_mset[intro] contributor ‹Balazs Toth› =
  Uniq_is_strictly_maximal_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) is_least_in_mset_iff_is_minimal_and_count_eq_one =
  is_least_in_mset_wrt_iff_is_minimal_and_count_eq_one[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) is_greatest_in_mset_iff_is_maximal_and_count_eq_one =
  is_greatest_in_mset_wrt_iff_is_maximal_and_count_eq_one[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) count_ge_2_if_minimal_in_mset_and_not_least_in_mset =
  count_ge_2_if_minimal_in_mset_wrt_and_not_least_in_mset_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) count_ge_2_if_maximal_in_mset_and_not_greatest_in_mset =
  count_ge_2_if_maximal_in_mset_wrt_and_not_greatest_in_mset_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) explode_greatest_in_mset =
  explode_greatest_in_mset_wrt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) multpHO_if_maximal_less_that_maximal =
  multpHO_if_maximal_wrt_less_that_maximal_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) multpDM_if_maximal_less_that_maximal =
  multpDM_if_maximal_wrt_less_that_maximal_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) multp_if_maximal_less_that_maximal =
  multp_if_maximal_wrt_less_that_maximal_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]

lemmas (in linorder) multpHO_if_same_maximal_and_count_lt =
  multpHO_if_same_maximal_wrt_and_count_lt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) multp_if_same_maximal_and_count_lt =
  multp_if_same_maximal_wrt_and_count_lt[OF transp_on_less asymp_on_less totalp_on_less]

lemmas (in linorder) less_than_maximal_if_multpHO =
  less_than_maximal_wrt_if_multpHO[OF transp_on_less asymp_on_less totalp_on_less]

lemma (in linorder)
  assumes"is_greatest_in_mset C L"
  shows "C - {#L#} = {#K ∈# C. K  L#}"
  using assms
  by (smt (verit, del_insts) add_diff_cancel_left' diff_subset_eq_self diff_zero filter_empty_mset
      filter_mset_add_mset filter_mset_eq_conv insert_DiffM2 local.is_greatest_in_mset_iff
      local.not_less_iff_gr_or_eq)

end