Theory Furstenberg_Topology

(*
  File:     Furstenberg_Topology.thy
  Author:   Manuel Eberl, TU München
*)
section ‹Furstenberg's topology and his proof of the infinitude of primes›
theory Furstenberg_Topology
  imports 
    "HOL-Real_Asymp.Real_Asymp" 
    "HOL-Analysis.Analysis" 
    "HOL-Number_Theory.Number_Theory"
begin

text ‹
  This article gives a formal version of Furstenberg's topological proof of the infinitude of
  primes~cite"furstenberg". He defines a topology on the integers based on arithmetic progressions
  (or, equivalently, residue classes).

  Apart from yielding a short proof of the infinitude of primes, this topology is also fairly
  `nice' in general: it is second countable, metrizable, and perfect. All of these (well-known)
  facts will be formally proven below.
›

subsection ‹Arithmetic progressions of integers›

text ‹
  We first define `bidirectional infinite arithmetic progressions' on ℤ› in the sense that 
  to an integer a› and a positive integer b›, we associate all the integers x› such that
  $x \equiv a\ (\text{mod}\ b)$, or, equivalently, $\{a + nb\mid n\in\mathbb{Z}\}$.
›

definition arith_prog :: "int  nat  int set" where
  "arith_prog a b = {x. [x = a] (mod int b)}"

lemma arith_prog_0_right [simp]: "arith_prog a 0 = {a}"
  by (simp add: arith_prog_def)

lemma arith_prog_Suc_0_right [simp]: "arith_prog a (Suc 0) = UNIV"
  by (auto simp: arith_prog_def)

lemma in_arith_progI [intro]: "[x = a] (mod b)  x  arith_prog a b"
  by (auto simp: arith_prog_def)

text ‹
  Two arithmetic progressions with the same period and noncongruent starting points are
  disjoint.
›
lemma arith_prog_disjoint:
  assumes "[a  a'] (mod int b)" and "b > 0"
  shows   "arith_prog a b  arith_prog a' b = {}"
  using assms by (auto simp: arith_prog_def cong_def)

text ‹
  Multiplying the period gives us a subset of the original progression.
›
lemma arith_prog_dvd_mono: "b dvd b'  arith_prog a b'  arith_prog a b"
  by (auto simp: arith_prog_def cong_dvd_modulus)

text ‹
  The following proves the alternative definition mentioned above.
›
lemma bij_betw_arith_prog:
  assumes "b > 0"
  shows   "bij_betw (λn. a + int b * n) UNIV (arith_prog a b)"
proof (rule bij_betwI[of _ _ _ "λx. (x - a) div int b"], goal_cases)
  case 1
  thus ?case 
    by (auto simp: arith_prog_def cong_add_lcancel_0 cong_mult_self_right mult_of_nat_commute)
next
  case 4
  thus ?case
    by (auto simp: arith_prog_def cong_iff_lin)
qed (use b > 0 in auto simp: arith_prog_def)

lemma arith_prog_altdef: "arith_prog a b = range (λn. a + int b * n)"
proof (cases "b = 0")
  case False
  thus ?thesis
    using bij_betw_arith_prog[of b] by (auto simp: bij_betw_def)
qed auto

text ‹
  A simple corollary from this is also that any such arithmetic progression is infinite.
›
lemma infinite_arith_prog: "b > 0  infinite (arith_prog a b)"
  using bij_betw_finite[OF bij_betw_arith_prog[of b]] by simp


subsection ‹The Furstenberg topology on ℤ›

