(*
  File:   Landau_More.thy
  Author: Andreas Lochbihler, Manuel Eberl <eberlm@in.tum.de>

  Some more facts about Landau symbols.
*)
theory Landau_More
imports
  "HOL-Library.Landau_Symbols"
  Landau_Simprocs
begin

(* Additional theorems, contributed by Andreas Lochbihler and adapted by Manuel Eberl *)

lemma bigo_const_inverse [simp]:
  assumes "filterlim f at_top F" "F \<noteq> bot"
  shows "(\<lambda>_. c) \<in> O[F](\<lambda>x. inverse (f x) :: real) \<longleftrightarrow> c = 0"
proof -
  {
    assume A: "(\<lambda>_. 1) \<in> O[F](\<lambda>x. inverse (f x))"
    from assms have "(\<lambda>_. 1) \<in> o[F](f)"
      by (simp add: eventually_nonzero_simps smallomega_iff_smallo filterlim_at_top_iff_smallomega)
    also from assms A have "f \<in> O[F](\<lambda>_. 1)"
      by (simp add: eventually_nonzero_simps landau_divide_simps)
    finally have False using assms by (simp add: landau_o.small_refl_iff)
  }
  thus ?thesis by (cases "c = 0") auto
qed

lemma const_in_smallo_const [simp]: "(\<lambda>_. b) \<in> o(\<lambda>_ :: _ :: linorder. c) \<longleftrightarrow> b = 0" (is "?lhs \<longleftrightarrow> ?rhs")
  by (cases "b = 0"; cases "c = 0") (simp_all add: landau_o.small_refl_iff)

lemma smallomega_1_conv_filterlim: "f \<in> \<omega>[F](\<lambda>_. 1) \<longleftrightarrow> filterlim f at_infinity F"
  by (auto intro: smallomegaI_filterlim_at_infinity dest: smallomegaD_filterlim_at_infinity)

lemma bigtheta_powr_1 [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<ge> 0) F \<Longrightarrow> (\<lambda>x. f x powr 1) \<in> \<Theta>[F](f)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigtheta_powr_0 [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<noteq> 0) F \<Longrightarrow> (\<lambda>x. f x powr 0) \<in> \<Theta>[F](\<lambda>_. 1)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigtheta_powr_nonzero [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<noteq> 0) F \<Longrightarrow> (\<lambda>x. if f x = 0 then g x else h x) \<in> \<Theta>[F](h)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigtheta_powr_nonzero' [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<noteq> 0) F \<Longrightarrow> (\<lambda>x. if f x \<noteq> 0 then g x else h x) \<in> \<Theta>[F](g)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigtheta_powr_nonneg [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<ge> 0) F \<Longrightarrow> (\<lambda>x. if f x \<ge> 0 then g x else h x) \<in> \<Theta>[F](g)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigtheta_powr_nonneg' [landau_simp]:
  "eventually (\<lambda>x. (f x :: real) \<ge> 0) F \<Longrightarrow> (\<lambda>x. if f x < 0 then g x else h x) \<in> \<Theta>[F](h)"
  by (intro bigthetaI_cong) (auto elim!: eventually_mono)

lemma bigo_powr_iff:
  assumes "0 < p" "eventually (\<lambda>x. f x \<ge> 0) F" "eventually (\<lambda>x. g x \<ge> 0) F"
  shows "(\<lambda>x. (f x :: real) powr p) \<in> O[F](\<lambda>x. g x powr p) \<longleftrightarrow> f \<in> O[F](g)" (is "?lhs \<longleftrightarrow> ?rhs")
proof
  assume ?lhs
  with assms bigo_powr[OF this, of "inverse p"] show ?rhs
    by (simp add: powr_powr landau_simps)
qed (insert assms, simp_all add: bigo_powr_nonneg)

lemma inverse_powr [simp]:
  assumes "(x::real) \<ge> 0"
  shows   "inverse x powr y = inverse (x powr y)"
proof (cases "x > 0")
  assume x: "x > 0"
  from x have "inverse x powr y = exp (y * ln (inverse x))" by (simp add: powr_def)
  also have "ln (inverse x) = -ln x" by (simp add: x ln_inverse)
  also have "exp (y * -ln x) = inverse (exp (y * ln x))" by (simp add: exp_minus)
  also from x have "exp (y * ln x) = x powr y" by (simp add: powr_def)
  finally show ?thesis .
qed (insert assms, simp)

lemma bigo_neg_powr_iff:
  assumes "p < 0" "eventually (\<lambda>x. f x \<ge> 0) F" "eventually (\<lambda>x. g x \<ge> 0) F"
                  "eventually (\<lambda>x. f x \<noteq> 0) F" "eventually (\<lambda>x. g x \<noteq> 0) F"
  shows "(\<lambda>x. (f x :: real) powr p) \<in> O[F](\<lambda>x. g x powr p) \<longleftrightarrow> g \<in> O[F](f)" (is "?lhs \<longleftrightarrow> ?rhs")
proof -
  have "(\<lambda>x. f x powr p) \<in> O[F](\<lambda>x. g x powr p) \<longleftrightarrow>
          (\<lambda>x. (inverse (f x)) powr -p) \<in> O[F](\<lambda>x. (inverse (g x)) powr -p)"
    using assms by (intro landau_o.big.cong_ex) (auto simp: powr_minus elim: eventually_mono)
  also from assms have "\<dots> \<longleftrightarrow> ((\<lambda>x. inverse (f x)) \<in> O[F](\<lambda>x. inverse (g x)))"
    by (subst bigo_powr_iff) simp_all
  also from assms have "\<dots> \<longleftrightarrow> g \<in> O[F](f)" by (simp add: landau_o.big.inverse_cancel)
  finally show ?thesis .
qed

lemma smallo_powr_iff:
  assumes "0 < p" "eventually (\<lambda>x. f x \<ge> 0) F" "eventually (\<lambda>x. g x \<ge> 0) F"
  shows "(\<lambda>x. (f x :: real) powr p) \<in> o[F](\<lambda>x. g x powr p) \<longleftrightarrow> f \<in> o[F](g)" (is "?lhs \<longleftrightarrow> ?rhs")
proof
  assume ?lhs
  with assms smallo_powr[OF this, of "inverse p"] show ?rhs
    by (simp add: powr_powr landau_simps)
qed (insert assms, simp_all add: smallo_powr_nonneg)

lemma smallo_neg_powr_iff:
  assumes "p < 0" "eventually (\<lambda>x. f x \<ge> 0) F" "eventually (\<lambda>x. g x \<ge> 0) F"
                  "eventually (\<lambda>x. f x \<noteq> 0) F" "eventually (\<lambda>x. g x \<noteq> 0) F"
  shows "(\<lambda>x. (f x :: real) powr p) \<in> o[F](\<lambda>x. g x powr p) \<longleftrightarrow> g \<in> o[F](f)" (is "?lhs \<longleftrightarrow> ?rhs")
proof -
  have "(\<lambda>x. f x powr p) \<in> o[F](\<lambda>x. g x powr p) \<longleftrightarrow>
          (\<lambda>x. (inverse (f x)) powr -p) \<in> o[F](\<lambda>x. (inverse (g x)) powr -p)"
    using assms by (intro landau_o.small.cong_ex) (auto simp: powr_minus elim: eventually_mono)
  also from assms have "\<dots> \<longleftrightarrow> ((\<lambda>x. inverse (f x)) \<in> o[F](\<lambda>x. inverse (g x)))"
    by (subst smallo_powr_iff) simp_all
  also from assms have "\<dots> \<longleftrightarrow> g \<in> o[F](f)" by (simp add: landau_o.small.inverse_cancel)
  finally show ?thesis .
qed

lemma const_smallo_powr:
  assumes "filterlim f at_top F" "F \<noteq> bot"
  shows "(\<lambda>_. c :: real) \<in> o[F](\<lambda>x. f x powr p) \<longleftrightarrow> p > 0 \<or> c = 0"
  by (rule linorder_cases[of p 0]; cases "c = 0")
     (insert assms smallo_powr_iff[of p "\<lambda>_. 1" F f] smallo_neg_powr_iff[of p f F "\<lambda>_. 1"],
      auto simp: landau_simps eventually_nonzero_simps smallo_1_iff[of F f] not_less
           dest: landau_o.small_asymmetric simp: eventually_False landau_o.small_refl_iff)

lemma bigo_const_powr:
  assumes "filterlim f at_top F" "F \<noteq> bot"
  shows "(\<lambda>_. c :: real) \<in> O[F](\<lambda>x. f x powr p) \<longleftrightarrow> p \<ge> 0 \<or> c = 0"
proof -
  from assms have A: "(\<lambda>_. 1) \<in> o[F](f)"
    by (simp add: filterlim_at_top_iff_smallomega smallomega_iff_smallo landau_o.small_imp_big)
  hence B: "(\<lambda>_. 1) \<in> O[F](f)" "f \<notin> O[F](\<lambda>_. 1)" using assms
    by (auto simp: landau_o.small_imp_big dest: landau_o.small_big_asymmetric)
  show ?thesis
    by (rule linorder_cases[of p 0]; cases "c = 0")
       (insert insert assms A B bigo_powr_iff[of p "\<lambda>_. 1" F f] bigo_neg_powr_iff[of p "\<lambda>_. 1" F f],
        auto simp: landau_simps eventually_nonzero_simps not_less dest: landau_o.small_asymmetric)
qed

lemma filterlim_powr_at_top:
  "(b::real) > 1 \<Longrightarrow> filterlim (\<lambda>x. b powr x) at_top at_top"
  unfolding powr_def mult.commute[of _ "ln b"]
  by (auto intro!: filterlim_compose[OF exp_at_top]
        filterlim_tendsto_pos_mult_at_top filterlim_ident)

lemma power_smallo_exponential:
  fixes b :: real
  assumes b: "b > 1"
  shows "(\<lambda>x. x powr n) \<in> o(\<lambda>x. b powr x)"
proof (rule smalloI_tendsto)
  from assms have "filterlim (\<lambda>x. x * ln b - n * ln x) at_top at_top"
    using [[simproc add: simplify_landau_sum]]
    by (simp add: filterlim_at_top_iff_smallomega eventually_nonzero_simps)
  hence "((\<lambda>x. exp (-(x * ln b - n * ln x))) \<longlongrightarrow> 0) at_top" (is ?A)
    by (intro filterlim_compose[OF exp_at_bot]
              filterlim_compose[OF filterlim_uminus_at_bot_at_top])
  also have "?A \<longleftrightarrow> ((\<lambda>x. x powr n / b powr x) \<longlongrightarrow> 0) at_top"
    using b eventually_gt_at_top[of 0]
    by (intro tendsto_cong)
       (auto simp: exp_diff powr_def field_simps exp_of_nat_mult elim: eventually_mono)
  finally show "((\<lambda>x. x powr n / b powr x) \<longlongrightarrow> 0) at_top" .
qed (insert assms, simp_all add: eventually_nonzero_simps)

lemma powr_fast_growth_tendsto:
  assumes gf: "g \<in> O[F](f)"
  and n: "n \<ge> 0"
  and k: "k > 1"
  and f: "filterlim f at_top F"
  and g: "eventually (\<lambda>x. g x \<ge> 0) F"
  shows "(\<lambda>x. g x powr n) \<in> o[F](\<lambda>x. k powr f x :: real)"
proof -
  from f have f': "eventually (\<lambda>x. f x \<ge> 0) F" by (simp add: eventually_nonzero_simps)
  from gf obtain c where c: "c > 0" "eventually (\<lambda>x. norm (g x) \<le> c * norm (f x)) F"
    by (elim landau_o.bigE)
  from c(2) g f' have "eventually (\<lambda>x. g x \<le> c * f x) F" by eventually_elim simp
  from c(2) g f' have "eventually (\<lambda>x. norm (g x powr n) \<le> norm (c powr n * f x powr n)) F"
    by eventually_elim (insert n c(1), auto simp: powr_mult [symmetric] intro!: powr_mono2)
  from landau_o.big_mono[OF this] c(1)
    have "(\<lambda>x. g x powr n) \<in> O[F](\<lambda>x. f x powr n)" by simp
  also from power_smallo_exponential f
    have "(\<lambda>x. f x powr n) \<in> o[F](\<lambda>x. k powr f x)" by (rule landau_o.small.compose) fact+
  finally show ?thesis .
qed

(* lemma bigo_const_inverse [simp]:
  "filterlim f at_top at_top \<Longrightarrow> (\<lambda>_ :: _ :: linorder. c) \<in> O(\<lambda>x. inverse (f x)) \<longleftrightarrow> c = 0"
  for f :: "_ \<Rightarrow> real"
by simp

lemma smallo_const_inverse [simp]:
  "filterlim f at_top at_top \<Longrightarrow> (\<lambda>_ :: _ :: linorder. c) \<in> o(\<lambda>x. inverse (f x)) \<longleftrightarrow> c = 0"
  for f :: "_ \<Rightarrow> real"
by(simp)
 *)
lemma bigo_abs_powr_iff [simp]:
  "0 < p \<Longrightarrow> (\<lambda>x. \<bar>f x :: real\<bar> powr p) \<in> O[F](\<lambda>x. \<bar>g x\<bar> powr p) \<longleftrightarrow> f \<in> O[F](g)"
  by(subst bigo_powr_iff; simp)

lemma smallo_abs_powr_iff [simp]:
  "0 < p \<Longrightarrow> (\<lambda>x. \<bar>f x :: real\<bar> powr p) \<in> o[F](\<lambda>x. \<bar>g x\<bar> powr p) \<longleftrightarrow> f \<in> o[F](g)"
  by(subst smallo_powr_iff; simp)

lemma const_smallo_inverse_powr:
  assumes "filterlim f at_top at_top"
  shows "(\<lambda>_ :: _ :: linorder. c :: real) \<in> o(\<lambda>x. inverse (f x powr p)) \<longleftrightarrow> (p \<ge> 0 \<longrightarrow> c = 0)"
proof(cases p "0 :: real" rule: linorder_cases)
  case p: greater
  have "(\<lambda>_. c) \<in> o(\<lambda>x. inverse (f x powr p)) \<longleftrightarrow> (\<lambda>_. \<bar>c\<bar>) \<in> o(\<lambda>x. inverse (f x powr p))" by simp
  also have "\<bar>c\<bar> = \<bar>(\<bar>c\<bar> powr (inverse p))\<bar> powr p" using p by(simp add: powr_powr)
  also { have "eventually (\<lambda>x. f x \<ge> 0) at_top" using assms by(simp add: filterlim_at_top)
    then have "o(\<lambda>x. inverse (f x powr p)) = o(\<lambda>x. \<bar>inverse (f x)\<bar> powr p)"
      by(intro landau_o.small.cong)(auto elim!: eventually_rev_mp)
    also have "(\<lambda>_. \<bar>(\<bar>c\<bar> powr inverse p)\<bar> powr p) \<in> \<dots> \<longleftrightarrow> (\<lambda>_. \<bar>c\<bar> powr (inverse p)) \<in> o(\<lambda>x. inverse (f x))"
      using p by(rule smallo_abs_powr_iff)
    also note calculation }
  also have "(\<lambda>_. \<bar>c\<bar> powr (inverse p)) \<in> o(\<lambda>x. inverse (f x)) \<longleftrightarrow> c = 0" using assms by simp
  finally show ?thesis using p by simp
next
  case equal
  from assms have "eventually (\<lambda>x. f x \<ge> 1) at_top" using assms by(simp add: filterlim_at_top)
  then have "o(\<lambda>x. inverse (f x powr p)) = o(\<lambda>x. 1)"
    by(intro landau_o.small.cong)(auto simp add: equal elim!: eventually_rev_mp)
  then show ?thesis using equal by simp
