Theory Parametric_Equiv_Depth

(*  Title:       Computational p-Adics: Parametric Equality and Depth
    Author:      Jeremy Sylvestre <jeremy.sylvestre at ualberta.ca>, 2025
    Maintainer:  Jeremy Sylvestre <jeremy.sylvestre at ualberta.ca>
*)


theory Parametric_Equiv_Depth

imports
  "HOL.Rat"
  "HOL-Computational_Algebra.Fraction_Field"
  "HOL-Computational_Algebra.Primes"
  "HOL-Library.Function_Algebras"
  "HOL-Library.Set_Algebras"
  "HOL.Topological_Spaces"
  Polynomial_Extras

begin


section ‹Common structures for adelic types›


subsection ‹Preliminaries›

lemma ex_least_nat_le':
  fixes P :: "nat  bool"
  assumes "P n"
  shows "kn. (i<k. ¬ P i)  P k"
  by (metis assms exists_least_iff not_le_imp_less)

lemma ex_ex1_least_nat_le:
  fixes P :: "nat  bool"
  assumes "n. P n"
  shows "∃!k. P k  (i. P i  k  i)"
  by (smt (verit) assms ex_least_nat_le' le_eq_less_or_eq nle_le)

lemma least_le_least:
  "Least Q  Least P"
  if  P : "∃!x. P x  (y. P y  x  y)"
  and Q : "∃!x. Q x  (y. Q y  x  y)"
  and PQ: "x. P x  Q x"
  for P Q :: "'a::order  bool"
  using PQ Least1_le[OF Q] Least1I[OF P] by blast

lemma linorder_wlog' [case_names le sym]:
  "
    a b. f a  f b  P a b;
    a b. P b a  P a b
    P a b"
  for f :: "'a  'b::linorder"
  by (cases rule: le_cases[of "f a" "f b"]) blast+

lemma log_powi_cancel [simp]:
  "a > 0  a  1  log a (a powi b) = b"
  by (cases "b  0", simp_all add: power_int_def power_inverse log_inverse)


subsection ‹Existence of primes›

class factorial_comm_ring = factorial_semiring + comm_ring
begin subclass comm_ring_1 .. end

class factorial_idom = factorial_semiring + idom
begin subclass factorial_comm_ring .. end

class nontrivial_factorial_semiring = factorial_semiring +
  assumes nontrivial_elem_exists: "x. x  0  normalize x  1"
begin

lemma primeE:
  obtains p where "prime p"
proof-
  from nontrivial_elem_exists obtain x where x: "x  0" "normalize x  1" by fast
  from x(1) obtain A
    where "x. x ∈# A  prime x" "normalize (prod_mset A) = normalize x"
    by (auto elim: prime_factorization_exists')
  with x(2) obtain p where "prime p" by fastforce
  with that show thesis by fast
qed

lemma primes_exist: "p. prime p"
proof-
  obtain p where "prime p" by (elim primeE)
  thus ?thesis by fast
qed

end (* class nontrivial_factorial_semiring *)

class nontrivial_factorial_comm_ring = nontrivial_factorial_semiring + comm_ring
begin
  subclass factorial_comm_ring ..
end

class nontrivial_factorial_idom = nontrivial_factorial_semiring + idom
begin
  subclass nontrivial_factorial_comm_ring ..
  subclass factorial_idom ..
end

instance int :: nontrivial_factorial_idom
proof
  have "(2::int)  0" "normalize (2::int)  1" by auto
  thus "x::int. x  0  normalize x  1" by fast
qed

typedef (overloaded) 'a prime =
  "{p::'a::nontrivial_factorial_semiring. prime p}"
  using primes_exist by fast

lemma Rep_prime_n0: "Rep_prime p  0"
  using Rep_prime[of p] prime_imp_nonzero by simp

lemma Rep_prime_not_unit: "¬ is_unit (Rep_prime p)"
  using Rep_prime[of p] by auto

abbreviation pdvd ::
  "'a::nontrivial_factorial_semiring prime  'a  bool" (infix "pdvd" 50)
  where "p pdvd a  (Rep_prime p) dvd a"
abbreviation "pmultiplicity p  multiplicity (Rep_prime p)"

lemma multiplicity_distinct_primes:
  "p  q  pmultiplicity p (Rep_prime q) = 0"
  using Rep_prime_inject Rep_prime
        multiplicity_distinct_prime_power[of "Rep_prime p" "Rep_prime q" 1]
  by    auto


subsection ‹Generic algebraic equality›

context
  fixes R
  assumes sympR  : "symp R"
    and   transpR: "transp R"
begin

lemma transp_left: "R x z  R y z  R x y"
  by (metis sympR sympD transpR transpD)

lemma transp_right: "R x y  R x z  R y z"
  by (metis sympR sympD transpR transpD)

lemma transp_iffs:
  assumes "R x y" shows "R x z  R y z" and "R z x  R z y"
  using assms transpD[OF transpR] transp_left transp_right by (blast, blast)

lemma transp_cong: "R w z" if "R x w" and "R y z" and "R x y"
  using that transpD[OF transpR] transp_right by blast

lemma transp_cong_sym: "R x y" if "R x w" and "R y z" and "R w z"
  using that transp_cong sympD[OF sympR] by blast

end (* context symp transp *)

lemma equivp_imp_symp: "equivp R  symp R"
  by (simp add: equivp_reflp_symp_transp)


locale generic_alg_equality =
  fixes   alg_eq :: "'a  'a  bool"
  assumes equivp: "equivp alg_eq"
begin

lemmas  refl          = equivp_reflp    [OF equivp]
  and   sym [sym]     = equivp_symp     [OF equivp]
  and   trans [trans] = equivp_transp   [OF equivp]
  and   trans_left    = transp_left     [OF equivp_imp_symp equivp_imp_transp, OF equivp equivp]
  and   trans_right   = transp_right    [OF equivp_imp_symp equivp_imp_transp, OF equivp equivp]
  and   trans_iffs    = transp_iffs     [OF equivp_imp_symp equivp_imp_transp, OF equivp equivp]
  and   cong          = transp_cong     [OF equivp_imp_symp equivp_imp_transp, OF equivp equivp]
  and   cong_sym      = transp_cong_sym [OF equivp_imp_symp equivp_imp_transp, OF equivp equivp]

lemma sym_iff: "alg_eq x y = alg_eq y x"
  using sym by blast

end (* locale generic_alg_equality *)


locale ab_group_alg_equality = generic_alg_equality alg_eq
  for alg_eq :: "'a::ab_group_add  'a  bool"
+
  assumes conv_0: "alg_eq x y  alg_eq (x - y) 0"
begin

lemmas trans0_iff = trans_iffs(1)[of _ _ 0]

lemma add_equiv0_left: "alg_eq (x + y) y" if "alg_eq x 0"
  using that conv_0 by fastforce

lemma add_equiv0_right:  "alg_eq (x + y) x" if "alg_eq y 0"
  using that conv_0 by fastforce

lemma add:
  "alg_eq (x1 + x2) (y1 + y2)" if "alg_eq x1 y1" and "alg_eq x2 y2"
proof (rule iffD2, rule conv_0)
  from that have "alg_eq (x1 - y1) 0" and "alg_eq (x2 - y2) 0"
    using conv_0 by auto
  hence "alg_eq ((x1 - y1) + (x2 - y2)) 0" using add_equiv0_left trans by blast
  moreover have "(x1 - y1) + (x2 - y2) = (x1 + x2) - (y1 + y2)" by simp
  ultimately show "alg_eq ((x1 + x2) - (y1 + y2)) 0" by simp
qed

lemma add_left: "alg_eq (x + y) (x + y')" if "alg_eq y y'"
  using that refl add by simp

lemma add_right: "alg_eq (x + y) (x' + y)" if "alg_eq x x'"
  using that refl add by simp

lemma add_0_left: "alg_eq (x + y) y" if "alg_eq x 0"
  using that add_right[of x 0] by simp

lemma add_0_right: "alg_eq (x + y) x" if "alg_eq y 0"
  using that add_left[of y 0] by simp

lemma sum_zeros:
  "finite A  aA. alg_eq (f a) 0  alg_eq (sum f A) 0"
  by (induct A rule: finite_induct, simp add: refl, use add in force)

lemma uminus: "alg_eq x y  alg_eq (-x) (-y)"
  using conv_0[of x y] conv_0[of "-y" "-x"] sym[of "-y" "-x"] by force

lemma uminus_iff:
  "alg_eq x y  alg_eq (-x) (-y)"
  using uminus[of x y] uminus[of "-x" "-y"] by fastforce

lemma add_0: "alg_eq (x + y) 0" if "alg_eq x 0" and "alg_eq y 0"
  using that add[of x 0 y 0] by auto

lemma minus:
  "alg_eq x1 y1  alg_eq x2 y2 
    alg_eq (x1 - x2) (y1 - y2)"
  using uminus[of x2 y2] add[of x1 y1 "-x2" "-y2"] by simp

lemma minus_0: "alg_eq (x - y) 0" if "alg_eq x 0" and "alg_eq y 0"
  using that minus[of x 0 y 0] by simp

lemma minus_left: "alg_eq (x - y) (x - y')" if "alg_eq y y'"
  using that refl minus by simp

lemma minus_right: "alg_eq (x - y) (x' - y)" if "alg_eq x x'"
  using that refl minus by simp

lemma minus_0_left: "alg_eq (x - y) (-y)" if "alg_eq x 0"
  using that refl minus_right[of x 0] by simp

lemma minus_0_right: "alg_eq (x - y) x" if "alg_eq y 0"
  using that refl minus_left[of y 0] by simp

end (* locale ab_group_alg_equality *)


locale ring_alg_equality = ab_group_alg_equality alg_eq
  for alg_eq :: "'a::comm_ring  'a  bool"
+
  assumes mult_0_right: "alg_eq y 0  alg_eq (x * y) 0"
begin

lemma mult_0_left: "alg_eq x 0  alg_eq (x * y) 0"
  using mult_0_right[of x y] by (simp add: mult.commute)

lemma mult:
  "alg_eq x1 y1  alg_eq x2 y2  alg_eq (x1 * x2) (y1 * y2)"
  using mult_0_right[of "x2 - y2" x1] mult_0_left[of "x1 - y1" y2] conv_0 trans
  by    (force simp add: algebra_simps)

lemma mult_left: "alg_eq y y'  alg_eq (x * y) (x * y')"
  using refl mult by force

lemma mult_right: "alg_eq x x'  alg_eq (x * y) (x' * y)"
  using refl mult by force

end (* locale ring_alg_equality *)


locale ring_1_alg_equality = ring_alg_equality alg_eq
  for alg_eq :: "'a::comm_ring_1  'a  bool"
begin

lemma mult_one_left: "alg_eq x 1  alg_eq (x * y) y"
  using mult_right by fastforce

lemma mult_one_right: "alg_eq y 1  alg_eq (x * y) x"
  using mult_left by fastforce

lemma prod_ones:
  "finite A  aA. alg_eq (f a) 1 
    alg_eq (prod f A) 1"
  by (induct A rule: finite_induct, simp add: refl, use mult in force)

lemma prod_with_zero:
  "alg_eq (prod f (insert a A)) 0" if "finite A" and "alg_eq (f a) 0"
  using that prod.insert[of "A - {a}" a f] mult_0_left by fastforce

lemma pow: "alg_eq (x ^ n) (y ^ n)" if "alg_eq x y"
  using that refl mult by (induct n, simp_all)

lemma pow_equiv0: "alg_eq (x ^ n) 0" if "n > 0" and "alg_eq x 0"
  by (metis that zero_power pow)

end (* locale ring_1_alg_equality *)


subsection ‹Indexed equality›

subsubsection ‹General structure›

locale p_equality =
  fixes p_equal :: "'a  'b::comm_ring  'b  bool"
  assumes p_alg_equality: "ring_alg_equality (p_equal p)"
begin

lemmas p_group_alg_equality   = ring_alg_equality.axioms(1)[OF p_alg_equality]
lemmas p_generic_alg_equality = ab_group_alg_equality.axioms(1)[OF p_group_alg_equality]

lemmas  equivp        = generic_alg_equality.equivp      [OF p_generic_alg_equality]
  and   refl[simp]    = generic_alg_equality.refl        [OF p_generic_alg_equality]
  and   sym  [sym]    = generic_alg_equality.sym         [OF p_generic_alg_equality]
  and   trans [trans] = generic_alg_equality.trans       [OF p_generic_alg_equality]
  and   sym_iff       = generic_alg_equality.sym_iff     [OF p_generic_alg_equality]
  and   trans_left    = generic_alg_equality.trans_left  [OF p_generic_alg_equality]
  and   trans_right   = generic_alg_equality.trans_right [OF p_generic_alg_equality]
  and   trans_iffs    = generic_alg_equality.trans_iffs  [OF p_generic_alg_equality]
  and   cong          = generic_alg_equality.cong        [OF p_generic_alg_equality]
  and   cong_sym      = generic_alg_equality.cong_sym    [OF p_generic_alg_equality]

lemmas  conv_0           = ab_group_alg_equality.conv_0           [OF p_group_alg_equality]
  and   trans0_iff       = ab_group_alg_equality.trans0_iff       [OF p_group_alg_equality]
  and   add_equiv0_left  = ab_group_alg_equality.add_equiv0_left  [OF p_group_alg_equality]
  and   add_equiv0_right = ab_group_alg_equality.add_equiv0_right [OF p_group_alg_equality]
  and   add              = ab_group_alg_equality.add              [OF p_group_alg_equality]
  and   add_left         = ab_group_alg_equality.add_left         [OF p_group_alg_equality]
  and   add_right        = ab_group_alg_equality.add_right        [OF p_group_alg_equality]
  and   add_0_left       = ab_group_alg_equality.add_0_left       [OF p_group_alg_equality]
  and   add_0_right      = ab_group_alg_equality.add_0_right      [OF p_group_alg_equality]
  and   sum_zeros        = ab_group_alg_equality.sum_zeros        [OF p_group_alg_equality]
  and   uminus           = ab_group_alg_equality.uminus           [OF p_group_alg_equality]
  and   uminus_iff       = ab_group_alg_equality.uminus_iff       [OF p_group_alg_equality]
  and   add_0            = ab_group_alg_equality.add_0            [OF p_group_alg_equality]
  and   minus            = ab_group_alg_equality.minus            [OF p_group_alg_equality]
  and   minus_0          = ab_group_alg_equality.minus_0          [OF p_group_alg_equality]
  and   minus_left       = ab_group_alg_equality.minus_left       [OF p_group_alg_equality]
  and   minus_right      = ab_group_alg_equality.minus_right      [OF p_group_alg_equality]
  and   minus_0_left     = ab_group_alg_equality.minus_0_left     [OF p_group_alg_equality]
  and   minus_0_right    = ab_group_alg_equality.minus_0_right    [OF p_group_alg_equality]

lemmas  mult_0_left  = ring_alg_equality.mult_0_left  [OF p_alg_equality]
  and   mult_0_right = ring_alg_equality.mult_0_right [OF p_alg_equality]
  and   mult         = ring_alg_equality.mult         [OF p_alg_equality]
  and   mult_left    = ring_alg_equality.mult_left    [OF p_alg_equality]
  and   mult_right   = ring_alg_equality.mult_right   [OF p_alg_equality]

definition globally_p_equal :: "'b  'b  bool"
  where globally_p_equal_def[simp]: "globally_p_equal x y = (p::'a. p_equal p x y)"

lemma globally_p_equalI[intro]: "globally_p_equal x y" if "p::'a. p_equal p x y"
  using that unfolding globally_p_equal_def by blast

lemma globally_p_equalD: "globally_p_equal x y  p_equal p x y"
  unfolding globally_p_equal_def by fast

lemma globally_p_nequalE:
  assumes "¬ globally_p_equal x y"
  obtains p where "¬ p_equal p x y"
  using     assms
  unfolding globally_p_equal_def
  by        fast

sublocale globally_p_equality: ring_alg_equality globally_p_equal
proof (standard, intro equivpI)
  show "reflp globally_p_equal"
    using refl unfolding globally_p_equal_def by (fastforce intro: reflpI)
  show "symp globally_p_equal"
    using sym unfolding globally_p_equal_def by (fastforce intro: sympI)
  show "transp globally_p_equal"
    using trans unfolding globally_p_equal_def by (fastforce intro: transpI)
  fix x y :: 'b
  show        "globally_p_equal x y = globally_p_equal (x - y) 0"
  and         "globally_p_equal y 0  globally_p_equal (x * y) 0"
    using     conv_0 mult_0_right
    unfolding globally_p_equal_def
    by        auto
qed

lemmas  globally_p_equal_equivp           = globally_p_equality.equivp
  and   globally_p_equal_refl[simp]       = globally_p_equality.refl
  and   globally_p_equal_sym              = globally_p_equality.sym
  and   globally_p_equal_trans            = globally_p_equality.trans
  and   globally_p_equal_trans_left       = globally_p_equality.trans_left
  and   globally_p_equal_trans_right      = globally_p_equality.trans_right
  and   globally_p_equal_trans_iffs       = globally_p_equality.trans_iffs
  and   globally_p_equal_cong             = globally_p_equality.cong
  and   globally_p_equal_conv_0           = globally_p_equality.conv_0
  and   globally_p_equal_trans0_iff       = globally_p_equality.trans0_iff
  and   globally_p_equal_add_equiv0_left  = globally_p_equality.add_equiv0_left
  and   globally_p_equal_add_equiv0_right = globally_p_equality.add_equiv0_right
  and   globally_p_equal_add              = globally_p_equality.add
  and   globally_p_equal_uminus           = globally_p_equality.uminus
  and   globally_p_equal_uminus_iff       = globally_p_equality.uminus_iff
  and   globally_p_equal_add_0            = globally_p_equality.add_0
  and   globally_p_equal_minus            = globally_p_equality.minus
  and   globally_p_equal_mult_0_left      = globally_p_equality.mult_0_left
  and   globally_p_equal_mult_0_right     = globally_p_equality.mult_0_right
  and   globally_p_equal_mult             = globally_p_equality.mult
  and   globally_p_equal_mult_left        = globally_p_equality.mult_left
  and   globally_p_equal_mult_right       = globally_p_equality.mult_right

lemma globally_p_equal_transfer_equals_rsp:
  "p_equal p x1 y1  p_equal p x2 y2"
  if "globally_p_equal x1 x2" and "globally_p_equal y1 y2"
  using that trans_iffs globally_p_equalD by blast

end  (* locale p_equality *)


locale p_equality_1 = p_equality p_equal
  for p_equal :: "'a  'b::comm_ring_1  'b  bool"
begin

lemma p_alg_equality_1: "ring_1_alg_equality (p_equal p)"
  using ring_1_alg_equality.intro p_alg_equality by blast

lemmas  mult_one_left  = ring_1_alg_equality.mult_one_left  [OF p_alg_equality_1]
  and   mult_one_right = ring_1_alg_equality.mult_one_right [OF p_alg_equality_1]
  and   prod_ones      = ring_1_alg_equality.prod_ones      [OF p_alg_equality_1]
  and   pow            = ring_1_alg_equality.pow            [OF p_alg_equality_1]
  and   pow_equiv0     = ring_1_alg_equality.pow_equiv0     [OF p_alg_equality_1]

end (* locale p_equality_1 *)


locale p_equality_no_zero_divisors = p_equality
+
  assumes no_zero_divisors:
    "¬ p_equal p x 0  ¬ p_equal p y 0 
      ¬ p_equal p (x * y) 0"
begin

lemma mult_equiv_0_iff:
  "p_equal p (x * y) 0 
    p_equal p x 0  p_equal p y 0"
  using mult_0_right no_zero_divisors by (fastforce simp add: mult.commute)

end (* locale p_equality_no_zero_divisors *)


locale p_equality_no_zero_divisors_1 = p_equality_no_zero_divisors p_equal
  for p_equal ::
    "'a  'b::comm_ring_1  'b  bool"
+
  assumes nontrivial: "x. ¬ p_equal p x 0"
begin

sublocale p_equality_1 ..

lemma one_p_nequal_zero: "¬ p_equal p (1::'b) (0::'b)"
  using nontrivial mult_equiv_0_iff[of p "1::'b"] by fastforce

lemma zero_p_nequal_one: "¬ p_equal p (0::'b) (1::'b)"
  using nontrivial one_p_nequal_zero sym by blast

lemma neg_one_p_nequal_zero: "¬ p_equal p (-1::'b) (0::'b)"
  using one_p_nequal_zero uminus by fastforce

lemma pow_equiv_0_iff: "p_equal p (x ^ n) 0  p_equal p x 0  n  0"
proof (cases n)
  case (Suc k) show ?thesis by (simp only: Suc, induct k, simp_all add: mult_equiv_0_iff)
qed (simp add: one_p_nequal_zero)

lemma pow_equiv_base: "p_equal p (x ^ n) (y ^ n)" if "p_equal p x y"
  using that mult by (induct n) auto

end (* locale p_equality_no_zero_divisors_1 *)


locale p_equality_div_inv = p_equality_no_zero_divisors_1 p_equal
  for p_equal ::
    "'a  'b::{comm_ring_1, inverse, divide_trivial} 
      'b  bool"
+
  assumes divide_inverse    : "x y :: 'b. x / y = x * inverse y"
    and   inverse_inverse   : "x::'b. inverse (inverse x) = x"
    and   inverse_mult      : "x y :: 'b. inverse (x * y) = inverse x * inverse y"
    and   inverse_equiv0_iff: "p_equal p (inverse x) 0  p_equal p x 0"
    and   divide_self_equiv : "¬ p_equal p x 0  p_equal p (x / x) 1"
begin

text ‹Global algebra facts›

lemma inverse_eq_divide: "inverse x = 1 / x" for x :: 'b
  using divide_inverse by auto

lemma inverse_div_inverse: "inverse x / inverse y = y / x" for x y :: 'b
  by (simp add: divide_inverse inverse_inverse)

lemma inverse_0[simp]: "inverse (0::'b) = 0"
  using inverse_eq_divide by simp

lemma inverse_1[simp]: "inverse (1::'b) = 1"
  using inverse_eq_divide by simp

lemma inverse_pow: "inverse (x ^ n) = inverse x ^ n" for x :: 'b
  by (induct n) (simp_all add: inverse_mult)

lemma power_int_minus: "power_int x (-n) = inverse (power_int x n)" for x :: 'b
  by (cases "n  0") (simp_all add: power_int_def inverse_pow inverse_inverse)

lemma power_int_minus_divide: "power_int x (-n) = 1 / (power_int x n)" for x :: 'b
  using power_int_minus inverse_eq_divide by presburger

lemma power_int_0_left_if: "power_int (0 :: 'b) m = (if m = 0 then 1 else 0)"
  by (auto simp add: power_int_def zero_power)

lemma power_int_0_left [simp]: "m  0  power_int (0 :: 'b) m = 0"
  by (simp add: power_int_0_left_if)

lemma power_int_1_left [simp]: "power_int (1::'b) n = 1"
  by (auto simp: power_int_def)

lemma inverse_power_int: "inverse (power_int x n) = power_int x (-n)" for x :: 'b
  using inverse_pow inverse_inverse unfolding power_int_def by (cases n) auto

lemma power_int_inverse:
 "power_int (inverse x) n = inverse (power_int x n)" for x :: 'b
proof (cases "n > 0")
  case True thus ?thesis using inverse_pow power_int_def by metis
next
  case False
  moreover define m where "m  - n"
  ultimately have "m  0" by fastforce
  hence "power_int (inverse x) (-m) = inverse (power_int x (-m))"
  proof (induct m rule: int_ge_induct)
    case (step m)
    from step(1) have "inverse x powi - (m + 1) = x * inverse x powi (- m)"
      using power_Suc[of x "nat m"]
      by    (fastforce simp add: Suc_nat_eq_nat_zadd1 inverse_inverse power_int_def)
    with step show ?case
      by (
        simp  add : inverse_inverse inverse_mult add.commute power_int_def
              flip: Suc_nat_eq_nat_zadd1
      )
  qed simp
  with m_def show ?thesis by force
qed

lemma power_int_mult_distrib:
  "power_int (x * y) m = power_int x m * power_int y m" for x y :: 'b
  by (cases "m  0") (simp_all add: power_int_def power_mult_distrib inverse_mult)

lemma inverse_divide: "inverse (a / b) = b / a" for a b :: 'b
  by (metis divide_inverse inverse_mult inverse_inverse mult.commute)

lemma minus_divide_left: "- (a / b) = (- a) / b" for a b :: 'b
  by (simp add: divide_inverse algebra_simps)

lemma times_divide_times_eq: "(a / b) * (c / d) = (a * c) / (b * d)" for a b :: 'b
  by (simp add: divide_inverse inverse_mult algebra_simps)

lemma divide_pow: "(x ^ n) / (y ^ n) =  (x / y) ^ n" for x y :: 'b
proof (induct n)
  case (Suc n) thus ?case using times_divide_times_eq[of x y "x ^ n" "y ^ n"] by force
qed simp

lemma power_int_divide_distrib:
  "power_int (x / y) m = power_int x m / power_int y m" for x y :: 'b
  by (cases "m  0") (simp_all add: power_int_def divide_pow inverse_divide inverse_div_inverse)

lemma power_int_one_over: "power_int (1 / x) n = 1 / power_int x n" for x :: 'b
  by (auto simp add: power_int_inverse simp flip: inverse_eq_divide)

lemma add_divide_distrib: "(a + b) / c = (a / c) + (b / c)" for a b c :: 'b
  by (simp add: divide_inverse algebra_simps)

lemma diff_divide_distrib: "(a - b) / c = (a / c) - (b / c)" for a b c :: 'b
  by (simp add: divide_inverse algebra_simps)

lemma times_divide_eq_right: "a * (b / c) = (a * b) / c" for a b c :: 'b
  using times_divide_times_eq[of a 1] by force

lemma times_divide_eq_left: "(b / c) * a = (b * a) / c" for a b c :: 'b
  using times_divide_eq_right by (simp add: ac_simps)

lemma divide_divide_eq_left: "(a / b) / c = a / (b * c)" for a b c :: 'b
  using divide_inverse mult.assoc inverse_mult[of b c] by metis

lemma swap_numerator: "a * (b / c) = (a / c) * b" for a b c :: 'b
  by (metis times_divide_eq_right mult.commute)

lemma divide_divide_times_eq: "(a / b) / (c / d) = (a * d) / (b * c)" for a b c d :: 'b
  by (metis divide_inverse inverse_divide times_divide_times_eq)

text ‹Algebra facts by place›

lemma right_inverse_equiv: "p_equal p (x * inverse x) 1" if "¬ p_equal p x 0"
  using that divide_self_equiv divide_inverse by force

lemma left_inverse_equiv: "p_equal p (inverse x * x) 1" if "¬ p_equal p x 0"
  using that right_inverse_equiv by (simp add: mult.commute)

lemma inverse_p_unique:
  "p_equal p (inverse x) y" if "p_equal p (x * y) 1"
proof-
  from that have x_n0: "¬ p_equal p x 0"
    using mult_0_left trans_right zero_p_nequal_one by blast
  with that have "p_equal p (inverse x * x * y) (inverse x * x * inverse x)"
    by (metis right_inverse_equiv trans_left mult_left mult.assoc)
  moreover
    have  "p_equal p (inverse x * x * y) y"
    and   "p_equal p (inverse x * x * inverse x) (inverse x)"
    using x_n0 left_inverse_equiv mult_right
    by    (fastforce, fastforce)
  ultimately show "p_equal p (inverse x) y" using cong sym by blast
qed

lemma inverse_equiv_imp_equiv:
  "p_equal p (inverse a) (inverse b)  p_equal p a b"
proof (cases "p_equal p a 0", metis inverse_equiv0_iff trans_right trans_left)
  case False
  moreover assume ab: "p_equal p (inverse a) (inverse b)"
  ultimately have "¬ p_equal p b 0" using inverse_equiv0_iff trans_right trans_left by meson
  moreover from False ab have
    "p_equal p b (a * (inverse b * b))"
    by (metis mult_left right_inverse_equiv trans_right mult_right mult_1_left mult.assoc)
  ultimately show "p_equal p a b"
    by (metis left_inverse_equiv mult_left trans sym mult_1_right)
qed

lemma inverse_pcong: "p_equal p (inverse x) (inverse y)" if "p_equal p x y"
proof (
  cases "p_equal p x 0", metis that trans_right inverse_equiv0_iff trans_left,
  intro inverse_p_unique
)
  case False with that show "p_equal p (x * inverse y) 1"
    using mult_right right_inverse_equiv cong refl by blast
qed

lemma inverse_equiv_iff_equiv:
  "p_equal p (inverse a) (inverse b)  p_equal p a b"
  using inverse_equiv_imp_equiv inverse_pcong by blast

lemma power_int_equiv_0_iff:
  "p_equal p (power_int x n) 0  p_equal p x 0  n  0"
proof (cases "n  0")
  case False thus ?thesis
    using inverse_pow[of x "nat (-n)"] inverse_equiv0_iff[of p "x ^ nat (- n)"]
          pow_equiv_0_iff[of p x "nat (- n)"]
    by    (force simp add: power_int_def)
qed (simp add: pow_equiv_0_iff power_int_def)

lemma power_diff_conv_inverse_equiv:
  "m  n  p_equal p (x ^ (n - m)) (x ^ n * inverse x ^ m)"
  if "¬ p_equal p x 0"
proof (induct n)
  case (Suc n) show ?case
  proof (cases "Suc n = m")
    case True show ?thesis
    proof (cases "m = 0")
      case False
      moreover have "p_equal p 1 (x ^ m * inverse x ^ m)"
        using that pow_equiv_0_iff right_inverse_equiv inverse_pow sym by metis
      ultimately show ?thesis using Suc Suc n = m by simp
    qed simp
  next
    case False
    with Suc(2) have mn: "m  n" by linarith
    with Suc(1) have "p_equal p (x ^ Suc (n - m)) (x ^ Suc n * inverse x ^ m)"
      using mult_left by (simp add: mult.assoc)
    moreover from mn have "Suc (n - m) = Suc n - m" by linarith
    ultimately show ?thesis by argo
  qed
qed simp

lemma power_int_add_1_equiv:
  "p_equal p (power_int x (m + 1)) (power_int x m * x)"
  if "¬ p_equal p x 0  m  -1"
proof (cases "p_equal p x 0")
  case True
  with that have "p_equal p (power_int x (m + 1)) 0" and "p_equal p (power_int x m * x) 0"
    using power_int_equiv_0_iff mult_0_right by auto
  thus ?thesis using trans_left by blast
next
  case False show ?thesis
  proof (cases "m + 1" rule: int_cases3)
    case zero
    hence "m = -1" by auto
    with False show ?thesis using left_inverse_equiv sym power_int_def by auto
  next
    case (pos n)
    hence "nat (m + 1) = Suc (nat m)" by auto
    moreover from pos have "power_int x (m + 1) = x ^ nat (m + 1)" by auto
    ultimately have "power_int x (m + 1) = power_int x m * x" by (simp add: power_int_def)
    thus ?thesis by fastforce
  next
    case (neg n)
    hence "nat (- m) = Suc n" by fastforce
    hence "power_int x m * x = inverse x ^ n * (inverse x * x)" unfolding power_int_def by auto
    with False neg show ?thesis
      using mult_left left_inverse_equiv mult_1_right sym unfolding power_int_def by fastforce
  qed
qed

lemma power_int_add_equiv:
  "¬ p_equal p x 0  m + n  0 
    p_equal p (power_int x (m + n)) (power_int x m * power_int x n)"
proof (induct m n rule: linorder_wlog)
  fix m n :: int assume mn: "m  n" and x: "¬ p_equal p x 0  m + n  0"
  show "p_equal p (power_int x (m + n)) (power_int x m * power_int x n)"
  proof (cases "p_equal p x 0")
    case True
    moreover from this x have "p_equal p (power_int x m) 0  p_equal p (power_int x n) 0"
      using power_int_equiv_0_iff by presburger
    ultimately show ?thesis
      using x mult_0_left mult_0_right power_int_equiv_0_iff trans_left by meson
  next
    case False
    have
      "m  n 
        p_equal p (power_int x (m + n)) (power_int x m * power_int x n)"
    proof (induct n rule: int_ge_induct)
      case base show ?case
      proof (cases "m  0")
        case True thus ?thesis
          using nat_add_distrib[of m] power_add[of x] unfolding power_int_def by fastforce
      next
        case False thus ?thesis
          using     nat_add_distrib[of "-m" "-m"] power_add[of "inverse x"]
          unfolding power_int_def
          by        auto
      qed
    next
      case (step n)
      from False have "p_equal p (x powi (m + (n + 1))) (x powi (m + n) * x)"
        using power_int_add_1_equiv[of p x "m + n"] by (simp add: add.assoc)
      with step(2) False have "p_equal p (x powi (m + (n + 1))) (x powi m * (x powi n * x))"
        using mult_right trans by (force simp flip: mult.assoc)
      with False show "p_equal p (x powi (m + (n + 1))) (x powi m * x powi (n + 1))"
        using trans power_int_add_1_equiv sym mult_left by meson
    qed
    with mn show ?thesis by fast
  qed
qed (simp add: ac_simps)

lemma power_int_diff_equiv:
  "p_equal p (power_int x (m - n)) (power_int x m / power_int x n)"
  if "¬ p_equal p x 0  m  n"
  using that power_int_add_equiv[of p x m "-n"] power_int_minus_divide times_divide_eq_right by auto

lemma power_int_minus_mult_equiv:
  "p_equal p (power_int x (n - 1) * x) (power_int x n)"
  if "¬ p_equal p x 0  n  0"
  using that sym power_int_add_1_equiv[of p x "n - 1"] by auto

lemma power_int_not_equiv_zero:
  "¬ p_equal p x 0  n = 0  ¬ p_equal p (power_int x n) 0"
  by (subst power_int_equiv_0_iff) auto

lemma power_int_equiv_base: "p_equal p (power_int x n) (power_int y n)" if "p_equal p x y"
  by (
    cases "n  0", metis that power_int_def pow_equiv_base,
    metis that inverse_pcong power_int_def pow_equiv_base
  )

lemma divide_equiv_0_iff:
  "p_equal p (x / y) 0  p_equal p x 0  p_equal p y 0"
  using divide_inverse mult_equiv_0_iff inverse_equiv0_iff by metis

lemma divide_pcong: "p_equal p (w / x) (y / z)" if "p_equal p w y" and "p_equal p x z"
  by (metis that divide_inverse inverse_pcong mult)

lemma divide_left_equiv: "p_equal p x y  p_equal p (x / z) (y / z)"
  by (metis mult_right divide_inverse)

lemma divide_right_equiv: "p_equal p x y  p_equal p (z / x) (z / y)"
  by (metis inverse_pcong mult_left divide_inverse)

lemma add_frac_equiv:
  "p_equal p ((a / b) + (c / d)) ((a * d + c * b) / (b * d))"
  if "¬ p_equal p b 0" and "¬ p_equal p d 0"
proof-
  from that have
    "(a * d + c * b) / (b * d) =
      a * d * (inverse b * inverse d) + c * b * (inverse b * inverse d)"
    by (metis add_divide_distrib divide_inverse inverse_mult)
  with that show ?thesis
    using mult_left[of p "d * inverse d" 1 "a / b"] mult_right[of p "b * inverse b" 1 "c / d"]
    by    (simp add: ac_simps divide_inverse right_inverse_equiv add sym)
qed

lemma mult_divide_mult_cancel_right:
  "p_equal p ((a * b) / (c * b)) (a / c)" if "¬ p_equal p b 0"
proof-
  have "(a * b) / (c * b) = a * b * (inverse c * inverse b)"
    by (metis divide_inverse inverse_mult)
  with that show ?thesis
    using mult_left[of p "b * inverse b" 1 "a / c"]
    by    (simp add: ac_simps divide_inverse right_inverse_equiv sym)
qed

lemma mult_divide_mult_cancel_right2:
  "p_equal p ((a * c) / (c * b)) (a / b)" if "¬ p_equal p c 0"
  by (metis that mult_divide_mult_cancel_right mult.commute)

lemma mult_divide_mult_cancel_left:
  "p_equal p ((c * a) / (c * b)) (a / b)" if "¬ p_equal p c 0"
  by (metis that mult_divide_mult_cancel_right mult.commute)

lemma mult_divide_mult_cancel_left2:
  "p_equal p ((c * a) / (b * c)) (a / b)" if "¬ p_equal p c 0"
  by (metis that mult_divide_mult_cancel_left mult.commute)

lemma mult_divide_cancel_right: "p_equal p ((a * b) / b) a" if "¬ p_equal p b 0"
  using that mult_divide_mult_cancel_right[of p b a 1] by auto

lemma mult_divide_cancel_left: "p_equal p ((a * b) / a) b" if "¬ p_equal p a 0"
  using that mult_divide_cancel_right mult.commute by metis

lemma divide_equiv_equiv: "(p_equal p (b / c) a) = (p_equal p b (a * c))" if "¬ p_equal p c 0"
proof
  assume "p_equal p (b / c) a"
  hence "p_equal p (b * (inverse c * c)) (a * c)" by (metis divide_inverse mult_right mult.assoc)
  with that show "p_equal p b (a * c)"
    by (metis left_inverse_equiv mult_left mult_1_right trans_right)
next
  assume "p_equal p b (a * c)"
  hence "p_equal p (b / c) (a * (c * inverse c))"
    by (metis mult_right mult.assoc divide_inverse)
  with that show "p_equal p (b / c) a"
    using mult_left right_inverse_equiv trans
    by    fastforce
qed

lemma neg_divide_equiv_equiv:
  "p_equal p (- (b / c)) a  p_equal p (-b) (a * c)" if "¬ p_equal p c 0"
  using that uminus_iff[of p "- (b / c)" a] divide_equiv_equiv uminus_iff[of p b] by force

lemma equiv_divide_imp:
  "p_equal p (a * c) b  p_equal p a (b / c)" if "¬ p_equal p c 0"
  using that divide_left_equiv mult_divide_cancel_right trans_right by blast

lemma add_divide_equiv_iff:
  "p_equal p (x + y / z) ((x * z + y) / z)" if "¬ p_equal p z 0"
  using that add sym mult_divide_cancel_right by (fastforce simp add: add_divide_distrib)

lemma divide_add_equiv_iff: "p_equal p (x / z + y) ((x + y * z) / z)" if "¬ p_equal p z 0"
  using that add_divide_equiv_iff[of p z y x] by (simp add: ac_simps)

lemma diff_divide_equiv_iff: "p_equal p (x - y / z) ((x * z - y) / z)" if "¬ p_equal p z 0"
  using that minus sym mult_divide_cancel_right by (fastforce simp add: diff_divide_distrib)

lemma minus_divide_add_equiv_iff:
  "p_equal p (- (x / z) + y) ((- x + y * z) / z)" if "¬ p_equal p z 0"
  by (metis that minus_divide_left divide_add_equiv_iff)

lemma divide_diff_equiv_iff: "p_equal p (x / z - y) ((x - y * z) / z)" if "¬ p_equal p z 0"
  by (metis that diff_conv_add_uminus minus_mult_left divide_add_equiv_iff)

lemma minus_divide_diff_equiv_iff:
  "p_equal p (- (x / z) - y) ((- x - y * z) / z)" if "¬ p_equal p z 0"
  by (metis that divide_diff_equiv_iff minus_divide_left)

lemma divide_mult_cancel_left: "p_equal p (a / (a * b)) (1 / b)" if "¬ p_equal p a 0"
  by (metis that divide_divide_eq_left divide_left_equiv divide_self_equiv)

lemma divide_mult_cancel_right: "p_equal p (b / (a * b)) (1 / a)" if "¬ p_equal p b 0"
  by (metis that divide_mult_cancel_left mult.commute)

lemma diff_frac_equiv:
  "p_equal p (x / y - w / z) ((x * z - w * y) / (y * z))"
  if "¬ p_equal p y 0" and "¬ p_equal p z 0"
proof-
  from that have "p_equal p (x / y - w / z) ((x * z + - (w * y)) / (y * z))"
    using diff_conv_add_uminus minus_divide_left add_frac_equiv minus_mult_left by metis
  thus ?thesis by force
qed

lemma frac_equiv_equiv:
  "p_equal p (x / y) (w / z)  p_equal p (x * z) (w * y)"
  if "¬ p_equal p y 0" and "¬ p_equal p z 0"
  by (metis that divide_equiv_equiv times_divide_eq_left sym)

lemma inverse_equiv_1_iff:
  "p_equal p (inverse x) 1  p_equal p x 1"
  using one_p_nequal_zero trans_right inverse_equiv0_iff mult_left right_inverse_equiv mult_1_right
        mult_right sym mult_1_left
  by    metis

lemma inverse_neg_1_equiv: "p_equal p (inverse (-1)) (-1)"
  using inverse_p_unique by auto

lemma globally_inverse_neg_1: "globally_p_equal (inverse (-1)) (-1)"
  using inverse_neg_1_equiv by blast

lemma inverse_uminus_equiv: "p_equal p (inverse (-x)) (- inverse x)"
  using inverse_mult[of "-1" x] inverse_neg_1_equiv mult_right by fastforce

lemma minus_divide_right_equiv: "p_equal p (a / (- b)) (- (a / b))"
  using divide_inverse inverse_uminus_equiv mult_left trans by fastforce

lemma minus_divide_divide_equiv: "p_equal p ((- a) / (- b)) (a / b)"
  using refl minus_divide_left minus_divide_right_equiv trans by fastforce

lemma divide_minus1_equiv: "p_equal p (x / (-1)) (- x)"
  using minus_divide_right_equiv[of p x 1] by simp

lemma divide_cancel_right:
  "p_equal p (a / c) (b / c)  p_equal p c 0  p_equal p a b"
proof
  assume "p_equal p (a / c) (b / c)"
  hence "p_equal p ((c * a) / c) ((c * b) / c)"
    using mult_left times_divide_eq_right by metis
  thus "p_equal p c 0  p_equal p a b" using mult_divide_cancel_left cong by blast
qed (metis divide_left_equiv divide_equiv_0_iff trans_left)

lemma divide_cancel_left:
  "p_equal p (c / a) (c / b)  p_equal p c 0  p_equal p a b"
proof
  assume *: "p_equal p (c / a) (c / b)"
  show "p_equal p c 0  p_equal p a b"
  proof (cases "p_equal p a 0" "p_equal p c 0" rule: case_split[case_product case_split])
    case False_False
    from * False_False(1) have "p_equal p ((c * a) / c) ((c * b) / c)"
      using divide_equiv_0_iff trans divide_equiv_equiv times_divide_eq_left sym divide_left_equiv
      by    metis
    with False_False(2) show ?thesis using mult_divide_cancel_left cong by blast
  qed (simp, metis * divide_equiv_0_iff trans0_iff trans_left, simp)
qed (metis divide_right_equiv divide_equiv_0_iff trans_left)

lemma divide_equiv_1_iff:
  "p_equal p (a / b) 1  ¬ p_equal p b 0  p_equal p a b"
proof (standard, standard)
  assume "p_equal p (a / b) 1"
  moreover from this show "¬ p_equal p b 0"
    using divide_equiv_0_iff zero_p_nequal_one trans_right by metis
  ultimately show "p_equal p a b"
    using mult_left mult_1_right times_divide_eq_right mult_divide_cancel_left trans_right
    by    metis
qed (metis divide_left_equiv divide_self_equiv trans)

lemma divide_equiv_minus_1_iff:
  "p_equal p (a / b) (- 1)  ¬ p_equal p b 0  p_equal p a (- b)"
proof-
  have "p_equal p (a / b) (- 1)  p_equal p (- (a / b)) 1"
    using uminus_iff by fastforce
  also have "  ¬ p_equal p (- b) 0  p_equal p a (- b)"
    using minus_divide_right_equiv sym trans_right divide_equiv_1_iff by blast
  finally show ?thesis using uminus_iff by fastforce
qed

end (* locale p_equality_div_inv *)


subsection ‹Indexed depth functions›

subsubsection ‹General structure›

locale p_equality_depth = p_equality +
  fixes p_depth :: "'a  'b::comm_ring  int"
  assumes depth_of_0[simp]: "p_depth p 0 = 0"
  and     depth_equiv: "p_equal p x y  p_depth p x = p_depth p y"
  and     depth_uminus: "p_depth p (-x) = p_depth p x"
  and     depth_pre_nonarch:
    "¬ p_equal p x 0  p_depth p x < p_depth p y 
      p_depth p (x + y) = p_depth p x"
    " ¬ p_equal p x 0; ¬ p_equal p (x + y) 0; p_depth p x = p_depth p y 
       p_depth p (x + y)  p_depth p x"
begin

lemma depth_equiv_0: "p_equal p x 0  p_depth p x = 0"
  using depth_of_0 depth_equiv by presburger

lemma depth_equiv_uminus: "p_equal p y (-x)  p_depth p y = p_depth p x"
  using depth_equiv depth_uminus by force

lemma depth_add_equiv0_left: "p_equal p x 0  p_depth p (x + y) = p_depth p y"
  using add_equiv0_left depth_equiv by blast

lemma depth_add_equiv0_right: "p_equal p y 0  p_depth p (x + y) = p_depth p x"
  using add_equiv0_right depth_equiv by blast

lemma depth_add_equiv:
  "p_depth p (x + y) = p_depth p (w + z)" if "p_equal p x w" and "p_equal p y z"
  using that add depth_equiv by auto

lemma depth_diff: "p_depth p (x - y) = p_depth p (y - x)"
  using depth_uminus[of p "y - x"] by auto

lemma depth_diff_equiv0_left: "p_equal p x 0  p_depth p (x - y) = p_depth p y"
  using depth_add_equiv0_right[of p x "-y"] depth_uminus by fastforce

lemma depth_diff_equiv0_right: "p_equal p y 0  p_depth p (x - y) = p_depth p x"
  using depth_diff_equiv0_left[of p y x] depth_uminus[of p "x - y"] by simp

lemma depth_diff_equiv:
  "p_depth p (x - y) = p_depth p (w - z)" if "p_equal p x w" and "p_equal p y z"
  using that minus depth_equiv by auto

lemma depth_pre_nonarch_diff_left:
  "p_depth p (x - y) = p_depth p x" if "¬ p_equal p x 0" and "p_depth p x < p_depth p y"
  using that depth_pre_nonarch(1)[of p x "-y"] depth_uminus by simp

lemma depth_pre_nonarch_diff_right:
  "p_depth p (x - y) = p_depth p y" if "¬ p_equal p y 0" and "p_depth p y < p_depth p x"
  using that depth_pre_nonarch_diff_left[of p y x] depth_uminus[of p "x - y"] by simp

lemma depth_nonarch:
  " ¬ p_equal p x 0; ¬ p_equal p y 0; ¬ p_equal p (x + y) 0 
     p_depth p (x + y)  min (p_depth p x) (p_depth p y)"
  using depth_pre_nonarch[of p x y] depth_pre_nonarch[of p y x]
  by    (cases "p_depth p x" "p_depth p y" rule: linorder_cases, auto simp add: add.commute)

lemma depth_nonarch_diff:
  " ¬ p_equal p x 0; ¬ p_equal p y 0; ¬ p_equal p (x - y) 0 
     p_depth p (x - y)  min (p_depth p x) (p_depth p y)"
  using depth_nonarch[of p x "-y"] depth_uminus uminus_iff by fastforce

lemma depth_sum_nonarch:
  "finite A  ¬ p_equal p (sum f A) 0 
    p_depth p (sum f A)  Min {p_depth p (f a) | a. a  A  ¬ p_equal p (f a) 0 }"
proof (induct A rule: finite_induct)
  case (insert a A)
  define D
    where "D  λA. {p_depth p (f a) |a. a  A  ¬ p_equal p (f a) 0}"
  consider  (fa0)     "p_equal p (f a) 0" |
            (sum0)    "p_equal p (sum f A) 0" |
            (neither) "¬ p_equal p (f a) 0" "¬ p_equal p (sum f A) 0"
    by blast
  thus "p_depth p (sum f (insert a A))  Min (D (insert a A))"
  proof cases
    case fa0
    with insert D_def have "p_depth p (sum f A)  Min (D A)" using add by fastforce
    moreover from fa0 insert(1,2) have "p_depth p (sum f (insert a A)) = p_depth p (sum f A)"
      using depth_add_equiv0_left by auto
    moreover from fa0 D_def have "D (insert a A) = D A" by blast
    ultimately show ?thesis by argo
  next
    case sum0
    with D_def insert(1,2,4) have "p_depth p (f a)  D (insert a A)" using add by force
    moreover from insert(1,2) sum0 have "p_depth p (sum f (insert a A)) = p_depth p (f a)"
      using depth_add_equiv0_right by fastforce
    ultimately show ?thesis using D_def insert(1) by force
  next
    case neither
    from D_def neither(1) have "D (insert a A) = insert (p_depth p (f a)) (D A)" by blast
    hence "Min (D (insert a A)) = Min (insert (p_depth p (f a)) (D A))" by simp
    also have " = min (p_depth p (f a)) (Min (D A))"
    proof (rule Min_insert)
      from D_def insert(1) show "finite (D A)" by simp
      from D_def insert(1) neither(2) show "D A  {}" using sum_zeros by fast
    qed
    finally show ?thesis using neither D_def insert depth_nonarch by fastforce
  qed
qed simp

lemma obtains_depth_sum_nonarch_witness:
  assumes "finite A" and "¬ p_equal p (sum f A) 0"
  obtains a
    where "a  A" and "¬ p_equal p (f a) 0" and "p_depth p (sum f A)  p_depth p (f a)"
proof-
  define D where "D  {p_depth p (f a) |a. a  A  ¬ p_equal p (f a) 0}"
  from assms D_def obtain a
    where a: "a  A" "¬ p_equal p (f a) 0" "Min D = p_depth p (f a)"
    using sum_zeros[of A p f] obtains_Min[of D] by auto
  from assms D_def a(3) have "p_depth p (sum f A)  p_depth p (f a)"
    using depth_sum_nonarch by fastforce
  with that a(1,2) show thesis by blast
qed

lemma depth_add_cancel_imp_eq_depth:
  "p_depth p x = p_depth p y" if "¬ p_equal p x 0" and "p_depth p (x + y) > p_depth p x"
  using that depth_pre_nonarch(1)[of p x y] depth_pre_nonarch(1)[of p y x] depth_add_equiv0_right
  by    (fastforce simp add: ac_simps)

lemma depth_diff_cancel_imp_eq_depth_left:
  "p_depth p x = p_depth p y" if  "¬ p_equal p x 0" and "p_depth p (x - y) > p_depth p x"
  using that depth_add_cancel_imp_eq_depth[of p x "-y"] depth_uminus by auto

lemma depth_diff_cancel_imp_eq_depth_right:
  "p_depth p x = p_depth p y" if  "¬ p_equal p y 0" and "p_depth p (x - y) > p_depth p y"
  by (metis that depth_diff_cancel_imp_eq_depth_left depth_diff)

lemma level_closed_add:
  "p_depth p (x + y)  n"
  if "¬ p_equal p (x + y) 0" and "p_depth p x  n" and "p_depth p y  n"
proof-
  consider
    "p_equal p x 0" | "p_equal p y 0" |
    (both_n0) "¬ p_equal p x 0" "¬ p_equal p y 0"
    by blast
  thus ?thesis
  proof cases
    case both_n0 with that show ?thesis using depth_nonarch by fastforce
  qed (simp add: that(3) depth_add_equiv0_left, simp add: that(2) depth_add_equiv0_right)
qed

lemma level_closed_diff:
  "p_depth p (x - y)  n"
  if "¬ p_equal p (x - y) 0" and "p_depth p x  n" and "p_depth p y  n"
  using that level_closed_add[of p x "-y"] depth_uminus by auto

definition p_depth_set :: "'a  int  'b set"
  where "p_depth_set p n = {x. ¬ p_equal p x 0  p_depth p x  n}"

definition global_depth_set :: "int  'b set"
  where
    "global_depth_set n =
      {x. p. ¬ p_equal p x 0  p_depth p x  n}"

lemma global_depth_set: "global_depth_set n = (p. p_depth_set p n)"
  unfolding p_depth_set_def global_depth_set_def by auto

lemma p_depth_setD:
  "¬ p_equal p x 0  x  p_depth_set p n 
    p_depth p x  n"
  unfolding p_depth_set_def by blast

lemma nonpos_p_depth_setD:
  "n  0  x  p_depth_set p n  p_depth p x  n"
  by (cases "p_equal p x 0", simp_all add: depth_equiv_0 p_depth_setD)

lemma global_depth_setD:
  "¬ p_equal p x 0  x  global_depth_set n 
    p_depth p x  n"
  unfolding global_depth_set_def by blast

lemma nonpos_global_depth_setD:
  "n  0  x  global_depth_set n  p_depth p x  n"
  by (cases "p_equal p x 0", simp_all add: depth_equiv_0 global_depth_setD)

lemma p_depth_set_0: "0  p_depth_set p n"
  unfolding p_depth_set_def by fastforce

lemma global_depth_set_0: "0  global_depth_set n"
  unfolding global_depth_set_def by fastforce

lemma p_depth_set_equiv0: "p_equal p x 0  x  p_depth_set p n"
  unfolding p_depth_set_def by blast

lemma p_depth_setI:
  "x  p_depth_set p n" if "¬ p_equal p x 0  p_depth p x  n"
  using that unfolding p_depth_set_def by blast

lemma p_depth_set_by_equivI:
  "x  p_depth_set p n" if "p_equal p x y" and "y  p_depth_set p n"
  by (metis that trans p_depth_setD depth_equiv p_depth_setI)

lemma global_depth_setI:
  "x  global_depth_set n"
  if "p. ¬ p_equal p x 0  p_depth p x  n"
  using that unfolding global_depth_set_def by blast

lemma global_depth_setI': "x  global_depth_set n" if "p. p_depth p x  n"
  by (intro global_depth_setI, rule that)

lemma global_depth_setI'': "x  global_depth_set n" if "p. x  p_depth_set p n"
  using that global_depth_set by blast

lemma p_depth_set_antimono:
  "n  m  p_depth_set p n  p_depth_set p m"
  unfolding p_depth_set_def by force

lemma global_depth_set_antimono:
  "n  m  global_depth_set n  global_depth_set m"
  unfolding global_depth_set_def by force

lemma p_depth_set_add:
  "x + y  p_depth_set p n" if "x  p_depth_set p n" and "y  p_depth_set p n"
proof (intro p_depth_setI, clarify)
  assume nequiv0: "¬ p_equal p (x + y) 0"
  consider "p_equal p x 0" | "p_equal p y 0" | "¬ p_equal p x 0" "¬ p_equal p y 0"
    by blast
  thus "p_depth p (x + y)  n"
    by (
      cases, metis that(2) nequiv0 add_0_left depth_equiv trans p_depth_setD,
      metis that(1) nequiv0 add_0_right depth_equiv trans p_depth_setD,
      metis that nequiv0 level_closed_add p_depth_setD
    )
qed

lemma global_depth_set_add:
  "x + y  global_depth_set n" if "x  global_depth_set n" and "y  global_depth_set n"
  using that p_depth_set_add global_depth_set by simp

lemma p_depth_set_uminus: "-x  p_depth_set p n" if "x  p_depth_set p n"
  using that uminus depth_uminus unfolding p_depth_set_def by fastforce

lemma global_depth_set_uminus: "-x  global_depth_set n" if "x  global_depth_set n"
  using that p_depth_set_uminus global_depth_set by simp

lemma p_depth_set_minus:
  "x - y  p_depth_set p n" if "x  p_depth_set p n" and "y  p_depth_set p n"
  using that p_depth_set_add p_depth_set_uminus by fastforce

lemma global_depth_set_minus:
  "x - y  global_depth_set n" if "x  global_depth_set n" and "y  global_depth_set n"
  using that p_depth_set_minus global_depth_set by simp

lemma p_depth_set_elt_set_plus:
  "x +o p_depth_set p n = p_depth_set p n" if "x  p_depth_set p n"
  unfolding elt_set_plus_def
proof (standard, safe)
  fix y assume y: "y  p_depth_set p n"
  define z where "z  y - x"
  with that y have "z  p_depth_set p n" using p_depth_set_minus by simp
  with z_def show "zp_depth_set p n. y = x + z" by force
qed (simp add: that p_depth_set_add)

lemma global_depth_set_elt_set_plus:
  "x +o global_depth_set n = global_depth_set n" if "x  global_depth_set n"
  unfolding elt_set_plus_def
proof (standard, safe)
  fix y assume y: "y  global_depth_set n"
  define z where "z  y - x"
  with that y have "z  global_depth_set n" using global_depth_set_minus by simp
  with z_def show "zglobal_depth_set n. y = x + z" by force
qed (simp add: that global_depth_set_add)

lemma p_depth_set_elt_set_plus_closed:
  "x +o p_depth_set p m  p_depth_set p n" if "m  n" and "x  p_depth_set p n"
  using that set_plus_mono p_depth_set_antimono p_depth_set_elt_set_plus by metis

lemma global_depth_set_elt_set_plus_closed:
  "x +o global_depth_set m  global_depth_set n"
  if "m  n" and "x  global_depth_set n"
  using that set_plus_mono global_depth_set_antimono global_depth_set_elt_set_plus by metis

lemma p_depth_set_plus_coeffs:
  "set xs  p_depth_set p n 
    set ys  p_depth_set p n 
    set (plus_coeffs xs ys)  p_depth_set p n"
proof (induct xs ys rule: list_induct2')
  case (4 x xs y ys) thus ?case
    using cCons_def[of "x + y" "plus_coeffs xs ys"] p_depth_set_add by auto
qed simp_all

lemma global_depth_set_plus_coeffs:
  "set (plus_coeffs xs ys)  global_depth_set n"
  if "set xs  global_depth_set n" and "set ys  global_depth_set n"
  using that p_depth_set_plus_coeffs[of xs _ n ys] global_depth_set[of n] by fastforce

lemma p_depth_set_poly_pCons:
  "set (coeffs (pCons a f))  p_depth_set p n"
  if "a  p_depth_set p n" and "set (coeffs f)  p_depth_set p n"
  by (cases "a = 0  f = 0") (simp add: p_depth_set_0, use that in auto)

lemma p_depth_set_poly_add:
  "set (coeffs (f + g))  p_depth_set p n"
  if "set (coeffs f)  p_depth_set p n" and "set (coeffs g)  p_depth_set p n"
  using that coeffs_plus_eq_plus_coeffs p_depth_set_plus_coeffs by metis

lemma global_depth_set_poly_add:
  "set (coeffs (f + g))  global_depth_set n"
  if  "set (coeffs f)  global_depth_set n"
  and "set (coeffs g)  global_depth_set n"
  using that p_depth_set_poly_add[of f _ n g] global_depth_set by fastforce

end  (* locale p_equality_depth *)


locale p_equality_depth_no_zero_divisors =
  p_equality_no_zero_divisors + p_equality_depth
+ assumes depth_mult_additive:
    "¬ p_equal p (x * y) 0  p_depth p (x * y) = p_depth p x + p_depth p y"
begin

lemma depth_mult3_additive:
  "p_depth p (x * y * z) = p_depth p x + p_depth p y + p_depth p z"
  if "¬ p_equal p (x * y * z) 0"
  using that mult_0_left depth_mult_additive by fastforce

lemma p_depth_set_times:
  "x * y  p_depth_set p n"
  if "n  0" and "x  p_depth_set p n" and "y  p_depth_set p n"
proof (intro p_depth_setI, clarify)
  assume "¬ p_equal p (x * y) 0"
  moreover from this have "¬ p_equal p x 0" and "¬ p_equal p y 0"
    by (metis mult_0_left, metis mult_0_right)
  ultimately show "p_depth p (x * y)  n"
    using that p_depth_setD[of p x] p_depth_setD[of p y] depth_mult_additive by fastforce
qed

lemma global_depth_set_times:
  "x * y  global_depth_set n"
  if "n  0" and "x  global_depth_set n" and "y  global_depth_set n"
  using that p_depth_set_times global_depth_set by simp

lemma poly_p_depth_set_poly:
  "set (coeffs f)  p_depth_set p n  poly f x  p_depth_set p n"
  if "n  0" and "x  p_depth_set p n"
  by (induct f, simp add: p_depth_set_0, use that p_depth_set_times p_depth_set_add in force)

lemma poly_global_depth_set_poly:
  "poly f x  global_depth_set n"
  if    "n  0"
  and   "x  global_depth_set n"
  and   "set (coeffs f)  global_depth_set n"
  using that poly_p_depth_set_poly global_depth_set
  by    fastforce

lemma p_depth_set_ideal:
  "x * y  p_depth_set p n"
  if "x  p_depth_set p 0" and "y  p_depth_set p n"
proof (intro p_depth_setI, clarify)
  assume "¬ p_equal p (x * y) 0"
  moreover from this have "¬ p_equal p x 0" and "¬ p_equal p y 0"
    by (metis mult_0_left, metis mult_0_right)
  ultimately show "p_depth p (x * y)  n"
    using that p_depth_setD[of p x] p_depth_setD[of p y] depth_mult_additive by fastforce
qed

lemma global_depth_set_ideal:
  "x * y  global_depth_set n"
  if "x  global_depth_set 0" and "y  global_depth_set n"
  using that p_depth_set_ideal global_depth_set by simp

lemma poly_p_depth_set_poly_ideal:
  "set (coeffs f)  p_depth_set p n  poly f x  p_depth_set p n"
  if "x  p_depth_set p 0"
  using that p_depth_set_0 p_depth_set_ideal p_depth_set_add by (induct f) auto

lemma poly_global_depth_set_poly_ideal:
  "poly f x  global_depth_set n"
  if "x  global_depth_set 0" and "set (coeffs f)  global_depth_set n"
  using that poly_p_depth_set_poly_ideal global_depth_set by fastforce

lemma p_depth_set_poly_smult:
  "set (coeffs (smult x f))  p_depth_set p n"
  if "n  0" and "x  p_depth_set p n" and "set (coeffs f)  p_depth_set p n"
proof-
  have "set (coeffs (smult x f)) = (*) x ` set (strip_while (λc. x * c = 0) (coeffs f))"
    using coeffs_smult_eq_smult_coeffs set_map by metis
  also have "  set (map ((*) x) (coeffs f))"
    using set_strip_while image_mono set_map by fastforce
  finally show ?thesis using that p_depth_set_times by fastforce
qed

lemma global_depth_set_poly_smult:
  "set (coeffs (smult x f))  global_depth_set n"
  if    "n  0"
  and   "x  global_depth_set n"
  and   "set (coeffs f)  global_depth_set n"
  using that p_depth_set_poly_smult global_depth_set
  by    fastforce

lemma p_depth_set_poly_smult_ideal:
  "set (coeffs (smult x f))  p_depth_set p n"
  if "x  p_depth_set p 0" and "set (coeffs f)  p_depth_set p n"
proof-
  have "set (coeffs (smult x f)) = (*) x ` set (strip_while (λc. x * c = 0) (coeffs f))"
    using coeffs_smult_eq_smult_coeffs set_map by metis
  also have "  set (map ((*) x) (coeffs f))"
    using set_strip_while image_mono set_map by fastforce
  finally show ?thesis using that p_depth_set_ideal by fastforce
qed

lemma global_depth_set_poly_smult_ideal:
  "set (coeffs (smult x f))  global_depth_set n"
  if "x  global_depth_set 0" and "set (coeffs f)  global_depth_set n"
  using that p_depth_set_poly_smult_ideal global_depth_set by fastforce

lemma p_depth_set_poly_times:
  "set (coeffs f)  p_depth_set p n 
    set (coeffs g)  p_depth_set p n 
    set (coeffs (f * g))  p_depth_set p n"
  if  "n  0"
proof (induct f)
  case (pCons a f)
  hence "set (coeffs (f * g))  p_depth_set p n" by auto
  moreover from pCons(1,3) have "a  p_depth_set p n" by fastforce
  ultimately show ?case
    using that pCons(4) mult_pCons_left p_depth_set_poly_add p_depth_set_poly_smult
          p_depth_set_poly_pCons p_depth_set_0
    by    auto
qed simp

lemma global_depth_set_poly_times:
  "set (coeffs (f * g))  global_depth_set n"
  if  "n  0"
  and "set (coeffs g)  global_depth_set n"
  and "set (coeffs f)  global_depth_set n"
  using that p_depth_set_poly_times[of n f _ g] global_depth_set by fastforce

lemma p_depth_set_poly_times_ideal:
  "set (coeffs f)  p_depth_set p 0 
    set (coeffs g)  p_depth_set p n 
    set (coeffs (f * g))  p_depth_set p n"
proof (induct f)
  case (pCons a f)
  hence "set (coeffs (f * g))  p_depth_set p n" by auto
  moreover from pCons(1,3) have "a  p_depth_set p 0" by fastforce
  ultimately show ?case
    using pCons(4) mult_pCons_left p_depth_set_poly_add p_depth_set_poly_smult_ideal
          p_depth_set_poly_pCons p_depth_set_0
    by    simp
qed simp

lemma global_depth_set_poly_times_ideal:
  "set (coeffs (f * g))  global_depth_set n"
  if  "set (coeffs f)  global_depth_set 0"
  and "set (coeffs g)  global_depth_set n"
  using that p_depth_set_poly_times_ideal[of f _ g] global_depth_set by fastforce

end


locale p_equality_depth_no_zero_divisors_1 =
  p_equality_no_zero_divisors_1 + p_equality_depth_no_zero_divisors
begin

lemma depth_of_1[simp]: "p_depth p 1 = 0"
  using one_p_nequal_zero depth_mult_additive[of p 1] by auto

lemma depth_of_neg1: "p_depth p (-1) = 0"
  using depth_uminus by auto

lemma depth_pow_additive: "p_depth p (x ^ n) = int n * p_depth p x"
proof (cases "p_equal p x 0")
  case True thus ?thesis using pow_equiv_0_iff depth_equiv_0 by (cases "n = 0") auto
next
  case False show ?thesis
  proof (induct n)
    case (Suc n)
    from False have "p_depth p (x ^ Suc n) = p_depth p x + p_depth p (x ^ n)"
      using pow_equiv_0_iff depth_mult_additive by force
    also from Suc have " = (int n + 1) * p_depth p x" by algebra
    finally show ?case by auto
  qed simp
qed

lemma depth_prod_summative:
  "finite X  ¬ p_equal p (X) 0 
    p_depth p (X) = sum (p_depth p) X"
  by (induct X rule: finite_induct, simp, use depth_mult_additive mult_0_right in force)

lemma p_depth_set_1: "1  p_depth_set p 0"
  using p_depth_setI by simp

lemma pos_p_depth_set_1: "n > 0  1  p_depth_set p n"
  using one_p_nequal_zero p_depth_setD by fastforce

lemma global_depth_set_1: "1  global_depth_set 0"
  using p_depth_set_1 global_depth_set by blast

lemma pos_global_depth_set_1: "n > 0  1  global_depth_set n"
  using pos_p_depth_set_1 global_depth_set by blast

lemma p_depth_set_neg1: "-1  p_depth_set p 0"
  using depth_of_neg1 p_depth_setI by simp

lemma global_depth_set_neg1: "-1  global_depth_set 0"
  using p_depth_set_neg1 global_depth_set by blast

lemma p_depth_set_pow:
  "x ^ Suc k  p_depth_set p n" if "n  0" and "x  p_depth_set p n"
  by (induct k, simp_all add: that p_depth_set_times)

lemma global_depth_set_pow:
  "x ^ Suc k  global_depth_set n" if "n  0" and "x  global_depth_set n"
  using that p_depth_set_pow global_depth_set by simp

lemma p_depth_set_0_pow: "x ^ k  p_depth_set p 0" if "x  p_depth_set p 0"
  by (induct k, simp_all add: that p_depth_set_1 p_depth_set_times)

lemma global_depth_set_0_pow: "x ^ k  global_depth_set 0" if "x  global_depth_set 0"
  using that p_depth_set_0_pow global_depth_set by simp

lemma p_depth_set_of_nat: "of_nat n  p_depth_set p 0"
  using p_depth_set_0 p_depth_set_1 p_depth_set_add by (induct n) simp_all

lemma global_depth_set_of_nat: "of_nat n  global_depth_set 0"
  using p_depth_set_of_nat global_depth_set by blast

lemma p_depth_set_of_int: "of_int n  p_depth_set p 0"
  using p_depth_set_of_nat p_depth_set_neg1 p_depth_set_minus by (induct n) simp_all

lemma global_depth_set_of_int: "of_int n  global_depth_set 0"
  using p_depth_set_of_int global_depth_set by blast

lemma p_depth_set_polyderiv:
  "set (coeffs f)  p_depth_set p n 
    set (coeffs (polyderiv f))  p_depth_set p n"
proof (induct f)
  case (pCons a f)
  from pCons(1) have
    "set (coeffs (polyderiv (pCons a f))) =
      set (plus_coeffs (coeffs f) (coeffs (pCons 0 (polyderiv f))))"
    using polyderiv_pCons coeffs_plus_eq_plus_coeffs by metis
  moreover from pCons(1,3) have "set (coeffs f)  p_depth_set p n" by fastforce
  ultimately show ?case
    using pCons(2) p_depth_set_poly_pCons p_depth_set_0 p_depth_set_plus_coeffs by presburger
qed simp

lemma global_depth_set_polyderiv:
  "set (coeffs (polyderiv f))  global_depth_set n"
  if "set (coeffs f)  global_depth_set n"
  using that p_depth_set_polyderiv global_depth_set by fastforce

lemma poly_p_depth_set_polyderiv:
  "poly (polyderiv f) x  p_depth_set p n"
  if "n  0" and "set (coeffs f)  p_depth_set p n" and "x  p_depth_set p n"
  using that poly_p_depth_set_poly p_depth_set_polyderiv by blast

lemma poly_global_depth_set_polyderiv:
  "poly (polyderiv f) x  global_depth_set n"
  if  "n  0"
  and "set (coeffs f)  global_depth_set n"
  and "x  global_depth_set n"
  using that poly_p_depth_set_polyderiv global_depth_set by fastforce

lemma p_set_depth_additive_poly_poly:
  "set (coeffs f)  p_depth_set p m 
    set (coeffs (coeff (additive_poly_poly f) n))  p_depth_set p m"
  if "m  0"
proof (induct f arbitrary: n rule: pCons_induct')
  case (pCons0 a) thus ?case using additive_poly_poly_pCons0 by (cases n, fastforce, fastforce)
next
  case (pCons a f) show ?case
  proof (cases n)
    case 0 with pCons(1,3) show ?thesis by (simp add: additive_poly_poly_coeff0 cCons_def)
  next
    case (Suc k)
    moreover have
      "set (coeffs (pCons 0 1 * coeff (additive_poly_poly f) n + coeff (additive_poly_poly f) k))
         p_depth_set p m"
      by (
        intro p_depth_set_poly_add p_depth_set_poly_times_ideal that pCons(2),
        simp add: p_depth_set_0 p_depth_set_1, use pCons(1,3) in auto
      )
    ultimately show
      "set (coeffs (coeff (additive_poly_poly (pCons a f)) n))  p_depth_set p m"
      using pCons(1) additive_poly_poly_pCons coeff_add coeff_smult coeff_pCons_Suc by fastforce
  qed
qed simp

lemma global_depth_set_additive_poly_poly:
  "set (coeffs (coeff (additive_poly_poly f) n))  global_depth_set m"
  if "m  0" and "set (coeffs f)  global_depth_set m"
  using that p_set_depth_additive_poly_poly global_depth_set by fastforce

lemma poly_p_depth_set_additive_poly_poly:
  "poly (coeff (additive_poly_poly f) n) x  p_depth_set p m"
  if "m  0" and "set (coeffs f)  p_depth_set p m" and "x  p_depth_set p m"
  using that poly_p_depth_set_poly p_set_depth_additive_poly_poly by auto

lemma poly_global_depth_set_additive_poly_poly:
  "poly (coeff (additive_poly_poly f) n) x  global_depth_set m"
  if  "m  0"
  and "set (coeffs f)  global_depth_set m"
  and "x  global_depth_set m"
  using that poly_p_depth_set_additive_poly_poly global_depth_set by fastforce

lemma sum_poly_additive_poly_poly_depth_bound:
  fixes p :: 'a and f :: "'b poly" and x y :: 'b and a b n :: nat
  defines "g  λi. poly (coeff (additive_poly_poly f) (Suc i)) x * y ^ (Suc i)"
  defines "S  sum g {a..b}"
  assumes coeffs: "set (coeffs f)  p_depth_set p 0"
    and   arg   : "x  p_depth_set p 0"
    and   sum   : "¬ p_equal p S 0"
  obtains j where "j  {a..b}" and "p_depth p S  int (Suc j) * p_depth p y"
proof-
  from S_def sum obtain j where j:
    "j  {a..b}" "¬ p_equal p (g j) 0" "p_depth p S  p_depth p (g j)"
    using obtains_depth_sum_nonarch_witness by blast
  from g_def j(2)
    have  y_coeff_n0: "¬ p_equal p (poly (coeff (additive_poly_poly f) (Suc j)) x) 0"
    using mult_0_left[of p _ "y ^ Suc j"]
    by    blast
  from g_def j(2,3) have
    "p_depth p (g j) =
      p_depth p (poly (coeff (additive_poly_poly f) (Suc j)) x) +
      int (Suc j) * p_depth p y"
    using depth_mult_additive[of p _ "y ^ Suc j"] depth_pow_additive[of p y "Suc j"] by auto
  also from coeffs arg
    have  "  int (Suc j) * p_depth p y"
    using y_coeff_n0 poly_p_depth_set_additive_poly_poly[of 0 f p x "Suc j"]
          p_depth_setD[of p "poly (coeff (additive_poly_poly f) (Suc j)) x" 0]
    by    auto
  finally have "p_depth p (g j)  int (Suc j) * p_depth p y" by blast
  with j(3) have "p_depth p S  int (Suc j) * p_depth p y" by simp
  with j(1) that show thesis by blast
qed

end (* locale p_equality_depth_no_zero_divisors_1 *)

subsubsection ‹Depth of inverses and division›

locale p_equality_depth_div_inv =
  p_equality_div_inv + p_equality_depth_no_zero_divisors_1
begin

lemma inverse_depth: "p_depth p (inverse x) = - p_depth p x"
proof (cases "p_equal p x 0")
  case False
  from False have "p_depth p (x * inverse x) = 0"
    using depth_equiv right_inverse_equiv depth_of_1 by metis
  moreover from False have "¬ p_equal p (x * inverse x) 0"
    using right_inverse_equiv trans_right one_p_nequal_zero by blast
  ultimately show ?thesis using depth_mult_additive by simp
qed (simp add: inverse_equiv0_iff depth_equiv_0)

lemma depth_powi_additive: "p_depth p (power_int x n) = n * p_depth p x"
proof (cases "n  0")
  case True thus ?thesis using depth_pow_additive unfolding power_int_def by auto
next
  case False
  moreover from this have "(- int (nat (-n))) = n" by simp
  ultimately show ?thesis
    using power_int_def inverse_pow inverse_depth depth_pow_additive minus_mult_left by metis
qed

lemma p_depth_set_inverse: "inverse x  p_depth_set p 0" if "p_depth p x = 0"
  using that inverse_depth p_depth_setI by force

lemma divide_depth: "p_depth p (x / y) = p_depth p x - p_depth p y" if "¬ p_equal p (x / y) 0"
  using that by (simp add: divide_inverse depth_mult_additive inverse_depth)

lemma p_depth_set_divide:
  "x / y  p_depth_set p n" if "x  p_depth_set p n" and "p_depth p y = 0"
  by (
    auto  intro   : p_depth_setI
          simp add: that p_depth_set_equiv0 divide_equiv_0_iff p_depth_setD divide_depth
  )

lemma p_depth_poly_deriv_quotient:
  "p_depth p ((poly f a) / (poly (polyderiv f) a))  n"
  if    "n  0"
  and   "set (coeffs f)  p_depth_set p n"
  and   "a  p_depth_set p n"
  and   "¬ p_equal p (poly f a) 0"
  and   "¬ p_equal p (poly (polyderiv f) a) 0"
  and   "p_depth p (poly f a)  2 * p_depth p (poly (polyderiv f) a)"
  using that divide_equiv_0_iff divide_depth poly_p_depth_set_polyderiv poly_p_depth_set_poly
        p_depth_setD[of p "poly (polyderiv f) a" n]
  by    force

lemma p_depth_set_poly_deriv_quotient:
  "(poly f a) / (poly (polyderiv f) a)  p_depth_set p n"
  if    "n  0"
  and   "set (coeffs f)  p_depth_set p n"
  and   "a  p_depth_set p n"
  and   "¬ p_equal p (poly f a) 0"
  and   "¬ p_equal p (poly (polyderiv f) a) 0"
  and   "p_depth p (poly f a)  2 * p_depth p (poly (polyderiv f) a)"
  using that divide_equiv_0_iff p_depth_poly_deriv_quotient p_depth_setI
  by    presburger

end (* locale p_equality_depth_div_inv *)


subsection ‹Global equality and restriction of places›

subsubsection ‹Locales›

locale global_p_equality = p_equality
+
  fixes   p_restrict :: "'b::comm_ring  ('a  bool)  'b"
  assumes p_restrict_equiv      : "P p  p_equal p (p_restrict x P) x"
  and     p_restrict_equiv0     : "¬ P p  p_equal p (p_restrict x P) 0"
begin

lemma p_restrict_equiv_sym: "P p  p_equal p x (p_restrict x P)"
  using p_restrict_equiv sym by presburger

lemma p_restrict_equiv0_iff:
  "p_equal p (p_restrict x P) 0  p_equal p x 0  ¬ P p"
  by (metis p_restrict_equiv p_restrict_equiv0 trans trans_right)

lemma p_restrict_restrict_equiv_conj:
  "p_equal p (p_restrict (p_restrict x P) Q) (p_restrict x (λp. P p  Q p))"
proof-
  consider (both) "Q p" "P p" | (Q) "Q p" "¬ P p" | (nQ) "¬ Q p" by blast
  thus ?thesis
  proof cases
    case both thus ?thesis using p_restrict_equiv trans trans_left by metis
  next
    case Q thus ?thesis using p_restrict_equiv p_restrict_equiv0 trans trans_left by metis
  next
    case nQ thus ?thesis using p_restrict_equiv0 trans_left by metis
  qed
qed

lemma p_restrict_restrict_equiv: "p_equal p (p_restrict (p_restrict x P) P) (p_restrict x P)"
  using p_restrict_restrict_equiv_conj[of p x P P] by metis

lemma p_restrict_0_equiv0: "p_equal p (p_restrict 0 P) 0"
  by (metis p_restrict_equiv p_restrict_equiv0)

lemma p_restrict_add_equiv: "p_equal p (p_restrict (x + y) P) (p_restrict x P + p_restrict y P)"
  by (
    cases "P p", metis p_restrict_equiv sym add trans,
    metis p_restrict_equiv0 trans_left add_0_left
  )

lemma p_restrict_add_mixed_equiv:
  "p_equal p (p_restrict x P + p_restrict y Q) (x + y)" if "P p" and "Q p"
  using that p_restrict_equiv add by simp

lemma p_restrict_add_mixed_equiv_drop_right:
  "p_equal p (p_restrict x P + p_restrict y Q) x" if "P p" and "¬ Q p"
  using that p_restrict_equiv[of P p x] p_restrict_equiv0[of Q p y] add by fastforce

lemma p_restrict_add_mixed_equiv_drop_left:
  "p_equal p (p_restrict x P + p_restrict y Q) y" if "¬ P p" and "Q p"
  using that p_restrict_equiv[of Q p y] p_restrict_equiv0[of P p x] add by fastforce

lemma p_restrict_add_mixed_equiv_drop_both:
  "p_equal p (p_restrict x P + p_restrict y Q) 0" if "¬ P p" and "¬ Q p"
  using that p_restrict_equiv0[of P p x] p_restrict_equiv0[of Q p y] add by fastforce

lemma p_restrict_uminus_equiv: "p_equal p (p_restrict (-x) P) (- p_restrict x P)"
  by (
    cases "P p", metis p_restrict_equiv sym uminus trans,
    metis p_restrict_equiv0 uminus trans_left minus_zero
  )

lemma p_restrict_minus_equiv: "p_equal p (p_restrict (x - y) P) (p_restrict x P - p_restrict y P)"
  by (
    cases "P p", metis p_restrict_equiv sym minus trans,
    metis p_restrict_equiv0 minus trans_left diff_zero
  )

lemma p_restrict_minus_mixed_equiv:
  "p_equal p (p_restrict x P - p_restrict y Q) (x - y)" if "P p" and "Q p"
  using that p_restrict_equiv minus by simp

lemma p_restrict_minus_mixed_equiv_drop_right:
  "p_equal p (p_restrict x P - p_restrict y Q) x" if "P p" and "¬ Q p"
  using that p_restrict_equiv[of P p x] p_restrict_equiv0[of Q p y] minus by fastforce

lemma p_restrict_minus_mixed_equiv_drop_left:
  "p_equal p (p_restrict x P - p_restrict y Q) (-y)" if "¬ P p" and "Q p"
  using that p_restrict_equiv[of Q p y] p_restrict_equiv0[of P p x] minus by fastforce

lemma p_restrict_minus_mixed_equiv_drop_both:
  "p_equal p (p_restrict x P - p_restrict y Q) 0" if "¬ P p" and "¬ Q p"
  using that p_restrict_equiv0[of P p x] p_restrict_equiv0[of Q p y] minus by fastforce

lemma p_restrict_times_equiv:
  "p_equal p ((p_restrict x P) * (p_restrict y Q))
    (p_restrict (x * y) (λp. P p  Q p))"
proof-
  consider (both) "P p" "Q p" | (P) "P p" "¬ Q p" | (nP) "¬ P p" by blast
  thus ?thesis
  proof cases
    case both thus ?thesis using p_restrict_equiv sym mult trans by metis
  next
    case P thus ?thesis
      using p_restrict_equiv p_restrict_equiv0 mult trans_left mult_zero_right by metis
  next
    case nP thus ?thesis
      using p_restrict_equiv p_restrict_equiv0 mult trans_left mult_zero_left by metis
  qed
qed

lemma p_restrict_times_equiv':
  "p_equal p ((p_restrict x P) * (p_restrict y P)) (p_restrict (x * y) P)"
  using p_restrict_times_equiv[of p x P y P] by simp

lemma p_restrict_p_equalI:
  "p_equal p (p_restrict x P) y"
  if  "P p  p_equal p x y"
  and "¬ P p  p_equal p y 0"
  by  (
    cases "P p", metis that(1) p_restrict_equiv trans, metis that(2) p_restrict_equiv0 trans_left
  )

lemma p_restrict_p_equal_p_restrictI:
  "p_equal p (p_restrict x P) (p_restrict y Q)"
  if  "(P p  Q p)    p_equal p x y"
  and "(P p  ¬ Q p)  p_equal p x 0"
  and "(¬ P p  Q p)  p_equal p y 0"
proof (cases "P p" "Q p" rule: case_split[case_product case_split])
  case True_True thus ?thesis using that(1) p_restrict_equiv sym cong by metis
next
  case True_False thus ?thesis using that(2) p_restrict_equiv p_restrict_equiv0 sym cong by metis
next
  case False_True thus ?thesis using that(3) p_restrict_equiv p_restrict_equiv0 sym cong by metis
next
  case False_False thus ?thesis using p_restrict_equiv0 refl cong by metis
qed

lemma p_restrict_p_equal_p_restrict_by_sameI:
  "p_equal p (p_restrict x P) (p_restrict y P)" if "P p  p_equal p x y"
  using that p_restrict_p_equal_p_restrictI by auto

end (* locale global_p_equality *)


locale global_p_equality_div_inv =
  p_equality_div_inv p_equal + global_p_equality p_equal p_restrict
  for p_equal    ::
    "'a  'b::{comm_ring_1, inverse, divide_trivial} 
      'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
begin

lemma inverse_p_restrict_equiv: "p_equal p (inverse (p_restrict x P)) (p_restrict (inverse x) P)"
  by (
    cases "p_equal p x 0  ¬ P p",
    metis p_restrict_equiv0_iff inverse_equiv0_iff trans_left,
    intro inverse_p_unique, metis p_restrict_times_equiv' p_restrict_equiv right_inverse_equiv trans
  )

end (* locale global_p_equality_div_inv *)


locale global_p_equal = global_p_equality
+ assumes global_imp_eq: "globally_p_equal x y  x = y"
begin

lemma global_eq_iff: "globally_p_equal x y  x = y"
  using global_imp_eq by fastforce

lemma p_restrict_true[simp] : "p_restrict x (λx. True) = x"
  using p_restrict_equiv[of "λx. True"] global_imp_eq by blast

lemma p_restrict_false[simp]: "p_restrict x (λx. False) = 0"
  using p_restrict_equiv0[of "λx. False"] global_imp_eq by blast

lemma p_restrict_restrict:
  "p_restrict (p_restrict x P) Q = p_restrict x (λp. P p  Q p)"
  using p_restrict_restrict_equiv_conj global_imp_eq by blast

lemma p_restrict_restrict'[simp]: "p_restrict (p_restrict x P) P = p_restrict x P"
  using p_restrict_restrict by fastforce

lemma p_restrict_image_restrict: "p_restrict x P = x" if "x  (λz. p_restrict z P) ` B"
proof-
  from that obtain z where "x = p_restrict z P" by blast
  thus ?thesis using p_restrict_restrict' by auto
qed

lemma p_restrict_zero: "p_restrict 0 P = 0"
  using p_restrict_0_equiv0 global_imp_eq by blast

lemma p_restrict_add: "p_restrict (x + y) P = p_restrict x P + p_restrict y P"
  using p_restrict_add_equiv global_imp_eq by blast

lemma p_restrict_uminus: "p_restrict (-x) P = - p_restrict x P"
  using p_restrict_uminus_equiv global_imp_eq by blast

lemma p_restrict_minus: "p_restrict (x - y) P = p_restrict x P - p_restrict y P"
  using p_restrict_minus_equiv global_imp_eq by blast

lemma p_restrict_times:
  "(p_restrict x P) * (p_restrict y Q) = p_restrict (x * y) (λp. P p  Q p)"
  using p_restrict_times_equiv global_imp_eq by blast

lemma times_p_restrict: "x * (p_restrict y P) = p_restrict (x * y) P"
  using p_restrict_times[of x "λp. True"] by auto

lemmas p_restrict_pre_finite_adele_simps =
  p_restrict_zero p_restrict_add p_restrict_uminus p_restrict_minus p_restrict_times

lemma p_equal_0_iff_p_restrict_eq_0: "p_equal p x 0  p_restrict x ((=) p) = 0"
proof (standard, rule global_imp_eq, standard)
  fix q show "p_equal p x 0  p_equal q (p_restrict x ((=) p)) 0"
    using p_restrict_equiv[of "(=) p"] trans p_restrict_equiv0[of "(=) p"]
    by    (cases "p = q", blast, blast)
qed (metis (full_types) p_restrict_equiv_sym)

lemma p_equal_iff_p_restrict_eq:
  "p_equal p x y  p_restrict x ((=) p) = p_restrict y ((=) p)"
  using conv_0 p_equal_0_iff_p_restrict_eq_0 p_restrict_minus by auto

lemma p_restrict_eqI:
  "y = p_restrict x P"
  if      P: "p. P p  p_equal p y x"
  and not_P: "p. ¬ P p  p_equal p y 0"
proof (intro global_imp_eq, standard)
  fix p :: 'a show "p_equal p y (p_restrict x P)"
    by (cases "P p", metis P p_restrict_equiv trans_left, metis not_P p_restrict_equiv0 trans_left)
qed

lemma p_restrict_eq_p_restrict_mixedI:
  "p_restrict x P = p_restrict y Q"
  if  both: "p::'a. P p  Q p  p_equal p x y"
  and P   : "p::'a. P p  ¬ Q p  p_equal p x 0"
  and Q   : "p::'a. ¬ P p  Q p  p_equal p y 0"
  by (
    intro p_restrict_eqI, metis both Q p_restrict_equiv trans p_restrict_equiv0 trans_left,
    metis P p_restrict_equiv trans p_restrict_equiv0
  )

lemma p_restrict_eq_p_restrictI:
  "p_restrict x P = p_restrict y P" if "p::'a. P p  p_equal p x y"
  by (intro p_restrict_eqI, metis that trans p_restrict_equiv, simp add: p_restrict_equiv0)

lemma p_restrict_eq_p_restrict_iff:
  "p_restrict x P = p_restrict y P 
    (p::'a. P p  p_equal p x y)"
  by (standard, safe, metis p_restrict_equiv trans_right, simp add: p_restrict_eq_p_restrictI)

lemma p_restrict_eq_zero_iff:
  "p_restrict x P = 0  (p. P p  p_equal p x 0)"
proof-
  have "(p_restrict x P = 0) = (p. p_equal p (p_restrict x P) 0)"
    using global_imp_eq by auto
  thus ?thesis by (metis p_restrict_equiv p_restrict_equiv0 trans trans_right)
qed

lemma p_restrict_decomp:
  "x = p_restrict x P + p_restrict x (λp. ¬ P p)"
proof (intro global_imp_eq, standard)
  fix p show "p_equal p x (p_restrict x P + p_restrict x (λp. ¬ P p))"
    using p_restrict_add_mixed_equiv_drop_left p_restrict_add_mixed_equiv_drop_right sym by metis
qed

lemma restrict_complement:
  "range (λx. p_restrict x P) + range (λx. p_restrict x (λp. ¬ P p))
    = UNIV"
  using p_restrict_decomp[of _ P]
        set_plus_intro[of
          "p_restrict _ P" "range (λx. p_restrict x P)"
          "p_restrict _ (λp. ¬ P p)"
          "range (λx. p_restrict x (λp. ¬ P p))"
        ]
  by    fastforce

end (* locale global_p_equal *)


locale global_p_equal_no_zero_divisors =
  global_p_equal p_equal p_restrict + p_equality_no_zero_divisors p_equal
  for p_equal :: "'a  'b::comm_ring  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"


locale global_p_equal_no_zero_divisors_1 =
  global_p_equal_no_zero_divisors p_equal p_restrict + p_equality_no_zero_divisors_1 p_equal
  for p_equal :: "'a  'b::comm_ring_1  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
begin

lemma pow_eq_0_iff: "x ^ n = 0  x = 0  n  0" for x :: 'b
  using global_eq_iff pow_equiv_0_iff[of _ x n] by force

end (* locale global_p_equal_no_zero_divisors_1 *)


locale global_p_equal_div_inv =
  global_p_equality_div_inv p_equal p_restrict
+ global_p_equal p_equal p_restrict
  for p_equal    ::
    "'a  'b::{comm_ring_1, inverse, divide_trivial} 
      'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
begin

lemma power_int_eq_0_iff:
  "power_int x n = 0  x = 0  n  0" for x :: 'b
  using global_eq_iff power_int_equiv_0_iff[of _ x n] by fastforce

lemma inverse_eq0_iff: "inverse x = 0  x = 0" for x :: 'b
  using inverse_equiv0_iff global_eq_iff by fastforce

lemma inverse_neg_1 [simp]: "inverse (-1::'b) = -1"
  using global_imp_eq globally_inverse_neg_1 by force

lemma inverse_uminus: "inverse (-x) = - inverse x" for x :: 'b
  using global_imp_eq inverse_uminus_equiv by blast

lemma global_right_inverse: "x * inverse x = 1" if "p. ¬ p_equal p x 0"
  using that global_imp_eq right_inverse_equiv by fast

lemma right_inverse: "x * inverse x = p_restrict 1 (λp. ¬ p_equal p x 0)"
proof (intro global_imp_eq, standard)
  fix p :: 'a show "p_equal p (x * inverse x) (p_restrict 1 (λp. ¬ p_equal p x 0))"
  by (
    cases "p_equal p x 0", metis inverse_equiv0_iff mult_0_right p_restrict_equiv0 trans_left,
    metis right_inverse_equiv p_restrict_equiv trans_left
  )
qed

lemma global_left_inverse: "inverse x * x = 1" if "p. ¬ p_equal p x 0"
  using that global_imp_eq left_inverse_equiv by fast

lemma left_inverse: "inverse x * x = p_restrict 1 (λp. ¬ p_equal p x 0)"
  by (metis right_inverse mult.commute)

lemma inverse_unique: "inverse x = y" if "x * y = 1" for x y :: 'b
  using that inverse_p_unique global_imp_eq[of "inverse x" y] by fastforce

lemma inverse_p_restrict: "inverse (p_restrict x P) = p_restrict (inverse x) P"
  using global_imp_eq inverse_p_restrict_equiv by blast

lemma power_diff_conv_inverse:
  "x ^ (n - m) =  x ^ n * inverse x ^ m" if "m < n"
  for x :: 'b
proof (intro global_imp_eq, standard)
  fix p show "p_equal p (x ^ (n - m)) (x ^ n * inverse x ^ m)"
  proof (cases "p_equal p x 0")
    case True
    from that True have "p_equal p (x ^ n * inverse x ^ m) 0"
      using pow_equiv_0_iff inverse_equiv0_iff mult_0_left mult_0_right by force
    moreover from that True have "p_equal p (x ^ (n - m)) 0" using pow_equiv_0_iff by fastforce
    ultimately show ?thesis using trans_left by fast
  next
    case False with that show ?thesis using power_diff_conv_inverse_equiv by simp
  qed
qed

lemma power_int_add_1: "power_int x (m + 1) = power_int x m * x" if "m  -1" for x :: 'b
  using that power_int_add_1_equiv by (auto intro: global_imp_eq)

lemma power_int_add:
  "power_int x (m + n) = power_int x m * power_int x n"
  if "(p. ¬ p_equal p x 0)  m + n  0" for x :: 'b
  using that power_int_add_equiv by (auto intro: global_imp_eq)

lemma power_int_diff:
  "power_int x (m - n) = power_int x m / power_int x n" if "m  n" for x :: 'b
  using that power_int_diff_equiv by (auto intro: global_imp_eq)

lemma power_int_minus_mult: "power_int x (n - 1) * x = power_int x n" if "n  0" for x :: 'b
  using that power_int_minus_mult_equiv by (auto intro: global_imp_eq)

lemma power_int_not_eq_zero:
  "x  0  n = 0  power_int x n  0" for x :: 'b
  by (subst power_int_eq_0_iff) auto

lemma minus_divide_right: "a / (- b) = - (a / b)" for a b :: 'b
  using global_imp_eq minus_divide_right_equiv by blast

lemma minus_divide_divide: "(- a) / (- b) = a / b" for a b :: 'b
  using global_imp_eq minus_divide_divide_equiv by blast

lemma divide_minus1 [simp]: "x / (-1) = - x" for x :: 'b
  using global_imp_eq divide_minus1_equiv by blast

lemma divide_self: "x / x = p_restrict 1 (λp. ¬ p_equal p x 0)"
  by (metis divide_inverse right_inverse)

lemma global_divide_self: "x / x = 1" if "p. ¬ p_equal p x 0"
  by (metis that global_right_inverse divide_inverse)

lemma global_mult_divide_mult_cancel_right:
  "(a * b) / (c * b) = a / c" if "p. ¬ p_equal p b 0"
  using that global_imp_eq mult_divide_mult_cancel_right[of _ b a c] by fastforce

lemma global_mult_divide_mult_cancel_left:
  "(c * a) / (c * b) = a / b" if "p. ¬ p_equal p c 0"
  by (metis that global_mult_divide_mult_cancel_right mult.commute)

lemma divide_p_restrict_left: "(p_restrict x P) / y = p_restrict (x / y) P"
  using p_restrict_eqI[of P "(p_restrict x P) / y"] p_restrict_equiv[of P _ x]
        p_restrict_equiv0[of P _ x] divide_left_equiv
  by    fastforce

lemma divide_p_restrict_right: "x / (p_restrict y P) = p_restrict (x / y) P"
  using p_restrict_eqI[of P "x / (p_restrict y P)"] p_restrict_equiv[of P _ y]
        p_restrict_equiv0[of P _ y] divide_right_equiv
  by    fastforce

lemma inj_divide_right:
  "inj (λb. b / a)  (p. ¬ p_equal p a 0)"
  unfolding inj_def
proof (standard, safe)
  fix p
  assume  inj: "x y :: 'b. x / a = y / a  x = y"
    and   a  : "p_equal p a 0"
  from a have "(p_restrict 1 ((=) p)) / a = (p_restrict 0 ((=) p)) / a"
    by (simp add: p_restrict_eq_p_restrictI divide_equiv_0_iff divide_p_restrict_left)
  with inj show False using p_restrict_eq_p_restrict_iff one_p_nequal_zero by blast
next
  fix x y :: 'b
  assume a: "p. ¬ p_equal p a 0 " and xy: "x / a = y / a"
  thus "x = y"
    using times_divide_eq_right[of a x a] times_divide_eq_right[of a y a]
          mult_divide_cancel_left[of _ a x] mult_divide_cancel_left[of _ a y]
          cong global_imp_eq[of x y]
    by    fastforce
qed

end (* locale global_p_equal_div_inv *)


locale global_p_equality_depth = global_p_equality + p_equality_depth
begin

lemma globally_p_equal_p_depth: "globally_p_equal x y  p_depth p x = p_depth p y"
  using depth_equiv globally_p_equalD by simp

lemma p_restrict_depth:
  "P p  p_depth p (p_restrict x P) = p_depth p x"
  "¬ P p  p_depth p (p_restrict x P) = 0"
  by (metis p_restrict_equiv depth_equiv, metis p_restrict_equiv0 depth_equiv_0)

lemma p_restrict_add_mixed_depth_drop_right:
  "p_depth p (p_restrict x P + p_restrict y Q) = p_depth p x" if "P p" and "¬ Q p"
  using that p_restrict_add_mixed_equiv_drop_right depth_equiv by auto

lemma p_restrict_add_mixed_depth_drop_left:
  "p_depth p (p_restrict x P + p_restrict y Q) = p_depth p y" if "¬ P p" and "Q p"
  using that p_restrict_add_mixed_equiv_drop_left depth_equiv by auto

lemma p_depth_set_p_restrict:
  "p_restrict x P  p_depth_set p n" if "P p  x  p_depth_set p n"
proof (rule p_depth_set_by_equivI)
  show "p_equal p (p_restrict x P) (if P p then x else 0)"
    using p_restrict_equiv p_restrict_equiv0 by simp
  from that show "(if P p then x else 0)  p_depth_set p n"
    using p_depth_set_0 by auto
qed

lemma global_depth_set_p_restrict:
  "p_restrict x P  global_depth_set n" if "x  global_depth_set n"
  using that p_depth_set_p_restrict global_depth_set by auto

lemma p_depth_set_closed_under_p_restrict_image:
  "(λx. p_restrict x P) ` p_depth_set p n  p_depth_set p n"
  using p_depth_set_p_restrict by blast

lemma global_depth_set_closed_under_p_restrict_image:
  "(λx. p_restrict x P) ` global_depth_set n  global_depth_set n"
  using global_depth_set_p_restrict by blast

lemma global_depth_set_p_restrictI:
  "p_restrict x P  global_depth_set n"
  if "p. P p  x  p_depth_set p n"
  by (
    intro global_depth_setI,
    metis that p_restrict_equiv p_restrict_equiv0 trans p_depth_setD p_restrict_depth(1)
  )

lemma p_depth_set_image_under_one_p_restrict_image:
  "(λx. p_restrict x ((=) p)) ` p_depth_set p n  global_depth_set n"
  using global_depth_set_p_restrictI by force

end (* locale global_p_equality_depth *)


locale global_p_equality_depth_no_zero_divisors_1 =
  p_equality_depth_no_zero_divisors_1 p_equal p_depth
+ global_p_equality_depth p_equal p_restrict p_depth
  for p_equal    ::
    "'a  'b::comm_ring_1  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
  and p_depth    :: "'a  'b  int"


locale global_p_equality_depth_div_inv =
  p_equality_depth_div_inv + global_p_equality_depth
begin

sublocale global_p_equality_div_inv ..

lemma global_depth_set_inverse:
  "p_restrict (inverse x) (λp. p_depth p x = 0)  global_depth_set 0"
  using global_depth_set_p_restrictI p_depth_set_inverse by simp

lemma global_depth_set_divide:
  "p_restrict (x / y) (λp. p_depth p y = 0)  global_depth_set n"
  if "x  global_depth_set n"
  using that global_depth_set_p_restrictI global_depth_set p_depth_set_divide by auto

lemma global_depth_set_divideI:
  "p_restrict (x / y) (λp. p_depth p y = 0)  global_depth_set n"
  if "p. p_depth p y = 0  x  p_depth_set p n"
  using that global_depth_set_p_restrictI global_depth_set p_depth_set_divide by auto

end


locale global_p_equal_depth = global_p_equal + global_p_equality_depth
begin

lemma p_depth_set_vs_global_depth_set_image_under_one_p_restrict_image:
  "(λx. p_restrict x ((=) p)) ` p_depth_set p n =
    (λx. p_restrict x ((=) p)) `global_depth_set n"
proof (standard, standard, clarify)
  fix x assume "x  p_depth_set p n"
  hence "p_restrict x ((=) p)  global_depth_set n"
    using p_depth_set_image_under_one_p_restrict_image by fast
  thus "p_restrict x ((=) p)  (λx. p_restrict x ((=) p)) `global_depth_set n"
    using p_restrict_restrict'[of x "(=) p", symmetric] by blast
qed (use global_depth_set in fast)

end

locale global_p_equal_depth_no_zero_divisors =
  global_p_equal_no_zero_divisors + p_equality_depth_no_zero_divisors
begin
sublocale global_p_equal_depth ..
end


locale global_p_equal_depth_div_inv =
  global_p_equality_depth_div_inv + global_p_equal_depth_no_zero_divisors
begin

sublocale global_p_equal_div_inv ..

lemma p_depth_set_eq_coset:
  "p_depth_set p n = (w powi n) *o (p_depth_set p 0)"
  if "p_depth p w = 1" and "p. ¬ p_equal p w 0"
  unfolding elt_set_times_def
proof safe
  fix x assume "x  p_depth_set p n"
  moreover define y where "y  w powi (-n) * x"
  ultimately have "y  p_depth_set p 0"
    using that(1) mult_0_right p_depth_setD depth_mult_additive depth_powi_additive
    by    (intro p_depth_setI, clarify, fastforce)
  moreover from y_def have "x = w powi n * y"
    using that(2) power_int_equiv_0_iff global_right_inverse power_int_minus
    by    (force simp flip: mult.assoc)
  ultimately show "yp_depth_set p 0. x = w powi n * y" by metis
next
  fix x assume "x  p_depth_set p 0"
  thus "w powi n * x  p_depth_set p n"
    using that(1) mult_0_right p_depth_setD depth_mult_additive depth_powi_additive
    by    (intro p_depth_setI, clarify, fastforce)
qed

lemma global_depth_set_eq_coset:
  "global_depth_set n = (w powi n) *o (global_depth_set 0)" if "p. p_depth p w = 1"
  unfolding elt_set_times_def
proof safe
  fix x assume x: "x  global_depth_set n"
  define y where "y  w powi (-n) * x"
  from that have "p. ¬ p_equal p w 0" using depth_equiv_0 by fastforce
  with y_def have "x = w powi n * y"
    using power_int_equiv_0_iff global_right_inverse power_int_minus mult.assoc mult_1_left by metis
  moreover from x y_def have "y  global_depth_set 0"
    using that mult_0_right global_depth_setD depth_mult_additive depth_powi_additive
    by    (intro global_depth_setI, fastforce)
  ultimately show "yglobal_depth_set 0. x = w powi n * y" by metis
next
  fix x assume "x  global_depth_set 0"
  thus "w powi n * x  global_depth_set n"
    using that mult_0_right global_depth_setD depth_mult_additive depth_powi_additive
    by    (intro global_depth_setI, fastforce)
qed

lemma p_depth_set_eq_coset':
  "p_depth_set p 0 = (w powi (-n)) *o (p_depth_set p n)"
  if "p_depth p w = 1" and "p. ¬ p_equal p w 0"
proof-
  from that(2) have "w powi (-n) * w powi n = 1"
    using power_int_minus power_int_equiv_0_iff global_left_inverse by simp
  with that show ?thesis
    using p_depth_set_eq_coset set_times_rearrange2 power_int_minus set_one_times by metis
qed

lemma global_depth_set_eq_coset':
  "global_depth_set 0 = (w powi (-n)) *o (global_depth_set n)" if "p. p_depth p w = 1"
proof-
  from that have "p. ¬ p_equal p w 0" using depth_equiv_0 by fastforce
  hence "w powi (-n) * w powi n = 1"
    using power_int_minus power_int_equiv_0_iff global_left_inverse by presburger
  with that show ?thesis
    using global_depth_set_eq_coset set_times_rearrange2 power_int_minus set_one_times by metis
qed

end (* locale global_p_equal_depth_div_inv *)


subsubsection ‹Embedding of base type constants›

locale global_p_equality_w_consts = global_p_equality p_equal p_restrict
  for p_equal    ::
    "'a  'b::comm_ring_1  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
+
  fixes func_of_consts :: "('a  'c::ring_1)  'b"
  assumes consts_1    [simp]: "func_of_consts 1 = 1"
    and   consts_diff [simp]: "func_of_consts (f - g) = func_of_consts f - func_of_consts g"
    and   consts_mult [simp]:
      "func_of_consts (f * g) = func_of_consts f * func_of_consts g"
begin

abbreviation "consts  func_of_consts"
abbreviation "const a  consts (λp. a)"

lemma consts_0[simp]: "consts 0 = 0"
  using consts_diff[of 0 0] by force

lemma const_0[simp]: "const 0 = 0"
  using consts_0 unfolding zero_fun_def by simp

lemma const_1[simp]: "const 1 = 1"
  using consts_1 unfolding one_fun_def by simp

lemma consts_uminus [simp]: "consts (- f) = - consts f"
  using consts_diff[of 0 f] by fastforce

lemma const_uminus [simp]: "const (-a) = - const a"
  using consts_uminus unfolding fun_Compl_def by auto

lemma const_diff [simp]: "const (a - b) = const a - const b"
  using consts_diff unfolding fun_diff_def by auto

lemma consts_add [simp]: "consts (f + g) = consts f + consts g"
proof-
  have "consts f + consts g = consts (f - (-g))" by (simp flip: diff_minus_eq_add)
  thus ?thesis by simp
qed

lemma const_add [simp]: "const (a + b) = const a + const b"
  using consts_add unfolding plus_fun_def by auto

lemma const_mult [simp]: "const (a * b) = const a * const b"
  using consts_mult unfolding times_fun_def by auto

lemma const_of_nat: "const (of_nat n) = of_nat n"
  by (induct n) simp_all

lemma const_of_int: "const (of_int z) = of_int z"
  by (cases z rule: int_cases2, simp_all add: const_of_nat)

end (* locale global_p_equality_w_consts *)


locale global_p_equal_w_consts =
  global_p_equal p_equal p_restrict
+ global_p_equality_w_consts p_equal p_restrict func_of_consts
  for p_equal ::
    "'a  'b::comm_ring_1  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
  and func_of_consts :: "('a  'c::ring_1)  'b"


locale global_p_equality_w_inj_consts = global_p_equality_w_consts
+
  assumes p_equal_func_of_consts_0: "p_equal p (consts f) 0  f p = 0"
begin

lemmas p_equal_func_of_const_0 = p_equal_func_of_consts_0[of _ "λp. _"]

lemma p_equal_func_of_consts:
  "p_equal p (consts f) (consts g)  f p = g p"
  by (metis conv_0 consts_diff p_equal_func_of_consts_0 fun_diff_def right_minus_eq)

lemma globally_p_equal_func_of_consts:
  "globally_p_equal (func_of_consts f) (func_of_consts g) 
    (p. f p = g p)"
  using p_equal_func_of_consts unfolding globally_p_equal_def by auto

lemma const_p_equal: "p_equal p (const a) (const b)  a = b"
  using p_equal_func_of_consts by force

end (* locale global_p_equality_w_inj_consts *)


locale global_p_equality_w_inj_consts_char_0 =
  global_p_equality_w_inj_consts p_equal p_restrict func_of_consts
  for p_equal        ::
    "'a  'b::comm_ring_1  'b  bool"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and func_of_consts :: "('a  'c::ring_char_0)  'b"
begin

lemma const_of_nat_p_equal_0_iff: "p_equal p (of_nat n) 0  n = 0"
  by (metis const_of_nat p_equal_func_of_const_0 of_nat_eq_0_iff)

lemma const_of_int_p_equal_0_iff: "p_equal p (of_int z) 0  z = 0"
  by (metis const_of_int of_int_eq_0_iff p_equal_func_of_const_0)

end (* locale global_p_equality_w_inj_consts_char_0 *)


locale global_p_equality_div_inv_w_inj_consts =
  p_equality_div_inv + global_p_equality_w_inj_consts
begin

lemma consts_divide_self_equiv: "p_equal p ((consts f) / (consts f)) 1" if "f p  0"
  using that
  by    (simp add: p_equal_func_of_consts divide_self_equiv flip: consts_0)

lemma const_divide_self_equiv: "p_equal p ((const c) / (const c)) 1" if "c  0"
  using that by (simp add: consts_divide_self_equiv)

end (* locale global_p_equality_div_inv_w_consts *)


locale global_p_equal_w_inj_consts =
  global_p_equal p_equal p_restrict
+ global_p_equality_w_inj_consts p_equal p_restrict
  for p_equal    ::
    "'a  'b::comm_ring_1  'b  bool"
  and p_restrict :: "'b  ('a  bool)  'b"
begin

lemma consts_eq_iff: "consts f = consts g  f = g"
  using global_eq_iff globally_p_equal_func_of_consts by blast

lemma consts_eq_0_iff: "consts f = 0  f = 0"
  using consts_eq_iff by force

lemma inj_consts: "inj consts"
  using consts_eq_iff injI by meson

lemma const_eq_iff: "const a = const b  a = b"
  by (metis consts_eq_iff)

lemma const_eq_0_iff: "const a = 0  a = 0"
  using consts_eq_0_iff unfolding zero_fun_def by metis

lemma inj_const: "inj const"
  using const_eq_iff injI by meson

end (* locale global_p_equal_w_inj_consts *)


locale global_p_equal_div_inv_w_inj_consts =
  global_p_equal_div_inv + global_p_equal_w_inj_consts
begin

sublocale global_p_equality_div_inv_w_inj_consts ..

lemma consts_divide_self:
  "(consts f) / (consts f) = p_restrict 1 (λp::'a. f p  0)"
  by (simp add: p_equal_func_of_consts_0 divide_self)

lemma const_right_inverse [simp]: "const a * inverse (const a) = 1" if "a  0"
  using that global_right_inverse p_equal_func_of_const_0 by blast

lemma const_left_inverse [simp]: "inverse (const a) * const a = 1" if "a  0"
  by (metis that const_right_inverse mult.commute)

lemma const_divide_self: "(const c) / (const c) = 1" if "c  0"
  using that by (simp add: divide_inverse)

lemma mult_const_divide_mult_const_cancel_right:
  "(y * const a) / (z * const a) = y / z" if "a  0"
  using that global_mult_divide_mult_cancel_right p_equal_func_of_const_0 by blast

lemma mult_const_divide_mult_const_cancel_left:
  "(const a * y) / (const a * z) = y / z" if "a  0"
  by (metis that mult_const_divide_mult_const_cancel_right mult.commute)

lemma mult_const_divide_mult_const_cancel_left_right:
  "(const a * y) / (z * const a) = y / z" if "a  0"
  by (metis that mult.commute mult_const_divide_mult_const_cancel_left)

lemma mult_const_divide_mult_const_cancel_right_left:
  "(y * const a) / (const a * z) = y / z" if "a  0"
  by (metis that mult.commute mult_const_divide_mult_const_cancel_left)

end (* locale global_p_equal_div_inv_w_consts *)


locale global_p_equal_div_inv_w_inj_consts_char_0 =
  global_p_equality_w_inj_consts_char_0 p_equal p_restrict func_of_consts
+ global_p_equal_div_inv_w_inj_consts p_equal p_restrict func_of_consts
  for p_equal        ::
    "'a  'b::{comm_ring_1, ring_char_0, inverse, divide_trivial} 
      'b  bool"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and func_of_consts :: "('a  'c::ring_char_0)  'b"
begin

context
  fixes n :: nat
  assumes "n  0"
begin

lemma left_inverse_const_of_nat[simp]: "inverse (of_nat n :: 'b) * of_nat n = 1"
  by (metis n  0 const_of_nat const_left_inverse of_nat_eq_0_iff)

lemma right_inverse_const_of_nat[simp]: "of_nat n * inverse (of_nat n :: 'b) = 1"
  by (metis left_inverse_const_of_nat mult.commute)

lemma divide_self_const_of_nat[simp]: "(of_nat n :: 'b) / of_nat n = 1"
  by (metis n  0 of_nat_eq_0_iff const_of_nat const_divide_self)

lemma mult_const_of_nat_divide_const_mult_of_nat_cancel_right:
  "(a * of_nat n) / (b * of_nat n) = a / b" for a b :: 'b
  using n  0 const_of_nat of_nat_eq_0_iff
        mult_const_divide_mult_const_cancel_right
  by    metis

lemma mult_const_of_nat_divide_const_mult_of_nat_cancel_left:
  "(of_nat n * a) / (of_nat n * b) = a / b" for a b :: 'b
  by (metis mult_const_of_nat_divide_const_mult_of_nat_cancel_right mult.commute)

end (* context nonzero nat *)

context
  fixes z :: int
  assumes "z  0"
begin

lemma left_inverse_const_of_int[simp]: "inverse (of_int z :: 'b) * of_int z = 1"
  by (metis z  0 const_of_int const_left_inverse of_int_eq_0_iff)

lemma right_inverse_const_of_int[simp]: "of_int z * inverse (of_int z :: 'b) = 1"
  by (metis left_inverse_const_of_int mult.commute)

lemma divide_self_const_of_int[simp]: "(of_int z :: 'b) / of_int z = 1"
  by (metis z  0 of_int_eq_0_iff const_of_int const_divide_self)

lemma mult_const_of_int_divide_const_mult_of_int_cancel_right:
  "(a * of_int z) / (b * of_int z) = a / b" for a b :: 'b
  by (
    metis z  0 const_of_int of_int_eq_0_iff
          mult_const_divide_mult_const_cancel_right
  )

lemma mult_const_of_int_divide_const_mult_of_int_cancel_left:
  "(of_int z * a) / (of_int z * b) = a / b" for a b :: 'b
  by (metis mult_const_of_int_divide_const_mult_of_int_cancel_right mult.commute)

end (* context nonzero int *)

context
  includes rat.lifting
begin

lift_definition const_of_rat :: "rat  'b"
  is "λx. of_int (fst x) / of_int (snd x)"
  unfolding ratrel_def
proof (clarify, unfold fst_conv snd_conv)
  fix a b c d :: int
  assume ratrel: "b  0" "d  0" "a * d = c * b"
  define oi :: "int  'b" where "oi  of_int"
  moreover from this ratrel(1,3) have "(oi a / oi b) * (oi d / oi d) = oi c / oi d"
    by (metis of_int_mult times_divide_eq_right divide_self_const_of_int mult_1_right mult.commute)
  ultimately show "oi a / oi b = oi c / oi d" using ratrel(2) by fastforce
qed

lemma inj_const_of_rat: "inj const_of_rat"
proof
  fix x y show "const_of_rat x = const_of_rat y  x = y"
  proof (transfer, clarify, unfold ratrel_iff fst_conv snd_conv, clarify)
    fix a b c d :: int
    define oi :: "int  'b" where "oi  of_int"
    assume ratrel: "b  0" "d  0" "oi a / oi b = oi c / oi d"
    from oi_def ratrel(1,3) have "oi a * oi d = (oi d / oi d) * oi c * oi b"
      by (metis swap_numerator divide_self_const_of_int mult_1_right mult.assoc mult.commute)
    with oi_def ratrel(2) show "a * d = c * b" using of_int_eq_iff by fastforce
  qed
qed

lemma const_of_rat_rat: "const_of_rat (Rat.Fract a b) = (of_int a :: 'b) / of_int b"
  by transfer simp

lemma const_of_rat_0 [simp]: "const_of_rat 0 = 0"
  by transfer simp

lemma const_of_rat_1 [simp]: "const_of_rat 1 = 1"
  by transfer simp

lemma const_of_rat_minus: "const_of_rat (- a) = - const_of_rat a"
  by (transfer, simp add: minus_divide_left)

lemma const_of_rat_add: "const_of_rat (a + b) = const_of_rat a + const_of_rat b"
proof (transfer, clarify, unfold fst_conv snd_conv of_int_add of_int_mult)
  fix a b c d :: int
  assume b: "b  0" and d: "d  0"
  define w x y z :: 'b
    where "w  of_int a" and "x  of_int b"
    and   "y  of_int c" and "z  of_int d"
  from d z_def have "(w * z + y * x) / (x * z) = w / x + (y * x) / (x * z)"
    by (
      metis add_divide_distrib mult_const_of_int_divide_const_mult_of_int_cancel_right
    )
  with b x_def show "(w * z + y * x) / (x * z) = w / x + y / z"
    by (metis mult.commute mult_const_of_int_divide_const_mult_of_int_cancel_right)
qed

lemma const_of_rat_mult: "const_of_rat (a * b) = const_of_rat a * const_of_rat b"
  by (transfer, simp, metis times_divide_times_eq)

lemma const_of_rat_p_equal_0_iff: "p_equal p (const_of_rat a) 0  a = 0"
  by (transfer fixing: p_equal p, simp, metis divide_equiv_0_iff const_of_int_p_equal_0_iff)

end (* context rat.lifting *)

lemma const_of_rat_eq_0_iff [simp]: "const_of_rat a = 0  a = 0"
  using const_of_rat_p_equal_0_iff global_eq_iff by fastforce

lemma const_of_rat_neg_one [simp]: "const_of_rat (- 1) = -1"
  by (simp add: const_of_rat_minus)

lemma const_of_rat_diff: "const_of_rat (a - b) = const_of_rat a - const_of_rat b"
  using const_of_rat_add[of a "-b"] by (simp add: const_of_rat_minus)

lemma const_of_rat_sum: "const_of_rat (aA. f a) = (aA. const_of_rat (f a))"
  by (induct rule: infinite_finite_induct) (auto simp: const_of_rat_add)

lemma const_of_rat_prod: "const_of_rat (aA. f a) = (aA. const_of_rat (f a))"
  by (induct rule: infinite_finite_induct) (auto simp: const_of_rat_mult)

lemma const_of_rat_inverse: "const_of_rat (inverse a) = inverse (const_of_rat a)"
  by (cases "a = 0", simp, rule inverse_unique[symmetric], simp flip: const_of_rat_mult)

lemma left_inverse_const_of_rat[simp]:
  "inverse (const_of_rat a) * const_of_rat a = 1" if "a  0"
  by (metis that const_of_rat_inverse const_of_rat_mult Fields.left_inverse const_of_rat_1)

lemma right_inverse_const_of_rat[simp]:
  "const_of_rat a * inverse (const_of_rat a) = 1" if "a  0"
  by (metis that left_inverse_const_of_rat mult.commute)

lemma const_of_rat_divide: "const_of_rat (a / b) = const_of_rat a / const_of_rat b"
  by (metis Fields.divide_inverse const_of_rat_mult const_of_rat_inverse divide_inverse)

lemma divide_self_const_of_rat[simp]: "(const_of_rat a) / (const_of_rat a) = 1" if "a  0"
  by (metis that divide_inverse right_inverse_const_of_rat)

lemma const_of_rat_power: "const_of_rat (a ^ n) = (const_of_rat a) ^ n"
  by (induct n) (simp_all add: const_of_rat_mult)

lemma const_of_rat_eq_iff [simp]: "const_of_rat a = const_of_rat b  a = b"
  by (metis inj_const_of_rat inj_def)

lemma zero_eq_const_of_rat_iff [simp]: "0 = const_of_rat a  0 = a"
  by simp

lemma const_of_rat_eq_1_iff [simp]: "const_of_rat a = 1  a = 1"
  using const_of_rat_eq_iff [of _ 1] by simp

lemma one_eq_const_of_rat_iff [simp]: "1 = const_of_rat a  1 = a"
  by simp

text ‹Collapse nested embeddings.›
lemma const_of_rat_of_nat_eq [simp]: "const_of_rat (of_nat n) = of_nat n"
  by (induct n) (simp_all add: const_of_rat_add)

lemma const_of_rat_of_int_eq [simp]: "const_of_rat (of_int z) = of_int z"
  by (cases z rule: int_diff_cases) (simp add: const_of_rat_diff)

text ‹Product of all p-adic embeddings of the field of rationals.›
definition range_const_of_rat :: "'b set" ("p")
  where "p = range const_of_rat"

lemma range_const_of_rat_cases [cases set: range_const_of_rat]:
  assumes "q  p"
  obtains (const_of_rat) r where "q = const_of_rat r"
proof -
  from assms have "q  range const_of_rat" by (simp only: range_const_of_rat_def)
  then obtain r where "q = const_of_rat r" ..
  then show thesis ..
qed

lemma range_const_of_rat_cases':
  assumes "x  p"
  obtains a b where "b > 0" "coprime a b" "x = of_int a / of_int b"
proof -
  from assms obtain r where "x = const_of_rat r"
    by (auto simp: range_const_of_rat_def)
  obtain a b where quot: "quotient_of r = (a,b)" by force
  have "b > 0" using quotient_of_denom_pos[OF quot] .
  moreover have "coprime a b" using quotient_of_coprime[OF quot] .
  moreover have "x = of_int a / of_int b" unfolding x = const_of_rat r
      quotient_of_div[OF quot] by (simp add: const_of_rat_divide)
  ultimately show ?thesis using that by blast
qed

lemma range_const_of_rat_of_rat [simp]: "const_of_rat r  p"
  by (simp add: range_const_of_rat_def)

lemma range_const_of_rat_of_int [simp]: "of_int z  p"
  by (subst const_of_rat_of_int_eq [symmetric]) (rule range_const_of_rat_of_rat)

lemma Ints_subset_range_const_of_rat: "  p"
  using Ints_cases range_const_of_rat_of_int by blast

lemma range_const_of_rat_of_nat [simp]: "of_nat n  p"
  by (subst const_of_rat_of_nat_eq [symmetric]) (rule range_const_of_rat_of_rat)

lemma Nats_subset_range_const_of_rat: "  p"
  using Ints_subset_range_const_of_rat Nats_subset_Ints by blast

lemma range_const_of_rat_0 [simp]: "0  p"
  unfolding range_const_of_rat_def by (rule range_eqI, rule const_of_rat_0 [symmetric])

lemma range_const_of_rat_1 [simp]: "1  p"
  unfolding range_const_of_rat_def by (rule range_eqI, rule const_of_rat_1 [symmetric])

lemma range_const_of_rat_add [simp]:
  "a  p 
    b  p 
    a + b  p"
  by (metis range_const_of_rat_cases range_const_of_rat_of_rat const_of_rat_add)

lemma range_const_of_rat_minus_iff [simp]:
  "- a  p 
    a  p"
  by (
    metis range_const_of_rat_cases range_const_of_rat_of_rat add.inverse_inverse
          const_of_rat_minus
  )

lemma range_const_of_rat_diff [simp]:
  "a  p 
    b  p 
    a - b  p"
  by (metis range_const_of_rat_add range_const_of_rat_minus_iff diff_conv_add_uminus)

lemma range_const_of_rat_mult [simp]:
  "a  p 
    b  p 
    a * b  p"
  by (metis range_const_of_rat_cases range_const_of_rat_of_rat const_of_rat_mult)

lemma range_const_of_rat_inverse [simp]:
  "a  p 
    inverse a  p"
  by (metis range_const_of_rat_cases range_const_of_rat_of_rat const_of_rat_inverse)

lemma range_const_of_rat_divide [simp]:
  "a  p 
    b  p 
    a / b  p"
  by (simp add: divide_inverse)

lemma range_const_of_rat_power [simp]:
  "a  p 
    a ^ n  p"
  by (metis range_const_of_rat_cases range_const_of_rat_of_rat const_of_rat_power)

lemma range_const_of_rat_sum [intro]:
  "(x. x  A  f x  p) 
    sum f A  p"
  by (induction A rule: infinite_finite_induct) auto

lemma range_const_of_rat_prod [intro]:
  "(x. x  A  f x  p)
     prod f A  p"
  by (induction A rule: infinite_finite_induct) auto

lemma range_const_of_rat_induct [case_names const_of_rat, induct set: range_const_of_rat]:
  "q  p 
    (r. P (const_of_rat r))  P q"
  by (rule range_const_of_rat_cases) auto

lemma p_adic_Rats_p_equal_0_iff:
  "a  p 
    p_equal p a 0  a = 0"
  by (induct a rule: range_const_of_rat_induct, simp, rule const_of_rat_p_equal_0_iff)

lemma range_const_of_rat_infinite: "¬ finite p"
  by (auto dest!: finite_imageD simp: inj_on_def infinite_UNIV_char_0 range_const_of_rat_def)

lemma left_inverse_range_const_of_rat:
  "a  p  a  0 
    inverse a * a = 1"
  by (
    induct a rule: range_const_of_rat_induct, rule left_inverse_const_of_rat,
    simp add: const_of_rat_p_equal_0_iff
  )

lemma right_inverse_range_const_of_rat:
  "a  p  a  0 
    a * inverse a = 1"
  by (metis left_inverse_range_const_of_rat mult.commute)

lemma divide_self_range_const_of_rat:
  "a  p  a  0 
    a / a = 1"
  by (metis divide_inverse right_inverse_range_const_of_rat)

lemma range_const_of_rat_add_iff:
  "a  p 
    b  p 
    a + b  p 
      a  p  b  p"
  by (metis range_const_of_rat_add range_const_of_rat_diff add_diff_cancel add_diff_cancel_left')

lemma range_const_of_rat_diff_iff:
  "a  p 
    b  p 
      a - b  p 
      a  p  b  p"
  by (metis range_const_of_rat_add_iff diff_add_cancel)

lemma range_const_of_rat_mult_iff:
  "a * b  p 
    a  p  b  p"
  if
    "a  p - {0} 
      b  p - {0}"
proof-
  have *:
    "a b :: 'b.
      a  p - {0} 
      a * b  p 
        b  p"
  proof
    fix a b :: 'b
    assume  "a  p - {0}"
      and   "a * b  p"
    thus "b  p"
      using range_const_of_rat_mult[of "inverse a"] left_inverse_range_const_of_rat[of a]
      by    (fastforce simp flip: mult.assoc)
  qed simp
  show ?thesis
  proof (cases "a  p - {0}")
    case True thus ?thesis using *[of a b] by fast
  next
    case False
    with that have "b  p - {0}" by blast
    moreover from this have
      "a * b  p 
        a  p"
      by (metis * mult.commute)
    ultimately show ?thesis by blast
  qed
qed

lemma range_const_of_rat_inverse_iff [simp]:
  "inverse a  p 
    a  p"
  by (metis range_const_of_rat_inverse inverse_inverse)

lemma range_const_of_rat_divide_iff:
  "a / b  p 
      a  p  b  p"
  if
    "a  p - {0} 
      b  p - {0}"
  using that range_const_of_rat_inverse inverse_inverse[of b] range_const_of_rat_inverse_iff
        divide_inverse[of a b] range_const_of_rat_mult_iff[of a "inverse b"]
  by    force

end (* locale global_p_equal_div_inv_w_inj_consts_char_0 *)

lemma Fract_eq0_iff: "Fraction_Field.Fract a b = 0  a = 0  b = 0"
  by transfer simp

locale global_p_equal_div_inv_w_inj_idom_consts =
  global_p_equal_div_inv_w_inj_consts p_equal p_restrict func_of_consts
+ global_p_equality_w_inj_consts p_equal p_restrict func_of_consts
  for p_equal        ::
    "'a  'b::{comm_ring_1, inverse, divide_trivial} 
      'b  bool"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and func_of_consts :: "('a  'c::idom)  'b"
begin

lift_definition const_of_fract :: "'c fract  'b"
  is "λx::'c × 'c. const (fst x) / const (snd x)"
proof (clarify, unfold fractrel_iff fst_conv snd_conv, clarify)
  fix a b c d :: 'c
  assume fractrel: "b  0" "d  0" "a * d = c * b"
  from fractrel(1,3) have "(const a / const b) * (const d / const d) = const c / const d"
    by (metis const_mult times_divide_eq_right const_divide_self mult_1_right swap_numerator)
  with fractrel(2) show "const a / const b = const c / const d"
    using const_divide_self by fastforce
qed

lemma inj_const_of_fract: "inj const_of_fract"
proof
  fix x y show "const_of_fract x = const_of_fract y  x = y"
  proof (transfer, clarify, unfold fractrel_iff fst_conv snd_conv, clarify)
    fix a b c d :: 'c
    assume fractrel: "b  0" "d  0" "const a / const b = const c / const d"
    from fractrel(1,3) have "const d * const a = const d * (const c / const d * const b)"
      by (metis swap_numerator const_divide_self mult_1_right)
    with fractrel(2) have "d * a = c * b"
      by (metis mult.assoc swap_numerator const_divide_self mult_1_left const_mult const_eq_iff)
    thus "a * d = c * b" by algebra
  qed
qed

lemma const_of_fract_0 [simp]: "const_of_fract 0 = 0"
  by transfer simp

lemma const_of_fract_1 [simp]: "const_of_fract 1 = 1"
  by transfer simp

lemma const_of_fract_Fract: "const_of_fract (Fraction_Field.Fract a b) = const a / const b"
  by transfer simp

lemma const_of_fract_p_eq_0_iff [simp]: "p_equal p (const_of_fract x) 0  x = 0"
  by  (cases x)
      (simp add: const_of_fract_Fract divide_equiv_0_iff p_equal_func_of_const_0 Fract_eq0_iff)

lemma const_of_fract_equal_0_iff [simp]: "const_of_fract x = 0  x = 0"
  by (simp flip: global_eq_iff)

lemma const_of_fract_minus: "const_of_fract (- a) = - const_of_fract a"
  by (transfer, simp add: minus_divide_left fun_Compl_def)

lemma const_of_fract_add: "const_of_fract (a + b) = const_of_fract a + const_of_fract b"
  by (
    transfer, clarify,
    simp add: add_divide_distrib mult_const_divide_mult_const_cancel_right
              mult_const_divide_mult_const_cancel_right_left
  )

lemma const_of_fract_mult: "const_of_fract (a * b) = const_of_fract a * const_of_fract b"
  by (transfer, simp add: times_divide_times_eq)

lemma const_of_fract_neg_one [simp]: "const_of_fract (- 1) = -1"
  by (simp add: const_of_fract_minus)

lemma const_of_fract_diff: "const_of_fract (a - b) = const_of_fract a - const_of_fract b"
  using const_of_fract_add[of a "-b"] by (simp add: const_of_fract_minus)

lemma const_of_fract_sum:
  "const_of_fract (aA. f a) = (aA. const_of_fract (f a))"
  by (induct rule: infinite_finite_induct) (auto simp: const_of_fract_add)

lemma const_of_fract_prod:
  "const_of_fract (aA. f a) = (aA. const_of_fract (f a))"
  by (induct rule: infinite_finite_induct) (auto simp: const_of_fract_mult)

lemma const_of_fract_inverse: "const_of_fract (inverse a) = inverse (const_of_fract a)"
  by (cases "a = 0", simp, rule inverse_unique[symmetric], simp flip: const_of_fract_mult)

lemma left_inverse_const_of_fract[simp]:
  "inverse (const_of_fract a) * const_of_fract a = 1" if "a  0"
  by (metis that const_of_fract_inverse const_of_fract_mult Fields.left_inverse const_of_fract_1)

lemma right_inverse_const_of_fract[simp]:
  "(const_of_fract a) * inverse (const_of_fract a) = 1" if "a  0"
  by (metis that left_inverse_const_of_fract mult.commute)

lemma const_of_fract_divide: "const_of_fract (a / b) = const_of_fract a / const_of_fract b"
  by (metis divide_inverse const_of_fract_mult const_of_fract_inverse Fields.divide_inverse)

lemma p_adic_prod_divide_self_of_fract[simp]:
  "(const_of_fract a) / (const_of_fract a) = 1" if "a  0"
  by (metis that divide_inverse right_inverse_const_of_fract)

lemma const_of_fract_power: "const_of_fract (a ^ n) = (const_of_fract a) ^ n"
  by (induct n) (simp_all add: const_of_fract_mult)

lemma const_of_fract_eq_iff [simp]:
  "const_of_fract a = const_of_fract b  a = b"
  by (metis inj_const_of_fract inj_def)

lemma zero_eq_const_of_fract_iff [simp]: "0 = const_of_fract a  0 = a"
  by simp

lemma const_of_fract_eq_1_iff [simp]: "const_of_fract a = 1  a = 1"
  using const_of_fract_eq_iff [of _ 1] by simp

lemma one_eq_const_of_fract_iff [simp]: "1 = const_of_fract a  1 = a"
  by simp

text ‹Collapse nested embeddings.›
lemma const_of_fract_numer_eq_const [simp]: "const_of_fract (Fraction_Field.Fract a 1) = const a"
  by (metis const_of_fract_Fract const_1 div_by_1)

lemma const_of_fract_of_nat_eq [simp]: "const_of_fract (of_nat n) = of_nat n"
  by (induct n) (simp_all add: const_of_fract_add)

lemma const_of_fract_of_int_eq [simp]: "const_of_fract (of_int z) = of_int z"
  by (cases z rule: int_diff_cases) (simp add: const_of_fract_diff)

text ‹Product of all p-adic embeddings of the field of fractions of the base ring.›
definition range_const_of_fract :: "'b set" ("𝒬p")
  where "𝒬p = range const_of_fract"

lemma range_const_of_fract_cases [cases set: range_const_of_fract]:
  assumes "q  𝒬p"
  obtains (const_of_fract) r where "q = const_of_fract r"
proof -
  from assms have "q  range const_of_fract" by (simp only: range_const_of_fract_def)
  then obtain r where "q = const_of_fract r" ..
  then show thesis ..
qed

lemma range_const_of_fract_cases':
  assumes "x  𝒬p"
  obtains a b where "b  0" "x = const a / const b"
proof -
  from assms obtain r where "x = const_of_fract r" by (auto simp: range_const_of_fract_def)
  moreover obtain a b where Fract: "b  0" "r = Fraction_Field.Fract a b"
    using Fract_cases by meson
  ultimately have "x = const a / const b" using const_of_fract_Fract by blast
  with that Fract(1) show thesis by fast
qed

lemma range_const_of_fract_of_fract [simp]:
  "const_of_fract r  𝒬p"
  by (simp add: range_const_of_fract_def)

lemma range_const_of_fract_const [simp]: "const a  𝒬p"
  unfolding range_const_of_fract_def
  by        (standard, rule const_of_fract_numer_eq_const[symmetric], simp)

lemma range_const_of_fract_of_int [simp]: "of_int z  𝒬p"
  by (subst const_of_fract_of_int_eq [symmetric]) (rule range_const_of_fract_of_fract)

lemma Ints_subset_range_const_of_fract: "  𝒬p"
  using Ints_cases range_const_of_fract_of_int by blast

lemma range_const_of_fract_of_nat [simp]: "of_nat n  𝒬p"
  by (subst const_of_fract_of_nat_eq [symmetric]) (rule range_const_of_fract_of_fract)

lemma Nats_subset_range_const_of_fract: "  𝒬p"
  using Ints_subset_range_const_of_fract Nats_subset_Ints by blast

lemma range_const_of_fract_0 [simp]: "0  𝒬p"
  unfolding range_const_of_fract_def by (rule range_eqI, rule const_of_fract_0 [symmetric])

lemma range_const_of_fract_1 [simp]: "1  𝒬p"
  unfolding range_const_of_fract_def by (rule range_eqI, rule const_of_fract_1 [symmetric])

lemma range_const_of_fract_add [simp]:
  "a  𝒬p 
    b  𝒬p 
    a + b  𝒬p"
  by (metis range_const_of_fract_cases range_const_of_fract_of_fract const_of_fract_add)

lemma range_const_of_fract_minus_iff [simp]:
  "- a  𝒬p 
    a  𝒬p"
  by (
    metis range_const_of_fract_cases range_const_of_fract_of_fract add.inverse_inverse
          const_of_fract_minus
  )

lemma range_const_of_fract_diff [simp]:
  "a  𝒬p 
    b  𝒬p 
    a - b  𝒬p"
  by (metis range_const_of_fract_add range_const_of_fract_minus_iff diff_conv_add_uminus)

lemma range_const_of_fract_mult [simp]:
  "a  𝒬p 
    b  𝒬p 
    a * b  𝒬p"
  by (metis range_const_of_fract_cases range_const_of_fract_of_fract const_of_fract_mult)

lemma range_const_of_fract_inverse [simp]:
  "a  𝒬p 
    inverse a  𝒬p"
  by (metis range_const_of_fract_cases range_const_of_fract_of_fract const_of_fract_inverse)

lemma range_const_of_fract_divide [simp]:
  "a  𝒬p 
    b  𝒬p 
    a / b  𝒬p"
  by (simp add: divide_inverse)

lemma range_const_of_fract_power [simp]:
  "a  𝒬p 
    a ^ n  𝒬p"
  by (metis range_const_of_fract_cases range_const_of_fract_of_fract const_of_fract_power)

lemma range_const_of_fract_sum [intro]:
  "(x. x  A 
    f x  𝒬p) 
    sum f A  𝒬p"
  by (induction A rule: infinite_finite_induct) auto

lemma range_const_of_fract [intro]:
  "(x. x  A  f x  𝒬p)
     prod f A  𝒬p"
  by (induction A rule: infinite_finite_induct) auto

lemma range_const_of_fract_induct [case_names const_of_fract, induct set: range_const_of_fract]:
  "q  𝒬p 
    (r. P (const_of_fract r))  P q"
  by (rule range_const_of_fract_cases) auto

lemma p_adic_Fracts_p_equal_0_iff:
  "a  𝒬p 
    p_equal p a 0  a = 0"
  by (induct a rule: range_const_of_fract_induct, simp)

lemma range_const_of_fract_infinite:
  "infinite 𝒬p" if "infinite (UNIV :: 'c set)"
  using that finite_imageD[OF _ inj_const] range_const_of_fract_const
        finite_subset[of "range const"]
  by    blast

lemma left_inverse_range_const_of_fract:
  "a  𝒬p  a  0 
    inverse a * a = 1"
  by (induct a rule: range_const_of_fract_induct, rule left_inverse_const_of_fract, simp)

lemma right_inverse_range_const_of_fract:
  "a  𝒬p  a  0 
    a * inverse a = 1"
  by (metis left_inverse_range_const_of_fract mult.commute)

lemma divide_self_range_const_of_fract:
  "a  𝒬p  a  0 
    a / a = 1"
  by (metis divide_inverse right_inverse_range_const_of_fract)

lemma range_const_of_fract_add_iff:
  "a  𝒬p 
    b  𝒬p 
    a + b  𝒬p 
      a  𝒬p  b  𝒬p"
  using range_const_of_fract_add range_const_of_fract_diff add_diff_cancel add_diff_cancel_left'
  by    metis

lemma range_const_of_fract_diff_iff:
  "a  𝒬p 
    b  𝒬p 
      a - b  𝒬p 
      a  𝒬p  b  𝒬p"
  by (metis range_const_of_fract_add_iff diff_add_cancel)

lemma range_const_of_fract_mult_iff:
  "a * b  𝒬p 
    a  𝒬p  b  𝒬p"
  if
    "a  𝒬p - {0} 
      b  𝒬p - {0}"
proof-
  have *:
    "a b :: 'b.
      a  𝒬p - {0} 
      a * b  𝒬p 
        b  𝒬p"
  proof
    fix a b :: 'b
    assume  "a  𝒬p - {0}"
      and   "a * b  𝒬p"
    thus "b  𝒬p"
      using range_const_of_fract_mult[of "inverse a"] left_inverse_range_const_of_fract[of a]
      by    (fastforce simp flip: mult.assoc)
  qed simp
  show ?thesis
  proof (cases "a  𝒬p - {0}")
    case True thus ?thesis using *[of a b] by fast
  next
    case False
    with that have "b  𝒬p - {0}" by blast
    moreover from this have
      "a * b  𝒬p 
        a  𝒬p"
      by (metis * mult.commute)
    ultimately show ?thesis by blast
  qed
qed

lemma range_const_of_fract_inverse_iff [simp]:
  "inverse a  𝒬p 
    a  𝒬p"
  by (metis range_const_of_fract_inverse inverse_inverse)

lemma range_const_of_fract_divide_iff:
  "a / b  𝒬p 
      a  𝒬p  b  𝒬p"
  if
    "a  𝒬p - {0} 
      b  𝒬p - {0}"
  using that range_const_of_fract_inverse inverse_inverse[of b]
        range_const_of_fract_inverse_iff divide_inverse[of a b]
        range_const_of_fract_mult_iff[of a "inverse b"]
  by    force

end (* locale global_p_equal_div_inv_w_idom_consts *)

locale global_p_equality_w_inj_index_consts = global_p_equality_w_inj_consts
+
  fixes index_inj :: "'a  'c"
  assumes index_inj  : "inj index_inj"
  and index_inj_nzero: "index_inj p  0"
begin

abbreviation "global_uniformizer  consts index_inj"
abbreviation "index_const p  const (index_inj p)"

lemma global_uniformizer_locally_uniformizes: "p_equal p global_uniformizer (index_const p)"
  using p_equal_func_of_consts by auto

lemma index_const_globally_nequiv_0: "¬ p_equal q (index_const p) 0"
  using p_equal_func_of_consts_0 index_inj_nzero by fast

lemma global_uniformizer_globally_nequiv_0: "¬ p_equal p global_uniformizer 0"
  using p_equal_func_of_consts_0 index_inj_nzero by simp

end (* locale global_p_equality_w_inj_index_consts *)

locale global_p_equality_no_zero_divisors_1_w_inj_index_consts =
  global_p_equality_w_inj_index_consts + p_equality_no_zero_divisors_1
begin

lemma index_const_pow_globally_nequiv_0: "¬ p_equal q (index_const p ^ n) 0"
  using index_const_globally_nequiv_0 pow_equiv_0_iff by fast

lemma global_uniformizer_pow_globally_nequiv_0: "¬ p_equal q (global_uniformizer ^ n) 0"
  using global_uniformizer_globally_nequiv_0 pow_equiv_0_iff by blast

end

locale global_p_equality_depth_w_inj_index_consts =
  p_equality_depth p_equal p_depth
+ global_p_equality_w_inj_index_consts p_equal p_restrict func_of_consts index_inj
  for p_equal        :: "'a  'b::comm_ring_1  'b  bool"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and p_depth        :: "'a  'b  int"
  and func_of_consts :: "('a  'c::{factorial_semiring,ring_1})  'b"
  and index_inj      :: "'a  'c"
+
  assumes p_depth_func_of_consts:
    "p_depth p (func_of_consts f) = int (multiplicity (index_inj p) (f p))"
  and index_inj_nunit  : "¬ is_unit (index_inj p)"
  and index_inj_coprime:
    "p  q  multiplicity (index_inj p) (index_inj q) = 0"
begin

lemma p_depth_set_consts: "func_of_consts f  p_depth_set p 0"
  using p_depth_func_of_consts p_depth_setI by simp

lemma global_depth_set_consts: "func_of_consts f  global_depth_set 0"
  using p_depth_set_consts global_depth_set by blast

lemma p_depth_1[simp]: "p_depth p (1::'b) = 0"
  using p_depth_func_of_consts[of p 1] by simp

lemma p_depth_index_const: "p_depth p (index_const p) = 1"
  using p_depth_func_of_consts index_inj_nzero index_inj_nunit multiplicity_self by fastforce

lemma p_depth_index_const': "p_depth q (index_const p) = of_bool (q = p)"
  by (cases "q = p", simp_all add: p_depth_index_const p_depth_func_of_consts index_inj_coprime)

lemma p_depth_global_uniformizer: "p_depth p (global_uniformizer::'b) = 1"
  using p_depth_func_of_consts index_inj_nzero index_inj_nunit multiplicity_self by fastforce

lemma p_depth_consts_func_ge0: "p_depth p (consts f)  0"
  using p_depth_func_of_consts by fastforce

end (* locale global_p_equality_depth_w_inj_index_consts *)


locale global_p_equal_depth_no_zero_divisors_w_inj_index_consts =
  global_p_equal p_equal p_restrict
+ global_p_equality_depth_no_zero_divisors_1 p_equal p_restrict p_depth
+ global_p_equality_depth_w_inj_index_consts p_equal p_restrict p_depth func_of_consts index_inj
  for p_equal        :: "'a  'b::comm_ring_1  'b  bool"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and p_depth        :: "'a  'b  int"
  and func_of_consts :: "('a  'c::{factorial_semiring,ring_1})  'b"
  and index_inj      :: "'a  'c"
begin

sublocale global_p_equal_w_inj_consts ..
sublocale global_p_equal_depth_no_zero_divisors ..

lemma p_depth_index_const_pow: "p_depth p ((index_const p) ^ n) = int n"
  using p_depth_index_const depth_pow_additive by auto

lemma p_depth_index_const_pow': "p_depth q ((index_const p) ^ n) = int n * of_bool (q = p)"
  using p_depth_index_const' depth_pow_additive by fastforce

lemma p_depth_global_uniformizer_pow: "p_depth p (global_uniformizer ^ n) = int n"
  using p_depth_global_uniformizer depth_pow_additive by auto

end

locale global_p_equal_depth_div_inv_w_inj_index_consts =
  global_p_equal_depth_div_inv p_equal p_depth p_restrict
+ global_p_equal_depth_no_zero_divisors_w_inj_index_consts
    p_equal p_restrict p_depth func_of_consts index_inj
  for p_equal        ::
    "'a  'b::{comm_ring_1, inverse, divide_trivial} 
      'b  bool"
  and p_depth        :: "'a  'b  int"
  and p_restrict     :: "'b  ('a  bool)  'b"
  and func_of_consts :: "('a  'c::{factorial_semiring,ring_1})  'b"
  and index_inj      :: "'a  'c"
begin

lemma index_const_powi_globally_nequiv_0: "¬ p_equal q (index_const p powi n) 0"
  using index_const_globally_nequiv_0 power_int_equiv_0_iff by simp

lemma global_uniformizer_powi_globally_nequiv_0: "¬ p_equal q (global_uniformizer powi n) 0"
  using global_uniformizer_globally_nequiv_0 power_int_equiv_0_iff by blast

lemma index_const_powi_add:
  "(index_const p) powi (m + n) = (index_const p) powi m * (index_const p) powi n"
  using power_int_add index_const_globally_nequiv_0 by blast

lemma global_uniformizer_powi_add:
  "global_uniformizer powi (m + n) = global_uniformizer powi m * global_uniformizer powi n"
  using power_int_add global_uniformizer_globally_nequiv_0 by blast

lemma p_depth_index_const_powi: "p_depth p (index_const p powi n) = n"
  by  (cases "n  0")
      (simp_all add: power_int_def p_depth_index_const_pow inverse_depth flip: inverse_pow)

lemma p_depth_index_const_powi': "p_depth q (index_const p powi n) = n * of_bool (q = p)"
  by (
    cases "q = p", simp add: p_depth_index_const_powi, cases "n  0",
    simp_all add: power_int_def p_depth_index_const_pow' inverse_depth flip: inverse_pow
  )

lemma p_depth_global_uniformizer_powi: "p_depth p (global_uniformizer powi n) = n"
  by (
    cases "n  0",
    simp_all add: power_int_def p_depth_global_uniformizer_pow inverse_depth flip: inverse_pow
  )

lemma local_uniformizer_locally_uniformizes:
  "p_depth p ((index_const p) powi (-n) * x) = 0" if "p_depth p x = n"
  using that depth_equiv_0 mult_0_right index_const_powi_globally_nequiv_0 no_zero_divisors
        depth_mult_additive p_depth_index_const_powi
  by    (cases "p_equal p x 0") auto

lemma global_uniformizer_locally_uniformizes:
  "p_depth p (global_uniformizer powi (-n) * x) = 0" if "p_depth p x = n"
  using that depth_equiv_0 mult_0_right global_uniformizer_powi_globally_nequiv_0 no_zero_divisors
        depth_mult_additive p_depth_global_uniformizer_powi
  by    (cases "p_equal p x 0") auto

lemma p_depth_set_eq_index_const_coset:
  "p_depth_set p n = ((index_const p) powi n) *o (p_depth_set p 0)"
  using p_depth_set_eq_coset p_depth_index_const index_const_globally_nequiv_0 by blast

lemma p_depth_set_eq_index_const_coset':
  "p_depth_set p 0 = ((index_const p) powi (-n)) *o (p_depth_set p n)"
  using p_depth_set_eq_coset' p_depth_index_const index_const_globally_nequiv_0 by blast

lemma p_depth_set_eq_global_uniformizer_coset:
  "p_depth_set p n = (global_uniformizer powi n) *o (p_depth_set p 0)"
  using p_depth_set_eq_coset p_depth_global_uniformizer global_uniformizer_globally_nequiv_0
  by    blast

lemma p_depth_set_eq_global_uniformizer_coset':
  "p_depth_set p 0 = (global_uniformizer powi (-n)) *o (p_depth_set p n)"
  using p_depth_set_eq_coset' p_depth_global_uniformizer global_uniformizer_globally_nequiv_0
  by    blast

lemma global_depth_set_eq_global_uniformizer_coset:
  "global_depth_set n = (global_uniformizer powi n) *o (global_depth_set 0)"
  using global_depth_set_eq_coset p_depth_global_uniformizer by simp

lemma global_depth_set_eq_global_uniformizer_coset':
  "global_depth_set 0 = (global_uniformizer powi (-n)) *o (global_depth_set n)"
  using global_depth_set_eq_coset' p_depth_global_uniformizer by simp

definition shift_p_depth :: "'a  int  'b  'b"
  where "shift_p_depth p n x = x * (consts (λq. if q = p then index_inj p else 1)) powi n"

lemma shift_p_depth_0[simp]: "shift_p_depth p n 0 = 0"
  by (simp add: shift_p_depth_def)

lemma shift_p_depth_by_0[simp]: "shift_p_depth p 0 x = x"
  unfolding shift_p_depth_def by fastforce

lemma shift_p_depth_add[simp]:
  "shift_p_depth p n (x + y) = shift_p_depth p n x + shift_p_depth p n y"
  using shift_p_depth_def distrib_right[of x y] by presburger

lemma shift_p_depth_uminus[simp]:
  "shift_p_depth p n (- x) = - shift_p_depth p n x"
  using shift_p_depth_def mult_minus_left[of x] by presburger

lemma shift_p_depth_minus[simp]:
  "shift_p_depth p n (x - y) = shift_p_depth p n x - shift_p_depth p n y"
  using shift_p_depth_add[of p n x "-y"] by auto

lemma shift_p_depth_equiv_at_place: "p_equal p (shift_p_depth p n x) (x * (index_const p powi n))"
  using shift_p_depth_def p_equal_func_of_consts power_int_equiv_base mult_left by simp

lemma shift_p_depth_equiv_at_other_places:
  "p_equal q (shift_p_depth p n x) x" if "q  p"
  using that p_equal_func_of_consts[of q _ 1] power_int_equiv_base power_int_1_left
        shift_p_depth_def mult_left
  by    fastforce

lemma shift_p_depth_equiv0_iff:
  "p_equal q (shift_p_depth p n x) 0  p_equal q x 0"
  using shift_p_depth_equiv_at_place trans0_iff mult_equiv_0_iff power_int_equiv_0_iff
        index_const_globally_nequiv_0 shift_p_depth_equiv_at_other_places
  by (metis (full_types))

lemma shift_p_depth_equiv_iff:
  "p_equal q (shift_p_depth p n x) (shift_p_depth p n y) = p_equal q x y"
  using conv_0 shift_p_depth_minus shift_p_depth_equiv0_iff by metis

lemma shift_p_depth_trivial_iff:
  "shift_p_depth p n x = x  n = 0  p_equal p x 0"
proof
  assume "shift_p_depth p n x = x"
  hence "p_equal p (x * (1 - index_const p powi n)) 0"
    using shift_p_depth_equiv_at_place conv_0 mult_1_right right_diff_distrib by metis
  thus "n = 0  p_equal p x 0"
    using mult_equiv_0_iff conv_0 sym depth_equiv p_depth_1 p_depth_index_const_powi by metis
next
  assume "n = 0  p_equal p x 0"
  moreover have "n = 0  shift_p_depth p n x = x" by simp
  moreover have "p_equal p x 0  p_equal p (shift_p_depth p n x) x"
    using shift_p_depth_equiv_at_place mult_0_left trans trans_left by blast
  ultimately have "p_equal p (shift_p_depth p n x) x" by fastforce
  thus "shift_p_depth p n x = x" using global_imp_eq shift_p_depth_equiv_at_other_places by force
qed

lemma shift_p_depth_times_right: "shift_p_depth p n (x * y) = x * shift_p_depth p n y"
proof (intro global_imp_eq, standard)
  fix q show "p_equal q (shift_p_depth p n (x * y)) (x * shift_p_depth p n y)"
  proof (cases "q = p")
    case True thus "p_equal q (shift_p_depth p n (x * y)) (x * shift_p_depth p n y)"
      using shift_p_depth_equiv_at_place[of p n "x * y"] mult.assoc[of x y]
            shift_p_depth_equiv_at_place mult_left sym trans
      by    presburger
  qed (use shift_p_depth_equiv_at_other_places mult_left sym trans in fast)
qed

lemma shift_p_depth_times_left: "shift_p_depth p n (x * y) = shift_p_depth p n x * y"
  using shift_p_depth_times_right mult.commute by metis

lemma shift_shift_p_depth: "shift_p_depth p n (shift_p_depth p m x) = shift_p_depth p (m + n) x"
proof (intro global_imp_eq, standard)
  fix q show "p_equal q (shift_p_depth p n (shift_p_depth p m x)) (shift_p_depth p (m + n) x)"
  proof (cases "q = p")
    case True
    thus ?thesis
      using shift_p_depth_equiv_at_place[of p n "shift_p_depth p m x"]
            shift_p_depth_equiv_at_place[of p m x]
            mult_right trans mult.assoc[of x, symmetric]
            index_const_globally_nequiv_0
            power_int_add[of "index_const p" m n]
            shift_p_depth_equiv_at_place[of p "m + n" x]
            trans_left[of
              p "shift_p_depth p n (shift_p_depth p m x)" "x * index_const p powi (m + n)"
              "shift_p_depth p (m + n) x"
            ]
      by    presburger
  next
    case False
    hence   "p_equal q (shift_p_depth p n (shift_p_depth p m x)) x"
      and   "p_equal q ((shift_p_depth p (m + n) x)) x"
      using shift_p_depth_equiv_at_other_places trans
      by    (blast, blast)
    thus ?thesis using trans_left by blast
  qed
qed

lemma shift_shift_p_depth_image:
  "shift_p_depth p n ` shift_p_depth p m ` A = shift_p_depth p (m + n) ` A"
proof safe
  fix a assume a: "a  A"
  have *: "shift_p_depth p n (shift_p_depth p m a) = shift_p_depth p (m + n) a"
    using shift_shift_p_depth by auto
  from a show "shift_p_depth p n (shift_p_depth p m a)  shift_p_depth p (m + n) ` A"
    using * by fast
  from a show "shift_p_depth p (m + n) a  shift_p_depth p n ` shift_p_depth p m ` A"
    using *[symmetric] by fast
qed

lemma shift_p_depth_at_place:
  "p_depth p (shift_p_depth p n x) = p_depth p x + n" if "n = 0  ¬ p_equal p x 0"
proof (cases "n = 0")
  case True
  moreover from this have "p_equal p (shift_p_depth p n x) x"
    using shift_p_depth_equiv_at_place power_int_0_right mult_1_right by metis
  ultimately show ?thesis using depth_equiv by fastforce
next
  case False
  with that have "¬ p_equal p (x * (index_const p powi n)) 0"
    using no_zero_divisors power_int_equiv_0_iff index_const_globally_nequiv_0 by auto
  hence "p_depth p (shift_p_depth p n x) = p_depth p x + p_depth p (index_const p powi n)"
    using depth_equiv shift_p_depth_equiv_at_place depth_mult_additive by metis
  thus ?thesis using p_depth_index_const_powi by simp
qed

lemma shift_p_depth_at_other_places:
  "p_depth q (shift_p_depth p n x) = p_depth q x" if "q  p"
  using that depth_equiv shift_p_depth_equiv_at_other_places by blast

lemma p_depth_set_shift_p_depth_closed:
  "shift_p_depth p n x  p_depth_set p m" if "p_depth p x  m - n"
  using that shift_p_depth_equiv0_iff shift_p_depth_at_place p_depth_setI by auto

lemma global_depth_set_shift_p_depth_closed:
  "shift_p_depth p n x  global_depth_set m"
  if  "x  global_depth_set m"
  and "¬ p_equal p x 0  p_depth p x  m - n"
  using that p_depth_set_shift_p_depth_closed p_depth_setD shift_p_depth_at_other_places
        shift_p_depth_equiv0_iff global_depth_setD global_depth_setI
  by    metis

lemma shift_p_depth_mem_p_depth_set:
  "p_depth p x  m - n"
  if "¬ p_equal p x 0" and "shift_p_depth p n x  p_depth_set p m"
  using that shift_p_depth_equiv0_iff shift_p_depth_at_place
        p_depth_setD[of p "shift_p_depth p n x"]
  by    fastforce

lemma shift_p_depth_mem_global_depth_set:
  "p_depth p x  m - n"
  if "¬ p_equal p x 0" and "shift_p_depth p n x  global_depth_set m"
  using that shift_p_depth_equiv0_iff shift_p_depth_at_place
        global_depth_setD[of p "shift_p_depth p n x"]
  by    fastforce

lemma shift_p_depth_p_depth_set: "shift_p_depth p n ` p_depth_set p m = p_depth_set p (m + n)"
proof safe
  fix x assume "x  p_depth_set p m"
  thus "shift_p_depth p n x  p_depth_set p (m + n)"
    using     shift_p_depth_at_place shift_p_depth_equiv0_iff
    unfolding p_depth_set_def
    by        simp
next
  fix x assume "x  p_depth_set p (m + n)"
  hence "¬ p_equal p x 0  p_depth p x  m + n"
    using p_depth_setD by blast
  hence
    "¬ p_equal p (shift_p_depth p (-n) x) 0 
      p_depth p (shift_p_depth p (-n) x)  m"
    using shift_p_depth_at_place shift_p_depth_equiv0_iff by force
  hence "shift_p_depth p (-n) x  p_depth_set p m" using p_depth_setI by simp
  moreover have "x = shift_p_depth p n (shift_p_depth p (-n) x)"
    using shift_shift_p_depth by force
  ultimately show "x  shift_p_depth p n ` p_depth_set p m" by blast
qed

lemma shift_p_depth_cong:
  "p_equal q (shift_p_depth p n x) (shift_p_depth p n y)" if "p_equal q x y"
  using that cong_sym shift_p_depth_equiv_at_place mult_right
        shift_p_depth_equiv_at_other_places
  by    (cases "q = p", blast, blast)

lemma shift_p_depth_p_restrict:
  "shift_p_depth p n (p_restrict x P) = p_restrict (shift_p_depth p n x) P"
proof (intro global_imp_eq, standard)
  fix q
  show "p_equal q (shift_p_depth p n (p_restrict x P)) (p_restrict (shift_p_depth p n x) P)"
    by (
      cases "P q",
      metis p_restrict_equiv shift_p_depth_cong trans_right,
      metis p_restrict_equiv0 shift_p_depth_cong shift_p_depth_0 trans_left
    )
qed

lemma shift_p_depth_p_restrict_global_depth_set_memI:
  "shift_p_depth p n (p_restrict x ((=) p))  global_depth_set m"
  if "¬ p_equal p x 0  p_depth p x  m - n"
  using that shift_p_depth_p_restrict shift_p_depth_equiv0_iff shift_p_depth_at_place
        global_depth_set_p_restrictI p_depth_setI
  by    fastforce

lemma shift_p_depth_p_restrict_global_depth_set_memI':
  "shift_p_depth p n (p_restrict x ((=) p))  global_depth_set (m + n)"
  if "x  p_depth_set p m"
proof-
 from that have "p_restrict x ((=) p)  p_depth_set p m"
  using p_depth_set_p_restrict by simp
  hence "p_restrict (shift_p_depth p n x) ((=) p)  p_depth_set p (m + n)"
    using shift_p_depth_p_depth_set shift_p_depth_p_restrict[of p n x "(=) p"] by force
  hence "p_restrict (shift_p_depth p n x) ((=) p)  global_depth_set (m + n)"
    using global_depth_set_p_restrictI[of "(=) p"] by force
  thus ?thesis using shift_p_depth_p_restrict[of p n x "(=) p"] by auto
qed

lemma shift_p_depth_p_restrict_global_depth_set_image:
  "shift_p_depth p n ` (λx. p_restrict x ((=) p)) ` global_depth_set m
    = (λx. p_restrict x ((=) p)) ` global_depth_set (m + n)"
proof safe
  fix x assume "x  global_depth_set m"
  hence
    "shift_p_depth p n (p_restrict x ((=) p))  global_depth_set (m + n)"
    using shift_p_depth_p_restrict_global_depth_set_memI' global_depth_set by blast
  moreover have
    "shift_p_depth p n (p_restrict x ((=) p)) =
      p_restrict (shift_p_depth p n (p_restrict x ((=) p))) ((=) p)"
    using shift_p_depth_p_restrict p_restrict_restrict' by presburger
  ultimately show
    "shift_p_depth p n (p_restrict x ((=) p)) 
      (λx. p_restrict x ((=) p)) ` global_depth_set (m + n)"
      by fast
next
  fix x assume "x  global_depth_set (m + n)"
  moreover define y where "y  shift_p_depth p (-n) (p_restrict x ((=) p))"
  ultimately have "y  global_depth_set m"
    using shift_p_depth_p_restrict_global_depth_set_memI'[of x p "m + n" "-n"]
          global_depth_set[of "m + n"]
    by    simp
  moreover from y_def have
    "p_restrict x ((=) p) = shift_p_depth p n (p_restrict y ((=) p))"
    using shift_p_depth_p_restrict[of p n y "(=) p"] p_restrict_restrict'[of x "(=) p"]
          shift_shift_p_depth[of p n "-n" "p_restrict x ((=) p)"]
    by    force
  ultimately show
    "p_restrict x ((=) p) 
      shift_p_depth p n ` (λx. p_restrict x ((=) p)) ` global_depth_set m"
    using y_def by fast
qed

end (* locale global_p_equal_depth_div_inv_w_inj_index_consts *)


subsection ‹Topological patterns›

subsubsection ‹By place›

text ‹Convergence of sequences as measured by depth›

context p_equality_depth
begin

definition p_limseq_condition ::
  "'a  (nat  'b)  'b  int 
    nat  bool"
  where
    "p_limseq_condition p X x n K =
      (kK. ¬ p_equal p (X k) x  p_depth p (X k - x) > n)"

lemma p_limseq_conditionI [intro]:
  "p_limseq_condition p X x n K"
  if
    "k. k  K  ¬ p_equal p (X k) x 
      p_depth p (X k - x) > n"
  using that unfolding p_limseq_condition_def by blast

lemma p_limseq_conditionD:
  "p_depth p (X k - x) > n"
  if "p_limseq_condition p X x n K" and "k  K" and "¬ p_equal p (X k) x"
  using that unfolding p_limseq_condition_def by blast

abbreviation p_limseq ::
  "'a  (nat  'b)  'b  bool"
  where "p_limseq p X x  (n. K. p_limseq_condition p X x n K)"

lemma p_limseq_p_cong:
  "p_limseq p X x"
  if "nN. p_equal p (X n) (Y n)" and "p_equal p x y" and "p_limseq p Y y"
proof
  fix n
  from that(3) obtain M where M: "p_limseq_condition p Y y n M" by fastforce
  define K where "K  max M N"
  with M that(1,2) have "p_limseq_condition p X x n K"
    using trans[of p "X _" "Y _" y] trans_left[of p "X _" y x] p_limseq_conditionD[of p Y y n M]
          depth_diff_equiv
    by    fastforce
  thus "K. p_limseq_condition p X x n K" by auto
qed

lemma p_limseq_p_constant: "p_limseq p X x" if "n. p_equal p (X n) x"
  using that by fast

lemma p_limseq_constant: "p_limseq p (λn. x) x"
  using p_limseq_p_constant by auto

lemma p_limseq_p_eventually_constant:
  "p_limseq p X x" if "F k in sequentially. p_equal p (X k) x"
proof
  fix n
  from that obtain K where K: "kK. p_equal p (X k) x"
    using eventually_sequentially by auto
  hence "p_limseq_condition p X x n K" by blast
  thus "K. p_limseq_condition p X x n K" by blast
qed

lemma p_limseq_0 [simp]: "p_limseq p 0 0"
  using p_limseq_p_constant by simp

lemma p_limseq_0_iff: "p_limseq p 0 x  p_equal p x 0"
proof
  have "¬ p_equal p x 0  ¬ p_limseq p 0 x"
  proof
    assume x: "¬ p_equal p x 0" "p_limseq p 0 x"
    have "n. p_depth p x > n"
    proof
      fix n
      from x obtain K :: nat where "kK. p_depth p x > n"
        using p_limseq_conditionD zero_fun_apply diff_0 depth_uminus sym by metis
      thus "p_depth p x > n" by fast
    qed
    thus False by fast
  qed
  thus "p_limseq p 0 x  p_equal p x 0" by blast
qed (use sym in force)

lemma p_limseq_add: "p_limseq p (X + Y) (x + y)" if "p_limseq p X x" and "p_limseq p Y y"
proof
  fix n
  from that obtain K_x K_y
    where K_x: "p_limseq_condition p X x n K_x"
    and   K_y: "p_limseq_condition p Y y n K_y"
    by    blast
  define K where "K  max K_x K_y"
  have "p_limseq_condition p (X + Y) (x + y) n K"
  proof (standard, unfold plus_fun_apply)
    fix k assume k: "k  K" "¬ p_equal p (X k + Y k) (x + y)"
    from K_def k(1) have k_K_x_y: "k  K_x" "k  K_y" by auto
    have *:
      "X Y x y.
        p_equal p (X k) x  p_depth p (Y k - y) > n 
          p_depth p (X k + Y k - (x + y)) > n"
    proof-
      fix X Y x y
      assume X: "p_equal p (X k) x" and Y: "p_depth p (Y k - y) > n"
      have "p_depth p (X k + Y k - (x + y)) = p_depth p (X k + (Y k - (x + y)))"
        by (simp add: algebra_simps)
      also from X have " = p_depth p (x + (Y k - (x + y)))"
        using add_right depth_equiv by blast
      also have " = p_depth p (Y k - y)" by fastforce
      finally show "p_depth p (X k + Y k - (x + y)) > n" using Y by metis
    qed
    from k(2) consider
      (x_not_y) "  p_equal p (X k) x" "¬ p_equal p (Y k) y" |
      (y_not_x) "¬ p_equal p (X k) x" "  p_equal p (Y k) y" |
      (neither) "¬ p_equal p (X k) x" "¬ p_equal p (Y k) y"
      using add by blast
    thus "p_depth p (X k + Y k - (x + y)) > n"
    proof cases
      case x_not_y
      from K_y k_K_x_y(2) x_not_y(2) have "p_depth p (Y k - y) > n"
        using p_limseq_conditionD by auto
      with x_not_y(1) show ?thesis using * by blast
    next
      case y_not_x
      from K_x k_K_x_y(1) y_not_x(1) have "p_depth p (X k - x) > n"
        using p_limseq_conditionD by auto
      with y_not_x(2) have "p_depth p (Y k + X k - (y + x)) > n" using * by force
      thus ?thesis by (simp add: algebra_simps)
    next
      case neither
      moreover from K_x K_y k_K_x_y this have "min (p_depth p (X k - x)) (p_depth p (Y k - y)) > n"
        using p_limseq_conditionD by simp
      moreover from k(2) have "¬ p_equal p (X k - x + (Y k - y)) 0"
        using conv_0 by (simp add: algebra_simps)
      ultimately have "p_depth p (X k - x + (Y k - y)) > n"
        using conv_0 depth_nonarch[of p "X k - x" "Y k - y"] by auto
      thus ?thesis by (simp add: algebra_simps)
    qed
  qed
  thus "K. p_limseq_condition p (X + Y) (x + y) n K" by blast
qed

lemma p_limseq_uminus: "p_limseq p (-X) (-x)" if "p_limseq p X x"
proof
  fix n from that obtain K where "p_limseq_condition p X x n K" by metis
  hence "p_limseq_condition p (-X) (-x) n K"
    using uminus depth_diff unfolding p_limseq_condition_def by auto
  thus "K. p_limseq_condition p (-X) (-x) n K" by blast
qed

lemma p_limseq_diff: "p_limseq p (X - Y) (x - y)" if "p_limseq p X x" and "p_limseq p Y y"
  using that p_limseq_uminus[of p Y y] p_limseq_add[of p X x "-Y" "-y"] by force

lemma p_limseq_conv_0: "p_limseq p X x  p_limseq p (λn. X n - x) 0"
proof
  assume "p_limseq p X x"
  moreover have "X - (λn. x) = (λn. X n - x)" by fastforce
  ultimately show "p_limseq p (λn. X n - x) 0"
    using p_limseq_diff[of p X x "λn. x"] p_limseq_constant by force
next
  assume "p_limseq p (λn. X n - x) 0"
  moreover have "(λn. X n - x) + (λn. x) = X" by force
  ultimately show "p_limseq p X x"
    using p_limseq_add[of p "λn. X n - x" 0 "λn. x"] p_limseq_constant by simp
qed

lemma p_limseq_unique: "p_equal p x y" if "p_limseq p X x" and "p_limseq p X y"
  using that p_limseq_diff[of p X x X y] p_limseq_0_iff[of p "x - y"] conv_0[of p] by auto

lemma p_limseq_eventually_nequiv_0:
  "F k in sequentially. ¬ p_equal p (X k) 0"
  if "¬ p_equal p x 0" and "p_limseq p X x"
proof-
  have "¬ (K. kK. p_equal p (X k) 0)"
  proof
    assume "K. kK. p_equal p (X k) 0"
    with that have "n. p_depth p x > n"
      by (metis p_limseq_conditionD trans_right depth_diff_equiv0_left)
    thus False by blast
  qed
  thus ?thesis using eventually_sequentially by force
qed

lemma p_limseq_bdd_depth:
  assumes "¬ p_equal p x 0" and "p_limseq p X x"
  obtains d where "k. ¬ p_equal p (X k) 0  p_depth p (X k)  d"
proof-
  from assms(2) obtain K where K: "p_limseq_condition p X x (p_depth p x) K" by auto
  define D where "D  {p_depth p x}  {p_depth p (X k) | k. k < K}"
  define d where "d  Max D"
  have "k. ¬ p_equal p (X k) 0  p_depth p (X k)  d"
  proof clarify
    fix k show "p_depth p (X k)  d"
    proof (cases "k  K")
      case True
      with assms(1) K have "p_depth p (X k)  p_depth p x"
        using depth_equiv depth_pre_nonarch_diff_right[of p x "X k"] p_limseq_conditionD
        by    fastforce
      also from D_def d_def have "  d" using Max_ge[of D "p_depth p x"] by simp
      finally show ?thesis by blast
    next
      case False with D_def d_def show ?thesis using Max_ge[of D "p_depth p (X k)"] by fastforce
    qed
  qed
  with that show thesis by blast
qed

lemma p_limseq_eventually_bdd_depth:
  "F k in sequentially. p_depth p (X k)  p_depth p x"
  if "¬ p_equal p x 0" and "p_limseq p X x"
proof-
  from that(2) obtain K where K: "p_limseq_condition p X x (p_depth p x) K" by auto
  have "kK. p_depth p (X k)  p_depth p x"
  proof clarify
    fix k assume "k  K"
    with that(1) K show "p_depth p (X k)  p_depth p x"
      using depth_equiv depth_pre_nonarch_diff_right[of p x "X k"] p_limseq_conditionD by fastforce
  qed
  thus ?thesis using eventually_sequentially by blast
qed

lemma p_limseq_delayed_iff:
  "p_limseq p X x  p_limseq p (λn. X (N + n)) x"
proof (standard, safe)
  fix n
  assume "p_limseq p X x"
  from this obtain K where "p_limseq_condition p X x n K" by fastforce
  hence "p_limseq_condition p (λn. X (N + n)) x n K"
    unfolding p_limseq_condition_def by simp
  thus "K. p_limseq_condition p (λn. X (N + n)) x n K" by blast
next
  fix n
  assume "p_limseq p (λn. X (N + n)) x"
  from this obtain M where M: "p_limseq_condition p (λn. X (N + n)) x n M" by auto
  define K where "K  M + N"
  have "p_limseq_condition p X x n K"
  proof
    fix k assume k: "k  K" and X: "¬ p_equal p (X k) x"
    from k K_def have "N + (k - N) = k" by simp
    moreover from k K_def have "k - N  M" by auto
    moreover from k K_def X have "¬ p_equal p (X (N + (k - N))) x"
      by (simp add: algebra_simps)
    ultimately show "p_depth p (X k - x) > n" using M p_limseq_conditionD by metis
  qed
  thus "K. p_limseq_condition p X x n K" by blast
qed

lemma p_depth_set_p_limseq_closed:
  "x  p_depth_set p n"
  if "p_limseq p X x" and "F k in sequentially. X k  p_depth_set p n"
proof (intro p_depth_setI, clarify)
  assume x_n0: "¬ p_equal p x 0"
  from that(1) obtain K where K: "p_limseq_condition p X x n K" by blast
  from that(2) obtain N where N: "kN. X k  p_depth_set p n"
    using eventually_sequentially by fastforce
  define k where "k  max N K"
  show "p_depth p x  n"
  proof (cases "p_equal p (X k) x")
    case True with N x_n0 k_def show ?thesis
      using trans_right[of p _ x] p_depth_setD[of p "X k"] depth_equiv by fastforce
  next
    case False
    with K k_def have X_x: "p_depth p (X k - x) > n"
      using p_limseq_conditionD by force
    show ?thesis
    proof (cases "p_equal p (X k) 0")
      case True thus ?thesis using X_x depth_diff_equiv0_left by simp
    next
      case False
      with N x_n0 k_def show "p_depth p x  n"
        using X_x depth_pre_nonarch_diff_right[of p x "X k"] p_depth_setD[of p _ n] by fastforce
    qed
  qed
qed

text ‹Cauchy sequences by place›

definition p_cauchy_condition ::
  "'a  (nat  'b)  int  nat  bool"
  where
    "p_cauchy_condition p X n K =
      (k1K. k2K.
        ¬ p_equal p (X k1) (X k2)  p_depth p (X k1 - X k2) > n
      )"

lemma p_cauchy_conditionI:
  "p_cauchy_condition p X n K"
  if
    "k k'. k  K  k'  K 
      ¬ p_equal p (X k) (X k')  p_depth p (X k - X k') > n"
  using that unfolding p_cauchy_condition_def by blast

lemma p_cauchy_conditionD:
  "p_depth p (X k - X k') > n"
  if  "p_cauchy_condition p X n K" and "k  K" and "k'  K"
  and "¬ p_equal p (X k) (X k')"
  using that unfolding p_cauchy_condition_def by blast

lemma p_cauchy_condition_mono_seq_tail:
  "p_cauchy_condition p X n K  p_cauchy_condition p X n K'"
  if "K'  K"
  using that unfolding p_cauchy_condition_def by auto

lemma p_cauchy_condition_mono_depth:
  "p_cauchy_condition p X n K  p_cauchy_condition p X m K"
  if "m  n"
  using that unfolding p_cauchy_condition_def by fastforce

abbreviation "p_cauchy p X  (n. K. p_cauchy_condition p X n K)"

lemma p_cauchy_condition_LEAST:
  "p_cauchy_condition p X n (LEAST K. p_cauchy_condition p X n K)"
  if "p_cauchy p X"
  using that Least1I[OF ex_ex1_least_nat_le] by blast

lemma p_cauchy_condition_LEAST_mono:
  "(LEAST K. p_cauchy_condition p X m K)  (LEAST K. p_cauchy_condition p X n K)"
  if "p_cauchy p X" and "m  n"
  using that least_le_least[OF ex_ex1_least_nat_le ex_ex1_least_nat_le]
        p_cauchy_condition_mono_depth
  by    force

definition p_consec_cauchy_condition ::
  "'a  (nat  'b)  int  nat  bool"
  where
    "p_consec_cauchy_condition p X n K =
      (kK.
        ¬ p_equal p (X (Suc k)) (X k)  p_depth p (X (Suc k) - X k) > n
      )"

lemma p_consec_cauchy_conditionI:
  "p_consec_cauchy_condition p X n K"
  if
    "k. k  K  ¬ p_equal p (X (Suc k)) (X k) 
      p_depth p (X (Suc k) - X k) > n"
  using that unfolding p_consec_cauchy_condition_def by blast

lemma p_consec_cauchy:
  "p_cauchy p X = (n. K. p_consec_cauchy_condition p X n K)"
proof (standard, safe)
  fix n
  assume "p_cauchy p X"
  from this obtain K where K: "p_cauchy_condition p X n K" by auto
  hence "p_consec_cauchy_condition p X n K"
    using p_cauchy_conditionD by (force intro: p_consec_cauchy_conditionI)
  thus "K. p_consec_cauchy_condition p X n K" by blast
next
  fix n
  assume "n. K. p_consec_cauchy_condition p X n K"
  from this obtain K where K: "p_consec_cauchy_condition p X n K" by blast
  have "p_cauchy_condition p X n K"
  proof (intro p_cauchy_conditionI)
    fix k k' show
      "k  K  k'  K 
        ¬ p_equal p (X k) (X k')  p_depth p (X k - X k') > n"
    proof (induct k' k rule: linorder_wlog)
      case (le a b)
      define c where "c  b - a"
      have
        "a  K  ¬ p_equal p (X (a + c)) (X a) 
          p_depth p (X (a + c) - X a) > n"
      proof (induct c)
        case (Suc c)
        have decomp: "X (a + Suc c) - X a = (X (a + Suc c) - X (a + c)) + (X (a + c) - X a)"
          by fastforce
        consider  "p_equal p (X (a + Suc c)) (X (a + c))"   |
                  "p_equal p (X (a + c)) (X a)"             |
                  "¬ p_equal p (X (a + Suc c)) (X (a + c))"
                  "¬ p_equal p (X (a + c)) (X a)"
          by blast
        thus ?case
        proof (cases, metis Suc trans decomp conv_0 depth_add_equiv0_left)
          case 2
          with Suc(3) have "¬ p_equal p (X (a + Suc c)) (X (a + c))" using trans by blast
          with K Suc(2) have "p_depth p (X (a + Suc c) - X (a + c)) > n"
            using p_consec_cauchy_condition_def[of p X n K] by force
          with 2 show ?thesis using decomp conv_0 depth_add_equiv0_right by presburger
        next
          case 3
          moreover from K Suc(2) 3(1) have "p_depth p (X (a + Suc c) - X (a + c)) > n"
            using p_consec_cauchy_condition_def[of p X n K] by force
          moreover from 3(2) Suc(1,2) have "p_depth p (X (a + c) - X a) > n" by fast
          ultimately show ?thesis using Suc(3) decomp conv_0 depth_nonarch[of p] by fastforce
        qed
      qed simp
      with le c_def show ?case by force
    qed (metis sym depth_diff)
  qed
  thus "K. p_cauchy_condition p X n K" by blast
qed

end (* context p_equality_depth *)

context p_equality_depth_no_zero_divisors
begin

lemma p_limseq_mult_both_0: "p_limseq p (X * Y) 0" if "p_limseq p X 0" and "p_limseq p Y 0"
proof
  fix n
  from that obtain K_X K_Y
    where "p_limseq_condition p X 0 ¦n¦ K_X"
    and   "p_limseq_condition p Y 0 ¦n¦ K_Y"
    by    metis
  moreover define K where "K  max K_X K_Y"
  ultimately have "p_limseq_condition p (X * Y) 0 n K"
    using     K_def mult_0_left mult_0_right depth_mult_additive
    unfolding p_limseq_condition_def
    by        fastforce
  thus "K. p_limseq_condition p (X * Y) 0 n K" by blast
qed

lemma p_limseq_constant_mult_0: "p_limseq p (λn. x * Y n) 0" if "p_limseq p Y 0"
proof
  fix n
  from that obtain K where "p_limseq_condition p Y 0 (n - p_depth p x) K" by auto
  hence "p_limseq_condition p (λn. x * Y n) 0 n K"
    using mult_0_right depth_mult_additive unfolding p_limseq_condition_def by auto
  thus "K. p_limseq_condition p (λn. x * Y n) 0 n K" by auto
qed

lemma p_limseq_mult_0_right: "p_limseq p (X * Y) 0" if "p_limseq p X x" and "p_limseq p Y 0"
proof-
  from that(1) have "p_limseq p (λn. X n - x) 0" using p_limseq_conv_0 by blast
  with that(2) have "p_limseq p ((λn. X n - x) * Y) 0" using p_limseq_mult_both_0 by blast
  moreover have "(λn. X n - x) * Y = X * Y - (λn. x * Y n)"
    unfolding times_fun_def fun_diff_def by (auto simp add: algebra_simps)
  ultimately have "p_limseq p (X * Y - (λn. x * Y n)) 0" by auto
  with that(2) have "p_limseq p (X * Y - (λn. x * Y n) + (λn. x * Y n)) 0"
    using p_limseq_constant_mult_0[of p Y x]
          p_limseq_add[of p "X * Y - (λn. x * Y n)" 0 "λn. x * Y n"]
    by    simp
  thus ?thesis by (simp add: algebra_simps)
qed

lemma p_limseq_mult: "p_limseq p (X * Y) (x * y)" if "p_limseq p X x" and "p_limseq p Y y"
proof-
  from that have "p_limseq p (X * (λn. Y n - y)) 0"
    using p_limseq_conv_0 p_limseq_mult_0_right by simp
  moreover from that have "p_limseq p (λn. y * (X n - x)) 0"
    using p_limseq_conv_0 p_limseq_constant_mult_0 by blast
  ultimately have "p_limseq p (X * (λn. Y n - y) + (λn. y * (X n - x))) 0"
    using p_limseq_add[of p "X * (λn. Y n - y)" 0] by force
  moreover have
    "X * (λn. Y n - y) + (λn. y * (X n - x)) = (λn. (X * Y) n - x * y)"
    by (auto simp add: algebra_simps)
  ultimately show "p_limseq p (X * Y) (x * y)" using p_limseq_conv_0 by metis
qed

lemma poly_continuous_p_open_nbhds:
  "p_limseq p (λn. poly f (X n)) (poly f x)" if "p_limseq p X x"
proof (induct f)
  case (pCons a f)
  have "(λn. poly (pCons a f) (X n)) = (λn. a) + X * (λn. poly f (X n))"
    by auto
  with that pCons(2) show ?case using p_limseq_constant p_limseq_add p_limseq_mult by simp
qed (simp flip: zero_fun_def)

end (* context p_equality_depth_no_zero_divisors *)

context p_equality_depth_no_zero_divisors_1
begin

lemma p_limseq_pow: "p_limseq p (λk. (X k)^n) (x ^ n)" if "p_limseq p X x"
proof (induct n)
  case (Suc n)
  moreover have "(λk. X k ^ Suc n) = X * (λk. X k ^ n)" by auto
  ultimately show ?case using that p_limseq_mult by simp
qed (simp add: p_limseq_constant)

lemma p_limseq_poly: "p_limseq p (λn. poly f (X n)) (poly f x)" if "p_limseq p X x"
proof (induct f)
  case (pCons a f)
  have "(λn. poly (pCons a f) (X n)) = (λn. a) + X * (λn. poly f (X n))"
    by auto
  with that pCons(2) show ?case using p_limseq_mult p_limseq_add p_limseq_constant by auto
qed (simp flip: zero_fun_def)

end (* context p_equality_depth_no_zero_divisors_1 *)

context p_equality_depth_div_inv
begin

lemma p_limseq_inverse:
  "p_limseq p (λn. inverse (X n)) (inverse x)"
  if "¬ p_equal p x 0" and "p_limseq p X x"
proof
  fix n
  from that obtain K1 where K1: "kK1. p_depth p (X k)  p_depth p x"
    using p_limseq_eventually_bdd_depth eventually_sequentially by fastforce
  from that obtain K2 where K2: "kK2. ¬ p_equal p (X k) 0"
    using p_limseq_eventually_nequiv_0 eventually_sequentially by force
  from that(2) obtain K3
    where K3: "p_limseq_condition p X x (¦n¦ + 2 * p_depth p x) K3"
    by    fast
  define K where "K  Max {K1, K2, K3}"
  have "p_limseq_condition p (λn. inverse (X n)) (inverse x) n K"
  proof
    fix k assume k: "k  K" and X_k: "¬ p_equal p (inverse (X k)) (inverse x)"
    from k(1) K_def K2 have "p_equal p (inverse (X k) * X k) 1" using left_inverse_equiv by simp
    moreover from that(1) have "p_equal p (x * inverse x) 1" using right_inverse_equiv by simp
    ultimately have "p_equal p (inverse (X k) * (x - X k) * inverse x) (inverse (X k) - inverse x)"
      using minus mult_left[of p "x * inverse x" 1 "inverse (X k)"]
            mult_right[of p "inverse (X k) * X k" 1 "inverse x"]
      by    (simp add: algebra_simps)
    with X_k have
      "p_depth p (inverse (X k) - inverse x) =
        p_depth p (inverse (X k)) + p_depth p (X k - x) + p_depth p (inverse x)"
      using depth_equiv trans_right conv_0 depth_mult3_additive depth_diff by metis
    also from k X_k K_def K1 K2 K3 have " > n"
      using inverse_depth inverse_equiv_iff_equiv p_limseq_conditionD
      by    (fastforce simp add: algebra_simps)
    finally show "p_depth p (inverse (X k) - inverse x) > n" by force
  qed
  thus "K. p_limseq_condition p (λn. inverse (X n)) (inverse x) n K" by blast
qed

lemma p_limseq_divide:
  "p_limseq p (λn. X n / Y n) (x / y)"
  if "¬ p_equal p y 0" and "p_limseq p X x" and "p_limseq p Y y"
proof-
  have "(λn. X n / Y n) = X * (λn. inverse (Y n))" using divide_inverse by force
  with that show ?thesis using p_limseq_mult p_limseq_inverse divide_inverse by simp
qed

end (* context p_equality_depth_div_inv *)

context global_p_equality_depth
begin

lemma p_limseq_p_restrict_iff:
  "p_limseq p (λn. p_restrict (X n) P) x  p_limseq p X x" if "P p"
  using that sym p_restrict_equiv p_limseq_p_cong[of 0 p "λn. p_restrict (X n) P" X x x]
        p_limseq_p_cong[of 0 p X "λn. p_restrict (X n) P" x x]
  by    auto

lemma p_limseq_p_restricted: "p_limseq p (λn. p_restrict (X n) P) 0" if "¬ P p"
  using that p_restrict_equiv0 p_limseq_p_cong by blast

end (* context global_p_equality_depth *)

text ‹Base of open sets at local places›

context global_p_equal_depth
begin

abbreviation p_open_nbhd :: "'a  int  'b  'b set"
  where "p_open_nbhd p n x  x +o p_depth_set p n"

abbreviation "local_p_open_nbhds p  { p_open_nbhd p n x | n x. True }"
abbreviation "p_open_nbhds          (p. local_p_open_nbhds p)"

lemma p_open_nbhd: "x  p_open_nbhd p n x"
  unfolding elt_set_plus_def using p_depth_set_0 by force

lemma local_p_open_nbhd_is_open: "generate_topology (local_p_open_nbhds p) (p_open_nbhd p n x)"
  using generate_topology.intros(4)[of _ "local_p_open_nbhds p"] by blast

lemma p_open_nbhd_is_open: "generate_topology p_open_nbhds (p_open_nbhd p n x)"
  using generate_topology.intros(4)[of _ p_open_nbhds] by blast

lemma local_p_open_nbhds_are_coarser:
  "generate_topology (local_p_open_nbhds p) S  generate_topology p_open_nbhds S"
proof (
  induct S rule: generate_topology.induct, rule generate_topology.UNIV,
  use generate_topology.Int in blast, use generate_topology.UN in blast
)
  case (Basis S)
  hence "S  p_open_nbhds" by blast
  thus ?case using generate_topology.Basis by meson
qed

lemma p_open_nbhd_diff_depth:
  "p_depth p (y - x)  n" if "¬ p_equal p y x" and "y  p_open_nbhd p n x"
proof-
  from that(2) have "y - x  p_depth_set p n"
    unfolding elt_set_plus_def by fastforce
  with that(1) show ?thesis using conv_0 p_depth_setD by blast
qed

lemma p_open_nbhd_eq_circle:
  "p_open_nbhd p n x = {y. ¬ p_equal p y x  p_depth p (y - x)  n}"
proof safe
(* simp add: p_open_nbhd_diff_depth *)
  fix y assume "y  p_open_nbhd p n x" "p_equal p y x"
  thus False using conv_0 p_depth_set_equiv0 unfolding elt_set_plus_def by force
next
  fix y assume "n  p_depth p (y - x)"
  thus "y  p_open_nbhd p n x"
    unfolding elt_set_plus_def using p_depth_setI by force
qed (simp add: p_open_nbhd_diff_depth)

lemma p_open_nbhd_circle_multicentre:
  "p_open_nbhd p n y = p_open_nbhd p n x" if "y  p_open_nbhd p n x"
proof-
  have *:
    "x y. y  p_open_nbhd p n x 
      p_open_nbhd p n y  p_open_nbhd p n x"
  proof clarify
    fix x y z assume yx: "y  p_open_nbhd p n x" and zy: "z  p_open_nbhd p n y"
    have "¬ p_equal p z x  p_depth p (z - x)  n"
    proof
      assume zx: "¬ p_equal p z x"
      have zyx: "z - x = (z - y) + (y - x)" by auto
      consider
        "p_equal p z y" | "p_equal p y x" | (neq) "¬ p_equal p z y" "¬ p_equal p y x"
        by fast
      thus "p_depth p (z - x)  n"
      proof (
        cases,
        metis zyx yx zx yx trans conv_0 depth_add_equiv0_left p_open_nbhd_diff_depth,
        metis zyx zy zx trans conv_0 depth_add_equiv0_right p_open_nbhd_diff_depth
      )
        case neq
        with yx zy have "p_depth p (z - y)  n" and "p_depth p (y - x)  n"
          using p_open_nbhd_diff_depth by simp_all
        hence "min (p_depth p (z - y)) (p_depth p (y - x))  n" by presburger
        moreover from neq zyx zx
          have  "p_depth p (z - x)  min (p_depth p (z - y)) (p_depth p (y - x))"
          by    (metis conv_0 depth_nonarch)
        ultimately show ?thesis by linarith
      qed
    qed
    thus "z  p_open_nbhd p n x" using p_open_nbhd_eq_circle by auto
  qed
  moreover from that have "x  p_open_nbhd p n y"
    using p_open_nbhd_eq_circle sym depth_diff by force
  ultimately show ?thesis using that by auto
qed

lemma p_open_nbhd_circle_multicentre':
  "x  p_open_nbhd p n y  y  p_open_nbhd p n x"
  using p_open_nbhd p_open_nbhd_circle_multicentre by blast

lemma p_open_nbhd_p_restrict:
  "p_open_nbhd p n (p_restrict x P) = p_open_nbhd p n x" if "P p"
proof (intro set_eqI)
  fix y from that show
    "y  p_open_nbhd p n (p_restrict x P) 
      y  p_open_nbhd p n x"
    using p_open_nbhd_eq_circle[of "p_restrict x P" p n]
          p_open_nbhd_eq_circle[of x p n] p_restrict_equiv[of P p x]
          depth_diff_equiv[of p y y "p_restrict x P" x]
          trans[of p y "p_restrict x P" x] trans_left[of p y x "p_restrict x P"]
    by    force
qed

lemma p_restrict_p_open_nbhd_mem_iff:
  "y  p_open_nbhd p n x  p_restrict y P  p_open_nbhd p n x" if "P p"
  using that p_open_nbhd_circle_multicentre' p_open_nbhd_p_restrict by auto

lemma p_open_nbhd_p_restrict_add_mixed_drop_right:
  "p_open_nbhd p n (p_restrict x P + p_restrict y Q) = p_open_nbhd p n x"
  if "P p" and "¬ Q p"
proof (intro set_eqI)
  fix z
  from that have *: "p_equal p (p_restrict x P + p_restrict y Q) x"
    using p_restrict_add_mixed_equiv_drop_right by simp
  hence
    "z  p_open_nbhd p n (p_restrict x P + p_restrict y Q) 
      ¬ p_equal p z x 
        p_depth p (z - (p_restrict x P + p_restrict y Q))  n"
    using p_open_nbhd_eq_circle trans trans_left by blast
  moreover have "p_depth p (z - (p_restrict x P + p_restrict y Q)) = p_depth p (z - x)"
    using * depth_diff_equiv by auto
  ultimately show
    "z  p_open_nbhd p n (p_restrict x P + p_restrict y Q) 
      z  p_open_nbhd p n x"
    using p_open_nbhd_eq_circle by auto
qed

lemma p_open_nbhd_p_restrict_add_mixed_drop_left:
  "p_open_nbhd p n (p_restrict x P + p_restrict y Q) = p_open_nbhd p n y"
  if "¬ P p" and "Q p"
  using that p_open_nbhd_p_restrict_add_mixed_drop_right[of Q p P y x] by (simp add: add.commute)

lemma p_open_nbhd_antimono:
  "p_open_nbhd p n x  p_open_nbhd p m x" if "m  n"
  using that p_open_nbhd_eq_circle by auto

lemma p_open_nbhds_open_subopen:
  "generate_topology (local_p_open_nbhds p) A =
    (aA. n. p_open_nbhd p n a  A)"
proof
  show
    "generate_topology (local_p_open_nbhds p) A 
      aA. n. p_open_nbhd p n a  A"
  proof (induct A rule: generate_topology.induct)
    case (Int A B) show ?case
    proof
      fix x assume x: "x  A  B"
      from x Int(2) obtain n_A where "p_open_nbhd p n_A x  A" by blast
      moreover from x Int(4) obtain n_B where "p_open_nbhd p n_B x  B" by blast
      moreover define n where "n = max n_A n_B"
      ultimately have "p_open_nbhd p n x  A  B"
        using p_open_nbhd_antimono[of n_A n x p] p_open_nbhd_antimono[of n_B n x p] by auto
      thus "n. p_open_nbhd p n x  A  B" by blast
    qed
  qed (simp, fast, use p_open_nbhd_circle_multicentre in blast)
next
  assume *: "aA. n. p_open_nbhd p n a  A"
  define f where "f  λa. (SOME n. p_open_nbhd p n a  A)"
  have "A = (aA. p_open_nbhd p (f a) a)"
  proof safe
    fix a show "a  A  a  (aA. p_open_nbhd p (f a) a)"
      using p_open_nbhd by blast
  next
    fix x a assume a: "a  A" and x: "x  p_open_nbhd p (f a) a"
    from a * have ex_n: "n. p_open_nbhd p n a  A" by fastforce
    from x f_def show "x  A" using someI_ex[OF ex_n] by blast
  qed
  moreover have "aA. generate_topology (local_p_open_nbhds p) (p_open_nbhd p (f a) a)"
    using generate_topology.Basis[of _ "local_p_open_nbhds p"] by blast
  ultimately show "generate_topology (local_p_open_nbhds p) A"
    using generate_topology.UN[of "(λa. p_open_nbhd p (f a) a) ` A" "local_p_open_nbhds p"]
    by    fastforce
qed

lemma p_restrict_p_open_set_mem_iff:
  "a  A  p_restrict a P  A"
  if "generate_topology (local_p_open_nbhds p) A" and "P p"
  using that p_open_nbhds_open_subopen p_open_nbhd p_restrict_p_open_nbhd_mem_iff by fast

lemma hausdorff:
  "p n. (p_open_nbhd p n x)  (p_open_nbhd p n y) = {}" if "x  y"
proof-
  from that obtain p :: 'a where p: "¬ p_equal p x y" using global_imp_eq by blast
  define n where "n  p_depth p (x - y) + 1"
  have "(p_open_nbhd p n x)  (p_open_nbhd p n y) = {}"
    unfolding elt_set_plus_def
  proof (intro equals0I, clarify)
    fix a b
    assume  a : "a  p_depth_set p n"
      and   b : "b  p_depth_set p n"
      and   ab: "x + a = y + b"
    from ab have "x - y = b - a" by (simp add: algebra_simps)
    with p a b have "p_depth p (x - y)  n"
      using p_depth_set_minus conv_0 p_depth_setD by metis
    with n_def show False by linarith
  qed
  thus ?thesis by blast
qed

sublocale t2_p_open_nbhds: t2_space "generate_topology p_open_nbhds"
proof (intro class.t2_space.intro, rule topological_space_generate_topology, unfold_locales)
  fix x y :: 'b assume "x  y"
  from this obtain p and n where pn: "(p_open_nbhd p n x)  (p_open_nbhd p n y) = {}"
    using hausdorff by presburger
  thus
    "V V'. generate_topology p_open_nbhds V  generate_topology p_open_nbhds V' 
      x  V  y  V'  V  V' = {}"
    using p_open_nbhd_is_open p_open_nbhd by meson
qed

abbreviation "p_open_nbhds_tendsto     t2_p_open_nbhds.tendsto"
abbreviation "p_open_nbhds_convergent  t2_p_open_nbhds.convergent"
abbreviation "p_open_nbhds_LIMSEQ      t2_p_open_nbhds.LIMSEQ"

lemma locally_limD: "K. p_limseq_condition p X x n K" if "p_open_nbhds_LIMSEQ X x"
proof-
  from that have "K. kK. X k  p_open_nbhd p (n + 1) x"
    using     p_open_nbhd_is_open p_open_nbhd
    unfolding t2_p_open_nbhds.tendsto_def eventually_sequentially
    by        presburger
  thus "K. p_limseq_condition p X x n K" using p_open_nbhd_eq_circle by force
qed

lemma globally_limseq_imp_locally_limseq:
  "p_limseq p X x" if "p_open_nbhds_LIMSEQ X x"
  using that locally_limD by blast

lemma globally_limseq_iff_locally_limseq: "p_open_nbhds_LIMSEQ X x = (p. p_limseq p X x)"
proof (standard, standard, use globally_limseq_imp_locally_limseq in fast)
  assume X: "p. p_limseq p X x"
  show "p_open_nbhds_LIMSEQ X x"
    unfolding t2_p_open_nbhds.tendsto_def eventually_sequentially
  proof clarify
    fix S :: "'b set"
    have
      "generate_topology p_open_nbhds S  x  S 
        (p. p_limseq p X x)  K. kK. X k  S"
    proof (induct S rule: generate_topology.induct)
      case (Int A B)
      from Int(2,4-6) obtain Ka and Kb
        where "kKa. X k  A"
        and   "kKb. X k  B"
        by    blast
      from this have "kmax Ka Kb. X k  A  B" by fastforce
      thus ?case by blast
    next
      case (Basis S)
      from Basis(1,2) obtain p n where "S = p_open_nbhd p n x"
        using p_open_nbhd_circle_multicentre by blast
      moreover from Basis(3) have "K. p_limseq_condition p X x (n - 1) K" by blast
      ultimately show ?case using p_limseq_conditionD p_open_nbhd_eq_circle by fastforce
    qed (simp, blast)
    with X show
      "generate_topology p_open_nbhds S 
        x  S  K. kK. X k  S"
      by fast
  qed
qed

lemma p_open_nbhds_LIMSEQ_eventually_p_constant:
  "p_equal p x c"
  if "p_open_nbhds_LIMSEQ X x" and "F k in sequentially. p_equal p (X k) c"
proof-
  have "¬ (¬ p_equal p x c)"
  proof
    assume contra: "¬ p_equal p x c"
    define d where "d  max 1 (p_depth p (x - c))"
    from that(1) obtain K1 where K1: "p_limseq_condition p X x d K1"
      using locally_limD by blast
    from that(2) obtain K2 where K2: "kK2. p_equal p (X k) c"
      using eventually_sequentially by auto
    define K where "K  max K1 K2"
    show False
    proof (cases "p_equal p (X K) x")
      case True with contra K2 K_def show False using trans_right[of p "X K"] by simp
    next
      case False
      with K1 K_def have "p_depth p (X K - x) > d" using p_limseq_conditionD by simp
      with d_def K2 K_def show False using depth_diff_equiv[of p "X K" c x x] depth_diff by simp
    qed
  qed
  thus ?thesis by blast
qed

lemma p_open_nbhds_LIMSEQ_p_restrict:
  "p_equal p x 0" if "p_open_nbhds_LIMSEQ (λn. p_restrict (X n) P) x" and "¬ P p"
    using that globally_limseq_iff_locally_limseq sym p_restrict_equiv0 p_limseq_0_iff
          p_limseq_p_cong[of 0 p 0 "λn. p_restrict (X n) P" x x]
   by     auto

lemma global_depth_set_p_open_nbhds_LIMSEQ_closed:
  "x  global_depth_set n"
  if  "p_open_nbhds_LIMSEQ X x"
  and "F k in sequentially. X k  global_depth_set n"
proof (intro global_depth_setI'')
  fix p
  from that(2) have "F k in sequentially. X k  p_depth_set p n"
    using eventually_sequentially global_depth_set by force
  with that(1) show "x  p_depth_set p n"
    using globally_limseq_imp_locally_limseq p_depth_set_p_limseq_closed by blast
qed

end (* context global_p_equal_depth *)

context global_p_equal_depth_div_inv_w_inj_index_consts
begin

lemma shift_p_depth_p_open_nbhd:
  "shift_p_depth p n ` p_open_nbhd p m x = p_open_nbhd p (m + n) (shift_p_depth p n x)"
proof safe
  fix y assume y: "y  p_open_nbhd p m x"
  show "shift_p_depth p n y  p_open_nbhd p (m + n) (shift_p_depth p n x)"
    unfolding p_open_nbhd_eq_circle
  proof clarify
    assume *: "¬ p_equal p (shift_p_depth p n y) (shift_p_depth p n x)"
    hence "¬ p_equal p y x" using shift_p_depth_equiv_iff by force
    moreover from this y have "p_depth p (y - x) + n  m + n"
      using p_open_nbhd_eq_circle by auto
    ultimately show "p_depth p (shift_p_depth p n y - shift_p_depth p n x)  m + n"
      using conv_0 shift_p_depth_at_place shift_p_depth_minus by metis
  qed
next
  fix y assume y: "y  p_open_nbhd p (m + n) (shift_p_depth p n x)"
  have "shift_p_depth p (-n) y  p_open_nbhd p m x"
  proof (cases "n = 0", use y in auto)
    case n: False show ?thesis
    proof (cases "p_equal p y (shift_p_depth p n x)")
      case True
      hence "p_equal p (shift_p_depth p (-n) y) x"
        using shift_p_depth_equiv_iff[of p p "-n" y] shift_shift_p_depth[of p "-n" n x] by force
      thus "shift_p_depth p (-n) y  p_open_nbhd p m x" using p_open_nbhd_eq_circle by simp
    next
      case False
      moreover from this y have "p_depth p (y - shift_p_depth p n x) + (-n)  m"
        using p_open_nbhd_diff_depth by fastforce
      moreover have "shift_p_depth p (-n) (shift_p_depth p n x) = x"
        using shift_shift_p_depth by simp
      ultimately have "p_depth p (shift_p_depth p (-n) y - x)  m"
        using n conv_0 shift_p_depth_at_place shift_p_depth_minus by metis
      with False show "shift_p_depth p (-n) y  p_open_nbhd p m x"
        using p_open_nbhd_eq_circle shift_p_depth_equiv_iff[of p p "-n" y "shift_p_depth p n x"]
              shift_shift_p_depth
        by    blast
    qed
  qed
  moreover have "y = shift_p_depth p n (shift_p_depth p (-n) y)"
    using shift_shift_p_depth by auto
  ultimately show "y  shift_p_depth p n ` p_open_nbhd p m x" by blast
qed

lemma shift_p_depth_p_open_set:
  "generate_topology (local_p_open_nbhds p) (shift_p_depth p n ` A)"
  if "generate_topology (local_p_open_nbhds p) A"
proof (rule iffD2, rule p_open_nbhds_open_subopen, clarify)
  fix a assume "a  A"
  with that obtain m where "p_open_nbhd p m a  A"
    using p_open_nbhds_open_subopen by fastforce
  hence "shift_p_depth p n ` p_open_nbhd p m a  shift_p_depth p n ` A" by fast
  moreover have
    "shift_p_depth p n ` p_open_nbhd p m a = p_open_nbhd p (m + n) (shift_p_depth p n a)"
    using shift_p_depth_p_open_nbhd by blast
  ultimately
    show  "m. p_open_nbhd p m (shift_p_depth p n a)  shift_p_depth p n ` A"
    by    fast
qed

end (* context global_p_equal_depth_div_inv_w_inj_index_consts *)

locale p_complete_global_p_equal_depth = global_p_equal_depth +
  assumes complete:
    "p_cauchy p X  p_open_nbhds_convergent (λn. p_restrict (X n) ((=) p))"
begin

lemma p_cauchyE:
  assumes "p_cauchy p X"
  obtains x where "p_open_nbhds_LIMSEQ (λn. p_restrict (X n) ((=) p)) x"
  using   assms complete t2_p_open_nbhds.convergent_def
  by      blast

end (* locale p_complete_global_p_equal_depth *)

text ‹Hensel's Lemma.›

context p_equality_div_inv
begin

― ‹A sequence to approach a root from within the ring of integers.›
primrec hensel_seq :: "'a  'b poly  'b  nat  'b"
  where "hensel_seq p f x 0 = x" |
  "hensel_seq p f x (Suc n) = (
    if p_equal p (poly f (hensel_seq p f x n)) 0 then hensel_seq p f x n
    else
      hensel_seq p f x n - (poly f (hensel_seq p f x n)) / (poly (polyderiv f) (hensel_seq p f x n))
  )"

lemma constant_hensel_seq_case:
  "k  n  hensel_seq p f x k = hensel_seq p f x n"
  if "p_equal p (poly f (hensel_seq p f x n)) 0"
proof (induct k)
  case (Suc k) thus ?case by (cases "n = Suc k", simp, use that in force)
qed simp

end (* context p_equality_div_inv *)

context p_equality_depth_div_inv
begin

― ‹Context for inductive steps leading to Hensel's Lemma.›
context
  fixes p :: 'a and f :: "'b poly" and x :: 'b and n :: nat
  assumes f_deg   : "degree f > 0"
    and   f_depth : "set (coeffs f)  p_depth_set p 0"
    and   d_hensel: "hensel_seq p f x n  p_depth_set p 0"
    and   f       : "¬ p_equal p (poly f (hensel_seq p f x n)) 0"
    and   deriv_f : "¬ p_equal p (poly (polyderiv f) (hensel_seq p f x n)) 0"
    and   df      :
      "p_depth p (poly f (hensel_seq p f x n)) >
        2 * p_depth p (poly (polyderiv f) (hensel_seq p f x n))"
begin

lemma hensel_seq_step_1:
  "p_depth p (poly f (hensel_seq p f x (Suc n))) >
    p_depth p (poly f ((hensel_seq p f x n)))"
  if  next_f: "¬ p_equal p (poly f (hensel_seq p f x (Suc n))) 0"
proof (cases f)
  case (pCons c q)
  define a a' where "a  hensel_seq p f x n" and "a'  hensel_seq p f x (Suc n)"
  define b where "b  - ((poly f a) / (poly (polyderiv f) a))"
  define c_add_poly :: "nat  'b poly"
    where "c_add_poly  coeff (additive_poly_poly f)"
  define  sum_fun Suc_sum_fun :: "nat  'b"
    where "sum_fun      λi. poly (c_add_poly i) a * b ^ i"
    and   "Suc_sum_fun  λi. poly (c_add_poly (Suc i)) a * b ^ (Suc i)"
  define S :: "'b" where "S  sum Suc_sum_fun {1..degree q}"
  moreover from sum_fun_def Suc_sum_fun_def have Suc_sum_fun: "Suc_sum_fun = sum_fun  Suc"
    by auto
  moreover have "{0..degree q} = {..degree q}" by auto
  moreover from f_deg pCons have "degree f = Suc (degree q)" by auto
  ultimately have "poly f a' = poly f a + poly (polyderiv f) a * b + S"
    using f a_def a'_def b_def c_add_poly_def sum_fun_def additive_poly_poly_decomp[of f a b]
          sum_up_index_split[of sum_fun 0] sum.atLeast_Suc_atMost_Suc_shift[of sum_fun 0]
          sum_up_index_split[of "sum_fun  Suc" 0] additive_poly_poly_coeff0[of f]
          additive_poly_poly_coeff1[of f]
    by    (simp add: ac_simps)
  with deriv_f a_def b_def have equiv_sum: "p_equal p (poly f a') S"
    using minus_divide_left times_divide_eq_right mult_divide_cancel_left add_left
          add.right_inverse add_equiv0_left
    by    metis
  with next_f a'_def have "¬ p_equal p S 0" using trans by fast
  from this f_depth d_hensel a_def c_add_poly_def Suc_sum_fun_def S_def obtain j where j:
    "j  {1..degree q}" "p_depth p S  int (Suc j) * p_depth p b"
    using sum_poly_additive_poly_poly_depth_bound[of f p] by force
  from f_depth f deriv_f d_hensel df a_def b_def have "p_depth p b  0"
    using p_depth_poly_deriv_quotient depth_uminus[of p b] by fastforce
  moreover from j(1) have "2  int (Suc j)" by force
  ultimately have "int (Suc j) * p_depth p b  2 * p_depth p b" using mult_right_mono by blast
  moreover from f deriv_f a_def b_def
    have  "p_depth p b = p_depth p (poly f a) - p_depth p (poly (polyderiv f) a)"
    using divide_equiv_0_iff depth_uminus divide_depth by metis
  ultimately show "p_depth p (poly f a') > p_depth p (poly f a)"
    using j(2) df a_def equiv_sum depth_equiv by force
qed

lemma hensel_seq_step_2:
  "¬ p_equal p (poly (polyderiv f) (hensel_seq p f x (Suc n))) 0"
  "p_depth p (poly (polyderiv f) (hensel_seq p f x (Suc n))) =
    p_depth p (poly (polyderiv f) (hensel_seq p f x n))"
proof-

  define a a' where "a  hensel_seq p f x n" and "a'  hensel_seq p f x (Suc n)"
  define b where "b  - ((poly f a) / (poly (polyderiv f) a))"
  from f deriv_f a_def b_def
    have  db: "p_depth p b = p_depth p (poly f a) - p_depth p (poly (polyderiv f) a)"
    using divide_equiv_0_iff depth_uminus divide_depth by metis

  have
    "p_depth p (poly (polyderiv f) (a')) = p_depth p (poly (polyderiv f) a) 
      ¬ p_equal p (poly (polyderiv f) a') 0"
  proof (cases "degree (polyderiv f)")
    case (Suc m)
    define  c_add_poly :: "nat  'b poly"
      where "c_add_poly  coeff (additive_poly_poly (polyderiv f))"
    define sum_fun :: "nat  'b"
      where "sum_fun  λi. poly (c_add_poly i) a * b ^ i"
    define S :: "'b" where "S  sum (sum_fun  Suc) {0..m}"
    from f a_def a'_def b_def Suc c_add_poly_def sum_fun_def S_def have diff_eq_sum:
      "poly (polyderiv f) a' - poly (polyderiv f) a = S"
      using additive_poly_poly_decomp[of "polyderiv f" a b] sum_up_index_split[of sum_fun 0]
            sum.atLeast_Suc_atMost_Suc_shift[of sum_fun 0]
      by    (simp add: algebra_simps additive_poly_poly_coeff0)
    show ?thesis
    proof (cases "p_equal p S 0", safe)
      case True
      from True show
        "p_depth p (poly (polyderiv f) a') = p_depth p (poly (polyderiv f) a)"
        using diff_eq_sum conv_0 depth_equiv by metis
      from deriv_f a_def True show "p_equal p (poly (polyderiv f) a') 0  False"
        using diff_eq_sum conv_0 trans_right by blast
    next
      case False
      from this f_depth d_hensel a_def S_def sum_fun_def c_add_poly_def obtain j
        where j: "p_depth p S  int (Suc j) * p_depth p b"
        using p_depth_set_polyderiv[of f p 0]
              sum_poly_additive_poly_poly_depth_bound[of "polyderiv f" p a b 0 m]
        by    force
      from f_depth d_hensel deriv_f a_def have "p_depth p (poly (polyderiv f) a)  0"
        using poly_p_depth_set_polyderiv[of 0 f p a] p_depth_setD[of p "poly (polyderiv f) a" 0]
        by    simp
      hence
        "p_depth p (poly (polyderiv f) a) 
          int (Suc j) * (2 * p_depth p (poly (polyderiv f) a) - p_depth p (poly (polyderiv f) a))"
        using mult_right_mono[of 1 "int (Suc j)"] by auto
      also from df a_def have " < int (Suc j) * p_depth p b" using db by simp
      finally have *:
        "p_depth p (poly (polyderiv f) a) <
          p_depth p (poly (polyderiv f) a' - poly (polyderiv f) a)"
        using j diff_eq_sum by fastforce
      with deriv_f a_def
        show  "p_depth p (poly (polyderiv f) a') = p_depth p (poly (polyderiv f) a)"
        using depth_diff_cancel_imp_eq_depth_right by blast
      show "p_equal p (poly (polyderiv f) a') 0  False"
        using * depth_diff_equiv0_left by fastforce
    qed
  qed (elim degree_eq_zeroE, simp, use deriv_f in fastforce)

  thus  "p_depth p (poly (polyderiv f) (a')) = p_depth p (poly (polyderiv f) a)"
    and "¬ p_equal p (poly (polyderiv f) a') 0"
    by  auto

qed

end (* Hensel's step context *)

― ‹Context for Hensel's Lemma.›
context
  fixes p :: 'a and f :: "'b poly" and x :: 'b
  assumes f_deg       : "degree f > 0"
    and   f_depth     : "set (coeffs f)  p_depth_set p 0"
    and   d_init      : "x  p_depth_set p 0"
    and   init_deriv  :
      "¬ p_equal p (poly f x) 0  ¬ p_equal p (poly (polyderiv f) x) 0"
    and   d_init_deriv:
      "¬ p_equal p (poly f x) 0 
        p_depth p (poly f x) > 2 * p_depth p (poly (polyderiv f) x)"
begin

lemma
      hensel_seq_adelic_int : "hensel_seq p f x n  p_depth_set p 0" (is "?A")
  and hensel_seq_poly_depth :
    "¬ p_equal p (poly f (hensel_seq p f x n)) 0 
      p_depth p (poly f (hensel_seq p f x n))  p_depth p (poly f x) + int n"
      (is "?B")
  and hensel_seq_deriv      :
    "¬ p_equal p (poly f (hensel_seq p f x n)) 0 
      ¬ p_equal p (poly (polyderiv f) (hensel_seq p f x n)) 0"
      (is "?C")
  and hensel_seq_deriv_depth:
    "¬ p_equal p (poly f (hensel_seq p f x n)) 0 
      p_depth p (poly (polyderiv f) (hensel_seq p f x n)) = p_depth p (poly (polyderiv f) x)"
      (is "?D")
  and hensel_seq_depth_ineq:
    "¬ p_equal p (poly f (hensel_seq p f x n)) 0 
      p_depth p (poly f (hensel_seq p f x n)) >
      2 * p_depth p (poly (polyderiv f) (hensel_seq p f x n))"
      (is "?E")
proof-

  have "?A  ?B  ?C  ?D  ?E"
  proof (induct n, use d_init init_deriv d_init_deriv in force)
    case (Suc n)
    define a a' where "a  hensel_seq p f x n" and "a'  hensel_seq p f x (Suc n)"
    show ?case
    proof (cases "p_equal p (poly f a) 0")
      case False show ?thesis
      proof (fold a_def a'_def, safe)

        from False Suc a_def a'_def
          have  prev_int        : "a  p_depth_set p 0"
          and   prev_poly_depth : "p_depth p (poly f a)  p_depth p (poly f x) + int n"
          and   prev_deriv      : "¬ p_equal p (poly (polyderiv f) a) 0"
          and   prev_deriv_depth:
            "p_depth p (poly (polyderiv f) a) = p_depth p (poly (polyderiv f) x)"
          and   prev_depth_ineq : "p_depth p (poly f a) > 2 * p_depth p (poly (polyderiv f) a)"
          by    auto

        define b where "b  (poly f a) / (poly (polyderiv f) a)"
        with f_depth False a_def a'_def b_def show "a'  p_depth_set p 0"
          using prev_int prev_deriv prev_depth_ineq p_depth_set_minus
                p_depth_set_poly_deriv_quotient[of 0 f p]
          by    force

        from f_deg f_depth a_def a'_def False
          show  next_deriv: "p_equal p (poly (polyderiv f) a') 0  False"
          using hensel_seq_step_2(1) prev_int prev_deriv prev_depth_ineq by blast

        from f_deg f_depth a_def a'_def False show next_deriv_depth:
          "p_depth p (poly (polyderiv f) a') = p_depth p (poly (polyderiv f) x)"
          by (metis hensel_seq_step_2(2) prev_int prev_deriv prev_depth_ineq prev_deriv_depth)

        moreover assume *: "¬ p_equal p (poly f a') 0"

        ultimately
          show  "p_depth p (poly f a') > 2 * p_depth p (poly (polyderiv f) a')"
          using f_deg f_depth a_def a'_def False hensel_seq_step_1 prev_int prev_deriv
                prev_depth_ineq next_deriv prev_depth_ineq prev_deriv_depth next_deriv_depth
          by    force

        from f_deg f_depth a_def a'_def False
          show  "p_depth p (poly f a')  p_depth p (poly f x) + int (Suc n)"
          using * hensel_seq_step_1 prev_int prev_deriv prev_depth_ineq next_deriv prev_poly_depth
          by    force

      qed
    qed (simp add: a_def Suc)
  qed

  thus ?A ?B ?C ?D ?E by auto

qed

lemma hensel_seq_cauchy: "p_cauchy p (hensel_seq p f x)"
proof (rule iffD2, rule p_consec_cauchy, clarify)
  fix n
  show "K. p_consec_cauchy_condition p (hensel_seq p f x) n K"
  proof (cases "N. p_equal p (poly f (hensel_seq p f x N)) 0")
    case True
    from this obtain N where N: "p_equal p (poly f (hensel_seq p f x N)) 0" by fast
    have "p_consec_cauchy_condition p (hensel_seq p f x) n N"
    proof (intro p_consec_cauchy_conditionI)
      fix k
      assume  "k  N"
        and   "¬ p_equal p (hensel_seq p f x (Suc k)) (hensel_seq p f x k)"
      with N show "p_depth p (hensel_seq p f x (Suc k) - hensel_seq p f x k) > n"
        using constant_hensel_seq_case[of p f x N k] by auto
    qed
    thus ?thesis by blast
  next
    case False
    hence *: "n. ¬ p_equal p (poly f (hensel_seq p f x n)) 0" by fast
    define d where "d  p_depth p (poly f x) - p_depth p (poly (polyderiv f) x)"
    define K where "K  nat (n - d + 1)"
    have "p_consec_cauchy_condition p (hensel_seq p f x) n K"
    proof (intro p_consec_cauchy_conditionI)
      fix k assume "k  K"
      moreover define a a'
        where "a  hensel_seq p f x k" and "a'  hensel_seq p f x (Suc k)"
      moreover define b where "b  - ((poly f a) / (poly (polyderiv f) a))"
      moreover from this a_def
        have  "p_depth p b = p_depth p (poly f a) - p_depth p (poly (polyderiv f) a)"
        using * hensel_seq_deriv divide_equiv_0_iff depth_uminus divide_depth by metis
      ultimately show "p_depth p (a' - a) > n"
        using d_def K_def * hensel_seq_deriv_depth hensel_seq_poly_depth by fastforce
    qed
    thus ?thesis by blast
  qed
qed

lemma p_limseq_poly_hensel_seq: "p_limseq p (λn. poly f (hensel_seq p f x n)) 0"
proof
  fix n
  define K where "K  nat (n - p_depth p (poly f x) + 1)"
  have "p_limseq_condition p (λn. poly f (hensel_seq p f x n)) 0 n K"
  proof (intro p_limseq_conditionI)
    fix k assume "k  K" and "¬ p_equal p (poly f (hensel_seq p f x k)) 0"
    with K_def show "p_depth p (poly f (hensel_seq p f x k) - 0) > n"
      using hensel_seq_poly_depth by fastforce
  qed
  thus "K. p_limseq_condition p (λn. poly f (hensel_seq p f x n)) 0 n K" by fast
qed

end (* Hensel's lemma context *)

end (* context p_equality_depth_div_inv *)


locale p_complete_global_p_equal_depth_div_inv =
  global_p_equal_depth_div_inv + p_complete_global_p_equal_depth
begin

lemma hensel:
  fixes p :: 'a and f :: "'b poly" and x :: 'b
  assumes "degree f > 0"
    and   "set (coeffs f)  p_depth_set p 0"
    and   "x  p_depth_set p 0"
    and
      "¬ p_equal p (poly f x) 0  ¬ p_equal p (poly (polyderiv f) x) 0"
    and
      "¬ p_equal p (poly f x) 0 
        p_depth p (poly f x) > 2 * p_depth p (poly (polyderiv f) x)"
  obtains z where "z  p_depth_set p 0" and "p_equal p (poly f z) 0"
proof-
  define X where "X  hensel_seq p f x"
  define res_X where "res_X  λn. p_restrict (X n) ((=) p)"
  from assms X_def res_X_def obtain z where "p_open_nbhds_LIMSEQ res_X z"
    using hensel_seq_cauchy p_cauchyE by blast
  hence "p_limseq p res_X z" using globally_limseq_iff_locally_limseq by blast
  with res_X_def have X_to_z: "p_limseq p X z" using p_limseq_p_restrict_iff by blast
  with assms X_def have "p_equal p (poly f z) 0"
    using poly_continuous_p_open_nbhds p_limseq_poly_hensel_seq p_limseq_unique by metis
  moreover from assms X_def have "z  p_depth_set p 0"
    using X_to_z hensel_seq_adelic_int p_depth_set_p_limseq_closed by fastforce
  ultimately show thesis using that by blast
qed

end (* locale p_complete_global_p_equal_depth_div_inv *)


subsubsection ‹Globally›

text ‹We use bounded depth to create a global metric.›

context p_equality_depth
begin

definition bdd_global_depth :: "'b  nat"
  where
    "bdd_global_depth x =
      (if globally_p_equal x 0 then 0 else Inf {nat (p_depth p x + 1) | p. ¬ p_equal p x 0})"
― ‹The plus-one is to distinguish depth-0 from negative depth.›

lemma bdd_global_depth_0[simp]: "globally_p_equal x 0  bdd_global_depth x = 0"
  unfolding bdd_global_depth_def by simp

lemma bdd_global_depthD:
  "bdd_global_depth x = Inf {nat (p_depth p x + 1) | p. ¬ p_equal p x 0}"
  if "¬ globally_p_equal x 0"
  using that unfolding bdd_global_depth_def by argo

lemma bdd_global_depthD_as_image:
  "bdd_global_depth x = (INF p{p. ¬ p_equal p x 0}. nat (p_depth p x + 1))"
  if "¬ globally_p_equal x 0"
  using that image_Collect unfolding bdd_global_depth_def by metis

lemma bdd_global_depth_cong:
  "bdd_global_depth x = bdd_global_depth y" if "globally_p_equal x y"
proof (cases "globally_p_equal x 0")
  case True with that show ?thesis using globally_p_equal_trans_right[of x y 0] by fastforce
next
  case False
  moreover from that have "{p. ¬ p_equal p x 0} = {p. ¬ p_equal p y 0}"
    using trans[of _ x y 0] trans_right[of _ x y 0] by auto
  moreover from that
    have  "(λp. nat (p_depth p x + 1)) = (λp. nat (p_depth p y + 1))"
    using depth_equiv[of _ x y]
    by    simp
  ultimately show ?thesis using that globally_p_equal_trans bdd_global_depthD_as_image by metis
qed

lemma bdd_global_depth_le:
  "bdd_global_depth x  nat (p_depth p x + 1)" if "¬ p_equal p x 0"
  using that bdd_global_depthD_as_image cINF_lower[of "λp. nat (p_depth p x + 1)"]
  by    fastforce

lemma bdd_global_depth_greatest:
  "bdd_global_depth x  B"
  if  "¬ p_equal p x 0"
  and "q. ¬ p_equal q x 0  nat (p_depth q x + 1)  B"
  using that bdd_global_depthD_as_image
        cINF_greatest[of
          "{p. ¬ p_equal p x 0}" B "λp. nat (p_depth p x + 1)"
        ]
  by    fastforce

lemma bdd_global_depth_witness:
  assumes "¬ globally_p_equal x 0"
  obtains p where "¬ p_equal p x 0" and "bdd_global_depth x = nat (p_depth p x + 1)"
proof-
  define D where "D  {nat (p_depth p x + 1) | p. ¬ p_equal p x 0}"
  from assms obtain q where "¬ p_equal q x 0" by blast
  with D_def have "nat (p_depth q x + 1)  D" by force
  with assms D_def obtain p
    where "¬ p_equal p x 0"
    and   "bdd_global_depth x = nat (p_depth p x + 1)"
    using wellorder_InfI[of _ D] bdd_global_depthD
    by    auto
  with that show thesis by meson
qed

lemma bdd_global_depth_eq_0:
  "bdd_global_depth x = 0" if "p_depth p x < 0"
  using that depth_equiv_0 bdd_global_depth_le[of p x] by fastforce

lemma bdd_global_depth_eq_0_iff:
  "bdd_global_depth x = 0 
    (p. p_depth p x < 0)  (globally_p_equal x 0)"
  (is "?L = ?R")
proof
  assume L: ?L show ?R
  proof clarify
    assume "¬ globally_p_equal x 0"
    from this obtain p where "¬ p_equal p x 0" and "bdd_global_depth x = nat (p_depth p x + 1)"
      using bdd_global_depth_witness[of x] by auto
    with L have "p_depth p x < 0" by force
    thus "p. p_depth p x < 0" by blast
  qed
next
  assume ?R thus ?L by (cases "globally_p_equal x 0", use bdd_global_depth_eq_0 in auto)
qed

lemma bdd_global_depth_diff: "bdd_global_depth (x - y) = bdd_global_depth (y - x)"
  using depth_diff conv_0 sym_iff[of _ x y] unfolding bdd_global_depth_def by simp

lemma bdd_global_depth_uminus: "bdd_global_depth (- x) = bdd_global_depth x"
  using depth_uminus uminus_iff[of _ x 0] unfolding bdd_global_depth_def by auto

lemma bdd_global_depth_nonarch:
  "bdd_global_depth (x + y)  min (bdd_global_depth x) (bdd_global_depth y)"
  if "¬ globally_p_equal (x + y) 0"
proof-
  consider  "globally_p_equal x 0" | "globally_p_equal y 0" |
            (n0) "¬ globally_p_equal x 0" "¬ globally_p_equal y 0"
    by      blast
  thus ?thesis
  proof cases
    case n0
    define N :: "'b  'a set" where "N  λx. {p. ¬ p_equal p x 0}"
    define dp1 where "dp1  λx p. p_depth p x + 1"
    define trunc_dp1 where "trunc_dp1  λx p. nat (dp1 x p)"
    define inf_trunc_dp1 :: "'b  nat"
      where "inf_trunc_dp1  λx. (INF pN x. trunc_dp1 x p)"
    from inf_trunc_dp1_def have inf_trunc_dp1:
      "p x. p  N x  inf_trunc_dp1 x  trunc_dp1 x p"
      using cINF_lower by fast
    define choose_dpth :: "'a  int" where
      "choose_dpth  λp.
        if p_equal p x 0 then dp1 y p
        else if p_equal p y 0 then dp1 x p
        else min (dp1 x p) (dp1 y p)"
    from that n0 N_def
      have  nempty: "N x  {}" "N y  {}" "N (x + y)  {}"
      using globally_p_equalI
      by    (blast, blast, blast)
    have
      "Inf ((nat  choose_dpth) ` (N x  N y)) =
        min (inf_trunc_dp1 x) (inf_trunc_dp1 y)"
    proof (intro cInf_eq_non_empty)
      fix n assume "n  (nat  choose_dpth) ` (N x  N y)"
      from this obtain p where "p  N x  N y" "n = nat (choose_dpth p)" by auto
      thus "min (inf_trunc_dp1 x) (inf_trunc_dp1 y)  n"
        using N_def trunc_dp1_def choose_dpth_def inf_trunc_dp1[of p x] inf_trunc_dp1[of p y]
        by    auto
    next
      fix n
      assume *:
        "m. m  (nat  choose_dpth) ` (N x  N y) 
          n  m"
      have "n  inf_trunc_dp1 x"
        unfolding inf_trunc_dp1_def
      proof (intro cInf_greatest)
        fix m assume "m  trunc_dp1 x ` N x"
        from this obtain p where "p  N x" "m = trunc_dp1 x p" by blast
        with N_def trunc_dp1_def choose_dpth_def * show "n  m"
          by (cases "p  N y", fastforce, fastforce)
      qed (simp add: nempty(1))
      moreover have "n  inf_trunc_dp1 y"
        unfolding inf_trunc_dp1_def
      proof (intro cInf_greatest)
        fix m assume "m  trunc_dp1 y ` N y"
        from this obtain p where "p  N y" "m = trunc_dp1 y p" by blast
        with N_def trunc_dp1_def choose_dpth_def * show "n  m"
          by (cases "p  N x", fastforce, fastforce)
      qed (simp add: nempty(2))
      ultimately
        show  "n  min (inf_trunc_dp1 x) (inf_trunc_dp1 y)"
        by    auto
    qed (simp add: nempty(1))
    moreover from n0(1) have "inf_trunc_dp1 x = bdd_global_depth x"
      using     bdd_global_depthD_as_image
      unfolding N_def inf_trunc_dp1_def trunc_dp1_def dp1_def
      by        auto
    moreover from n0(2) have "inf_trunc_dp1 y = bdd_global_depth y"
      using     bdd_global_depthD_as_image
      unfolding N_def inf_trunc_dp1_def trunc_dp1_def dp1_def
      by        auto
    ultimately have
      "min (bdd_global_depth x) (bdd_global_depth y) =
        Inf ((nat  choose_dpth) ` (N x  N y))"
      by presburger
    also have "  inf_trunc_dp1 (x + y)"
      unfolding inf_trunc_dp1_def
    proof (intro cINF_mono nempty(3))
      fix p assume p: "p  N (x + y)"
      with N_def have nequiv0:
        "¬ p_equal p (x + y) 0" "¬ p_equal p x 0  ¬ p_equal p y 0"
        using add[of p x 0 y 0] by auto
      from nequiv0(2) consider
        "p_equal p x 0" | "p_equal p y 0" "¬ p_equal p x 0" |
        (neither) "¬ p_equal p x 0" "¬ p_equal p y 0"
        by blast
      hence "(nat  choose_dpth) p  trunc_dp1 (x + y) p"
      proof cases
        case neither with nequiv0(1) show "(nat  choose_dpth) p  trunc_dp1 (x + y) p"
          using depth_nonarch
          by    (fastforce simp add: choose_dpth_def trunc_dp1_def dp1_def)
      qed (
        simp_all add: choose_dpth_def trunc_dp1_def dp1_def depth_add_equiv0_left
                      depth_add_equiv0_right
      )
      moreover from N_def nequiv0(2) have "p  N x  N y" by force
      ultimately show
        "qN x  N y. (nat  choose_dpth) q  trunc_dp1 (x + y) p"
        by blast
    qed simp
    finally show ?thesis
      using that N_def inf_trunc_dp1_def trunc_dp1_def dp1_def bdd_global_depthD_as_image by simp
  qed simp_all
qed

definition bdd_global_dist :: "'b  'b  real" where
  "bdd_global_dist x y =
    (if globally_p_equal x y then 0 else inverse (2 ^ bdd_global_depth (x - y)))"

lemma bdd_global_dist_eqD [simp]: "bdd_global_dist x y = 0" if "globally_p_equal x y"
  using that unfolding bdd_global_dist_def by simp

lemma bdd_global_distD:
  "bdd_global_dist x y = inverse (2 ^ bdd_global_depth (x - y))" if "¬ globally_p_equal x y"
  using that unfolding bdd_global_dist_def by fastforce

lemma bdd_global_dist_cong:
  "bdd_global_dist w x = bdd_global_dist y z"
  if "globally_p_equal w y" and "globally_p_equal x z"
proof (cases "globally_p_equal w x")
  case True with that show ?thesis using globally_p_equal_cong using bdd_global_dist_eqD by metis
next
  case False with that show ?thesis
    using globally_p_equal_sym globally_p_equal_cong bdd_global_distD globally_p_equal_minus
          bdd_global_depth_cong[of "w - x" "y - z"]
    by    metis
qed

lemma bdd_global_dist_nonneg: "bdd_global_dist x y  0"
  unfolding bdd_global_dist_def by auto

lemma bdd_global_dist_bdd: "bdd_global_dist x y  1"
  using     le_imp_inverse_le[of 1 "2 ^ bdd_global_depth (x - y)"]
  unfolding bdd_global_dist_def
  by        auto

lemma bdd_global_dist_lessI:
  "bdd_global_dist x y < e"
  if pos: "e > 0"
  and depth:
    "p. ¬ p_equal p x y 
      p_depth p (x - y)  log 2 (inverse e)"
proof (cases "e > 1")
  case True
  moreover have "bdd_global_dist x y  1" by (rule bdd_global_dist_bdd)
  ultimately show ?thesis by linarith
next
  case False
  hence small: "e  1" by linarith
  show ?thesis
  proof (cases "globally_p_equal x y")
    case False
    define d where "d  log 2 (inverse e)"
    have
      "p. ¬ p_equal p x y 
        nat (p_depth p (x - y) + 1)  nat (d + 1)"
    proof clarify
      fix p assume "¬ p_equal p x y"
      with depth d_def have *: "p_depth p (x - y)  d" by simp
      moreover from pos d_def have "d  0"
        using small le_imp_inverse_le zero_le_log_cancel_iff[of 2 "inverse e"] by force
      ultimately have "p_depth p (x - y) + 1 > 0" by linarith
      with * show "nat (p_depth p (x - y) + 1)  nat (d + 1)"
        using le_nat_iff[of "p_depth p (x - y) + 1" "nat d"] by simp
    qed
    with False have "bdd_global_depth (x - y)  nat (d + 1)"
      using conv_0 bdd_global_depth_greatest by auto
    with d_def have "bdd_global_depth (x - y)  d + 1" by auto
    with pos d_def have "e > inverse (2 ^ bdd_global_depth (x - y))"
      using real_of_int_floor_add_one_gt[of "log 2 (inverse e)"]
            log_pow_cancel[of 2 "bdd_global_depth (x - y)"]
            log_less_cancel_iff[of 2 "inverse e" "2 ^ bdd_global_depth (x - y)"]
            less_imp_inverse_less
      by    fastforce
    moreover from False have "bdd_global_dist x y = inverse (2 ^ bdd_global_depth (x - y))"
      using bdd_global_distD by force
    ultimately show ?thesis by simp
  qed (simp add: pos)
qed

lemma bdd_global_dist_less_imp:
  "p_depth p (x - y)  log 2 (inverse e)"
  if "e > 0" and "e  1" and "¬ p_equal p x y" and "bdd_global_dist x y < e"
proof-
  from that have "log 2 (inverse e) < bdd_global_depth (x - y)"
    using bdd_global_distD inverse_less_imp_less[of _ "inverse e"] log_less[of 2 "inverse e"]
    by    fastforce
  hence "log 2 (inverse e) < bdd_global_depth (x - y)" by linarith
  with that(3) have "log 2 (inverse e) < int (nat (p_depth p (x - y) + 1))"
    using conv_0 bdd_global_depth_le by fastforce
  moreover from that(1,2) have "log 2 (inverse e)  0"
    using le_imp_inverse_le zero_le_log_cancel_iff[of 2 "inverse e"] by force
  ultimately show ?thesis by linarith
qed

lemma bdd_global_dist_less_pow2_iff:
  "bdd_global_dist x y < inverse (2 ^ n) 
    (p. ¬ p_equal p x y  p_depth p (x - y)  int n)"
proof (cases "globally_p_equal x y")
  case False show ?thesis
  proof (standard, standard, clarify)
    fix p assume "¬ p_equal p x y"
    moreover assume "bdd_global_dist x y < inverse (2 ^ n)"
    ultimately show "p_depth p (x - y)  int n"
      using le_imp_inverse_le[of 1 "2 ^ n"] bdd_global_dist_less_imp[of "inverse (2 ^ n)" p x y]
      by    force
  next
    assume "p. ¬ p_equal p x y  p_depth p (x - y)  int n"
    thus "bdd_global_dist x y < inverse (2 ^ n)" by (auto intro: bdd_global_dist_lessI)
  qed
qed simp

lemma bdd_global_dist_sym: "bdd_global_dist x y = bdd_global_dist y x"
  using globally_p_equal_sym bdd_global_depth_diff unfolding bdd_global_dist_def by force

lemma bdd_global_dist_conv0: "bdd_global_dist x y = bdd_global_dist (x - y) 0"
  using globally_p_equal_conv_0 unfolding bdd_global_dist_def by simp

lemma bdd_global_dist_conv0': "bdd_global_dist x y = bdd_global_dist 0 (x - y)"
  using bdd_global_dist_conv0 bdd_global_dist_sym by simp

lemma bdd_global_dist_nonarch:
  "bdd_global_dist x y  max (bdd_global_dist x z) (bdd_global_dist y z)"
proof (cases "globally_p_equal x y")
  case True thus ?thesis
    using bdd_global_dist_nonneg[of x z] bdd_global_dist_nonneg[of y z] by simp
next
  case xy: False show ?thesis
  proof (cases "globally_p_equal x z")
    case True thus ?thesis
      using bdd_global_dist_cong[of x z y y] bdd_global_dist_sym by auto
  next
    case xz: False show ?thesis
    proof (cases "globally_p_equal y z")
      case True thus ?thesis
        using bdd_global_dist_cong[of y z x x] bdd_global_dist_sym by auto
    next
      case False with xy xz show ?thesis
      proof (induct x y rule: linorder_wlog'[of "λx. bdd_global_dist x z"])
        case (le x y)
        from le(2) have "¬ globally_p_equal ((x - z) + (z - y)) 0"
          using globally_p_equal_conv_0[of x y] by fastforce
        moreover from le(1,3,4) have "bdd_global_depth (x - z)  bdd_global_depth (y - z)"
          using bdd_global_distD inverse_le_imp_le by auto
        ultimately have "bdd_global_depth (x - y)  bdd_global_depth (y - z)"
          using bdd_global_depth_nonarch[of "x - z" "z - y"] bdd_global_depth_diff[of y z] by auto
        with le(1,2,4)
          show  "bdd_global_dist x y  max (bdd_global_dist x z) (bdd_global_dist y z)"
          using le_imp_inverse_le bdd_global_distD
          by    auto
      next
        case (sym x y)
        hence "bdd_global_dist y x  max (bdd_global_dist y z) (bdd_global_dist x z)"
          using globally_p_equal_sym[of y x] by blast
        thus ?case using bdd_global_dist_sym max.commute by metis
      qed
    qed
  qed
qed

lemma bdd_global_dist_ball_multicentre:
  "{z. bdd_global_dist y z < e} = {z. bdd_global_dist x z < e}"
  if "bdd_global_dist x y < e"
proof-
  define B where "B  λx. {z. bdd_global_dist x z < e}"
  moreover have "x y. bdd_global_dist x y < e  B y  B x"
    unfolding B_def
  proof clarify
    fix x y z
    assume "bdd_global_dist x y < e" and "bdd_global_dist y z < e"
    thus "bdd_global_dist x z < e"
      using bdd_global_dist_nonarch[of x z y] bdd_global_dist_sym by simp
  qed
  ultimately show ?thesis using that bdd_global_dist_sym by fastforce
qed

lemma bdd_global_dist_ball_at_0:
  "{z. bdd_global_dist 0 z < inverse (2 ^ n)} = global_depth_set (int n)"
  using bdd_global_dist_less_pow2_iff bdd_global_dist_sym unfolding global_depth_set_def by auto

lemma bdd_global_dist_ball_UNIV:
  "{z. bdd_global_dist x z < e} = UNIV" if "e > 1"
  using that bdd_global_dist_bdd order_le_less_subst1[of _ id 1 e] by fastforce

lemma bdd_global_dist_ball_at_0_normalize:
  "{z. bdd_global_dist 0 z < e} = global_depth_set (log 2 (inverse e))"
  if "e > 0" and "e  1"
  using     that bdd_global_dist_less_imp bdd_global_dist_sym bdd_global_dist_lessI
  unfolding global_depth_set_def
  by        fastforce

lemma bdd_global_dist_ball_translate:
  "{z. bdd_global_dist x z < e} = x +o {z. bdd_global_dist 0 z < e}"
  unfolding elt_set_plus_def
proof safe
  fix y assume "bdd_global_dist x y < e"
  hence "bdd_global_dist 0 (y - x) < e"
    using bdd_global_dist_sym bdd_global_dist_conv0' by presburger
  moreover have "y = x + (y - x)" by auto
  ultimately show "b{z. bdd_global_dist 0 z < e}. y = x + b" by fast
next
  fix y assume "bdd_global_dist 0 y < e"
  thus "bdd_global_dist x (x + y) < e"
    using bdd_global_dist_conv0'[of "x + y" x] bdd_global_dist_sym by auto
qed

lemma bdd_global_dist_left_translate_continuous:
  "bdd_global_dist (x + y) (x + z) < e" if "bdd_global_dist y z < e"
  using that bdd_global_dist_conv0[of y z] bdd_global_dist_conv0[of "x + y" "x + z"] by simp

lemma bdd_global_dist_right_translate_continuous:
  "bdd_global_dist (y + x) (z + x) < e" if "bdd_global_dist y z < e"
  using that bdd_global_dist_conv0[of y z] bdd_global_dist_conv0[of "y + x" "z + x"] by simp

definition global_cauchy_condition ::
  "(nat  'b)  nat  nat  bool"
  where
    "global_cauchy_condition X n K =
      (k1K. k2K. p.
        ¬ p_equal p (X k1) (X k2)  nat (p_depth p (X k1 - X k2)) > n
      )"

lemma global_cauchy_conditionI:
  "global_cauchy_condition X n K"
  if
    "p k k'. k  K  k'  K 
      ¬ p_equal p (X k) (X k')  nat (p_depth p (X k - X k')) > n"
  using that unfolding global_cauchy_condition_def by blast

lemma global_cauchy_conditionD:
  "nat (p_depth p (X k - X k')) > n"
  if  "global_cauchy_condition X n K" and "k  K" and "k'  K"
  and "¬ p_equal p (X k) (X k')"
  using that unfolding global_cauchy_condition_def by blast

lemma global_cauchy_condition_mono_seq_tail:
  "global_cauchy_condition X n K  global_cauchy_condition X n K'"
  if "K'  K"
  using that unfolding global_cauchy_condition_def by auto

lemma global_cauchy_condition_mono_depth:
  "global_cauchy_condition X n K  global_cauchy_condition X m K"
  if "m  n"
  using that unfolding global_cauchy_condition_def by fastforce

abbreviation "globally_cauchy X  (n. K. global_cauchy_condition X n K)"

lemma global_cauchy_condition_LEAST:
  "global_cauchy_condition X n (LEAST K. global_cauchy_condition X n K)" if "globally_cauchy X"
  using that Least1I[OF ex_ex1_least_nat_le] by blast

lemma global_cauchy_condition_LEAST_mono:
  "(LEAST K. global_cauchy_condition X m K)  (LEAST K. global_cauchy_condition X n K)"
  if "globally_cauchy X" and "m  n"
  using that least_le_least[OF ex_ex1_least_nat_le ex_ex1_least_nat_le]
        global_cauchy_condition_mono_depth
  by    force

definition bdd_global_uniformity :: "('b × 'b) filter"
  where "bdd_global_uniformity = (INF e{0 <..}. principal {(x, y). bdd_global_dist x y < e})"

end (* context p_equality_depth *)

context global_p_equal_depth
begin

sublocale metric_space_by_bdd_depth:
  metric_space bdd_global_dist bdd_global_uniformity
    "λU. xU.
      eventually (λ(x', y). x' = x  y  U) bdd_global_uniformity"
proof
  fix x y z
  define dxy dxz dyz
    where "dxy  bdd_global_dist x y"
    and   "dxz  bdd_global_dist x z"
    and   "dyz  bdd_global_dist y z"
  hence "dxy  max dxz dyz" using bdd_global_dist_nonarch by metis
  also from dxz_def dyz_def have "  max dxz dyz + min dxz dyz"
    using bdd_global_dist_def by auto
  finally show "dxy  dxz + dyz" by auto
  from dxy_def show "(dxy = 0) = (x = y)" using global_eq_iff unfolding bdd_global_dist_def by simp
qed (simp only: bdd_global_uniformity_def, simp)

lemma nonneg_global_depth_set_LIMSEQ_closed:
  "x  global_depth_set (int n)"
  if  lim: "metric_space_by_bdd_depth.LIMSEQ X x"
  and seq: "F k in sequentially. X k  global_depth_set (int n)"
proof (intro global_depth_setI)
  fix p assume p: "¬ p_equal p x 0"
  from seq obtain K where K: "kK. X k  global_depth_set (int n)"
    using eventually_sequentially by fastforce
  from lim have "F k in sequentially. bdd_global_dist (X k) x < inverse (2 ^ n)"
    using metric_space_by_bdd_depth.tendstoD[of X x sequentially "inverse (2 ^ n)"] by fastforce
  from this obtain K' :: nat
    where K': "kK'. bdd_global_dist (X k) x < inverse (2 ^ n)"
    using eventually_sequentially
    by    meson
  define m where "m  max K K'"
  consider
    (eq) "p_equal p (X m) x" |
    (0)  "¬ p_equal p (X m) x"   "p_equal p (X m) 0" |
    (n0) "¬ p_equal p (X m) x" "¬ p_equal p (X m) 0"
    by blast
  thus "p_depth p x  int n"
  proof cases
    case eq
    moreover from this K m_def have "X m  global_depth_set (int n)" by auto
    ultimately show ?thesis using p trans_right global_depth_setD depth_equiv by metis
  next
    case 0 with K' m_def show ?thesis
      using bdd_global_dist_less_pow2_iff[of "X m" x n] depth_diff_equiv0_left by auto
  next
    case n0
    with p K K' m_def show ?thesis
      using bdd_global_dist_less_pow2_iff[of "X m" x n] global_depth_setD[of p "X m" "int n"]
            depth_pre_nonarch_diff_right[of p x "X m"]
      by    force
  qed
qed

lemma globally_cauchy_vs_bdd_metric_Cauchy:
  "globally_cauchy X = metric_space_by_bdd_depth.Cauchy X" for X :: "nat  'b"
proof
  assume X: "globally_cauchy X" show "metric_space_by_bdd_depth.Cauchy X"
  proof (intro metric_space_by_bdd_depth.metric_CauchyI)
    fix e :: real assume pos: "e > 0"
    show "M. mM. nM. bdd_global_dist (X m) (X n) < e"
    proof (cases "e > 1")
      case True
      hence "m0. n0. bdd_global_dist (X m) (X n) < e"
        using bdd_global_dist_bdd order_le_less_subst1[of _ id 1 e] by fastforce
      thus ?thesis by blast
    next
      case False
      hence small: "e  1" by simp
      define d where "d  log 2 (inverse e)"
      from X obtain M where M: "global_cauchy_condition X (nat d) M" by fast
      have "mM. nM. bdd_global_dist (X m) (X n) < e"
      proof (clarify, intro bdd_global_dist_lessI pos)
        fix m n :: nat and p :: 'a
        assume "m  M" and "n  M" and "¬ p_equal p (X m) (X n)"
        with M have "nat (p_depth p (X m - X n)) > nat d" using global_cauchy_conditionD by blast
        moreover from d_def pos small have "d  0"
          using le_imp_inverse_le zero_le_log_cancel_iff[of 2 "inverse e"] by force
        ultimately show "p_depth p (X m - X n)  log 2 (inverse e)"
          using d_def by simp
      qed
      thus ?thesis by blast
    qed
  qed
next
  assume X: "metric_space_by_bdd_depth.Cauchy X" show "globally_cauchy X"
  proof
    fix n
    from X obtain M
      where M:
        "mM. m'M. bdd_global_dist (X m) (X m') < inverse (2 ^ Suc n)"
      using metric_space_by_bdd_depth.metric_CauchyD[of X "inverse (2 ^ Suc n)"]
      by    auto
    hence "global_cauchy_condition X n M"
      using bdd_global_dist_less_pow2_iff[of _ _ "Suc n"]
      by    (fastforce intro: global_cauchy_conditionI)
    thus "M. global_cauchy_condition X n M" by fast
  qed
qed

end (* context global_p_equal_depth *)

context global_p_equal_depth_no_zero_divisors
begin

lemma bdd_global_dist_bdd_mult_continuous:
  "bdd_global_dist (w * x) (w * y) < e"
  if  pos  : "e > 0"
  and bdd  : "p. ¬ p_equal p w 0  p_depth p w  n"
  and input: "bdd_global_dist x y < min (e * 2 powi (n - 1)) 1"
proof (cases "e > 1", use bdd_global_dist_ball_UNIV in blast, intro bdd_global_dist_lessI pos)
  case False
  fix p assume p: "¬ p_equal p (w * x) (w * y)"
  from p have w: "¬ p_equal p w 0"
    using mult_0_left cong[of p 0 "w * x" 0 "w * y"] sym by force
  from p have xy: "¬ p_equal p x y" using mult_left by force
  from p have wxy: "¬ p_equal p (w * (x - y)) 0"
    using conv_0 right_diff_distrib[of w x y] by auto
  define d' where "d'  e * 2 powi (n - 1)"
  define d  where "d   min d' 1"
  define m  where "m   log 2 (inverse d)"
  from pos d_def d'_def have "d > 0" "d  1" by auto
  with input m_def d_def d'_def have "m  p_depth p (x - y)"
    using xy bdd_global_dist_less_imp[of d p x y] by fast
  with bdd have *: "n + m  p_depth p (w * x - w * y)"
    using w wxy right_diff_distrib[of w x y] depth_mult_additive[of p w "x - y"]
          bdd_global_depth_le[of p w]
    by    force
  moreover have "n + m  log 2 (inverse e)"
  proof (cases "e * 2 powi (n - 1) < 1")
    case True
    moreover from m_def have "real_of_int (n + m)  real_of_int n + (- log 2 d - 1)"
      using real_of_int_floor_ge_diff_one log_inverse by fastforce
    ultimately show ?thesis using d_def d'_def pos by (simp add: log_mult log_inverse algebra_simps)
  next
    case False
    moreover from this pos have "log 2 (e * 2 powi (n - 1))  0"
      by (simp add: le_imp_inverse_le log_mono log_inverse)
    ultimately show ?thesis using pos m_def d_def d'_def
    by (fastforce simp add: log_mult log_inverse)
  qed
  ultimately show  "p_depth p (w * x - w * y)  log 2 (inverse e)" by linarith
qed

lemma bdd_global_dist_limseq_bdd_mult:
  "metric_space_by_bdd_depth.LIMSEQ (λk. w * X k) (w * x)"
  if  bdd: "p. ¬ p_equal p w 0  p_depth p w  n"
  and seq: "metric_space_by_bdd_depth.LIMSEQ X x"
proof
  fix e :: real assume e: "e > 0"
  define d where "d  min (e * 2 powi (n - 1)) 1"
  with e have d: "d > 0" by auto
  with seq obtain K :: nat where "kK. bdd_global_dist (X k) x < d"
    using metric_space_by_bdd_depth.tendstoD[of X x] eventually_sequentially by meson
  with bdd e d_def have "kK. bdd_global_dist (w * X k) (w * x) < e"
    using bdd_global_dist_bdd_mult_continuous by blast
  thus "F k in sequentially. bdd_global_dist (w * X k) (w * x) < e"
    using eventually_sequentially by blast
qed

end (* context global_p_equal_depth_no_zero_divisors *)

context global_p_equal_depth_div_inv_w_inj_index_consts
begin

lemma bdd_global_dist_limseq_global_uniformizer_powi_mult:
  "metric_space_by_bdd_depth.LIMSEQ
    (λk. global_uniformizer powi n * X k) (global_uniformizer powi n * x)"
  if "metric_space_by_bdd_depth.LIMSEQ X x"
  using that bdd_global_dist_limseq_bdd_mult[of _ n] p_depth_global_uniformizer_powi by auto

lemma global_depth_set_LIMSEQ_closed:
  "x  global_depth_set n"
  if  lim  : "metric_space_by_bdd_depth.LIMSEQ X x"
  and depth: "F k in sequentially. X k  global_depth_set n"
proof-
  define Y and y
    where "Y  λk. global_uniformizer powi (-n) * X k"
    and   "y  global_uniformizer powi (-n) * x"
  from depth Y_def have "F k in sequentially. Y k  global_depth_set 0"
    using global_depth_set_eq_global_uniformizer_coset'[of n] eventually_sequentially by auto
  with Y_def y_def lim have "y  global_depth_set 0"
    using bdd_global_dist_limseq_global_uniformizer_powi_mult
          nonneg_global_depth_set_LIMSEQ_closed[of Y y 0]
    by    simp
  moreover from y_def have "global_uniformizer powi n * y = x"
    using global_uniformizer_powi_add[of n "-n"] by (simp add: ac_simps)
  ultimately show ?thesis
    using global_depth_set_eq_global_uniformizer_coset[of n] by blast
qed

end (* context global_p_equality_w_inj_index_consts *)


subsection ‹Notation›

consts
  p_equal :: "'a  'b  'b  bool"
  globally_p_equal ::
    "'b  'b  bool"
    ("(_/ p/ _)" [51, 51] 50)
  p_restrict :: "'b  ('a  bool)  'b" (infixl "prestrict" 70)
  p_depth :: "'a  'b  int"
  p_depth_set :: "'a  int  'b set" ("𝒫@__")
  global_depth_set :: "int  'b set" ("𝒫p_")
  global_unfrmzr_pows :: "('a  'c)  'b"

abbreviation p_equal_notation :: "'b  'a  'b  bool"
  ("(_/ ≃⇩_/ _)" [51, 51, 51] 50)
  where "p_equal_notation x p y  p_equal p x y"

abbreviation p_nequal_notation :: "'b  'a  'b  bool"
  ("(_/ ¬≃⇩_/ _)" [51, 51, 51] 50)
  where "p_nequal_notation x p y  ¬ p_equal p x y"

abbreviation p_depth_notation :: "'b  'a  int"
  ("(_°⇧_)" [51, 51] 50)
  where "p_depth_notation x p  p_depth p x"

abbreviation p_depth_set_0_notation :: "'a  'b set" ("𝒪@_")
  where "p_depth_set_0_notation p  p_depth_set p 0"

abbreviation p_depth_set_1_notation :: "'a  'b set" ("𝒫@_")
  where "p_depth_set_1_notation p  p_depth_set p 1"

abbreviation global_depth_set_0_notation :: "'b set" ("𝒪p")
  where "global_depth_set_0_notation  global_depth_set 0"

abbreviation global_depth_set_1_notation :: "'b set" ("𝒫p")
  where "global_depth_set_1_notation  global_depth_set 1"

abbreviation "𝔭  global_unfrmzr_pows"

end (* theory *)