text ‹
  The typeclass-based topology is somewhat nicer to use in Isabelle/HOL, but the integers, 
  of course, already have a topology associated to them. We therefore need to introduce a type
  copy of the integers and furnish them with the new topology. We can easily convert between
  them and the `proper' integers using Lifting and Transfer.
›
typedef fbint = "UNIV :: int set"
  morphisms int_of_fbint fbint ..

setup_lifting type_definition_fbint

lift_definition arith_prog_fb :: "int  nat  fbint set" is "arith_prog" .

instantiation fbint :: topological_space
begin

text ‹
  Furstenberg defined the topology as the one generated by all arithmetic progressions.
  We use a slightly more explicit equivalent formulation that exploits the fact that
  the intersection of two arithmetic progressions is again an arithmetic progression (or empty).
›
lift_definition open_fbint :: "fbint set  bool" is
  "λU. (xU. b>0. arith_prog x b  U)" .

text ‹
  We now prove that this indeed forms a topology.
›
instance proof
  show "open (UNIV :: fbint set)"
    by transfer auto
next
  fix U V :: "fbint set"
  assume "open U" and "open V"
  show "open (U  V)"
  proof (use open U open V in transfer, safe)
    fix U V :: "int set" and x :: int
    assume U: "xU. b>0. arith_prog x b  U" and V: "xV. b>0. arith_prog x b  V"
    assume x: "x  U" "x  V"
    from U x obtain b1 where b1: "b1 > 0" "arith_prog x b1  U" by auto
    from V x obtain b2 where b2: "b2 > 0" "arith_prog x b2  V" by auto
    from b1 b2 have "lcm b1 b2 > 0" "arith_prog x (lcm b1 b2)  U  V"
      using arith_prog_dvd_mono[of b1 "lcm b1 b2" x] arith_prog_dvd_mono[of b2 "lcm b1 b2" x]
      by (auto simp: lcm_pos_nat)
    thus "b>0. arith_prog x b  U  V" by blast
  qed
next
  fix F :: "fbint set set"
  assume *: "UF. open U"
  show "open (F)"
  proof (use * in transfer, safe)
    fix F :: "int set set" and U :: "int set" and x :: int
    assume F: "UF. xU. b>0. arith_prog x b  U"
    assume "x  U" "U  F"
    with F obtain b where b: "b > 0" "arith_prog x b  U" by blast
    with U  F show "b>0. arith_prog x b  F"
      by blast
  qed
qed

end

text ‹
  Since any non-empty open set contains an arithmetic progression and arithmetic progressions
  are infinite, we obtain that all nonempty open sets are infinite.
›
lemma open_fbint_imp_infinite:
  fixes U :: "fbint set"
  assumes "open U" and "U  {}"
  shows   "infinite U"
  using assms
proof transfer
  fix U :: "int set"
  assume *: "xU. b>0. arith_prog x b  U" and "U  {}"
  from U  {} obtain x where "x  U" by auto
  with * obtain b where b: "b > 0" "arith_prog x b  U" by auto
  from b have "infinite (arith_prog x b)"
    using infinite_arith_prog by blast
  with b show "infinite U"
    using finite_subset by blast
qed

lemma not_open_finite_fbint [simp]:
  assumes "finite (U :: fbint set)" "U  {}"
  shows   "¬open U"
  using open_fbint_imp_infinite assms by blast

text ‹
  More or less by definition, any arithmetic progression is open.
›
lemma open_arith_prog_fb [intro]:
  assumes "b > 0"
  shows   "open (arith_prog_fb a b)"
  using assms
proof transfer
  fix a :: int and b :: nat
  assume "b > 0"
  show "xarith_prog a b. b'>0. arith_prog x b'  arith_prog a b"
  proof (intro ballI exI[of _ b] conjI)
    fix x assume "x  arith_prog a b"
    thus "arith_prog x b  arith_prog a b"
      using cong_trans by (auto simp: arith_prog_def )
  qed (use b > 0 in auto)
qed

text ‹
  Slightly less obviously, any arithmetic progression is also closed.
  This can be seen by realising that for a period b›, we can partition the integers
  into b› congruence classes and then the complement of each congruence class is the 
  union of the other b - 1› classes, and unions of open sets are open.
›
lemma closed_arith_prog_fb [intro]:
  assumes "b > 0"
  shows   "closed (arith_prog_fb a b)"
proof -
  have "open (-arith_prog_fb a b)"
  proof -
    have "-arith_prog_fb a b = (i{1..<b}. arith_prog_fb (a+i) b)"
    proof (transfer fixing: b)
      fix a :: int
      have disjoint: "x  arith_prog a b" if "x  arith_prog (a + int i) b" "i  {1..<b}" for x i
      proof -
        have "[a  a + int i] (mod int b)"
        proof
          assume "[a = a + int i] (mod int b)"
          hence "[a + 0 = a + int i] (mod int b)" by simp
          hence "[0 = int i] (mod int b)" by (subst (asm) cong_add_lcancel) auto
          with that show False by (auto simp: cong_def)
        qed
        thus ?thesis using arith_prog_disjoint[of a "a + int i" b] b > 0 that by auto
      qed

      have covering: "x  arith_prog a b  x  (i{1..<b}. arith_prog (a + int i) b)" for x
      proof -
        define i where "i = nat ((x - a) mod (int b))"
        have "[a + int i = a + (x - a) mod int b] (mod int b)"
          unfolding i_def using b > 0 by simp
        also have "[a + (x - a) mod int b = a + (x - a)] (mod int b)"
          by (intro cong_add) auto
        finally have "[x = a + int i] (mod int b)"
          by (simp add: cong_sym_eq)
        hence "x  arith_prog (a + int i) b"
          using b > 0 by (auto simp: arith_prog_def)
        moreover have "i < b" using b > 0 
          by (auto simp: i_def nat_less_iff)
        ultimately show ?thesis using b > 0
          by (cases "i = 0") auto
      qed

      from disjoint and covering show "- arith_prog a b = (i{1..<b}. arith_prog (a + int i) b)"
        by blast
    qed 
    also from b > 0 have "open "
      by auto
    finally show ?thesis .
  qed
  thus ?thesis by (simp add: closed_def)
qed

subsection ‹The infinitude of primes›

text ‹
  The infinite of the primes now follows quite obviously: The multiples of any prime form a
  closed set, so if there were only finitely many primes, the union of all of these would also
  be open. However, since any number other than ±1› has a prime divisor, the union of all these
  sets is simply ℤ∖{±1}›, which is obviously ‹not› closed since the finite set {±1}› is not
  open.
›
theorem "infinite {p::nat. prime p}"
proof  
  assume fin: "finite {p::nat. prime p}"
  define A where "A = (p{p::nat. prime p}. arith_prog_fb 0 p)"
  have "closed A"
    unfolding A_def using fin by (intro closed_Union) (auto simp: prime_gt_0_nat)
  hence "open (-A)"
    by (simp add: closed_def)
  also have "A = -{fbint 1, fbint (-1)}"
    unfolding A_def
  proof transfer
    show "(p{p::nat. prime p}. arith_prog 0 p) = - {1, - 1}"
    proof (intro equalityI subsetI)
      fix x :: int assume x: "x  -{1, -1}"
      hence "¦x¦  1" by auto
      show "x  (p{p::nat. prime p}. arith_prog 0 p)"
      proof (cases "x = 0")
        case True
        thus ?thesis
          by (auto simp: A_def intro!: exI[of _ 2])
      next
        case [simp]: False
        obtain p where p: "prime p" "p dvd x"
          using prime_divisor_exists[of x] and ¦x¦  1 by auto
        hence "x  arith_prog 0 (nat p)" using prime_gt_0_int[of p]
          by (auto simp: arith_prog_def cong_0_iff)
        thus ?thesis using p
          by (auto simp: A_def intro!: exI[of _ "nat p"])
      qed
    qed (auto simp: A_def arith_prog_def cong_0_iff)
  qed
  also have "-(-{fbint 1, fbint (-1)}) = {fbint 1, fbint (-1)}"
    by simp
  finally have "open {fbint 1, fbint (-1)}" .
  thus False by simp
qed




subsection ‹Additional topological properties›

text ‹
  Just for fun, let us also show a few more properties of Furstenberg's topology.
  First, we show the equivalence to the above to Furstenberg's original definition
  (the topology generated by all arithmetic progressions).
›

theorem topological_basis_fbint: "topological_basis {arith_prog_fb a b |a b. b > 0}"
  unfolding topological_basis_def
proof safe
  fix a :: int and b :: nat
  assume "b > 0"
  thus "open (arith_prog_fb a b)"
    by auto
next
  fix U :: "fbint set" assume "open U"
  hence "xU. b. b > 0  arith_prog_fb (int_of_fbint x) b  U"
    by transfer
  hence "f. xU. f x > 0  arith_prog_fb (int_of_fbint x) (f x)  U"
    by (subst (asm) bchoice_iff)
  then obtain f where f: "xU. f x > 0  arith_prog_fb (int_of_fbint x) (f x)  U" ..
  define B where "B = (λx. arith_prog_fb (int_of_fbint x) (f x)) ` U"
  have "B  {arith_prog_fb a b |a b. b > 0}"
    using f by (auto simp: B_def)
  moreover have "B = U"
  proof safe
    fix x assume "x  U"
    hence "x  arith_prog_fb (int_of_fbint x) (f x)"
      using f by transfer auto
    with x  U show "x  B" by (auto simp: B_def)
  qed (use f in auto simp: B_def)
  ultimately show "B'{arith_prog_fb a b |a b. 0 < b}.  B' = U" by auto