next
  case less
  from assms have nonneg: "\<forall>\<^sub>F x in at_top. 0 \<le> f x" by(simp add: filterlim_at_top)
  with assms have "\<forall>\<^sub>F x in at_top. \<bar>\<bar>c\<bar> powr (1 / - p)\<bar> / d \<le> \<bar>f x\<bar>" (is "\<forall>\<^sub>F x in _. ?c \<le> _") if "d > 0" for d
    by(fastforce dest!: spec[where x="?c"] simp add: filterlim_at_top elim: eventually_rev_mp)
  then have "(\<lambda>_. \<bar>c\<bar> powr (1 / - p)) \<in> o(f)" by(intro landau_o.smallI)(simp add: field_simps)
  then have "(\<lambda>_. \<bar>\<bar>c\<bar> powr (1 / - p)\<bar> powr - p) \<in> o(\<lambda>x. \<bar>f x\<bar> powr - p)"
    using less by(subst smallo_powr_iff) simp_all
  also have "(\<lambda>_. \<bar>\<bar>c\<bar> powr (1 / - p)\<bar> powr - p) = (\<lambda>_. \<bar>c\<bar>)" using less by(simp add: powr_powr)
  also have "o(\<lambda>x. \<bar>f x\<bar> powr - p) = o(\<lambda>x. f x powr - p)" using nonneg
    by(auto intro!: landau_o.small.cong elim: eventually_rev_mp)
  finally have "(\<lambda>_. c) \<in> o(\<lambda>x. f x powr - p)" by simp
  with less show ?thesis by(simp add: powr_minus[symmetric])
qed

lemma bigo_const_inverse_powr:
  assumes "filterlim f at_top at_top"
  shows "(\<lambda>_ :: _ :: linorder. c :: real) \<in> O(\<lambda>x. inverse (f x powr p)) \<longleftrightarrow> c = 0 \<or> p \<le> 0"
proof(cases p "0 :: real" rule: linorder_cases)
  case p_pos: greater
  have "(\<lambda>_. c) \<in> O(\<lambda>x. inverse (f x powr p)) \<longleftrightarrow> (\<lambda>_. \<bar>c\<bar>) \<in> O(\<lambda>x. inverse (f x powr p))" by simp
  also have "\<bar>c\<bar> = \<bar>(\<bar>c\<bar> powr inverse p)\<bar> powr p" using p_pos by(simp add: powr_powr)
  also { have "eventually (\<lambda>x. f x \<ge> 0) at_top" using assms by(simp add: filterlim_at_top)
    then have "O(\<lambda>x. inverse (f x powr p)) = O(\<lambda>x. \<bar>inverse (f x)\<bar> powr p)"
      by(intro landau_o.big.cong)(auto elim!: eventually_rev_mp)
    also have "(\<lambda>_. \<bar>(\<bar>c\<bar> powr inverse p)\<bar> powr p) \<in> \<dots> \<longleftrightarrow>
                 (\<lambda>_. \<bar>c\<bar> powr (inverse p)) \<in> O(\<lambda>x. inverse (f x))"
      using p_pos by (rule bigo_abs_powr_iff)
    also note calculation }
  also have "(\<lambda>_. \<bar>c\<bar> powr (inverse p)) \<in> O(\<lambda>x. inverse (f x)) \<longleftrightarrow> c = 0" using assms by simp
  finally show ?thesis using p_pos by simp
next
  case equal
  from assms have "eventually (\<lambda>x. f x \<ge> 1) at_top" using assms by(simp add: filterlim_at_top)
  then have "O(\<lambda>x. inverse (f x powr p)) = O(\<lambda>x. 1)"
    by(intro landau_o.big.cong) (auto simp add: equal elim!: eventually_rev_mp)
  then show ?thesis using equal by simp
next
  case less
  from assms have *: "\<forall>\<^sub>F x in at_top. 1 \<le> f x" by(simp add: filterlim_at_top)
  then have "(\<lambda>_. \<bar>c\<bar> powr (1 / - p)) \<in> O(f)"
    by(intro bigoI[where c="\<bar>c\<bar> powr (1 / - p)"])
      (auto intro: order_trans[OF _ mult_left_mono, rotated] elim!: eventually_rev_mp[OF _ always_eventually])
  then have "(\<lambda>_. \<bar>\<bar>c\<bar> powr (1 / - p)\<bar> powr - p) \<in> O(\<lambda>x. \<bar>f x\<bar> powr - p)"
    using less by (subst bigo_powr_iff) simp_all
  also have "(\<lambda>_. \<bar>\<bar>c\<bar> powr (1 / - p)\<bar> powr - p) = (\<lambda>_. \<bar>c\<bar>)" using less by(simp add: powr_powr)
  also have "O(\<lambda>x. \<bar>f x\<bar> powr - p) = O(\<lambda>x. f x powr - p)" using *
    by (auto intro!: landau_o.big.cong elim: eventually_rev_mp)
  finally have "(\<lambda>_. c) \<in> O(\<lambda>x. f x powr - p)" by simp
  thus ?thesis using less by (simp add: powr_minus[symmetric])
qed

end


(*  Title:      HOL/Nonstandard_Analysis/HyperNat.thy
    Author:     Jacques D. Fleuriot
    Copyright:  1998  University of Cambridge

Converted to Isar and polished by lcp
*)

section \<open>Hypernatural numbers\<close>

theory HyperNat
  imports StarDef
begin

type_synonym hypnat = "nat star"

abbreviation hypnat_of_nat :: "nat \<Rightarrow> nat star"
  where "hypnat_of_nat \<equiv> star_of"

definition hSuc :: "hypnat \<Rightarrow> hypnat"
  where hSuc_def [transfer_unfold]: "hSuc = *f* Suc"

subsection \<open>Properties Transferred from Naturals\<close>

lemma hSuc_not_zero [iff]: "\<And>m. hSuc m \<noteq> 0"
  by transfer (rule Suc_not_Zero)

lemma zero_not_hSuc [iff]: "\<And>m. 0 \<noteq> hSuc m"
  by transfer (rule Zero_not_Suc)

lemma hSuc_hSuc_eq [iff]: "\<And>m n. hSuc m = hSuc n \<longleftrightarrow> m = n"
  by transfer (rule nat.inject)

lemma zero_less_hSuc [iff]: "\<And>n. 0 < hSuc n"
  by transfer (rule zero_less_Suc)

lemma hypnat_minus_zero [simp]: "\<And>z::hypnat. z - z = 0"
  by transfer (rule diff_self_eq_0)

lemma hypnat_diff_0_eq_0 [simp]: "\<And>n::hypnat. 0 - n = 0"
  by transfer (rule diff_0_eq_0)

lemma hypnat_add_is_0 [iff]: "\<And>m n::hypnat. m + n = 0 \<longleftrightarrow> m = 0 \<and> n = 0"
  by transfer (rule add_is_0)

lemma hypnat_diff_diff_left: "\<And>i j k::hypnat. i - j - k = i - (j + k)"
  by transfer (rule diff_diff_left)

lemma hypnat_diff_commute: "\<And>i j k::hypnat. i - j - k = i - k - j"
  by transfer (rule diff_commute)

lemma hypnat_diff_add_inverse [simp]: "\<And>m n::hypnat. n + m - n = m"
  by transfer (rule diff_add_inverse)

lemma hypnat_diff_add_inverse2 [simp]:  "\<And>m n::hypnat. m + n - n = m"
  by transfer (rule diff_add_inverse2)

lemma hypnat_diff_cancel [simp]: "\<And>k m n::hypnat. (k + m) - (k + n) = m - n"
  by transfer (rule diff_cancel)

lemma hypnat_diff_cancel2 [simp]: "\<And>k m n::hypnat. (m + k) - (n + k) = m - n"
  by transfer (rule diff_cancel2)

lemma hypnat_diff_add_0 [simp]: "\<And>m n::hypnat. n - (n + m) = 0"
  by transfer (rule diff_add_0)

lemma hypnat_diff_mult_distrib: "\<And>k m n::hypnat. (m - n) * k = (m * k) - (n * k)"
  by transfer (rule diff_mult_distrib)

lemma hypnat_diff_mult_distrib2: "\<And>k m n::hypnat. k * (m - n) = (k * m) - (k * n)"
  by transfer (rule diff_mult_distrib2)

lemma hypnat_le_zero_cancel [iff]: "\<And>n::hypnat. n \<le> 0 \<longleftrightarrow> n = 0"
  by transfer (rule le_0_eq)

lemma hypnat_mult_is_0 [simp]: "\<And>m n::hypnat. m * n = 0 \<longleftrightarrow> m = 0 \<or> n = 0"
  by transfer (rule mult_is_0)

lemma hypnat_diff_is_0_eq [simp]: "\<And>m n::hypnat. m - n = 0 \<longleftrightarrow> m \<le> n"
  by transfer (rule diff_is_0_eq)

lemma hypnat_not_less0 [iff]: "\<And>n::hypnat. \<not> n < 0"
  by transfer (rule not_less0)

lemma hypnat_less_one [iff]: "\<And>n::hypnat. n < 1 \<longleftrightarrow> n = 0"
  by transfer (rule less_one)

lemma hypnat_add_diff_inverse: "\<And>m n::hypnat. \<not> m < n \<Longrightarrow> n + (m - n) = m"
  by transfer (rule add_diff_inverse)

lemma hypnat_le_add_diff_inverse [simp]: "\<And>m n::hypnat. n \<le> m \<Longrightarrow> n + (m - n) = m"
  by transfer (rule le_add_diff_inverse)

lemma hypnat_le_add_diff_inverse2 [simp]: "\<And>m n::hypnat. n \<le> m \<Longrightarrow> (m - n) + n = m"
  by transfer (rule le_add_diff_inverse2)

declare hypnat_le_add_diff_inverse2 [OF order_less_imp_le]

lemma hypnat_le0 [iff]: "\<And>n::hypnat. 0 \<le> n"
  by transfer (rule le0)

lemma hypnat_le_add1 [simp]: "\<And>x n::hypnat. x \<le> x + n"
  by transfer (rule le_add1)

lemma hypnat_add_self_le [simp]: "\<And>x n::hypnat. x \<le> n + x"
  by transfer (rule le_add2)

lemma hypnat_add_one_self_less [simp]: "x < x + 1" for x :: hypnat
  by (fact less_add_one)

lemma hypnat_neq0_conv [iff]: "\<And>n::hypnat. n \<noteq> 0 \<longleftrightarrow> 0 < n"
  by transfer (rule neq0_conv)

lemma hypnat_gt_zero_iff: "0 < n \<longleftrightarrow> 1 \<le> n" for n :: hypnat
  by (auto simp add: linorder_not_less [symmetric])

lemma hypnat_gt_zero_iff2: "0 < n \<longleftrightarrow> (\<exists>m. n = m + 1)" for n :: hypnat
  by (auto intro!: add_nonneg_pos exI[of _ "n - 1"] simp: hypnat_gt_zero_iff)

lemma hypnat_add_self_not_less: "\<not> x + y < x" for x y :: hypnat
  by (simp add: linorder_not_le [symmetric] add.commute [of x])

lemma hypnat_diff_split: "P (a - b) \<longleftrightarrow> (a < b \<longrightarrow> P 0) \<and> (\<forall>d. a = b + d \<longrightarrow> P d)"
  for a b :: hypnat
  \<comment> \<open>elimination of \<open>-\<close> on \<open>hypnat\<close>\<close>
proof (cases "a < b" rule: case_split)
  case True
  then show ?thesis
    by (auto simp add: hypnat_add_self_not_less order_less_imp_le hypnat_diff_is_0_eq [THEN iffD2])
next
  case False
  then show ?thesis
    by (auto simp add: linorder_not_less dest: order_le_less_trans)
qed

subsection \<open>Properties of the set of embedded natural numbers\<close>

lemma of_nat_eq_star_of [simp]: "of_nat = star_of"
proof
  show "of_nat n = star_of n" for n
    by transfer simp
qed

lemma Nats_eq_Standard: "(Nats :: nat star set) = Standard"
  by (auto simp: Nats_def Standard_def)

lemma hypnat_of_nat_mem_Nats [simp]: "hypnat_of_nat n \<in> Nats"
  by (simp add: Nats_eq_Standard)

lemma hypnat_of_nat_one [simp]: "hypnat_of_nat (Suc 0) = 1"
  by transfer simp

lemma hypnat_of_nat_Suc [simp]: "hypnat_of_nat (Suc n) = hypnat_of_nat n + 1"
  by transfer simp

lemma of_nat_eq_add:
  fixes d::hypnat
  shows "of_nat m = of_nat n + d \<Longrightarrow> d \<in> range of_nat"
proof (induct n arbitrary: d)
  case (Suc n)
  then show ?case
    by (metis Nats_def Nats_eq_Standard Standard_simps(4) hypnat_diff_add_inverse of_nat_in_Nats)
qed auto

lemma Nats_diff [simp]: "a \<in> Nats \<Longrightarrow> b \<in> Nats \<Longrightarrow> a - b \<in> Nats" for a b :: hypnat
  by (simp add: Nats_eq_Standard)

subsection \<open>Infinite Hypernatural Numbers -- \<^term>\<open>HNatInfinite\<close>\<close>

text \<open>The set of infinite hypernatural numbers.\<close>
definition HNatInfinite :: "hypnat set"
  where "HNatInfinite = {n. n \<notin> Nats}"

lemma Nats_not_HNatInfinite_iff: "x \<in> Nats \<longleftrightarrow> x \<notin> HNatInfinite"
  by (simp add: HNatInfinite_def)

lemma HNatInfinite_not_Nats_iff: "x \<in> HNatInfinite \<longleftrightarrow> x \<notin> Nats"
  by (simp add: HNatInfinite_def)

lemma star_of_neq_HNatInfinite: "N \<in> HNatInfinite \<Longrightarrow> star_of n \<noteq> N"
  by (auto simp add: HNatInfinite_def Nats_eq_Standard)

lemma star_of_Suc_lessI: "\<And>N. star_of n < N \<Longrightarrow> star_of (Suc n) \<noteq> N \<Longrightarrow> star_of (Suc n) < N"
  by transfer (rule Suc_lessI)

lemma star_of_less_HNatInfinite:
  assumes N: "N \<in> HNatInfinite"
  shows "star_of n < N"
proof (induct n)
  case 0
  from N have "star_of 0 \<noteq> N"
    by (rule star_of_neq_HNatInfinite)
  then show ?case by simp
next
  case (Suc n)
  from N have "star_of (Suc n) \<noteq> N"
    by (rule star_of_neq_HNatInfinite)
  with Suc show ?case
    by (rule star_of_Suc_lessI)
qed

lemma star_of_le_HNatInfinite: "N \<in> HNatInfinite \<Longrightarrow> star_of n \<le> N"
  by (rule star_of_less_HNatInfinite [THEN order_less_imp_le])

subsubsection \<open>Closure Rules\<close>

lemma Nats_less_HNatInfinite: "x \<in> Nats \<Longrightarrow> y \<in> HNatInfinite \<Longrightarrow> x < y"
  by (auto simp add: Nats_def star_of_less_HNatInfinite)

lemma Nats_le_HNatInfinite: "x \<in> Nats \<Longrightarrow> y \<in> HNatInfinite \<Longrightarrow> x \<le> y"
  by (rule Nats_less_HNatInfinite [THEN order_less_imp_le])

lemma zero_less_HNatInfinite: "x \<in> HNatInfinite \<Longrightarrow> 0 < x"
  by (simp add: Nats_less_HNatInfinite)

lemma one_less_HNatInfinite: "x \<in> HNatInfinite \<Longrightarrow> 1 < x"
  by (simp add: Nats_less_HNatInfinite)

lemma one_le_HNatInfinite: "x \<in> HNatInfinite \<Longrightarrow> 1 \<le> x"
  by (simp add: Nats_le_HNatInfinite)

lemma zero_not_mem_HNatInfinite [simp]: "0 \<notin> HNatInfinite"
  by (simp add: HNatInfinite_def)

lemma Nats_downward_closed: "x \<in> Nats \<Longrightarrow> y \<le> x \<Longrightarrow> y \<in> Nats" for x y :: hypnat
  using HNatInfinite_not_Nats_iff Nats_le_HNatInfinite by fastforce

