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]: 
  "∃⇩≤⇩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]: 
  "∃⇩≤⇩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 multp⇩H⇩O_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⇩H⇩O 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 "multp⇩H⇩O R M1 M2"
    unfolding multp⇩H⇩O_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 multp⇩D⇩M_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⇩D⇩M R M1 M2"
  using multp⇩H⇩O_if_maximal_wrt_less_that_maximal_wrt[OF assms, THEN multp⇩H⇩O_imp_multp⇩D⇩M] .
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 multp⇩D⇩M_if_maximal_wrt_less_that_maximal_wrt[OF assms, THEN multp⇩D⇩M_imp_multp] .
lemma multp⇩H⇩O_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⇩H⇩O 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 multp⇩H⇩O_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 multp⇩H⇩O_if_same_maximal_wrt_and_count_lt[
      OF assms, THEN multp⇩H⇩O_imp_multp⇩D⇩M, THEN multp⇩D⇩M_imp_multp] .
lemma less_than_maximal_wrt_if_multp⇩H⇩O:
  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
    "multp⇩H⇩O 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 multp⇩H⇩O_implies_one_step_strong(2)[OF ‹multp⇩H⇩O 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 
  "is_strictly_minimal_in_mset ≡ is_strictly_minimal_in_mset_wrt (<)"
abbreviation (in order) is_strictly_maximal_in_mset where 
  "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  =
  is_strictly_minimal_in_mset_wrt_iff[OF transp_on_less asymp_on_less]
lemmas (in order) is_strictly_maximal_in_mset_iff  =
  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]  =
  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]  =
  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]  =
  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]  =
  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) multp⇩H⇩O_if_maximal_less_that_maximal =
  multp⇩H⇩O_if_maximal_wrt_less_that_maximal_wrt[OF transp_on_less asymp_on_less
    totalp_on_less]
lemmas (in linorder) multp⇩D⇩M_if_maximal_less_that_maximal =
  multp⇩D⇩M_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) multp⇩H⇩O_if_same_maximal_and_count_lt =
  multp⇩H⇩O_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_multp⇩H⇩O =
  less_than_maximal_wrt_if_multp⇩H⇩O[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