qed
    
lemma open_fbint_altdef: "open = generate_topology {arith_prog_fb a b |a b. b > 0}"
  using topological_basis_imp_subbasis[OF topological_basis_fbint] .


text ‹
  From this, we can immediately see that it is second countable:
›
instance fbint :: second_countable_topology
proof
  have "countable ((λ(a,b). arith_prog_fb a b) ` (UNIV × {b. b > 0}))"
    by (intro countable_image) auto
  also have " = {arith_prog_fb a b |a b. b > 0}"
    by auto
  ultimately show "B::fbint set set. countable B  open = generate_topology B"
    unfolding open_fbint_altdef by auto
qed

text ‹
  A trivial consequence of the fact that nonempty open sets in this topology are infinite
  is that it is a perfect space:
›
instance fbint :: perfect_space
  by standard auto


text ‹
  It is also Hausdorff, since given any two distinct integers, we can easily
  construct two non-overlapping arithmetic progressions that each contain one of them.
  We do not ‹really› have to prove this since we will get it for free later on when we
  show that it is a metric space, but here is the proof anyway:
›
instance fbint :: t2_space
proof
  fix x y :: fbint
  assume "x  y"
  define d where "d = nat ¦int_of_fbint x - int_of_fbint y¦ + 1"
  from x  y have "d > 0"
    unfolding d_def by transfer auto
  define U where "U = arith_prog_fb (int_of_fbint x) d"
  define V where "V = arith_prog_fb (int_of_fbint y) d"

  have "U  V = {}" unfolding U_def V_def d_def
  proof (use x  y in transfer, rule arith_prog_disjoint)
    fix x y :: int
    assume "x  y"
    show "[x  y] (mod int (nat ¦x - y¦ + 1))"
    proof
      assume "[x = y] (mod int (nat ¦x - y¦ + 1))"
      hence "¦x - y¦ + 1 dvd ¦x - y¦"
        by (auto simp: cong_iff_dvd_diff algebra_simps)
      hence "¦x - y¦ + 1  ¦x - y¦"
        by (rule zdvd_imp_le) (use x  y in auto)
      thus False by simp
    qed
  qed auto
  moreover have "x  U" "y  V"
    unfolding U_def V_def by (use d > 0 in transfer, fastforce)+
  moreover have "open U" "open V"
    using d > 0 by (auto simp: U_def V_def)
  ultimately show "U V. open U  open V  x  U  y  V  U  V = {}" by blast