lemma HNatInfinite_upward_closed: "x \<in> HNatInfinite \<Longrightarrow> x \<le> y \<Longrightarrow> y \<in> HNatInfinite"
  using HNatInfinite_not_Nats_iff Nats_downward_closed by blast

lemma HNatInfinite_add: "x \<in> HNatInfinite \<Longrightarrow> x + y \<in> HNatInfinite"
  using HNatInfinite_upward_closed hypnat_le_add1 by blast

lemma HNatInfinite_diff: "\<lbrakk>x \<in> HNatInfinite; y \<in> Nats\<rbrakk> \<Longrightarrow> x - y \<in> HNatInfinite"
  by (metis HNatInfinite_not_Nats_iff Nats_add Nats_le_HNatInfinite le_add_diff_inverse)

lemma HNatInfinite_is_Suc: "x \<in> HNatInfinite \<Longrightarrow> \<exists>y. x = y + 1" for x :: hypnat
  using hypnat_gt_zero_iff2 zero_less_HNatInfinite by blast

subsection \<open>Existence of an infinite hypernatural number\<close>

text \<open>\<open>\<omega>\<close> is in fact an infinite hypernatural number = \<open>[<1, 2, 3, \<dots>>]\<close>\<close>
definition whn :: hypnat
  where hypnat_omega_def: "whn = star_n (\<lambda>n::nat. n)"