qed

(* TODO Move? *)
text ‹
  Next, we need a small lemma: Given an additional assumption, a $T_2$ space is also $T_3$:
›
lemma t2_space_t3_spaceI:
  assumes "(x :: 'a :: t2_space) U. x  U  open U 
             V. x  V  open V  closure V  U"
  shows   "OFCLASS('a, t3_space_class)"
proof
  fix X :: "'a set" and z :: 'a
  assume X: "closed X" "z  X"
  with assms[of z "-X"] obtain V where V: "z  V" "open V" "closure V  -X"
    by auto
  show "U V. open U  open V  z  U  X  V  U  V = {}"
    by (rule exI[of _ V], rule exI[of _ "-closure V"])
       (use X V closure_subset[of V] in auto)
qed  

text ‹
  Since the Furstenberg topology is $T_2$ and every arithmetic progression is also closed,
  we can now easily show that it is also $T_3$ (i.\,e.\ regular). 
  Again, we do not really need this proof, but here it is:
›
instance fbint :: t3_space
proof (rule t2_space_t3_spaceI)
  fix x :: fbint and U :: "fbint set"
  assume "x  U" and "open U"
  then obtain b where b: "b > 0" "arith_prog_fb (int_of_fbint x) b  U"
    by transfer blast
  define V where "V = arith_prog_fb (int_of_fbint x) b"
  have "x  V"
    unfolding V_def by transfer auto
  moreover have "open V" "closed V"
    using b > 0 by (auto simp: V_def)
  ultimately show "V. x  V  open V  closure V  U"
    using b by (intro exI[of _ V]) (auto simp: V_def)
qed


subsection ‹Metrizability›

text ‹
  The metrizability of Furstenberg's topology (i.\,e.\ that it is induced by some metric) can
  be shown from the fact that it is second countable and $T_3$ using Urysohn's Metrization Theorem, 
  but this is not available in Isabelle yet. Let us therefore give an ‹explicit› metric, 
  as described by Zulfeqarr~cite"zulfeqarr". We follow the exposition by Dirmeier~cite"dirmeier".

  First, we define a kind of norm on the integers. The norm depends on a real parameter q > 1›.
  The value of q› does not matter in the sense that all values induce the same topology
  (which we will show). For the final definition, we then simply pick q = 2›.
›

locale fbnorm =
  fixes q :: "real"
  assumes q_gt_1: "q > 1"
begin

definition N :: "int  real" where
  "N n = (k. if k = 0  int k dvd n then 0 else 1 / q ^ k)"

lemma N_summable: "summable (λk. if k = 0  int k dvd n then 0 else 1 / q ^ k)"
  by (rule summable_comparison_test[OF _ summable_geometric[of "1/q"]])
     (use q_gt_1 in auto intro!: exI[of _ 0] simp: power_divide)

lemma N_sums: "(λk. if k = 0  int k dvd n then 0 else 1 / q ^ k) sums N n"
  using N_summable unfolding N_def by (rule summable_sums)

lemma N_nonneg: "N n  0"
  by (rule sums_le[OF _ sums_zero N_sums]) (use q_gt_1 in auto)

lemma N_uminus [simp]: "N (-n) = N n"
  by (simp add: N_def)

lemma N_minus_commute: "N (x - y) = N (y - x)"
  using N_uminus[of "x - y"] by (simp del: N_uminus)

lemma N_zero [simp]: "N 0 = 0"
  by (simp add: N_def)

lemma not_dvd_imp_N_ge:
  assumes "¬n dvd a" "n > 0"
  shows   "N a  1 / q ^ n"
  by (rule sums_le[OF _ sums_single[of n] N_sums]) (use q_gt_1 assms in auto)

lemma N_lt_imp_dvd:
  assumes "N a < 1 / q ^ n" and "n > 0"
  shows   "n dvd a"
  using not_dvd_imp_N_ge[of n a] assms by auto

lemma N_pos:
  assumes "n  0"
  shows   "N n > 0"
proof -
  have "0 < 1 / q ^ (nat ¦n¦+1)"
    using q_gt_1 by simp
  also have "¬1 + ¦n¦ dvd ¦n¦"
    using zdvd_imp_le[of "1 + ¦n¦" "¦n¦"] assms by auto
  hence "1 / q ^ (nat ¦n¦+1)  N n"
    by (intro not_dvd_imp_N_ge) (use assms in auto)
  finally show ?thesis .
qed

lemma N_zero_iff [simp]: "N n = 0  n = 0"
  using N_pos[of n] by (cases "n = 0") auto

lemma N_triangle_ineq: "N (n + m)  N n + N m"
proof (rule sums_le)
  let ?I = "λn k. if k = 0  int k dvd n then 0 else 1 / q ^ k"
  show "?I (n + m) sums N (n + m)"
    by (rule N_sums)
  show "(λk. ?I n k + ?I m k) sums (N n + N m)"
    by (intro sums_add N_sums)
qed (use q_gt_1 in auto)