lemma hypnat_of_nat_neq_whn: "hypnat_of_nat n \<noteq> whn"
  by (simp add: FreeUltrafilterNat.singleton' hypnat_omega_def star_of_def star_n_eq_iff)

lemma whn_neq_hypnat_of_nat: "whn \<noteq> hypnat_of_nat n"
  by (simp add: FreeUltrafilterNat.singleton hypnat_omega_def star_of_def star_n_eq_iff)

lemma whn_not_Nats [simp]: "whn \<notin> Nats"
  by (simp add: Nats_def image_def whn_neq_hypnat_of_nat)

lemma HNatInfinite_whn [simp]: "whn \<in> HNatInfinite"
  by (simp add: HNatInfinite_def)

lemma lemma_unbounded_set [simp]: "eventually (\<lambda>n::nat. m < n) \<U>"
  by (rule filter_leD[OF FreeUltrafilterNat.le_cofinite])
     (auto simp add: cofinite_eq_sequentially eventually_at_top_dense)

lemma hypnat_of_nat_eq: "hypnat_of_nat m  = star_n (\<lambda>n::nat. m)"
  by (simp add: star_of_def)

lemma SHNat_eq: "Nats = {n. \<exists>N. n = hypnat_of_nat N}"
  by (simp add: Nats_def image_def)

lemma Nats_less_whn: "n \<in> Nats \<Longrightarrow> n < whn"
  by (simp add: Nats_less_HNatInfinite)

lemma Nats_le_whn: "n \<in> Nats \<Longrightarrow> n \<le> whn"
  by (simp add: Nats_le_HNatInfinite)

lemma hypnat_of_nat_less_whn [simp]: "hypnat_of_nat n < whn"
  by (simp add: Nats_less_whn)

lemma hypnat_of_nat_le_whn [simp]: "hypnat_of_nat n \<le> whn"
  by (simp add: Nats_le_whn)

lemma hypnat_zero_less_hypnat_omega [simp]: "0 < whn"
  by (simp add: Nats_less_whn)

lemma hypnat_one_less_hypnat_omega [simp]: "1 < whn"
  by (simp add: Nats_less_whn)

subsubsection \<open>Alternative characterization of the set of infinite hypernaturals\<close>

text \<open>\<^term>\<open>HNatInfinite = {N. \<forall>n \<in> Nats. n < N}\<close>\<close>

text\<open>unused, but possibly interesting\<close>
lemma HNatInfinite_FreeUltrafilterNat_eventually:
  assumes "\<And>k::nat. eventually (\<lambda>n. f n \<noteq> k) \<U>"
  shows "eventually (\<lambda>n. m < f n) \<U>"
proof (induct m)
  case 0
  then show ?case
    using assms eventually_mono by fastforce
next
  case (Suc m)
  then show ?case
    using assms [of "Suc m"] eventually_elim2 by fastforce
qed

lemma HNatInfinite_iff: "HNatInfinite = {N. \<forall>n \<in> Nats. n < N}"
  using HNatInfinite_def Nats_less_HNatInfinite by auto

subsubsection \<open>Alternative Characterization of \<^term>\<open>HNatInfinite\<close> using Free Ultrafilter\<close>

lemma HNatInfinite_FreeUltrafilterNat:
  "star_n X \<in> HNatInfinite \<Longrightarrow> \<forall>u. eventually (\<lambda>n. u < X n) \<U>"
  by (metis (full_types) starP2_star_of starP_star_n star_less_def star_of_less_HNatInfinite)

lemma FreeUltrafilterNat_HNatInfinite:
  "\<forall>u. eventually (\<lambda>n. u < X n) \<U> \<Longrightarrow> star_n X \<in> HNatInfinite"
  by (auto simp add: star_less_def starP2_star_n HNatInfinite_iff SHNat_eq hypnat_of_nat_eq)

lemma HNatInfinite_FreeUltrafilterNat_iff:
  "(star_n X \<in> HNatInfinite) = (\<forall>u. eventually (\<lambda>n. u < X n) \<U>)"
  by (rule iffI [OF HNatInfinite_FreeUltrafilterNat FreeUltrafilterNat_HNatInfinite])

subsection \<open>Embedding of the Hypernaturals into other types\<close>

definition of_hypnat :: "hypnat \<Rightarrow> 'a::semiring_1_cancel star"
  where of_hypnat_def [transfer_unfold]: "of_hypnat = *f* of_nat"

lemma of_hypnat_0 [simp]: "of_hypnat 0 = 0"
  by transfer (rule of_nat_0)

lemma of_hypnat_1 [simp]: "of_hypnat 1 = 1"
  by transfer (rule of_nat_1)

lemma of_hypnat_hSuc: "\<And>m. of_hypnat (hSuc m) = 1 + of_hypnat m"
  by transfer (rule of_nat_Suc)

lemma of_hypnat_add [simp]: "\<And>m n. of_hypnat (m + n) = of_hypnat m + of_hypnat n"
  by transfer (rule of_nat_add)

lemma of_hypnat_mult [simp]: "\<And>m n. of_hypnat (m * n) = of_hypnat m * of_hypnat n"
  by transfer (rule of_nat_mult)

lemma of_hypnat_less_iff [simp]:
  "\<And>m n. of_hypnat m < (of_hypnat n::'a::linordered_semidom star) \<longleftrightarrow> m < n"
  by transfer (rule of_nat_less_iff)

lemma of_hypnat_0_less_iff [simp]:
  "\<And>n. 0 < (of_hypnat n::'a::linordered_semidom star) \<longleftrightarrow> 0 < n"
  by transfer (rule of_nat_0_less_iff)

lemma of_hypnat_less_0_iff [simp]: "\<And>m. \<not> (of_hypnat m::'a::linordered_semidom star) < 0"
  by transfer (rule of_nat_less_0_iff)

lemma of_hypnat_le_iff [simp]:
  "\<And>m n. of_hypnat m \<le> (of_hypnat n::'a::linordered_semidom star) \<longleftrightarrow> m \<le> n"
  by transfer (rule of_nat_le_iff)

lemma of_hypnat_0_le_iff [simp]: "\<And>n. 0 \<le> (of_hypnat n::'a::linordered_semidom star)"
  by transfer (rule of_nat_0_le_iff)

lemma of_hypnat_le_0_iff [simp]: "\<And>m. (of_hypnat m::'a::linordered_semidom star) \<le> 0 \<longleftrightarrow> m = 0"
  by transfer (rule of_nat_le_0_iff)

lemma of_hypnat_eq_iff [simp]:
  "\<And>m n. of_hypnat m = (of_hypnat n::'a::linordered_semidom star) \<longleftrightarrow> m = n"
  by transfer (rule of_nat_eq_iff)

lemma of_hypnat_eq_0_iff [simp]: "\<And>m. (of_hypnat m::'a::linordered_semidom star) = 0 \<longleftrightarrow> m = 0"
  by transfer (rule of_nat_eq_0_iff)

lemma HNatInfinite_of_hypnat_gt_zero:
  "N \<in> HNatInfinite \<Longrightarrow> (0::'a::linordered_semidom star) < of_hypnat N"
  by (rule ccontr) (simp add: linorder_not_less)

end


(*  Title:      HOL/ZF/LProd.thy
    Author:     Steven Obua

    Introduces the lprod relation.
    See "Partizan Games in Isabelle/HOLZF", available from http://www4.in.tum.de/~obua/partizan
*)

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

inductive_set
  lprod :: "('a * 'a) set \<Rightarrow> ('a list * 'a list) set"
  for R :: "('a * 'a) set"
where
  lprod_single[intro!]: "(a, b) \<in> R \<Longrightarrow> ([a], [b]) \<in> lprod R"
| lprod_list[intro!]: "(ah@at, bh@bt) \<in> lprod R \<Longrightarrow> (a,b) \<in> R \<or> a = b \<Longrightarrow> (ah@a#at, bh@b#bt) \<in> lprod R"

lemma "(as,bs) \<in> lprod R \<Longrightarrow> length as = length bs"
  apply (induct as bs rule: lprod.induct)
  apply auto
  done

lemma "(as, bs) \<in> lprod R \<Longrightarrow> 1 \<le> length as \<and> 1 \<le> length bs"
  apply (induct as bs rule: lprod.induct)
  apply auto
  done

lemma lprod_subset_elem: "(as, bs) \<in> lprod S \<Longrightarrow> S \<subseteq> R \<Longrightarrow> (as, bs) \<in> lprod R"
  apply (induct as bs rule: lprod.induct)
  apply (auto)
  done

lemma lprod_subset: "S \<subseteq> R \<Longrightarrow> lprod S \<subseteq> lprod R"
  by (auto intro: lprod_subset_elem)

lemma lprod_implies_mult: "(as, bs) \<in> lprod R \<Longrightarrow> trans R \<Longrightarrow> (multiset_of as, multiset_of bs) \<in> mult R"
proof (induct as bs rule: lprod.induct)
  case (lprod_single a b)
  note step = one_step_implies_mult[
    where r=R and I="{#}" and K="{#a#}" and J="{#b#}", simplified]
  show ?case by (auto intro: lprod_single step)
next
  case (lprod_list ah at bh bt a b)
  then have transR: "trans R" by auto
  have as: "multiset_of (ah @ a # at) = multiset_of (ah @ at) + {#a#}" (is "_ = ?ma + _")
    by (simp add: algebra_simps)
  have bs: "multiset_of (bh @ b # bt) = multiset_of (bh @ bt) + {#b#}" (is "_ = ?mb + _")
    by (simp add: algebra_simps)
  from lprod_list have "(?ma, ?mb) \<in> mult R"
    by auto
  with mult_implies_one_step[OF transR] have
    "\<exists>I J K. ?mb = I + J \<and> ?ma = I + K \<and> J \<noteq> {#} \<and> (\<forall>k\<in>set_of K. \<exists>j\<in>set_of J. (k, j) \<in> R)"
    by blast
  then obtain I J K where
    decomposed: "?mb = I + J \<and> ?ma = I + K \<and> J \<noteq> {#} \<and> (\<forall>k\<in>set_of K. \<exists>j\<in>set_of J. (k, j) \<in> R)"
    by blast
  show ?case
  proof (cases "a = b")
    case True
    have "((I + {#b#}) + K, (I + {#b#}) + J) \<in> mult R"
      apply (rule one_step_implies_mult[OF transR])
      apply (auto simp add: decomposed)
      done
    then show ?thesis
      apply (simp only: as bs)
      apply (simp only: decomposed True)
      apply (simp add: algebra_simps)
      done
  next
    case False
    from False lprod_list have False: "(a, b) \<in> R" by blast
    have "(I + (K + {#a#}), I + (J + {#b#})) \<in> mult R"
      apply (rule one_step_implies_mult[OF transR])
      apply (auto simp add: False decomposed)
      done
    then show ?thesis
      apply (simp only: as bs)
      apply (simp only: decomposed)
      apply (simp add: algebra_simps)
      done
  qed
qed

lemma wf_lprod[simp,intro]:
  assumes wf_R: "wf R"
  shows "wf (lprod R)"
proof -
  have subset: "lprod (R^+) \<subseteq> inv_image (mult (R^+)) multiset_of"
    by (auto simp add: lprod_implies_mult trans_trancl)
  note lprodtrancl = wf_subset[OF wf_inv_image[where r="mult (R^+)" and f="multiset_of",
    OF wf_mult[OF wf_trancl[OF wf_R]]], OF subset]
  note lprod = wf_subset[OF lprodtrancl, where p="lprod R", OF lprod_subset, simplified]
  show ?thesis by (auto intro: lprod)
qed

definition gprod_2_2 :: "('a * 'a) set \<Rightarrow> (('a * 'a) * ('a * 'a)) set" where
  "gprod_2_2 R \<equiv> { ((a,b), (c,d)) . (a = c \<and> (b,d) \<in> R) \<or> (b = d \<and> (a,c) \<in> R) }"

definition gprod_2_1 :: "('a * 'a) set \<Rightarrow> (('a * 'a) * ('a * 'a)) set" where
  "gprod_2_1 R \<equiv>  { ((a,b), (c,d)) . (a = d \<and> (b,c) \<in> R) \<or> (b = c \<and> (a,d) \<in> R) }"

lemma lprod_2_3: "(a, b) \<in> R \<Longrightarrow> ([a, c], [b, c]) \<in> lprod R"
  by (auto intro: lprod_list[where a=c and b=c and
    ah = "[a]" and at = "[]" and bh="[b]" and bt="[]", simplified])

lemma lprod_2_4: "(a, b) \<in> R \<Longrightarrow> ([c, a], [c, b]) \<in> lprod R"
  by (auto intro: lprod_list[where a=c and b=c and
    ah = "[]" and at = "[a]" and bh="[]" and bt="[b]", simplified])

lemma lprod_2_1: "(a, b) \<in> R \<Longrightarrow> ([c, a], [b, c]) \<in> lprod R"
  by (auto intro: lprod_list[where a=c and b=c and
    ah = "[]" and at = "[a]" and bh="[b]" and bt="[]", simplified])

lemma lprod_2_2: "(a, b) \<in> R \<Longrightarrow> ([a, c], [c, b]) \<in> lprod R"
  by (auto intro: lprod_list[where a=c and b=c and
    ah = "[a]" and at = "[]" and bh="[]" and bt="[b]", simplified])

lemma [simp, intro]:
  assumes wfR: "wf R" shows "wf (gprod_2_1 R)"
proof -
  have "gprod_2_1 R \<subseteq> inv_image (lprod R) (\<lambda> (a,b). [a,b])"
    by (auto simp add: gprod_2_1_def lprod_2_1 lprod_2_2)
  with wfR show ?thesis
    by (rule_tac wf_subset, auto)
qed

lemma [simp, intro]:
  assumes wfR: "wf R" shows "wf (gprod_2_2 R)"
proof -
  have "gprod_2_2 R \<subseteq> inv_image (lprod R) (\<lambda> (a,b). [a,b])"
    by (auto simp add: gprod_2_2_def lprod_2_3 lprod_2_4)
  with wfR show ?thesis
    by (rule_tac wf_subset, auto)
qed

lemma lprod_3_1: assumes "(x', x) \<in> R" shows "([y, z, x'], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="y" and b="y" and ah="[]" and at="[z,x']" and bh="[x]" and bt="[z]", simplified])
  apply (auto simp add: lprod_2_1 assms)
  done

lemma lprod_3_2: assumes "(z',z) \<in> R" shows "([z', x, y], [x,y,z]) \<in> lprod R"
  apply (rule lprod_list[where a="y" and b="y" and ah="[z',x]" and at="[]" and bh="[x]" and bt="[z]", simplified])
  apply (auto simp add: lprod_2_2 assms)
  done

lemma lprod_3_3: assumes xr: "(xr, x) \<in> R" shows "([xr, y, z], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="y" and b="y" and ah="[xr]" and at="[z]" and bh="[x]" and bt="[z]", simplified])
  apply (simp add: xr lprod_2_3)
  done

lemma lprod_3_4: assumes yr: "(yr, y) \<in> R" shows "([x, yr, z], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="x" and b="x" and ah="[]" and at="[yr,z]" and bh="[]" and bt="[y,z]", simplified])
  apply (simp add: yr lprod_2_3)
  done

lemma lprod_3_5: assumes zr: "(zr, z) \<in> R" shows "([x, y, zr], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="x" and b="x" and ah="[]" and at="[y,zr]" and bh="[]" and bt="[y,z]", simplified])
  apply (simp add: zr lprod_2_4)
  done

lemma lprod_3_6: assumes y': "(y', y) \<in> R" shows "([x, z, y'], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="z" and b="z" and ah="[x]" and at="[y']" and bh="[x,y]" and bt="[]", simplified])
  apply (simp add: y' lprod_2_4)
  done

lemma lprod_3_7: assumes z': "(z',z) \<in> R" shows "([x, z', y], [x, y, z]) \<in> lprod R"
  apply (rule lprod_list[where a="y" and b="y" and ah="[x, z']" and at="[]" and bh="[x]" and bt="[z]", simplified])
  apply (simp add: z' lprod_2_4)
  done

definition perm :: "('a \<Rightarrow> 'a) \<Rightarrow> 'a set \<Rightarrow> bool" where
   "perm f A \<equiv> inj_on f A \<and> f ` A = A"

lemma "((as,bs) \<in> lprod R) =
  (\<exists> f. perm f {0 ..< (length as)} \<and>
  (\<forall> j. j < length as \<longrightarrow> ((nth as j, nth bs (f j)) \<in> R \<or> (nth as j = nth bs (f j)))) \<and>
  (\<exists> i. i < length as \<and> (nth as i, nth bs (f i)) \<in> R))"
oops

lemma "trans R \<Longrightarrow> (ah@a#at, bh@b#bt) \<in> lprod R \<Longrightarrow> (b, a) \<in> R \<or> a = b \<Longrightarrow> (ah@at, bh@bt) \<in> lprod R"
oops

end


(* Authors: F. Maric, M. Spasic *)
section \<open>Linearly Ordered Rational Vectors\<close>
theory Simplex_Algebra
  imports
    HOL.Rat
    HOL.Real_Vector_Spaces
begin

class scaleRat =
  fixes scaleRat :: "rat \<Rightarrow> 'a \<Rightarrow> 'a" (infixr "*R" 75)
begin

abbreviation
  divideRat :: "'a \<Rightarrow> rat \<Rightarrow> 'a" (infixl "'/R" 70)
  where
    "x /R r == scaleRat (inverse r) x"
end

class rational_vector = scaleRat + ab_group_add +
  assumes scaleRat_right_distrib: "scaleRat a (x + y) = scaleRat a x + scaleRat a y"
    and scaleRat_left_distrib: "scaleRat (a + b) x = scaleRat a x + scaleRat b x"
    and scaleRat_scaleRat: "scaleRat a (scaleRat b x) = scaleRat (a * b) x"
    and scaleRat_one: "scaleRat 1 x = x"

interpretation rational_vector:
  vector_space "scaleRat :: rat \<Rightarrow> 'a \<Rightarrow> 'a::rational_vector"
  by (unfold_locales) (simp_all add: scaleRat_right_distrib  scaleRat_left_distrib scaleRat_scaleRat scaleRat_one)

class ordered_rational_vector = rational_vector + order

class linordered_rational_vector = ordered_rational_vector + linorder +
  assumes plus_less: "(a::'a) < b \<Longrightarrow> a + c < b + c" and
    scaleRat_less1: "\<lbrakk>(a::'a) < b; k > 0\<rbrakk> \<Longrightarrow> (k *R a) < (k *R b)" and
    scaleRat_less2: "\<lbrakk>(a::'a) < b; k < 0\<rbrakk> \<Longrightarrow> (k *R a) > (k *R b)"
begin

lemma scaleRat_leq1: "\<lbrakk> a \<le> b; k > 0\<rbrakk> \<Longrightarrow> k *R a \<le> k *R b"
  unfolding le_less
  using scaleRat_less1[of a b k]
  by auto

lemma scaleRat_leq2: "\<lbrakk> a \<le> b; k < 0\<rbrakk> \<Longrightarrow> k *R a \<ge> k *R b"
  unfolding le_less
  using scaleRat_less2[of a b k]
  by auto

lemma zero_scaleRat
  [simp]: "0 *R v = zero"
  using scaleRat_left_distrib[of 0 0 v]
  by auto

lemma scaleRat_zero
  [simp]: "a *R (0::'a) = 0"
  using scaleRat_right_distrib[of a 0 0]
  by auto

lemma scaleRat_uminus [simp]:
  "-1 *R x = - (x :: 'a)"
proof-
  have "0  = -1 *R x + x"
    using scaleRat_left_distrib[of "-1" 1 x]
    by (simp add: scaleRat_one)
  have "-x = 0 - x"
    by simp
  then have "-x = -1 *R x + x - x"
    using \<open>0  = -1 *R x + x\<close>
    by simp
  then show ?thesis
    by (simp add: add_assoc)
qed

lemma minus_lt: "(a::'a) < b \<longleftrightarrow> a - b < 0"
  using plus_less[of a b "-b"]
  using plus_less[of "a - b" 0 b]
  by (auto simp add: add_assoc)

lemma minus_gt: "(a::'a) < b \<longleftrightarrow> 0 < b - a"
  using plus_less[of a b "-a"]
  using plus_less[of 0 "b-a" a]
  by (auto simp add: add_assoc)

lemma minus_leq:
  "(a::'a) \<le> b \<longleftrightarrow> a - b \<le> 0"
proof-
  have *: "a \<le> b \<Longrightarrow> a - b \<le> (0 :: 'a)"
    using minus_gt[of a b]
    using scaleRat_less2[of 0 "b-a" "-1"]
    by (auto simp add: not_less_iff_gr_or_eq)
  have **: "a - b \<le> 0 \<Longrightarrow> a \<le> b"
  proof-
    assume "a - b \<le> 0"
    show ?thesis
    proof(cases "a - b < 0")
      case True
      then show ?thesis
        using plus_less[of "a - b" 0 b]
        by (simp add: add_assoc )
    next
      case False
      then show ?thesis
        using \<open>a - b \<le> 0\<close>
        by (simp add:antisym_conv1)
    qed
  qed
  show ?thesis
    using * **
    by auto
qed

lemma minus_geq: "(a::'a) \<le> b \<longleftrightarrow> 0 \<le> b - a"
proof-
  have *: "a \<le> b \<Longrightarrow> 0 \<le> b - a"
    using minus_gt[of a b]
    by (auto simp add: not_less_iff_gr_or_eq)
  have **: "0 \<le> b - a \<Longrightarrow> a \<le> b"
  proof-
    assume "0 \<le> b - a"
    show ?thesis
    proof(cases "0 < b - a")
      case True
      then show ?thesis
        using plus_less[of 0 "b - a" a]
        by (simp add: add_assoc)
    next
      case False
      then show ?thesis
        using \<open>0 \<le> b - a\<close>
        using eq_iff[of "b - a" 0]
        by auto
    qed
  qed
  show ?thesis
    using * **
    by auto
qed

lemma divide_lt:
  "\<lbrakk>c *R (a::'a) < b; (c::rat) > 0 \<rbrakk> \<Longrightarrow> a < (1/c) *R b"
  using scaleRat_less1[of "c *R a" b "1/c"]
  by (simp add: scaleRat_one scaleRat_scaleRat)

lemma divide_gt:
  "\<lbrakk>c *R (a::'a) > b;(c::rat) > 0\<rbrakk> \<Longrightarrow> a > (1/c) *R b"
  using scaleRat_less1[of b "c *R a" "1/c"]
  by (simp add: scaleRat_one scaleRat_scaleRat)

lemma divide_leq:
  "\<lbrakk>c *R (a::'a) \<le>  b; (c::rat) > 0\<rbrakk> \<Longrightarrow> a \<le> (1/c) *R b"
proof(cases "c *R a < b")
  assume "c > 0"
  case True
  then show ?thesis
    using divide_lt[of c a b]
    using \<open>c > 0\<close>
    by simp
next
  assume "c *R a \<le>  b" "c > 0"
  case False
  then have *: "c *R a = b"
    using \<open>c *R a \<le> b\<close>
    by simp
  then show ?thesis
    using \<open>c > 0\<close>
    by (auto simp add: scaleRat_one scaleRat_scaleRat)
qed

lemma divide_geq:
  "\<lbrakk>c *R (a::'a) \<ge> b; (c::rat) > 0\<rbrakk> \<Longrightarrow> a \<ge> (1/c) *R b"
proof(cases "c *R a > b")
  assume "c > 0"
  case True
  then show ?thesis
    using divide_gt[of b c a]
    using \<open>c > 0\<close>
    by simp
next
  assume "c *R a \<ge> b" "c > 0"
  case False
  then have *: "c *R a = b"
    using \<open>c *R a \<ge> b\<close>
    by simp
  then show ?thesis
    using \<open>c > 0\<close>
    by (auto simp add: scaleRat_one scaleRat_scaleRat)
qed

lemma divide_lt1:
  "\<lbrakk>c *R (a::'a) < b; (c::rat) < 0\<rbrakk> \<Longrightarrow> a > (1/c) *R b"
  using scaleRat_less2[of "c *R a" b "1/c"]
  by (simp add: scaleRat_scaleRat scaleRat_one)

lemma divide_gt1:
  "\<lbrakk>c *R (a::'a) > b; (c::rat) < 0 \<rbrakk> \<Longrightarrow>  a < (1/c) *R b"
  using scaleRat_less2[of b "c *R a" "1/c"]
  by (simp add: scaleRat_scaleRat scaleRat_one)

lemma divide_leq1:
  "\<lbrakk>c *R (a::'a) \<le>  b; (c::rat) < 0\<rbrakk> \<Longrightarrow> a \<ge> (1/c) *R b"
proof(cases "c *R a < b")
  assume "c < 0"
  case True
  then show ?thesis
    using divide_lt1[of c a b]
    using \<open>c < 0\<close>
    by simp
next
  assume "c *R a \<le>  b" "c < 0"
  case False
  then have *: "c *R a = b"
    using \<open>c *R a \<le> b\<close>
    by simp
  then show ?thesis
    using \<open>c < 0\<close>
    by (auto simp add: scaleRat_one scaleRat_scaleRat)
qed

lemma divide_geq1:
  "\<lbrakk>c *R (a::'a) \<ge> b; (c::rat) < 0\<rbrakk> \<Longrightarrow> a \<le> (1/c) *R b"
proof(cases "c *R a > b")
  assume "c < 0"
  case True
  then show ?thesis
    using divide_gt1[of b c a]
    using \<open>c < 0\<close>
    by simp
next
  assume "c *R a \<ge> b" "c < 0"
  case False
  then have *: "c *R a = b"
    using \<open>c *R a \<ge> b\<close>
    by simp
  then show ?thesis
    using \<open>c < 0\<close>
    by (auto simp add: scaleRat_one scaleRat_scaleRat)
qed

end

class lrv = linordered_rational_vector + one +
  assumes zero_neq_one: "0 \<noteq> 1"

subclass (in linordered_rational_vector) ordered_ab_semigroup_add
proof
  fix a b c
  assume "a \<le> b"
  then show "c + a \<le> c + b"
    using plus_less[of a b c]
    by (auto simp add: add_ac le_less)
qed

instantiation rat :: rational_vector
begin
definition scaleRat_rat :: "rat \<Rightarrow> rat \<Rightarrow> rat" where
  [simp]: "x *R y = x * y"
instance by standard (auto simp add: field_simps)
end

instantiation rat :: ordered_rational_vector
begin
instance ..
end

instantiation rat :: linordered_rational_vector
begin
instance by standard (auto simp add: field_simps)
end

instantiation rat :: lrv
begin
instance by standard (auto simp add: field_simps)
end

lemma uminus_less_lrv[simp]: fixes a b :: "'a :: lrv"
  shows "- a < - b \<longleftrightarrow> b < a"
proof -
  have "(- a < - b) = (-1 *R a < -1 *R b)" by simp
  also have "\<dots> \<longleftrightarrow> (b < a)"
    using scaleRat_less2[of _ _ "-1"] scaleRat_less2[of "-1 *R a" "-1 *R b" "-1"] by auto
  finally show ?thesis .
qed

end


\<^marker>\<open>creator "Kevin Kappelmann"\<close>
paragraph \<open>Injective\<close>
theory Functions_Injective
  imports
    Functions_Base
    HOL_Syntax_Bundles_Lattices
begin

consts injective_on :: "'a \<Rightarrow> ('b \<Rightarrow> 'c) \<Rightarrow> bool"

overloading
  injective_on_pred \<equiv> "injective_on :: ('a \<Rightarrow> bool) \<Rightarrow> ('a \<Rightarrow> 'b) \<Rightarrow> bool"
begin
  definition "injective_on_pred P f \<equiv> \<forall>x x'. P x \<longrightarrow> P x' \<longrightarrow> f x = f x' \<longrightarrow> x = x'"
end

lemma injective_onI [intro]:
  assumes "\<And>x x'. P x \<Longrightarrow> P x' \<Longrightarrow> f x = f x' \<Longrightarrow> x = x'"
  shows "injective_on P f"
  unfolding injective_on_pred_def using assms by blast

lemma injective_onD:
  assumes "injective_on P f"
  and "P x" "P x'"
  and "f x = f x'"
  shows "x = x'"
  using assms unfolding injective_on_pred_def by blast

definition "injective (f :: 'a \<Rightarrow> _) \<equiv> injective_on (\<top> :: 'a \<Rightarrow> bool) f"

lemma injective_eq_injective_on:
  "injective (f :: 'a \<Rightarrow> _) = injective_on (\<top> :: 'a \<Rightarrow> bool) f"
  unfolding injective_def ..

lemma injectiveI [intro]:
  assumes "\<And>x x'. f x = f x' \<Longrightarrow> x = x'"
  shows "injective f"
  unfolding injective_eq_injective_on using assms by (intro injective_onI)

lemma injectiveD:
  assumes "injective f"
  and "f x = f x'"
  shows "x = x'"
  using assms unfolding injective_eq_injective_on by (auto dest: injective_onD)

lemma injective_on_if_injective:
  fixes P :: "'a \<Rightarrow> bool" and f :: "'a \<Rightarrow> _"
  assumes "injective f"
  shows "injective_on P f"
  using assms by (intro injective_onI) (blast dest: injectiveD)

paragraph \<open>Instantiations\<close>

lemma injective_id: "injective id" by auto

end


(*  Title:      HOL/Divides.thy
    Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
    Copyright   1999  University of Cambridge
*)

section \<open>More on quotient and remainder\<close>

theory Divides
imports Parity
begin

subsection \<open>More on division\<close>

inductive eucl_rel_int :: "int \<Rightarrow> int \<Rightarrow> int \<times> int \<Rightarrow> bool"
  where eucl_rel_int_by0: "eucl_rel_int k 0 (0, k)"
  | eucl_rel_int_dividesI: "l \<noteq> 0 \<Longrightarrow> k = q * l \<Longrightarrow> eucl_rel_int k l (q, 0)"
  | eucl_rel_int_remainderI: "sgn r = sgn l \<Longrightarrow> \<bar>r\<bar> < \<bar>l\<bar>
      \<Longrightarrow> k = q * l + r \<Longrightarrow> eucl_rel_int k l (q, r)"

lemma eucl_rel_int_iff:
  "eucl_rel_int k l (q, r) \<longleftrightarrow>
    k = l * q + r \<and>
     (if 0 < l then 0 \<le> r \<and> r < l else if l < 0 then l < r \<and> r \<le> 0 else q = 0)"
  by (cases "r = 0")
    (auto elim!: eucl_rel_int.cases intro: eucl_rel_int_by0 eucl_rel_int_dividesI eucl_rel_int_remainderI
    simp add: ac_simps sgn_1_pos sgn_1_neg)

lemma unique_quotient_lemma:
  assumes "b * q' + r' \<le> b * q + r" "0 \<le> r'" "r' < b" "r < b" shows "q' \<le> (q::int)"
proof -
  have "r' + b * (q'-q) \<le> r"
    using assms by (simp add: right_diff_distrib)
  moreover have "0 < b * (1 + q - q') "
    using assms by (simp add: right_diff_distrib distrib_left)
  moreover have "b * q' < b * (1 + q)"
    using assms by (simp add: right_diff_distrib distrib_left)
  ultimately show ?thesis
    using assms by (simp add: mult_less_cancel_left)
qed

lemma unique_quotient_lemma_neg:
  "b * q' + r' \<le> b*q + r \<Longrightarrow> r \<le> 0 \<Longrightarrow> b < r \<Longrightarrow> b < r' \<Longrightarrow> q \<le> (q'::int)"
  by (rule_tac b = "-b" and r = "-r'" and r' = "-r" in unique_quotient_lemma) auto

lemma unique_quotient:
  "eucl_rel_int a b (q, r) \<Longrightarrow> eucl_rel_int a b (q', r') \<Longrightarrow> q = q'"
  apply (rule order_antisym)
   apply (simp_all add: eucl_rel_int_iff linorder_neq_iff split: if_split_asm)
     apply (blast intro: order_eq_refl [THEN unique_quotient_lemma] order_eq_refl [THEN unique_quotient_lemma_neg] sym)+
  done

lemma unique_remainder:
  "eucl_rel_int a b (q, r) \<Longrightarrow> eucl_rel_int a b (q', r') \<Longrightarrow> r = r'"
apply (subgoal_tac "q = q'")
 apply (simp add: eucl_rel_int_iff)
apply (blast intro: unique_quotient)
done

lemma eucl_rel_int:
  "eucl_rel_int k l (k div l, k mod l)"
proof (cases k rule: int_cases3)
  case zero
  then show ?thesis
    by (simp add: eucl_rel_int_iff divide_int_def modulo_int_def)
next
  case (pos n)
  then show ?thesis
    using div_mult_mod_eq [of n]
    by (cases l rule: int_cases3)
      (auto simp del: of_nat_mult of_nat_add
        simp add: mod_greater_zero_iff_not_dvd of_nat_mult [symmetric] of_nat_add [symmetric] algebra_simps
        eucl_rel_int_iff divide_int_def modulo_int_def)
next
  case (neg n)
  then show ?thesis
    using div_mult_mod_eq [of n]
    by (cases l rule: int_cases3)
      (auto simp del: of_nat_mult of_nat_add
        simp add: mod_greater_zero_iff_not_dvd of_nat_mult [symmetric] of_nat_add [symmetric] algebra_simps
        eucl_rel_int_iff divide_int_def modulo_int_def)
qed

lemma divmod_int_unique:
  assumes "eucl_rel_int k l (q, r)"
  shows div_int_unique: "k div l = q" and mod_int_unique: "k mod l = r"
  using assms eucl_rel_int [of k l]
  using unique_quotient [of k l] unique_remainder [of k l]
  by auto

lemma div_abs_eq_div_nat:
  "\<bar>k\<bar> div \<bar>l\<bar> = int (nat \<bar>k\<bar> div nat \<bar>l\<bar>)"
  by (simp add: divide_int_def)

lemma mod_abs_eq_div_nat:
  "\<bar>k\<bar> mod \<bar>l\<bar> = int (nat \<bar>k\<bar> mod nat \<bar>l\<bar>)"
  by (simp add: modulo_int_def)

lemma zdiv_int:
  "int (a div b) = int a div int b"
  by (simp add: divide_int_def)

lemma zmod_int:
  "int (a mod b) = int a mod int b"
  by (simp add: modulo_int_def)

lemma div_sgn_abs_cancel:
  fixes k l v :: int
  assumes "v \<noteq> 0"
  shows "(sgn v * \<bar>k\<bar>) div (sgn v * \<bar>l\<bar>) = \<bar>k\<bar> div \<bar>l\<bar>"
proof -
  from assms have "sgn v = - 1 \<or> sgn v = 1"
    by (cases "v \<ge> 0") auto
  then show ?thesis
    using assms unfolding divide_int_def [of "sgn v * \<bar>k\<bar>" "sgn v * \<bar>l\<bar>"]
    by (fastforce simp add: not_less div_abs_eq_div_nat)
qed

lemma div_eq_sgn_abs:
  fixes k l v :: int
  assumes "sgn k = sgn l"
  shows "k div l = \<bar>k\<bar> div \<bar>l\<bar>"
proof (cases "l = 0")
  case True
  then show ?thesis
    by simp
next
  case False
  with assms have "(sgn k * \<bar>k\<bar>) div (sgn l * \<bar>l\<bar>) = \<bar>k\<bar> div \<bar>l\<bar>"
    using div_sgn_abs_cancel [of l k l] by simp
  then show ?thesis
    by (simp add: sgn_mult_abs)
qed

lemma div_dvd_sgn_abs:
  fixes k l :: int
  assumes "l dvd k"
  shows "k div l = (sgn k * sgn l) * (\<bar>k\<bar> div \<bar>l\<bar>)"
proof (cases "k = 0 \<or> l = 0")
  case True
  then show ?thesis
    by auto
next
  case False
  then have "k \<noteq> 0" and "l \<noteq> 0"
    by auto
  show ?thesis
  proof (cases "sgn l = sgn k")
    case True
    then show ?thesis
      by (simp add: div_eq_sgn_abs)
  next
    case False
    with \<open>k \<noteq> 0\<close> \<open>l \<noteq> 0\<close>
    have "sgn l * sgn k = - 1"
      by (simp add: sgn_if split: if_splits)
    with assms show ?thesis
      unfolding divide_int_def [of k l]
      by (auto simp add: zdiv_int ac_simps)
  qed
qed

lemma div_noneq_sgn_abs:
  fixes k l :: int
  assumes "l \<noteq> 0"
  assumes "sgn k \<noteq> sgn l"
  shows "k div l = - (\<bar>k\<bar> div \<bar>l\<bar>) - of_bool (\<not> l dvd k)"
  using assms
  by (simp only: divide_int_def [of k l], auto simp add: not_less zdiv_int)

subsubsection \<open>General Properties of div and mod\<close>

lemma div_pos_pos_trivial [simp]:
  "k div l = 0" if "k \<ge> 0" and "k < l" for k l :: int
  using that by (simp add: unique_euclidean_semiring_class.div_eq_0_iff division_segment_int_def)

lemma mod_pos_pos_trivial [simp]:
  "k mod l = k" if "k \<ge> 0" and "k < l" for k l :: int
  using that by (simp add: mod_eq_self_iff_div_eq_0)

lemma div_neg_neg_trivial [simp]:
  "k div l = 0" if "k \<le> 0" and "l < k" for k l :: int
  using that by (cases "k = 0") (simp, simp add: unique_euclidean_semiring_class.div_eq_0_iff division_segment_int_def)

lemma mod_neg_neg_trivial [simp]:
  "k mod l = k" if "k \<le> 0" and "l < k" for k l :: int
  using that by (simp add: mod_eq_self_iff_div_eq_0)

lemma div_pos_neg_trivial:
  "k div l = - 1" if "0 < k" and "k + l \<le> 0" for k l :: int
  apply (rule div_int_unique [of _ _ _ "k + l"])
  apply (use that in \<open>auto simp add: eucl_rel_int_iff\<close>)
  done

lemma mod_pos_neg_trivial:
  "k mod l = k + l" if "0 < k" and "k + l \<le> 0" for k l :: int
  apply (rule mod_int_unique [of _ _ "- 1"])
  apply (use that in \<open>auto simp add: eucl_rel_int_iff\<close>)
  done

text \<open>There is neither \<open>div_neg_pos_trivial\<close> nor \<open>mod_neg_pos_trivial\<close>
  because \<^term>\<open>0 div l = 0\<close> would supersede it.\<close>

subsubsection \<open>Laws for div and mod with Unary Minus\<close>

lemma zminus1_lemma:
     "eucl_rel_int a b (q, r) ==> b \<noteq> 0
      ==> eucl_rel_int (-a) b (if r=0 then -q else -q - 1,
                          if r=0 then 0 else b-r)"
by (force simp add: eucl_rel_int_iff right_diff_distrib)

lemma zdiv_zminus1_eq_if:
     "b \<noteq> (0::int)
      \<Longrightarrow> (-a) div b = (if a mod b = 0 then - (a div b) else  - (a div b) - 1)"
by (blast intro: eucl_rel_int [THEN zminus1_lemma, THEN div_int_unique])

lemma zmod_zminus1_eq_if:
     "(-a::int) mod b = (if a mod b = 0 then 0 else  b - (a mod b))"
proof (cases "b = 0")
  case False
  then show ?thesis
    by (blast intro: eucl_rel_int [THEN zminus1_lemma, THEN mod_int_unique])
qed auto

lemma zmod_zminus1_not_zero:
  fixes k l :: int
  shows "- k mod l \<noteq> 0 \<Longrightarrow> k mod l \<noteq> 0"
  by (simp add: mod_eq_0_iff_dvd)

lemma zmod_zminus2_not_zero:
  fixes k l :: int
  shows "k mod - l \<noteq> 0 \<Longrightarrow> k mod l \<noteq> 0"
  by (simp add: mod_eq_0_iff_dvd)

lemma zdiv_zminus2_eq_if:
  "b \<noteq> (0::int)
      ==> a div (-b) =
          (if a mod b = 0 then - (a div b) else  - (a div b) - 1)"
  by (auto simp add: zdiv_zminus1_eq_if div_minus_right)

lemma zmod_zminus2_eq_if:
  "a mod (-b::int) = (if a mod b = 0 then 0 else  (a mod b) - b)"
  by (auto simp add: zmod_zminus1_eq_if mod_minus_right)

subsubsection \<open>Monotonicity in the First Argument (Dividend)\<close>

lemma zdiv_mono1:
  fixes b::int
  assumes "a \<le> a'" "0 < b" shows "a div b \<le> a' div b"
proof (rule unique_quotient_lemma)
  show "b * (a div b) + a mod b \<le> b * (a' div b) + a' mod b"
    using assms(1) by auto
qed (use assms in auto)

lemma zdiv_mono1_neg:
  fixes b::int
  assumes "a \<le> a'" "b < 0" shows "a' div b \<le> a div b"
proof (rule unique_quotient_lemma_neg)
  show "b * (a div b) + a mod b \<le> b * (a' div b) + a' mod b"
    using assms(1) by auto
qed (use assms in auto)

subsubsection \<open>Monotonicity in the Second Argument (Divisor)\<close>

lemma q_pos_lemma:
  fixes q'::int
  assumes "0 \<le> b'*q' + r'" "r' < b'" "0 < b'"
  shows "0 \<le> q'"
proof -
  have "0 < b'* (q' + 1)"
    using assms by (simp add: distrib_left)
  with assms show ?thesis
    by (simp add: zero_less_mult_iff)
qed

lemma zdiv_mono2_lemma:
  fixes q'::int
  assumes eq: "b*q + r = b'*q' + r'" and le: "0 \<le> b'*q' + r'" and "r' < b'" "0 \<le> r" "0 < b'" "b' \<le> b"
  shows "q \<le> q'"
proof -
  have "0 \<le> q'"
    using q_pos_lemma le \<open>r' < b'\<close> \<open>0 < b'\<close> by blast
  moreover have "b*q = r' - r + b'*q'"
    using eq by linarith
  ultimately have "b*q < b* (q' + 1)"
    using mult_right_mono assms unfolding distrib_left by fastforce
  with assms show ?thesis
    by (simp add: mult_less_cancel_left_pos)
qed

lemma zdiv_mono2:
  fixes a::int
  assumes "0 \<le> a" "0 < b'" "b' \<le> b" shows "a div b \<le> a div b'"
proof (rule zdiv_mono2_lemma)
  have "b \<noteq> 0"
    using assms by linarith
  show "b * (a div b) + a mod b = b' * (a div b') + a mod b'"
    by simp
qed (use assms in auto)

lemma zdiv_mono2_neg_lemma:
    fixes q'::int
    assumes "b*q + r = b'*q' + r'" "b'*q' + r' < 0" "r < b" "0 \<le> r'" "0 < b'" "b' \<le> b"
    shows "q' \<le> q"
proof -
  have "b'*q' < 0"
    using assms by linarith
  with assms have "q' \<le> 0"
    by (simp add: mult_less_0_iff)
  have "b*q' \<le> b'*q'"
    by (simp add: \<open>q' \<le> 0\<close> assms(6) mult_right_mono_neg)
  then have "b*q' < b* (q + 1)"
    using assms by (simp add: distrib_left)
  then show ?thesis
    using assms by (simp add: mult_less_cancel_left)
qed

lemma zdiv_mono2_neg:
  fixes a::int
  assumes "a < 0" "0 < b'" "b' \<le> b" shows "a div b' \<le> a div b"
proof (rule zdiv_mono2_neg_lemma)
  have "b \<noteq> 0"
    using assms by linarith
  show "b * (a div b) + a mod b = b' * (a div b') + a mod b'"
    by simp
qed (use assms in auto)

lemma div_pos_geq:
  fixes k l :: int
  assumes "0 < l" and "l \<le> k"
  shows "k div l = (k - l) div l + 1"
proof -
  have "k = (k - l) + l" by simp
  then obtain j where k: "k = j + l" ..
  with assms show ?thesis by (simp add: div_add_self2)
qed

lemma mod_pos_geq:
  fixes k l :: int
  assumes "0 < l" and "l \<le> k"
  shows "k mod l = (k - l) mod l"
proof -
  have "k = (k - l) + l" by simp
  then obtain j where k: "k = j + l" ..
  with assms show ?thesis by simp
qed

subsubsection \<open>Splitting Rules for div and mod\<close>

text\<open>The proofs of the two lemmas below are essentially identical\<close>

lemma split_pos_lemma:
 "0<k \<Longrightarrow>
    P(n div k :: int)(n mod k) = (\<forall>i j. 0\<le>j \<and> j<k \<and> n = k*i + j \<longrightarrow> P i j)"
  by auto

lemma split_neg_lemma:
 "k<0 \<Longrightarrow>
    P(n div k :: int)(n mod k) = (\<forall>i j. k<j \<and> j\<le>0 \<and> n = k*i + j \<longrightarrow> P i j)"
  by auto

lemma split_zdiv:
 "P(n div k :: int) =
  ((k = 0 \<longrightarrow> P 0) \<and>
   (0<k \<longrightarrow> (\<forall>i j. 0\<le>j \<and> j<k \<and> n = k*i + j \<longrightarrow> P i)) \<and>
   (k<0 \<longrightarrow> (\<forall>i j. k<j \<and> j\<le>0 \<and> n = k*i + j \<longrightarrow> P i)))"
proof (cases "k = 0")
  case False
  then show ?thesis
    unfolding linorder_neq_iff
    by (auto simp add: split_pos_lemma [of concl: "\<lambda>x y. P x"] split_neg_lemma [of concl: "\<lambda>x y. P x"])
qed auto

lemma split_zmod:
 "P(n mod k :: int) =
  ((k = 0 \<longrightarrow> P n) \<and>
   (0<k \<longrightarrow> (\<forall>i j. 0\<le>j \<and> j<k \<and> n = k*i + j \<longrightarrow> P j)) \<and>
   (k<0 \<longrightarrow> (\<forall>i j. k<j \<and> j\<le>0 \<and> n = k*i + j \<longrightarrow> P j)))"
proof (cases "k = 0")
  case False
  then show ?thesis
    unfolding linorder_neq_iff
    by (auto simp add: split_pos_lemma [of concl: "\<lambda>x y. P y"] split_neg_lemma [of concl: "\<lambda>x y. P y"])
qed auto

text \<open>Enable (lin)arith to deal with \<^const>\<open>divide\<close> and \<^const>\<open>modulo\<close>
  when these are applied to some constant that is of the form
  \<^term>\<open>numeral k\<close>:\<close>
declare split_zdiv [of _ _ "numeral k", arith_split] for k
declare split_zmod [of _ _ "numeral k", arith_split] for k

subsubsection \<open>Computing \<open>div\<close> and \<open>mod\<close> with shifting\<close>

lemma pos_eucl_rel_int_mult_2:
  assumes "0 \<le> b"
  assumes "eucl_rel_int a b (q, r)"
  shows "eucl_rel_int (1 + 2*a) (2*b) (q, 1 + 2*r)"
  using assms unfolding eucl_rel_int_iff by auto

lemma neg_eucl_rel_int_mult_2:
  assumes "b \<le> 0"
  assumes "eucl_rel_int (a + 1) b (q, r)"
  shows "eucl_rel_int (1 + 2*a) (2*b) (q, 2*r - 1)"
  using assms unfolding eucl_rel_int_iff by auto

text\<open>computing div by shifting\<close>

lemma pos_zdiv_mult_2: "(0::int) \<le> a ==> (1 + 2*b) div (2*a) = b div a"
  using pos_eucl_rel_int_mult_2 [OF _ eucl_rel_int]
  by (rule div_int_unique)

lemma neg_zdiv_mult_2:
  assumes A: "a \<le> (0::int)" shows "(1 + 2*b) div (2*a) = (b+1) div a"
  using neg_eucl_rel_int_mult_2 [OF A eucl_rel_int]
  by (rule div_int_unique)

lemma zdiv_numeral_Bit0 [simp]:
  "numeral (Num.Bit0 v) div numeral (Num.Bit0 w) =
    numeral v div (numeral w :: int)"
  unfolding numeral.simps unfolding mult_2 [symmetric]
  by (rule div_mult_mult1, simp)

lemma zdiv_numeral_Bit1 [simp]:
  "numeral (Num.Bit1 v) div numeral (Num.Bit0 w) =
    (numeral v div (numeral w :: int))"
  unfolding numeral.simps
  unfolding mult_2 [symmetric] add.commute [of _ 1]
  by (rule pos_zdiv_mult_2, simp)

lemma pos_zmod_mult_2:
  fixes a b :: int
  assumes "0 \<le> a"
  shows "(1 + 2 * b) mod (2 * a) = 1 + 2 * (b mod a)"
  using pos_eucl_rel_int_mult_2 [OF assms eucl_rel_int]
  by (rule mod_int_unique)

lemma neg_zmod_mult_2:
  fixes a b :: int
  assumes "a \<le> 0"
  shows "(1 + 2 * b) mod (2 * a) = 2 * ((b + 1) mod a) - 1"
  using neg_eucl_rel_int_mult_2 [OF assms eucl_rel_int]
  by (rule mod_int_unique)

lemma zmod_numeral_Bit0 [simp]:
  "numeral (Num.Bit0 v) mod numeral (Num.Bit0 w) =
    (2::int) * (numeral v mod numeral w)"
  unfolding numeral_Bit0 [of v] numeral_Bit0 [of w]
  unfolding mult_2 [symmetric] by (rule mod_mult_mult1)

lemma zmod_numeral_Bit1 [simp]:
  "numeral (Num.Bit1 v) mod numeral (Num.Bit0 w) =
    2 * (numeral v mod numeral w) + (1::int)"
  unfolding numeral_Bit1 [of v] numeral_Bit0 [of w]
  unfolding mult_2 [symmetric] add.commute [of _ 1]
  by (rule pos_zmod_mult_2, simp)

lemma zdiv_eq_0_iff:
  "i div k = 0 \<longleftrightarrow> k = 0 \<or> 0 \<le> i \<and> i < k \<or> i \<le> 0 \<and> k < i" (is "?L = ?R")
  for i k :: int
proof
  assume ?L
  moreover have "?L \<longrightarrow> ?R"
    by (rule split_zdiv [THEN iffD2]) simp
  ultimately show ?R
    by blast
next
  assume ?R then show ?L
    by auto
qed

lemma zmod_trival_iff:
  fixes i k :: int
  shows "i mod k = i \<longleftrightarrow> k = 0 \<or> 0 \<le> i \<and> i < k \<or> i \<le> 0 \<and> k < i"
proof -
  have "i mod k = i \<longleftrightarrow> i div k = 0"
    by safe (insert div_mult_mod_eq [of i k], auto)
  with zdiv_eq_0_iff
  show ?thesis
    by simp
qed

subsubsection \<open>Quotients of Signs\<close>

lemma div_eq_minus1: "0 < b \<Longrightarrow> - 1 div b = - 1" for b :: int
  by (simp add: divide_int_def)

lemma zmod_minus1: "0 < b \<Longrightarrow> - 1 mod b = b - 1" for b :: int
  by (auto simp add: modulo_int_def)

lemma div_neg_pos_less0:
  fixes a::int
  assumes "a < 0" "0 < b"
  shows "a div b < 0"
proof -
  have "a div b \<le> - 1 div b"
    using zdiv_mono1 assms by auto
  also have "... \<le> -1"
    by (simp add: assms(2) div_eq_minus1)
  finally show ?thesis
    by force
qed

lemma div_nonneg_neg_le0: "[| (0::int) \<le> a; b < 0 |] ==> a div b \<le> 0"
  by (drule zdiv_mono1_neg, auto)

lemma div_nonpos_pos_le0: "[| (a::int) \<le> 0; b > 0 |] ==> a div b \<le> 0"
  by (drule zdiv_mono1, auto)

text\<open>Now for some equivalences of the form \<open>a div b >=< 0 \<longleftrightarrow> \<dots>\<close>
conditional upon the sign of \<open>a\<close> or \<open>b\<close>. There are many more.
They should all be simp rules unless that causes too much search.\<close>

lemma pos_imp_zdiv_nonneg_iff:
      fixes a::int
      assumes "0 < b"
      shows "(0 \<le> a div b) = (0 \<le> a)"
proof
  show "0 \<le> a div b \<Longrightarrow> 0 \<le> a"
    using assms
    by (simp add: linorder_not_less [symmetric]) (blast intro: div_neg_pos_less0)
next
  assume "0 \<le> a"
  then have "0 div b \<le> a div b"
    using zdiv_mono1 assms by blast
  then show "0 \<le> a div b"
    by auto
qed

lemma pos_imp_zdiv_pos_iff:
  "0<k \<Longrightarrow> 0 < (i::int) div k \<longleftrightarrow> k \<le> i"
  using pos_imp_zdiv_nonneg_iff[of k i] zdiv_eq_0_iff[of i k] by arith

lemma neg_imp_zdiv_nonneg_iff:
  fixes a::int
  assumes "b < 0"
  shows "(0 \<le> a div b) = (a \<le> 0)"
  using assms by (simp add: div_minus_minus [of a, symmetric] pos_imp_zdiv_nonneg_iff del: div_minus_minus)

(*But not (a div b \<le> 0 iff a\<le>0); consider a=1, b=2 when a div b = 0.*)
lemma pos_imp_zdiv_neg_iff: "(0::int) < b ==> (a div b < 0) = (a < 0)"
  by (simp add: linorder_not_le [symmetric] pos_imp_zdiv_nonneg_iff)

(*Again the law fails for \<le>: consider a = -1, b = -2 when a div b = 0*)
lemma neg_imp_zdiv_neg_iff: "b < (0::int) ==> (a div b < 0) = (0 < a)"
  by (simp add: linorder_not_le [symmetric] neg_imp_zdiv_nonneg_iff)

lemma nonneg1_imp_zdiv_pos_iff:
  fixes a::int
  assumes "0 \<le> a"
  shows "a div b > 0 \<longleftrightarrow> a \<ge> b \<and> b>0"
proof -
  have "0 < a div b \<Longrightarrow> b \<le> a"
    using div_pos_pos_trivial[of a b] assms by arith
  moreover have "0 < a div b \<Longrightarrow> b > 0"
    using assms div_nonneg_neg_le0[of a b]  by(cases "b=0"; force)
  moreover have "b \<le> a \<and> 0 < b \<Longrightarrow> 0 < a div b"
    using int_one_le_iff_zero_less[of "a div b"] zdiv_mono1[of b a b] by simp
  ultimately show ?thesis
    by blast
qed

lemma zmod_le_nonneg_dividend: "(m::int) \<ge> 0 \<Longrightarrow> m mod k \<le> m"
  by (rule split_zmod[THEN iffD2]) (fastforce dest: q_pos_lemma intro: split_mult_pos_le)

subsubsection \<open>Further properties\<close>

lemma div_int_pos_iff:
  "k div l \<ge> 0 \<longleftrightarrow> k = 0 \<or> l = 0 \<or> k \<ge> 0 \<and> l \<ge> 0
    \<or> k < 0 \<and> l < 0"
  for k l :: int
proof (cases "k = 0 \<or> l = 0")
  case False
  then show ?thesis
   apply (auto simp add: pos_imp_zdiv_nonneg_iff neg_imp_zdiv_nonneg_iff)
    by (meson neg_imp_zdiv_neg_iff not_le not_less_iff_gr_or_eq)
qed auto

lemma mod_int_pos_iff:
  "k mod l \<ge> 0 \<longleftrightarrow> l dvd k \<or> l = 0 \<and> k \<ge> 0 \<or> l > 0"
  for k l :: int
proof (cases "l > 0")
  case False
  then show ?thesis
    by (simp add: dvd_eq_mod_eq_0) (use neg_mod_sign [of l k] in \<open>auto simp add: le_less not_less\<close>)
qed auto

text \<open>Simplify expressions in which div and mod combine numerical constants\<close>

lemma int_div_pos_eq: "\<lbrakk>(a::int) = b * q + r; 0 \<le> r; r < b\<rbrakk> \<Longrightarrow> a div b = q"
  by (rule div_int_unique [of a b q r]) (simp add: eucl_rel_int_iff)

lemma int_div_neg_eq: "\<lbrakk>(a::int) = b * q + r; r \<le> 0; b < r\<rbrakk> \<Longrightarrow> a div b = q"
  by (rule div_int_unique [of a b q r],
    simp add: eucl_rel_int_iff)

lemma int_mod_pos_eq: "\<lbrakk>(a::int) = b * q + r; 0 \<le> r; r < b\<rbrakk> \<Longrightarrow> a mod b = r"
  by (rule mod_int_unique [of a b q r],
    simp add: eucl_rel_int_iff)

lemma int_mod_neg_eq: "\<lbrakk>(a::int) = b * q + r; r \<le> 0; b < r\<rbrakk> \<Longrightarrow> a mod b = r"
  by (rule mod_int_unique [of a b q r],
    simp add: eucl_rel_int_iff)

lemma abs_div: "(y::int) dvd x \<Longrightarrow> \<bar>x div y\<bar> = \<bar>x\<bar> div \<bar>y\<bar>"
  unfolding dvd_def by (cases "y=0") (auto simp add: abs_mult)

text\<open>Suggested by Matthias Daum\<close>
lemma int_power_div_base:
  fixes k :: int
  assumes "0 < m" "0 < k"
  shows "k ^ m div k = (k::int) ^ (m - Suc 0)"
proof -
  have eq: "k ^ m = k ^ ((m - Suc 0) + Suc 0)"
    by (simp add: assms)
  show ?thesis
    using assms by (simp only: power_add eq) auto
qed

text \<open>Distributive laws for function \<open>nat\<close>.\<close>

lemma nat_div_distrib:
  assumes "0 \<le> x"
  shows "nat (x div y) = nat x div nat y"
proof (cases y "0::int" rule: linorder_cases)
  case less
  with assms show ?thesis
    using div_nonneg_neg_le0 by auto
next
  case greater
  then show ?thesis
    by (simp add: nat_eq_iff pos_imp_zdiv_nonneg_iff zdiv_int)
qed auto

(*Fails if y<0: the LHS collapses to (nat z) but the RHS doesn't*)
lemma nat_mod_distrib:
  assumes "0 \<le> x" "0 \<le> y"
  shows "nat (x mod y) = nat x mod nat y"
proof (cases "y = 0")
  case False
  with assms show ?thesis
    by (simp add: nat_eq_iff zmod_int)
qed auto

text\<open>Suggested by Matthias Daum\<close>
lemma int_div_less_self:
  fixes x::int
  assumes "0 < x" "1 < k"
  shows  "x div k < x"
proof -
  have "nat x div nat k < nat x"
    by (simp add: assms)
  with assms show ?thesis
    by (simp add: nat_div_distrib [symmetric])
qed

lemma mod_eq_dvd_iff_nat:
  "m mod q = n mod q \<longleftrightarrow> q dvd m - n" if "m \<ge> n" for m n q :: nat
proof -
  have "int m mod int q = int n mod int q \<longleftrightarrow> int q dvd int m - int n"
    by (simp add: mod_eq_dvd_iff)
  with that have "int (m mod q) = int (n mod q) \<longleftrightarrow> int q dvd int (m - n)"
    by (simp only: of_nat_mod of_nat_diff)
  then show ?thesis
    by simp
qed

lemma mod_eq_nat1E:
  fixes m n q :: nat
  assumes "m mod q = n mod q" and "m \<ge> n"
  obtains s where "m = n + q * s"
proof -
  from assms have "q dvd m - n"
    by (simp add: mod_eq_dvd_iff_nat)
  then obtain s where "m - n = q * s" ..
  with \<open>m \<ge> n\<close> have "m = n + q * s"
    by simp
  with that show thesis .
qed

lemma mod_eq_nat2E:
  fixes m n q :: nat
  assumes "m mod q = n mod q" and "n \<ge> m"
  obtains s where "n = m + q * s"
  using assms mod_eq_nat1E [of n q m] by (auto simp add: ac_simps)

lemma nat_mod_eq_lemma:
  assumes "(x::nat) mod n = y mod n" and "y \<le> x"
  shows "\<exists>q. x = y + n * q"
  using assms by (rule mod_eq_nat1E) rule

lemma nat_mod_eq_iff: "(x::nat) mod n = y mod n \<longleftrightarrow> (\<exists>q1 q2. x + n * q1 = y + n * q2)"
  (is "?lhs = ?rhs")
proof
  assume H: "x mod n = y mod n"
  {assume xy: "x \<le> y"
    from H have th: "y mod n = x mod n" by simp
    from nat_mod_eq_lemma[OF th xy] have ?rhs
      apply clarify  apply (rule_tac x="q" in exI) by (rule exI[where x="0"], simp)}
  moreover
  {assume xy: "y \<le> x"
    from nat_mod_eq_lemma[OF H xy] have ?rhs
      apply clarify  apply (rule_tac x="0" in exI) by (rule_tac x="q" in exI, simp)}
  ultimately  show ?rhs using linear[of x y] by blast
next
  assume ?rhs then obtain q1 q2 where q12: "x + n * q1 = y + n * q2" by blast
  hence "(x + n * q1) mod n = (y + n * q2) mod n" by simp
  thus  ?lhs by simp
qed

subsection \<open>Numeral division with a pragmatic type class\<close>

text \<open>
  The following type class contains everything necessary to formulate
  a division algorithm in ring structures with numerals, restricted
  to its positive segments.  This is its primary motivation, and it
  could surely be formulated using a more fine-grained, more algebraic
  and less technical class hierarchy.
\<close>

class unique_euclidean_semiring_numeral = unique_euclidean_semiring_with_nat + linordered_semidom +
  assumes div_less: "0 \<le> a \<Longrightarrow> a < b \<Longrightarrow> a div b = 0"
    and mod_less: " 0 \<le> a \<Longrightarrow> a < b \<Longrightarrow> a mod b = a"
    and div_positive: "0 < b \<Longrightarrow> b \<le> a \<Longrightarrow> a div b > 0"
    and mod_less_eq_dividend: "0 \<le> a \<Longrightarrow> a mod b \<le> a"
    and pos_mod_bound: "0 < b \<Longrightarrow> a mod b < b"
    and pos_mod_sign: "0 < b \<Longrightarrow> 0 \<le> a mod b"
    and mod_mult2_eq: "0 \<le> c \<Longrightarrow> a mod (b * c) = b * (a div b mod c) + a mod b"
    and div_mult2_eq: "0 \<le> c \<Longrightarrow> a div (b * c) = a div b div c"
  assumes discrete: "a < b \<longleftrightarrow> a + 1 \<le> b"
  fixes divmod :: "num \<Rightarrow> num \<Rightarrow> 'a \<times> 'a"
    and divmod_step :: "num \<Rightarrow> 'a \<times> 'a \<Rightarrow> 'a \<times> 'a"
  assumes divmod_def: "divmod m n = (numeral m div numeral n, numeral m mod numeral n)"
    and divmod_step_def: "divmod_step l qr = (let (q, r) = qr
    in if r \<ge> numeral l then (2 * q + 1, r - numeral l)
    else (2 * q, r))"
    \<comment> \<open>These are conceptually definitions but force generated code
    to be monomorphic wrt. particular instances of this class which
    yields a significant speedup.\<close>
begin

lemma divmod_digit_1:
  assumes "0 \<le> a" "0 < b" and "b \<le> a mod (2 * b)"
  shows "2 * (a div (2 * b)) + 1 = a div b" (is "?P")
    and "a mod (2 * b) - b = a mod b" (is "?Q")
proof -
  from assms mod_less_eq_dividend [of a "2 * b"] have "b \<le> a"
    by (auto intro: trans)
  with \<open>0 < b\<close> have "0 < a div b" by (auto intro: div_positive)
  then have [simp]: "1 \<le> a div b" by (simp add: discrete)
  with \<open>0 < b\<close> have mod_less: "a mod b < b" by (simp add: pos_mod_bound)
  define w where "w = a div b mod 2"
  then have w_exhaust: "w = 0 \<or> w = 1" by auto
  have mod_w: "a mod (2 * b) = a mod b + b * w"
    by (simp add: w_def mod_mult2_eq ac_simps)
  from assms w_exhaust have "w = 1"
    by (auto simp add: mod_w) (insert mod_less, auto)
  with mod_w have mod: "a mod (2 * b) = a mod b + b" by simp
  have "2 * (a div (2 * b)) = a div b - w"
    by (simp add: w_def div_mult2_eq minus_mod_eq_mult_div ac_simps)
  with \<open>w = 1\<close> have div: "2 * (a div (2 * b)) = a div b - 1" by simp
  then show ?P and ?Q
    by (simp_all add: div mod add_implies_diff [symmetric])
qed

lemma divmod_digit_0:
  assumes "0 < b" and "a mod (2 * b) < b"
  shows "2 * (a div (2 * b)) = a div b" (is "?P")
    and "a mod (2 * b) = a mod b" (is "?Q")
proof -
  define w where "w = a div b mod 2"
  then have w_exhaust: "w = 0 \<or> w = 1" by auto
  have mod_w: "a mod (2 * b) = a mod b + b * w"
    by (simp add: w_def mod_mult2_eq ac_simps)
  moreover have "b \<le> a mod b + b"
  proof -
    from \<open>0 < b\<close> pos_mod_sign have "0 \<le> a mod b" by blast
    then have "0 + b \<le> a mod b + b" by (rule add_right_mono)
    then show ?thesis by simp
  qed
  moreover note assms w_exhaust
  ultimately have "w = 0" by auto
  with mod_w have mod: "a mod (2 * b) = a mod b" by simp
  have "2 * (a div (2 * b)) = a div b - w"
    by (simp add: w_def div_mult2_eq minus_mod_eq_mult_div ac_simps)
  with \<open>w = 0\<close> have div: "2 * (a div (2 * b)) = a div b" by simp
  then show ?P and ?Q
    by (simp_all add: div mod)
qed

lemma mod_double_modulus:
  assumes "m > 0" "x \<ge> 0"
  shows   "x mod (2 * m) = x mod m \<or> x mod (2 * m) = x mod m + m"
proof (cases "x mod (2 * m) < m")
  case True
  thus ?thesis using assms using divmod_digit_0(2)[of m x] by auto
next
  case False
  hence *: "x mod (2 * m) - m = x mod m"
    using assms by (intro divmod_digit_1) auto
  hence "x mod (2 * m) = x mod m + m"
    by (subst * [symmetric], subst le_add_diff_inverse2) (use False in auto)
  thus ?thesis by simp
qed

lemma fst_divmod:
  "fst (divmod m n) = numeral m div numeral n"
  by (simp add: divmod_def)

lemma snd_divmod:
  "snd (divmod m n) = numeral m mod numeral n"
  by (simp add: divmod_def)

text \<open>
  This is a formulation of one step (referring to one digit position)
  in school-method division: compare the dividend at the current
  digit position with the remainder from previous division steps
  and evaluate accordingly.
\<close>

lemma divmod_step_eq [simp]:
  "divmod_step l (q, r) = (if numeral l \<le> r
    then (2 * q + 1, r - numeral l) else (2 * q, r))"
  by (simp add: divmod_step_def)

text \<open>
  This is a formulation of school-method division.
  If the divisor is smaller than the dividend, terminate.
  If not, shift the dividend to the right until termination
  occurs and then reiterate single division steps in the
  opposite direction.
\<close>

lemma divmod_divmod_step:
  "divmod m n = (if m < n then (0, numeral m)
    else divmod_step n (divmod m (Num.Bit0 n)))"
proof (cases "m < n")
  case True then have "numeral m < numeral n" by simp
  then show ?thesis
    by (simp add: prod_eq_iff div_less mod_less fst_divmod snd_divmod)
next
  case False
  have "divmod m n =
    divmod_step n (numeral m div (2 * numeral n),
      numeral m mod (2 * numeral n))"
  proof (cases "numeral n \<le> numeral m mod (2 * numeral n)")
    case True
    with divmod_step_eq
      have "divmod_step n (numeral m div (2 * numeral n), numeral m mod (2 * numeral n)) =
        (2 * (numeral m div (2 * numeral n)) + 1, numeral m mod (2 * numeral n) - numeral n)"
        by simp
    moreover from True divmod_digit_1 [of "numeral m" "numeral n"]
      have "2 * (numeral m div (2 * numeral n)) + 1 = numeral m div numeral n"
      and "numeral m mod (2 * numeral n) - numeral n = numeral m mod numeral n"
      by simp_all
    ultimately show ?thesis by (simp only: divmod_def)
  next
    case False then have *: "numeral m mod (2 * numeral n) < numeral n"
      by (simp add: not_le)
    with divmod_step_eq
      have "divmod_step n (numeral m div (2 * numeral n), numeral m mod (2 * numeral n)) =
        (2 * (numeral m div (2 * numeral n)), numeral m mod (2 * numeral n))"
        by auto
    moreover from * divmod_digit_0 [of "numeral n" "numeral m"]
      have "2 * (numeral m div (2 * numeral n)) = numeral m div numeral n"
      and "numeral m mod (2 * numeral n) = numeral m mod numeral n"
      by (simp_all only: zero_less_numeral)
    ultimately show ?thesis by (simp only: divmod_def)
  qed
  then have "divmod m n =
    divmod_step n (numeral m div numeral (Num.Bit0 n),
      numeral m mod numeral (Num.Bit0 n))"
    by (simp only: numeral.simps distrib mult_1)
  then have "divmod m n = divmod_step n (divmod m (Num.Bit0 n))"
    by (simp add: divmod_def)
  with False show ?thesis by simp
qed

text \<open>The division rewrite proper -- first, trivial results involving \<open>1\<close>\<close>

lemma divmod_trivial [simp]:
  "divmod m Num.One = (numeral m, 0)"
  "divmod num.One (num.Bit0 n) = (0, Numeral1)"
  "divmod num.One (num.Bit1 n) = (0, Numeral1)"
  using divmod_divmod_step [of "Num.One"] by (simp_all add: divmod_def)

text \<open>Division by an even number is a right-shift\<close>

lemma divmod_cancel [simp]:
  "divmod (Num.Bit0 m) (Num.Bit0 n) = (case divmod m n of (q, r) \<Rightarrow> (q, 2 * r))" (is ?P)
  "divmod (Num.Bit1 m) (Num.Bit0 n) = (case divmod m n of (q, r) \<Rightarrow> (q, 2 * r + 1))" (is ?Q)
proof -
  have *: "\<And>q. numeral (Num.Bit0 q) = 2 * numeral q"
    "\<And>q. numeral (Num.Bit1 q) = 2 * numeral q + 1"
    by (simp_all only: numeral_mult numeral.simps distrib) simp_all
  have "1 div 2 = 0" "1 mod 2 = 1" by (auto intro: div_less mod_less)
  then show ?P and ?Q
    by (simp_all add: fst_divmod snd_divmod prod_eq_iff split_def * [of m] * [of n] mod_mult_mult1
      div_mult2_eq [of _ _ 2] mod_mult2_eq [of _ _ 2]
      add.commute del: numeral_times_numeral)
qed

text \<open>The really hard work\<close>

lemma divmod_steps [simp]:
  "divmod (num.Bit0 m) (num.Bit1 n) =
      (if m \<le> n then (0, numeral (num.Bit0 m))
       else divmod_step (num.Bit1 n)
             (divmod (num.Bit0 m)
               (num.Bit0 (num.Bit1 n))))"
  "divmod (num.Bit1 m) (num.Bit1 n) =
      (if m < n then (0, numeral (num.Bit1 m))
       else divmod_step (num.Bit1 n)
             (divmod (num.Bit1 m)
               (num.Bit0 (num.Bit1 n))))"
  by (simp_all add: divmod_divmod_step)

lemmas divmod_algorithm_code = divmod_step_eq divmod_trivial divmod_cancel divmod_steps

text \<open>Special case: divisibility\<close>

definition divides_aux :: "'a \<times> 'a \<Rightarrow> bool"
where
  "divides_aux qr \<longleftrightarrow> snd qr = 0"

lemma divides_aux_eq [simp]:
  "divides_aux (q, r) \<longleftrightarrow> r = 0"
  by (simp add: divides_aux_def)

lemma dvd_numeral_simp [simp]:
  "numeral m dvd numeral n \<longleftrightarrow> divides_aux (divmod n m)"
  by (simp add: divmod_def mod_eq_0_iff_dvd)

text \<open>Generic computation of quotient and remainder\<close>

lemma numeral_div_numeral [simp]:
  "numeral k div numeral l = fst (divmod k l)"
  by (simp add: fst_divmod)

lemma numeral_mod_numeral [simp]:
  "numeral k mod numeral l = snd (divmod k l)"
  by (simp add: snd_divmod)

lemma one_div_numeral [simp]:
  "1 div numeral n = fst (divmod num.One n)"
  by (simp add: fst_divmod)

lemma one_mod_numeral [simp]:
  "1 mod numeral n = snd (divmod num.One n)"
  by (simp add: snd_divmod)

text \<open>Computing congruences modulo \<open>2 ^ q\<close>\<close>

lemma cong_exp_iff_simps:
  "numeral n mod numeral Num.One = 0
    \<longleftrightarrow> True"
  "numeral (Num.Bit0 n) mod numeral (Num.Bit0 q) = 0
    \<longleftrightarrow> numeral n mod numeral q = 0"
  "numeral (Num.Bit1 n) mod numeral (Num.Bit0 q) = 0
    \<longleftrightarrow> False"
  "numeral m mod numeral Num.One = (numeral n mod numeral Num.One)
    \<longleftrightarrow> True"
  "numeral Num.One mod numeral (Num.Bit0 q) = (numeral Num.One mod numeral (Num.Bit0 q))
    \<longleftrightarrow> True"
  "numeral Num.One mod numeral (Num.Bit0 q) = (numeral (Num.Bit0 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> False"
  "numeral Num.One mod numeral (Num.Bit0 q) = (numeral (Num.Bit1 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> (numeral n mod numeral q) = 0"
  "numeral (Num.Bit0 m) mod numeral (Num.Bit0 q) = (numeral Num.One mod numeral (Num.Bit0 q))
    \<longleftrightarrow> False"
  "numeral (Num.Bit0 m) mod numeral (Num.Bit0 q) = (numeral (Num.Bit0 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> numeral m mod numeral q = (numeral n mod numeral q)"
  "numeral (Num.Bit0 m) mod numeral (Num.Bit0 q) = (numeral (Num.Bit1 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> False"
  "numeral (Num.Bit1 m) mod numeral (Num.Bit0 q) = (numeral Num.One mod numeral (Num.Bit0 q))
    \<longleftrightarrow> (numeral m mod numeral q) = 0"
  "numeral (Num.Bit1 m) mod numeral (Num.Bit0 q) = (numeral (Num.Bit0 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> False"
  "numeral (Num.Bit1 m) mod numeral (Num.Bit0 q) = (numeral (Num.Bit1 n) mod numeral (Num.Bit0 q))
    \<longleftrightarrow> numeral m mod numeral q = (numeral n mod numeral q)"
  by (auto simp add: case_prod_beta dest: arg_cong [of _ _ even])

end

hide_fact (open) div_less mod_less mod_less_eq_dividend mod_mult2_eq div_mult2_eq

instantiation nat :: unique_euclidean_semiring_numeral
begin

definition divmod_nat :: "num \<Rightarrow> num \<Rightarrow> nat \<times> nat"
where
  divmod'_nat_def: "divmod_nat m n = (numeral m div numeral n, numeral m mod numeral n)"

definition divmod_step_nat :: "num \<Rightarrow> nat \<times> nat \<Rightarrow> nat \<times> nat"
where
  "divmod_step_nat l qr = (let (q, r) = qr
    in if r \<ge> numeral l then (2 * q + 1, r - numeral l)
    else (2 * q, r))"

instance by standard
  (auto simp add: divmod'_nat_def divmod_step_nat_def div_greater_zero_iff div_mult2_eq mod_mult2_eq)

end

declare divmod_algorithm_code [where ?'a = nat, code]

lemma Suc_0_div_numeral [simp]:
  fixes k l :: num
  shows "Suc 0 div numeral k = fst (divmod Num.One k)"
  by (simp_all add: fst_divmod)

lemma Suc_0_mod_numeral [simp]:
  fixes k l :: num
  shows "Suc 0 mod numeral k = snd (divmod Num.One k)"
  by (simp_all add: snd_divmod)

instantiation int :: unique_euclidean_semiring_numeral
begin

definition divmod_int :: "num \<Rightarrow> num \<Rightarrow> int \<times> int"
where
  "divmod_int m n = (numeral m div numeral n, numeral m mod numeral n)"

definition divmod_step_int :: "num \<Rightarrow> int \<times> int \<Rightarrow> int \<times> int"
where
  "divmod_step_int l qr = (let (q, r) = qr
    in if r \<ge> numeral l then (2 * q + 1, r - numeral l)
    else (2 * q, r))"

instance
  by standard (auto intro: zmod_le_nonneg_dividend simp add: divmod_int_def divmod_step_int_def
    pos_imp_zdiv_pos_iff zmod_zmult2_eq zdiv_zmult2_eq)

end

declare divmod_algorithm_code [where ?'a = int, code]

context
begin

qualified definition adjust_div :: "int \<times> int \<Rightarrow> int"
where
  "adjust_div qr = (let (q, r) = qr in q + of_bool (r \<noteq> 0))"

qualified lemma adjust_div_eq [simp, code]:
  "adjust_div (q, r) = q + of_bool (r \<noteq> 0)"
  by (simp add: adjust_div_def)

qualified definition adjust_mod :: "int \<Rightarrow> int \<Rightarrow> int"
where
  [simp]: "adjust_mod l r = (if r = 0 then 0 else l - r)"

lemma minus_numeral_div_numeral [simp]:
  "- numeral m div numeral n = - (adjust_div (divmod m n) :: int)"
proof -
  have "int (fst (divmod m n)) = fst (divmod m n)"
    by (simp only: fst_divmod divide_int_def) auto
  then show ?thesis
    by (auto simp add: split_def Let_def adjust_div_def divides_aux_def divide_int_def)
qed

lemma minus_numeral_mod_numeral [simp]:
  "- numeral m mod numeral n = adjust_mod (numeral n) (snd (divmod m n) :: int)"
proof (cases "snd (divmod m n) = (0::int)")
  case True
  then show ?thesis
    by (simp add: mod_eq_0_iff_dvd divides_aux_def)
next
  case False
  then have "int (snd (divmod m n)) = snd (divmod m n)" if "snd (divmod m n) \<noteq> (0::int)"
    by (simp only: snd_divmod modulo_int_def) auto
  then show ?thesis
    by (simp add: divides_aux_def adjust_div_def)
      (simp add: divides_aux_def modulo_int_def)
qed

lemma numeral_div_minus_numeral [simp]:
  "numeral m div - numeral n = - (adjust_div (divmod m n) :: int)"
proof -
  have "int (fst (divmod m n)) = fst (divmod m n)"
    by (simp only: fst_divmod divide_int_def) auto
  then show ?thesis
    by (auto simp add: split_def Let_def adjust_div_def divides_aux_def divide_int_def)
qed

lemma numeral_mod_minus_numeral [simp]:
  "numeral m mod - numeral n = - adjust_mod (numeral n) (snd (divmod m n) :: int)"
proof (cases "snd (divmod m n) = (0::int)")
  case True
  then show ?thesis
    by (simp add: mod_eq_0_iff_dvd divides_aux_def)
next
  case False
  then have "int (snd (divmod m n)) = snd (divmod m n)" if "snd (divmod m n) \<noteq> (0::int)"
    by (simp only: snd_divmod modulo_int_def) auto
  then show ?thesis
    by (simp add: divides_aux_def adjust_div_def)
      (simp add: divides_aux_def modulo_int_def)
qed

lemma minus_one_div_numeral [simp]:
  "- 1 div numeral n = - (adjust_div (divmod Num.One n) :: int)"
  using minus_numeral_div_numeral [of Num.One n] by simp

lemma minus_one_mod_numeral [simp]:
  "- 1 mod numeral n = adjust_mod (numeral n) (snd (divmod Num.One n) :: int)"
  using minus_numeral_mod_numeral [of Num.One n] by simp

lemma one_div_minus_numeral [simp]:
  "1 div - numeral n = - (adjust_div (divmod Num.One n) :: int)"
  using numeral_div_minus_numeral [of Num.One n] by simp

lemma one_mod_minus_numeral [simp]:
  "1 mod - numeral n = - adjust_mod (numeral n) (snd (divmod Num.One n) :: int)"
  using numeral_mod_minus_numeral [of Num.One n] by simp

end

lemma divmod_BitM_2_eq [simp]:
  \<open>divmod (Num.BitM m) (Num.Bit0 Num.One) = (numeral m - 1, (1 :: int))\<close>
  by (cases m) simp_all

lemma bit_numeral_Bit0_Suc_iff [simp]:
  \<open>bit (numeral (Num.Bit0 m) :: int) (Suc n) \<longleftrightarrow> bit (numeral m :: int) n\<close>
  by (simp add: bit_Suc)

lemma bit_numeral_Bit1_Suc_iff [simp]:
  \<open>bit (numeral (Num.Bit1 m) :: int) (Suc n) \<longleftrightarrow> bit (numeral m :: int) n\<close>
  by (simp add: bit_Suc)

lemma div_positive_int:
  "k div l > 0" if "k \<ge> l" and "l > 0" for k l :: int
  using that div_positive [of l k] by blast

subsubsection \<open>Dedicated simproc for calculation\<close>

text \<open>
  There is space for improvement here: the calculation itself
  could be carried out outside the logic, and a generic simproc
  (simplifier setup) for generic calculation would be helpful.
\<close>

simproc_setup numeral_divmod
  ("0 div 0 :: 'a :: unique_euclidean_semiring_numeral" | "0 mod 0 :: 'a :: unique_euclidean_semiring_numeral" |
   "0 div 1 :: 'a :: unique_euclidean_semiring_numeral" | "0 mod 1 :: 'a :: unique_euclidean_semiring_numeral" |
   "0 div - 1 :: int" | "0 mod - 1 :: int" |
   "0 div numeral b :: 'a :: unique_euclidean_semiring_numeral" | "0 mod numeral b :: 'a :: unique_euclidean_semiring_numeral" |
   "0 div - numeral b :: int" | "0 mod - numeral b :: int" |
   "1 div 0 :: 'a :: unique_euclidean_semiring_numeral" | "1 mod 0 :: 'a :: unique_euclidean_semiring_numeral" |
   "1 div 1 :: 'a :: unique_euclidean_semiring_numeral" | "1 mod 1 :: 'a :: unique_euclidean_semiring_numeral" |
   "1 div - 1 :: int" | "1 mod - 1 :: int" |
   "1 div numeral b :: 'a :: unique_euclidean_semiring_numeral" | "1 mod numeral b :: 'a :: unique_euclidean_semiring_numeral" |
   "1 div - numeral b :: int" |"1 mod - numeral b :: int" |
   "- 1 div 0 :: int" | "- 1 mod 0 :: int" | "- 1 div 1 :: int" | "- 1 mod 1 :: int" |
   "- 1 div - 1 :: int" | "- 1 mod - 1 :: int" | "- 1 div numeral b :: int" | "- 1 mod numeral b :: int" |
   "- 1 div - numeral b :: int" | "- 1 mod - numeral b :: int" |
   "numeral a div 0 :: 'a :: unique_euclidean_semiring_numeral" | "numeral a mod 0 :: 'a :: unique_euclidean_semiring_numeral" |
   "numeral a div 1 :: 'a :: unique_euclidean_semiring_numeral" | "numeral a mod 1 :: 'a :: unique_euclidean_semiring_numeral" |
   "numeral a div - 1 :: int" | "numeral a mod - 1 :: int" |
   "numeral a div numeral b :: 'a :: unique_euclidean_semiring_numeral" | "numeral a mod numeral b :: 'a :: unique_euclidean_semiring_numeral" |
   "numeral a div - numeral b :: int" | "numeral a mod - numeral b :: int" |
   "- numeral a div 0 :: int" | "- numeral a mod 0 :: int" |
   "- numeral a div 1 :: int" | "- numeral a mod 1 :: int" |
   "- numeral a div - 1 :: int" | "- numeral a mod - 1 :: int" |
   "- numeral a div numeral b :: int" | "- numeral a mod numeral b :: int" |
   "- numeral a div - numeral b :: int" | "- numeral a mod - numeral b :: int") =
\<open> let
    val if_cong = the (Code.get_case_cong \<^theory> \<^const_name>\<open>If\<close>);
    fun successful_rewrite ctxt ct =
      let
        val thm = Simplifier.rewrite ctxt ct
      in if Thm.is_reflexive thm then NONE else SOME thm end;
  in fn phi =>
    let
      val simps = Morphism.fact phi (@{thms div_0 mod_0 div_by_0 mod_by_0 div_by_1 mod_by_1
        one_div_numeral one_mod_numeral minus_one_div_numeral minus_one_mod_numeral
        one_div_minus_numeral one_mod_minus_numeral
        numeral_div_numeral numeral_mod_numeral minus_numeral_div_numeral minus_numeral_mod_numeral
        numeral_div_minus_numeral numeral_mod_minus_numeral
        div_minus_minus mod_minus_minus Divides.adjust_div_eq of_bool_eq one_neq_zero
        numeral_neq_zero neg_equal_0_iff_equal arith_simps arith_special divmod_trivial
        divmod_cancel divmod_steps divmod_step_eq fst_conv snd_conv numeral_One
        case_prod_beta rel_simps Divides.adjust_mod_def div_minus1_right mod_minus1_right
        minus_minus numeral_times_numeral mult_zero_right mult_1_right}
        @ [@{lemma "0 = 0 \<longleftrightarrow> True" by simp}]);
      fun prepare_simpset ctxt = HOL_ss |> Simplifier.simpset_map ctxt
        (Simplifier.add_cong if_cong #> fold Simplifier.add_simp simps)
    in fn ctxt => successful_rewrite (Simplifier.put_simpset (prepare_simpset ctxt) ctxt) end
  end
\<close>

subsubsection \<open>Code generation\<close>

definition divmod_nat :: "nat \<Rightarrow> nat \<Rightarrow> nat \<times> nat"
  where "divmod_nat m n = (m div n, m mod n)"

lemma fst_divmod_nat [simp]:
  "fst (divmod_nat m n) = m div n"
  by (simp add: divmod_nat_def)

lemma snd_divmod_nat [simp]:
  "snd (divmod_nat m n) = m mod n"
  by (simp add: divmod_nat_def)

lemma divmod_nat_if [code]:
  "Divides.divmod_nat m n = (if n = 0 \<or> m < n then (0, m) else
    let (q, r) = Divides.divmod_nat (m - n) n in (Suc q, r))"
  by (simp add: prod_eq_iff case_prod_beta not_less le_div_geq le_mod_geq)

lemma [code]:
  fixes k :: int
  shows
    "k div 0 = 0"
    "k mod 0 = k"
    "0 div k = 0"
    "0 mod k = 0"
    "k div Int.Pos Num.One = k"
    "k mod Int.Pos Num.One = 0"
    "k div Int.Neg Num.One = - k"
    "k mod Int.Neg Num.One = 0"
    "Int.Pos m div Int.Pos n = (fst (divmod m n) :: int)"
    "Int.Pos m mod Int.Pos n = (snd (divmod m n) :: int)"
    "Int.Neg m div Int.Pos n = - (Divides.adjust_div (divmod m n) :: int)"
    "Int.Neg m mod Int.Pos n = Divides.adjust_mod (Int.Pos n) (snd (divmod m n) :: int)"
    "Int.Pos m div Int.Neg n = - (Divides.adjust_div (divmod m n) :: int)"
    "Int.Pos m mod Int.Neg n = - Divides.adjust_mod (Int.Pos n) (snd (divmod m n) :: int)"
    "Int.Neg m div Int.Neg n = (fst (divmod m n) :: int)"
    "Int.Neg m mod Int.Neg n = - (snd (divmod m n) :: int)"
  by simp_all

code_identifier
  code_module Divides \<rightharpoonup> (SML) Arith and (OCaml) Arith and (Haskell) Arith

subsection \<open>Lemmas of doubtful value\<close>

lemma div_geq: "m div n = Suc ((m - n) div n)" if "0 < n" and " \<not> m < n" for m n :: nat
  by (rule le_div_geq) (use that in \<open>simp_all add: not_less\<close>)

lemma mod_geq: "m mod n = (m - n) mod n" if "\<not> m < n" for m n :: nat
  by (rule le_mod_geq) (use that in \<open>simp add: not_less\<close>)

lemma mod_eq_0D: "\<exists>q. m = d * q" if "m mod d = 0" for m d :: nat
  using that by (auto simp add: mod_eq_0_iff_dvd)

lemma pos_mod_conj: "0 < b \<Longrightarrow> 0 \<le> a mod b \<and> a mod b < b" for a b :: int
  by simp

lemma neg_mod_conj: "b < 0 \<Longrightarrow> a mod b \<le> 0 \<and> b < a mod b" for a b :: int
  by simp

lemma zmod_eq_0_iff: "m mod d = 0 \<longleftrightarrow> (\<exists>q. m = d * q)" for m d :: int
  by (auto simp add: mod_eq_0_iff_dvd)

(* REVISIT: should this be generalized to all semiring_div types? *)
lemma zmod_eq_0D [dest!]: "\<exists>q. m = d * q" if "m mod d = 0" for m d :: int
  using that by auto

end


theory heap_SortPermutes2
imports Main
        "$HIPSTER_HOME/IsaHipster"
begin

datatype 'a list = Nil2 | Cons2 "'a" "'a list"

datatype Nat = Z | S "Nat"

datatype Heap = Node "Heap" "Nat" "Heap" | Nil2

fun plus :: "Nat => Nat => Nat" where
"plus (Z) y = y"
| "plus (S n) y = S (plus n y)"

fun or2 :: "bool => bool => bool" where
"or2 True y = True"
| "or2 False y = y"

fun null :: "'a list => bool" where
"null (Nil2) = True"
| "null (Cons2 y z) = False"

fun le :: "Nat => Nat => bool" where
"le (Z) y = True"
| "le (S z) (Z) = False"
| "le (S z) (S x2) = le z x2"

fun merge :: "Heap => Heap => Heap" where
"merge (Node z x2 x3) (Node x4 x5 x6) =
   (if le x2 x5 then Node (merge x3 (Node x4 x5 x6)) x2 z else
      Node (merge (Node z x2 x3) x6) x5 x4)"
| "merge (Node z x2 x3) (Nil2) = Node z x2 x3"
| "merge (Nil2) y = y"

fun toList :: "Nat => Heap => Nat list" where
"toList (Z) y = Nil2"
| "toList (S z) (Node x2 x3 x4) =
     Cons2 x3 (toList z (merge x2 x4))"
| "toList (S z) (Nil2) = Nil2"

fun insert2 :: "Nat => Heap => Heap" where
"insert2 x y = merge (Node Nil2 x Nil2) y"

fun toHeap :: "Nat list => Heap" where
"toHeap (Nil2) = Nil2"
| "toHeap (Cons2 y xs) = insert2 y (toHeap xs)"

fun heapSize :: "Heap => Nat" where
"heapSize (Node l y r) = S (plus (heapSize l) (heapSize r))"
| "heapSize (Nil2) = Z"

fun toList2 :: "Heap => Nat list" where
"toList2 x = toList (heapSize x) x"

fun equal2 :: "Nat => Nat => bool" where
"equal2 (Z) (Z) = True"
| "equal2 (Z) (S z) = False"
| "equal2 (S x2) (Z) = False"
| "equal2 (S x2) (S y2) = equal2 x2 y2"

fun elem :: "Nat => Nat list => bool" where
"elem x (Nil2) = False"
| "elem x (Cons2 z ys) = or2 (equal2 x z) (elem x ys)"

fun dot :: "('b => 'c) => ('a => 'b) => 'a => 'c" where
"dot x y z = x (y z)"

fun hsort :: "Nat list => Nat list" where
"hsort x =
   dot (% (y :: Heap) => toList2 y) (% (z :: Nat list) => toHeap z) x"

fun delete :: "Nat => Nat list => Nat list" where
"delete x (Nil2) = Nil2"
| "delete x (Cons2 z ys) =
     (if equal2 x z then ys else Cons2 z (delete x ys))"

fun and2 :: "bool => bool => bool" where
"and2 True y = y"
| "and2 False y = False"

fun isPermutation :: "Nat list => Nat list => bool" where
"isPermutation (Nil2) y = null y"
| "isPermutation (Cons2 z xs) y =
     and2 (elem z y) (isPermutation xs (delete z y))"

(*hipster plus
          or2
          null
          le
          merge
          toList
          insert2
          toHeap
          heapSize
          toList2
          equal2
          elem
          dot
          hsort
          delete
          and2
          isPermutation *)

theorem x0 :
  "!! (x :: Nat list) . isPermutation (hsort x) x"
  by (tactic \<open>Subgoal.FOCUS_PARAMS (K (Tactic_Data.hard_tac @{context})) @{context} 1\<close>)

end