lemma N_1: "N 1 = 1 / (q * (q - 1))"
proof (rule sums_unique2)
  have "(λk. if k = 0  int k dvd 1 then 0 else 1 / q ^ k) sums N 1"
    by (rule N_sums)
  also have "(λk. if k = 0  int k dvd 1 then 0 else 1 / q ^ k) =
               (λk. if k  {0, 1} then 0 else (1 / q) ^ k)"
    by (simp add: power_divide cong: if_cong)
  finally show "(λk. if k  {0, 1} then 0 else (1 / q) ^ k) sums N 1" .

  have "(λk. if k  {0, 1} then 0 else (1 / q) ^ k) sums
                 (1 / (1 - 1 / q) + (- (1 / q) - 1))"
    by (rule sums_If_finite_set'[OF geometric_sums]) (use q_gt_1 in auto)
  also have " = 1 / (q * (q - 1))"
    using q_gt_1 by (simp add: field_simps)
  finally show "(λk. if k  {0, 1} then 0 else (1 / q) ^ k) sums " .
qed

text ‹
  It follows directly from the definition that norms fulfil a kind of monotonicity property
  with respect to divisibility: the norm of a number is at most as large as the norm of any of
  its factors:
›
lemma N_dvd_mono:
  assumes "m dvd n"
  shows   "N n  N m"
proof (rule sums_le[OF _ N_sums N_sums])
  fix k :: nat
  show "(if k = 0  int k dvd n then 0 else 1 / q ^ k) 
        (if k = 0  int k dvd m then 0 else 1 / q ^ k)"
    using q_gt_1 assms by auto
qed

text ‹
  In particular, this means that 1 and -1 have the greatest norm.
›
lemma N_le_N_1: "N n  N 1"
  by (rule N_dvd_mono) auto

text ‹
  Primes have relatively large norms, almost reaching the norm of 1:
›
lemma N_prime:
  assumes "prime p"
  shows   "N p = N 1 - 1 / q ^ nat p"
proof (rule sums_unique2)
  define p' where "p' = nat p"
  have p: "p = int p'"
    using assms by (auto simp: p'_def prime_ge_0_int)
  have "prime p'"
    using assms by (simp add: p)

  have "(λk. if k = 0  int k dvd p then 0 else 1 / q ^ k) sums N p"
    by (rule N_sums)
  also have "int k dvd p  k  {1, p'}" for k
    using assms by (auto simp: p prime_nat_iff)
  hence "(λk. if k = 0  int k dvd p then 0 else 1 / q ^ k) =
         (λk. if k  {0, 1, p'} then 0 else (1 / q) ^ k)"
    using assms q_gt_1 by (simp add: power_divide cong: if_cong)
  finally show " sums N p" .

  have "(λk. if k  {0, 1, p'} then 0 else (1 / q) ^ k) sums
                 (1 / (1 - 1 / q) + (- (1 / q) - (1 / q) ^ p' - 1))"
    by (rule sums_If_finite_set'[OF geometric_sums])
       (use prime p' q_gt_1 prime_gt_Suc_0_nat[of p'] in auto simp:)
  also have " = N 1 - 1 / q ^ p'"
    using q_gt_1 by (simp add: field_simps N_1)
  finally show "(λk. if k  {0, 1, p'} then 0 else (1 / q) ^ k) sums " .
qed

lemma N_2: "N 2 = 1 / (q ^ 2 * (q - 1))"
  using q_gt_1 by (auto simp: N_prime N_1 field_simps power2_eq_square)

lemma N_less_N_1:
  assumes "n  1" "n  -1"
  shows   "N n < N 1"
proof (cases "n = 0")
  case False
  then obtain p where p: "prime p" "p dvd n"
    using prime_divisor_exists[of n] assms by force
  hence "N n  N p" by (intro N_dvd_mono)
  also from p have "N p < N 1"
    using q_gt_1 by (simp add: N_prime)
  finally show ?thesis .
qed (use q_gt_1 in auto simp: N_1)

text ‹
  Composites, on the other hand, do not achieve this:
›
lemma nonprime_imp_N_lt:
  assumes "¬prime_elem n" "¦n¦  1" "n  0"
  shows   "N n < N 1 - 1 / q ^ nat ¦n¦"
proof -
  obtain p where p: "prime p" "p dvd n"
    using prime_divisor_exists[of n] assms by auto
  define p' where "p' = nat p"
  have p': "p = int p'"
    using p by (auto simp: p'_def prime_ge_0_int)
  have "prime p'"
    using p by (simp add: p')

  define n' where "n' = nat ¦n¦"
  have "n' > 1"
    using assms by (auto simp: n'_def)

  have "N n  1 / (q * (q - 1)) - 1 / q ^ p' - 1 / q ^ n'"
  proof (rule sums_le)
    show "(λk. if k = 0  int k dvd n then 0 else 1 / q ^ k) sums N n"
      by (rule N_sums)
  next
    from assms p have "n'  p'"
      by (auto simp: n'_def p'_def nat_eq_iff)
    hence "(λk. if k  {0, 1, p', n'} then 0 else (1 / q) ^ k) sums
                   (1 / (1 - 1 / q) + (- (1 / q) - (1 / q) ^ p' - (1 / q) ^ n' - 1))"
      by (intro sums_If_finite_set'[OF geometric_sums])
         (use prime p' q_gt_1 prime_gt_Suc_0_nat[of p'] n' > 1 in auto simp:)
    also have " = 1 / (q * (q - 1)) - 1 / q ^ p' - 1 / q ^ n'"
      using q_gt_1 by (simp add: field_simps)
    finally show "(λk. if k  {0, 1, p', n'} then 0 else (1 / q) ^ k) sums " .
  next
    show "k. (if k = 0  int k dvd n then 0 else 1 / q ^ k)
          (if k  {0, 1, p', n'} then 0 else (1 / q) ^ k)"
      using q_gt_1 p by (auto simp: p'_def n'_def power_divide)
  qed
  also have " < 1 / (q * (q - 1)) - 1 / q ^ n'"
    using q_gt_1 by simp
  finally show ?thesis by (simp add: n'_def N_1)
qed

text ‹
  This implies that one can use the norm as a primality test:
›
lemma prime_iff_N_eq:
  assumes "n  0"
  shows   "prime_elem n  N n = N 1 - 1 / q ^ nat ¦n¦"
proof -
  have *: "prime_elem n  N n = N 1 - 1 / q ^ nat ¦n¦" if "n > 0" for n
  proof -
    consider "n = 1" | "prime n" | "¬prime n" "n > 1"
      using n > 0 by force
    thus ?thesis
    proof cases
      assume "n = 1"
      thus ?thesis using q_gt_1
        by (auto simp: N_1)
    next
      assume n: "¬prime n" "n > 1"
      with nonprime_imp_N_lt[of n] show ?thesis by simp
    qed (auto simp: N_prime prime_ge_0_int)
  qed

  show ?thesis
  proof (cases "n > 0")
    case True
    with * show ?thesis by blast
  next
    case False
    with *[of "-n"] assms show ?thesis by simp
  qed
qed

text ‹
  Factorials, on the other hand, have very small norms:
›
lemma N_fact_le: "N (fact m)  1 / (q - 1) * 1 / q ^ m"
proof (rule sums_le[OF _ N_sums])
  have "(λk. 1 / q ^ k / q ^ Suc m) sums (q / (q - 1) / q ^ Suc m)"
    using geometric_sums[of "1 / q"] q_gt_1 
    by (intro sums_divide) (auto simp: field_simps)
  also have "(q / (q - 1) / q ^ Suc m) = 1 / (q - 1) * 1 / q ^ m"
    using q_gt_1 by (simp add: field_simps)
  also have "(λk. 1 / q ^ k / q ^ Suc m) = (λk. 1 / q ^ (k + Suc m))"
    using q_gt_1 by (simp add: field_simps power_add)
  also have " = (λk. if k + Suc m  m then 0 else 1 / q ^ (k + Suc m))"
    by auto
  finally have " sums (1 / (q - 1) * 1 / q ^ m)" .
  also have "?this  (λk. if k  m then 0 else 1 / q ^ k) sums (1 / (q - 1) * 1 / q ^ m)"
    by (rule sums_zero_iff_shift) auto
  finally show  .
next
  fix k :: nat
  have "int k dvd fact m" if "k > 0" "k  m"
  proof -
    have "int k dvd int (fact m)"
      unfolding int_dvd_int_iff using that by (simp add: dvd_fact)
    thus "int k dvd fact m"
      unfolding of_nat_fact by simp
  qed  
  thus "(if k = 0  int k dvd fact m then 0 else 1 / q ^ k) 
        (if k  m then 0 else 1 / q ^ k)" using q_gt_1 by auto
qed

lemma N_prime_mono:
  assumes "prime p" "prime p'" "p  p'"
  shows   "N p  N p'"
  using assms q_gt_1 by (auto simp add: N_prime field_simps nat_le_iff prime_ge_0_int)

lemma N_prime_ge:
  assumes "prime p"
  shows   "N p  1 / (q2 * (q - 1))"
proof -
  have "1 / (q ^ 2 * (q - 1)) = N 2"
    using q_gt_1 by (auto simp: N_prime N_1 field_simps power2_eq_square)
  also have "  N p"
    using assms by (intro N_prime_mono) (auto simp: prime_ge_2_int)
  finally show ?thesis .
qed

lemma N_prime_elem_ge:
  assumes "prime_elem p"
  shows   "N p  1 / (q2 * (q - 1))"
proof (cases "p  0")
  case True
  with assms N_prime_ge show ?thesis by auto
next
  case False
  with assms N_prime_ge[of "-p"] show ?thesis by auto
qed


text ‹
  Next, we use this norm to derive a metric:
›

lift_definition dist :: "fbint  fbint  real" is
  "λx y. N (x - y)" .

lemma dist_self [simp]: "dist x x = 0"
  by transfer simp

lemma dist_sym [simp]: "dist x y = dist y x"
  by transfer (simp add: N_minus_commute)

lemma dist_pos: "x  y  dist x y > 0"
  by transfer (use N_pos in simp)

lemma dist_eq_0_iff [simp]: "dist x y = 0  x = y"
  using dist_pos[of x y] by (cases "x = y") auto

lemma dist_triangle_ineq: "dist x z  dist x y + dist y z"
proof transfer
  fix x y z :: int
  show "N (x - z)  N (x - y) + N (y - z)"
    using N_triangle_ineq[of "x - y" "y - z"] by simp
qed


text ‹
  Lastly, we show that the metric we defined indeed induces the Furstenberg topology.
›
theorem dist_induces_open:
  "open U  (xU. e>0. y. dist x y < e  y  U)"
proof (transfer, safe)
  fix U :: "int set" and x :: int
  assume *: "xU. b>0. arith_prog x b  U"
  assume "x  U"
  with * obtain b where b: "b > 0" "arith_prog x b  U" by blast
  define e where "e = 1 / q ^ b"

  show "e>0. y. N (x - y) < e  y  U"
  proof (rule exI; safe?)
    show "e > 0" using q_gt_1 by (simp add: e_def)
  next
    fix y assume "N (x - y) < e"
    also have " = 1 / q ^ b" by fact
    finally have "b dvd (x - y)"
      by (rule N_lt_imp_dvd) fact
    hence "y  arith_prog x b"
      by (auto simp: arith_prog_def cong_iff_dvd_diff dvd_diff_commute)
    with b show "y  U" by blast
  qed

next

  fix U :: "int set" and x :: int
  assume *: "xU. e>0. y. N (x - y) < e  y  U"
  assume "x  U"
  with * obtain e where e: "e > 0" "y. N (x - y) < e  y  U" by blast
  have "eventually (λN. 1 / (q - 1) * 1 / q ^ N < e) at_top"
    using q_gt_1 e > 0 by real_asymp
  then obtain m where m: "1 / (q - 1) * 1 / q ^ m < e"
    by (auto simp: eventually_at_top_linorder)
  define b :: nat where "b = fact m"

  have "arith_prog x b  U"
  proof
    fix y assume "y  arith_prog x b"
    show "y  U"
    proof (cases "y = x")
      case False
      from y  arith_prog x b obtain n where y: "y = x + int b * n"
        by (auto simp: arith_prog_altdef)
      from y and y  x have [simp]: "n  0" by auto
      have "N (x - y) = N (int b * n)" by (simp add: y)
      also have "  N (int b)"
        by (rule N_dvd_mono) auto
      also have "  1 / (q - 1) * 1 / q ^ m"
        using N_fact_le by (simp add: b_def)
      also have " < e" by fact
      finally show "y  U" using e by auto
    qed (use x  U in auto)
  qed
  moreover have "b > 0" by (auto simp: b_def)
  ultimately show "b>0. arith_prog x b  U"
    by blast
qed
  
end


text ‹
  We now show that the Furstenberg space is a metric space with this metric (with q = 2›),
  which essentially only amounts to plugging together all the results from above.
›

interpretation fb: fbnorm 2
  by standard auto


instantiation fbint :: dist
begin

definition dist_fbint where "dist_fbint = fb.dist"

instance ..

end


instantiation fbint :: uniformity_dist
begin

definition uniformity_fbint :: "(fbint × fbint) filter" where
  "uniformity_fbint = (INF e{0 <..}. principal {(x, y). dist x y < e})"

instance by standard (simp add: uniformity_fbint_def)

end


instance fbint :: open_uniformity
proof
  fix U :: "fbint set"
  show "open U = (xU. eventually (λ(x',y). x' = x  y  U) uniformity)"
    unfolding eventually_uniformity_metric dist_fbint_def
    using fb.dist_induces_open by simp
qed


instance fbint :: metric_space
  by standard (use fb.dist_triangle_ineq in auto simp: dist_fbint_def)

text ‹
  In particular, we can now show that the sequence n!› tends to 0 in the Furstenberg topology:
›
lemma tendsto_fbint_fact: "(λn. fbint (fact n))  fbint 0"
proof -
  have "(λn. dist (fbint (fact n)) (fbint 0))  0"
  proof (rule tendsto_sandwich[OF always_eventually always_eventually]; safe?)
    fix n :: nat
    show "dist (fbint (fact n)) (fbint 0)  1 / 2 ^ n"
      unfolding dist_fbint_def by (transfer fixing: n) (use fb.N_fact_le[of n] in simp)
    show "dist (fbint (fact n)) (fbint 0)  0"
      by simp
    show "(λn. 1 / 2 ^ n :: real)  0"
      by real_asymp
  qed simp_all
  thus ?thesis
    using tendsto_dist_iff by metis
qed

end