/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Shing Tak Lam, Mario Carneiro
-/
import data.int.modeq
import data.nat.log
import data.nat.parity
import data.list.indexes
import data.list.palindrome
import tactic.interval_cases
import tactic.linarith

/-!
# Digits of a natural number

This provides a basic API for extracting the digits of a natural number in a given base,
and reconstructing numbers from their digits.

We also prove some divisibility tests based on digits, in particular completing
Theorem #85 from https://www.cs.ru.nl/~freek/100/.

A basic `norm_digits` tactic is also provided for proving goals of the form
`nat.digits a b = l` where `a` and `b` are numerals.
-/

namespace nat
variables {n : ℕ}

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digits_aux_0 : ℕ → list ℕ
| 0 := []
| (n+1) := [n+1]

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digits_aux_1 (n : ℕ) : list ℕ := list.repeat 1 n

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digits_aux (b : ℕ) (h : 2 ≤ b) : ℕ → list ℕ
| 0 := []
| (n+1) :=
  have (n+1)/b < n+1 := nat.div_lt_self (nat.succ_pos _) h,
  (n+1) % b :: digits_aux ((n+1)/b)

@[simp] lemma digits_aux_zero (b : ℕ) (h : 2 ≤ b) : digits_aux b h 0 = [] := by rw digits_aux

lemma digits_aux_def (b : ℕ) (h : 2 ≤ b) (n : ℕ) (w : 0 < n) :
  digits_aux b h n = n % b :: digits_aux b h (n/b) :=
begin
  cases n,
  { cases w, },
  { rw [digits_aux], }
end

/--
`digits b n` gives the digits, in little-endian order,
of a natural number `n` in a specified base `b`.

In any base, we have `of_digits b L = L.foldr (λ x y, x + b * y) 0`.
* For any `2 ≤ b`, we have `l < b` for any `l ∈ digits b n`,
  and the last digit is not zero.
  This uniquely specifies the behaviour of `digits b`.
* For `b = 1`, we define `digits 1 n = list.repeat 1 n`.
* For `b = 0`, we define `digits 0 n = [n]`, except `digits 0 0 = []`.

Note this differs from the existing `nat.to_digits` in core, which is used for printing numerals.
In particular, `nat.to_digits b 0 = [0]`, while `digits b 0 = []`.
-/
def digits : ℕ → ℕ → list ℕ
| 0 := digits_aux_0
| 1 := digits_aux_1
| (b+2) := digits_aux (b+2) (by norm_num)

@[simp] lemma digits_zero (b : ℕ) : digits b 0 = [] :=
by rcases b with _|⟨_|⟨_⟩⟩; simp [digits, digits_aux_0, digits_aux_1]

@[simp] lemma digits_zero_zero : digits 0 0 = [] := rfl

@[simp] lemma digits_zero_succ (n : ℕ) : digits 0 (n.succ) = [n+1] := rfl

theorem digits_zero_succ' : ∀ {n : ℕ} (w : 0 < n), digits 0 n = [n]
| 0 h := absurd h dec_trivial
| (n+1) _ := rfl

@[simp] lemma digits_one (n : ℕ) : digits 1 n = list.repeat 1 n := rfl

@[simp] lemma digits_one_succ (n : ℕ) : digits 1 (n + 1) = 1 :: digits 1 n := rfl

@[simp] lemma digits_add_two_add_one (b n : ℕ) :
  digits (b+2) (n+1) = (((n+1) % (b+2)) :: digits (b+2) ((n+1) / (b+2))) :=
by { rw [digits, digits_aux_def], exact succ_pos n }

theorem digits_def' : ∀ {b : ℕ} (h : 2 ≤ b) {n : ℕ} (w : 0 < n),
  digits b n = n % b :: digits b (n/b)
| 0 h := absurd h dec_trivial
| 1 h := absurd h dec_trivial
| (b+2) h := digits_aux_def _ _

@[simp]
lemma digits_of_lt (b x : ℕ) (w₁ : 0 < x) (w₂ : x < b) : digits b x = [x] :=
begin
  cases b,
  { cases w₂ },
  { cases b,
    { interval_cases x, },
    { cases x,
      { cases w₁, },
      { rw [digits_add_two_add_one, nat.div_eq_of_lt w₂, digits_zero, nat.mod_eq_of_lt w₂] } } }
end

lemma digits_add (b : ℕ) (h : 2 ≤ b) (x y : ℕ) (w : x < b) (w' : 0 < x ∨ 0 < y) :
  digits b (x + b * y) = x :: digits b y :=
begin
  cases b,
  { cases h, },
  { cases b,
    { norm_num at h, },
    { cases y,
      { norm_num at w',
        simp [w, w'], },
      dsimp [digits],
      rw digits_aux_def,
      { congr,
        { simp [nat.add_mod, nat.mod_eq_of_lt w], },
        { simp [mul_comm (b+2), nat.add_mul_div_right, nat.div_eq_of_lt w], } },
      { apply nat.succ_pos, }, }, },
end

/--
`of_digits b L` takes a list `L` of natural numbers, and interprets them
as a number in semiring, as the little-endian digits in base `b`.
-/
-- If we had a function converting a list into a polynomial,
-- and appropriate lemmas about that function,
-- we could rewrite this in terms of that.
def of_digits {α : Type*} [semiring α] (b : α) : list ℕ → α
| [] := 0
| (h :: t) := h + b * of_digits t

lemma of_digits_eq_foldr {α : Type*} [semiring α] (b : α) (L : list ℕ) :
  of_digits b L = L.foldr (λ x y, x + b * y) 0 :=
begin
  induction L with d L ih,
  { refl, },
  { dsimp [of_digits], rw ih, },
end

lemma of_digits_eq_sum_map_with_index_aux (b : ℕ) (l : list ℕ) :
  ((list.range l.length).zip_with ((λ (i a : ℕ), a * b ^ i) ∘ succ) l).sum =
  b * ((list.range l.length).zip_with (λ i a, a * b ^ i) l).sum :=
begin
  suffices : (list.range l.length).zip_with (((λ (i a : ℕ), a * b ^ i) ∘ succ)) l =
      (list.range l.length).zip_with (λ i a, b * (a * b ^ i)) l,
  { simp [this] },
  congr,
  ext,
  simp [pow_succ],
  ring
end

lemma of_digits_eq_sum_map_with_index (b : ℕ) (L : list ℕ):
  of_digits b L = (L.map_with_index (λ i a, a * b ^ i)).sum :=
begin
  rw [list.map_with_index_eq_enum_map, list.enum_eq_zip_range,
      list.map_uncurry_zip_eq_zip_with, of_digits_eq_foldr],
  induction L with hd tl hl,
  { simp },
  { simpa [list.range_succ_eq_map, list.zip_with_map_left, of_digits_eq_sum_map_with_index_aux]
      using or.inl hl }
end

@[simp] lemma of_digits_singleton {b n : ℕ} : of_digits b [n] = n := by simp [of_digits]

@[simp] lemma of_digits_one_cons {α : Type*} [semiring α] (h : ℕ) (L : list ℕ) :
  of_digits (1 : α) (h :: L) = h + of_digits 1 L :=
by simp [of_digits]

lemma of_digits_append {b : ℕ} {l1 l2 : list ℕ} :
  of_digits b (l1 ++ l2) = of_digits b l1 + b^(l1.length) * of_digits b l2 :=
begin
  induction l1 with hd tl IH,
  { simp [of_digits] },
  { rw [of_digits, list.cons_append, of_digits, IH, list.length_cons, pow_succ'],
    ring }
end

@[norm_cast] lemma coe_of_digits (α : Type*) [semiring α] (b : ℕ) (L : list ℕ) :
  ((of_digits b L : ℕ) : α) = of_digits (b : α) L :=
begin
  induction L with d L ih,
  { simp [of_digits], },
  { dsimp [of_digits], push_cast, rw ih, }
end

@[norm_cast] lemma coe_int_of_digits (b : ℕ) (L : list ℕ) :
  ((of_digits b L : ℕ) : ℤ) = of_digits (b : ℤ) L :=
begin
  induction L with d L ih,
  { refl, },
  { dsimp [of_digits], push_cast }
end

lemma digits_zero_of_eq_zero {b : ℕ} (h : 1 ≤ b) {L : list ℕ} (w : of_digits b L = 0) :
  ∀ l ∈ L, l = 0 :=
begin
  induction L with d L ih,
  { intros l m,
    cases m, },
  { intros l m,
    dsimp [of_digits] at w,
    rcases m with ⟨rfl⟩,
    { apply nat.eq_zero_of_add_eq_zero_right w },
    { exact ih ((nat.mul_right_inj h).mp (nat.eq_zero_of_add_eq_zero_left w)) _ m, }, }
end

lemma digits_of_digits
  (b : ℕ) (h : 2 ≤ b) (L : list ℕ)
  (w₁ : ∀ l ∈ L, l < b) (w₂ : ∀ (h : L ≠ []), L.last h ≠ 0) :
  digits b (of_digits b L) = L :=
begin
  induction L with d L ih,
  { dsimp [of_digits], simp },
  { dsimp [of_digits],
    replace w₂ := w₂ (by simp),
    rw digits_add b h,
    { rw ih,
      { intros l m, apply w₁, exact list.mem_cons_of_mem _ m, },
      { intro h,
        { rw [list.last_cons h] at w₂,
            convert w₂, }}},
    { exact w₁ d (list.mem_cons_self _ _) },
    { by_cases h' : L = [],
      { rcases h' with rfl,
        simp at w₂,
        left,
        apply nat.pos_of_ne_zero,
        exact w₂ },
      { right,
        apply nat.pos_of_ne_zero,
        contrapose! w₂,
        apply digits_zero_of_eq_zero _ w₂,
        { rw list.last_cons h',
          exact list.last_mem h', },
        { exact le_of_lt h, }, }, }, },
end

lemma of_digits_digits (b n : ℕ) : of_digits b (digits b n) = n :=
begin
  cases b with b,
  { cases n with n,
    { refl, },
    { change of_digits 0 [n+1] = n+1,
      dsimp [of_digits],
      simp, } },
  { cases b with b,
    { induction n with n ih,
      { refl, },
      { simp only [ih, add_comm 1, of_digits_one_cons, nat.cast_id, digits_one_succ], } },
    { apply nat.strong_induction_on n _, clear n,
      intros n h,
      cases n,
      { rw digits_zero, refl, },
      { simp only [nat.succ_eq_add_one, digits_add_two_add_one],
        dsimp [of_digits],
        rw h _ (nat.div_lt_self' n b),
        rw [nat.mod_add_div], }, }, },
end

lemma of_digits_one (L : list ℕ) : of_digits 1 L = L.sum :=
begin
  induction L with d L ih,
  { refl, },
  { simp [of_digits, list.sum_cons, ih], }
end

/-!
### Properties

This section contains various lemmas of properties relating to `digits` and `of_digits`.
-/

lemma digits_eq_nil_iff_eq_zero {b n : ℕ} : digits b n = [] ↔ n = 0 :=
begin
  split,
  { intro h,
    have : of_digits b (digits b n) = of_digits b [], by rw h,
    convert this,
    rw of_digits_digits },
  { rintro rfl,
    simp }
end

lemma digits_ne_nil_iff_ne_zero {b n : ℕ} : digits b n ≠ [] ↔ n ≠ 0 :=
not_congr digits_eq_nil_iff_eq_zero

lemma digits_eq_cons_digits_div {b n : ℕ} (h : 2 ≤ b) (w : 0 < n) :
  digits b n = ((n % b) :: digits b (n / b)) :=
begin
  rcases b with _|_|b,
  { rw [digits_zero_succ' w, nat.mod_zero, nat.div_zero, nat.digits_zero_zero] },
  { norm_num at h },
  rcases n with _|n,
  { norm_num at w },
  simp,
end

lemma digits_last {b : ℕ} (m : ℕ) (h : 2 ≤ b) (p q) :
  (digits b m).last p = (digits b (m/b)).last q :=
begin
  by_cases hm : m = 0,
  { simp [hm], },
  simp only [digits_eq_cons_digits_div h (nat.pos_of_ne_zero hm)],
  rw list.last_cons,
end

lemma digits.injective (b : ℕ) : function.injective b.digits :=
function.left_inverse.injective (of_digits_digits b)

@[simp] lemma digits_inj_iff {b n m : ℕ} :
  b.digits n = b.digits m ↔ n = m :=
(digits.injective b).eq_iff

lemma digits_len (b n : ℕ) (hb : 2 ≤ b) (hn : 0 < n) :
  (b.digits n).length = b.log n + 1 :=
begin
  induction n using nat.strong_induction_on with n IH,
  rw [digits_eq_cons_digits_div hb hn, list.length],
  cases (n / b).eq_zero_or_pos with h h,
  { have posb : 0 < b := zero_lt_two.trans_le hb,
    simp [h, log_eq_zero_iff, ←nat.div_eq_zero_iff posb] },
  { have hb' : 1 < b := one_lt_two.trans_le hb,
    have : n / b < n := div_lt_self hn hb',
    rw [IH _ this h, log_div_base, tsub_add_cancel_of_le],
    rw [succ_le_iff],
    refine log_pos hb' _,
    contrapose! h,
    rw div_eq_of_lt h }
end

lemma last_digit_ne_zero (b : ℕ) {m : ℕ} (hm : m ≠ 0) :
  (digits b m).last (digits_ne_nil_iff_ne_zero.mpr hm) ≠ 0 :=
begin
  rcases b with _|_|b,
  { cases m,
    { cases hm rfl },
    { simp } },
  { cases m, { cases hm rfl },
    simp_rw [digits_one, list.last_repeat_succ 1 m],
    norm_num },
  revert hm,
  apply nat.strong_induction_on m,
  intros n IH hn,
  have hnpos : 0 < n := nat.pos_of_ne_zero hn,
  by_cases hnb : n < b + 2,
  { simp_rw [digits_of_lt b.succ.succ n hnpos hnb],
    exact pos_iff_ne_zero.mp hnpos },
  { rw digits_last n (show 2 ≤ b + 2, from dec_trivial),
    refine IH _ (nat.div_lt_self hnpos dec_trivial) _,
    { rw ←pos_iff_ne_zero,
      exact nat.div_pos (le_of_not_lt hnb) dec_trivial } },
end

/-- The digits in the base b+2 expansion of n are all less than b+2 -/
lemma digits_lt_base' {b m : ℕ} : ∀ {d}, d ∈ digits (b+2) m → d < b+2 :=
begin
  apply nat.strong_induction_on m,
  intros n IH d hd,
  cases n with n,
  { rw digits_zero at hd, cases hd }, -- base b+2 expansion of 0 has no digits
  rw digits_add_two_add_one at hd,
  cases hd,
  { rw hd, exact n.succ.mod_lt (by linarith) },
  { exact IH _ (nat.div_lt_self (nat.succ_pos _) (by linarith)) hd }
end

/-- The digits in the base b expansion of n are all less than b, if b ≥ 2 -/
lemma digits_lt_base {b m d : ℕ} (hb : 2 ≤ b) (hd : d ∈ digits b m) : d < b :=
begin
  rcases b with _ | _ | b; try {linarith},
  exact digits_lt_base' hd,
end

/-- an n-digit number in base b + 2 is less than (b + 2)^n -/
lemma of_digits_lt_base_pow_length' {b : ℕ} {l : list ℕ} (hl : ∀ x ∈ l, x < b+2) :
  of_digits (b+2) l < (b+2)^(l.length) :=
begin
  induction l with hd tl IH,
  { simp [of_digits], },
  { rw [of_digits, list.length_cons, pow_succ],
    have : (of_digits (b + 2) tl + 1) * (b+2) ≤ (b + 2) ^ tl.length * (b+2) :=
      mul_le_mul (IH (λ x hx, hl _ (list.mem_cons_of_mem _ hx)))
                 (by refl) dec_trivial (nat.zero_le _),
    suffices : ↑hd < b + 2,
    { linarith },
    norm_cast,
    exact hl hd (list.mem_cons_self _ _) }
end

/-- an n-digit number in base b is less than b^n if b ≥ 2 -/
lemma of_digits_lt_base_pow_length {b : ℕ} {l : list ℕ} (hb : 2 ≤ b) (hl : ∀ x ∈ l, x < b) :
  of_digits b l < b^l.length :=
begin
  rcases b with _ | _ | b; try { linarith },
  exact of_digits_lt_base_pow_length' hl,
end

/-- Any number m is less than (b+2)^(number of digits in the base b + 2 representation of m) -/
lemma lt_base_pow_length_digits' {b m : ℕ} : m < (b + 2) ^ (digits (b + 2) m).length :=
begin
  convert of_digits_lt_base_pow_length' (λ _, digits_lt_base'),
  rw of_digits_digits (b+2) m,
end

/-- Any number m is less than b^(number of digits in the base b representation of m) -/
lemma lt_base_pow_length_digits {b m : ℕ} (hb : 2 ≤ b) : m < b^(digits b m).length :=
begin
  rcases b with _ | _ | b; try { linarith },
  exact lt_base_pow_length_digits',
end

lemma of_digits_digits_append_digits {b m n : ℕ} :
  of_digits b (digits b n ++ digits b m) = n + b ^ (digits b n).length * m:=
by rw [of_digits_append, of_digits_digits, of_digits_digits]

lemma digits_len_le_digits_len_succ (b n : ℕ) : (digits b n).length ≤ (digits b (n + 1)).length :=
begin
  rcases n.eq_zero_or_pos with rfl|hn,
  { simp },
  cases lt_or_le b 2 with hb hb,
  { rcases b with _|_|b,
    { simp [digits_zero_succ', hn] },
    { simp, },
    { simpa [succ_lt_succ_iff] using hb } },
  simpa [digits_len, hb, hn] using log_mono_right (le_succ _)
end

lemma le_digits_len_le (b n m : ℕ) (h : n ≤ m) : (digits b n).length ≤ (digits b m).length :=
monotone_nat_of_le_succ (digits_len_le_digits_len_succ b) h

lemma pow_length_le_mul_of_digits {b : ℕ} {l : list ℕ} (hl : l ≠ []) (hl2 : l.last hl ≠ 0):
  (b + 2) ^ l.length ≤ (b + 2) * of_digits (b+2) l :=
begin
  rw [←list.init_append_last hl],
  simp only [list.length_append, list.length, zero_add, list.length_init, of_digits_append,
    list.length_init, of_digits_singleton, add_comm (l.length - 1), pow_add, pow_one],
  apply nat.mul_le_mul_left,
  refine le_trans _ (nat.le_add_left _ _),
  have : 0 < l.last hl, { rwa [pos_iff_ne_zero] },
  convert nat.mul_le_mul_left _ this, rw [mul_one]
end

/--
Any non-zero natural number `m` is greater than
(b+2)^((number of digits in the base (b+2) representation of m) - 1)
-/
lemma base_pow_length_digits_le' (b m : ℕ) (hm : m ≠ 0) :
  (b + 2) ^ ((digits (b + 2) m).length) ≤ (b + 2) * m :=
begin
  have : digits (b + 2) m ≠ [], from digits_ne_nil_iff_ne_zero.mpr hm,
  convert pow_length_le_mul_of_digits this (last_digit_ne_zero _ hm),
  rwa of_digits_digits,
end

/--
Any non-zero natural number `m` is greater than
b^((number of digits in the base b representation of m) - 1)
-/
lemma base_pow_length_digits_le (b m : ℕ) (hb : 2 ≤ b): m ≠ 0 → b ^ ((digits b m).length) ≤ b * m :=
begin
  rcases b with _ | _ | b; try { linarith },
  exact base_pow_length_digits_le' b m,
end

/-! ### Modular Arithmetic -/

-- This is really a theorem about polynomials.
lemma dvd_of_digits_sub_of_digits {α : Type*} [comm_ring α]
  {a b k : α} (h : k ∣ a - b) (L : list ℕ) :
  k ∣ of_digits a L - of_digits b L :=
begin
  induction L with d L ih,
  { change k ∣ 0 - 0, simp, },
  { simp only [of_digits, add_sub_add_left_eq_sub],
    exact dvd_mul_sub_mul h ih, }
end

lemma of_digits_modeq' (b b' : ℕ) (k : ℕ) (h : b ≡ b' [MOD k]) (L : list ℕ) :
  of_digits b L ≡ of_digits b' L [MOD k] :=
begin
  induction L with d L ih,
  { refl, },
  { dsimp [of_digits],
    dsimp [nat.modeq] at *,
    conv_lhs { rw [nat.add_mod, nat.mul_mod, h, ih], },
    conv_rhs { rw [nat.add_mod, nat.mul_mod], }, }
end

lemma of_digits_modeq (b k : ℕ) (L : list ℕ) : of_digits b L ≡ of_digits (b % k) L [MOD k] :=
of_digits_modeq' b (b % k) k (b.mod_modeq k).symm L

lemma of_digits_mod (b k : ℕ) (L : list ℕ) : of_digits b L % k = of_digits (b % k) L % k :=
of_digits_modeq b k L

lemma of_digits_zmodeq' (b b' : ℤ) (k : ℕ) (h : b ≡ b' [ZMOD k]) (L : list ℕ) :
  of_digits b L ≡ of_digits b' L [ZMOD k] :=
begin
  induction L with d L ih,
  { refl, },
  { dsimp [of_digits],
    dsimp [int.modeq] at *,
    conv_lhs { rw [int.add_mod, int.mul_mod, h, ih], },
    conv_rhs { rw [int.add_mod, int.mul_mod], }, }
end

lemma of_digits_zmodeq (b : ℤ) (k : ℕ) (L : list ℕ) :
  of_digits b L ≡ of_digits (b % k) L [ZMOD k] :=
of_digits_zmodeq' b (b % k) k (b.mod_modeq  ↑k).symm L

lemma modeq_digits_sum (b b' : ℕ) (h : b' % b = 1) (n : ℕ) :
   n ≡ (digits b' n).sum [MOD b] :=
begin
  rw ←of_digits_one,
  conv { congr, skip, rw ←(of_digits_digits b' n) },
  convert of_digits_modeq _ _ _,
  exact h.symm,
end

lemma modeq_three_digits_sum (n : ℕ) : n ≡ (digits 10 n).sum [MOD 3] :=
modeq_digits_sum 3 10 (by norm_num) n

lemma modeq_nine_digits_sum (n : ℕ) : n ≡ (digits 10 n).sum [MOD 9] :=
modeq_digits_sum 9 10 (by norm_num) n

lemma zmodeq_of_digits_digits (b b' : ℕ) (c : ℤ) (h : b' ≡ c [ZMOD b]) (n : ℕ) :
  n ≡ of_digits c (digits b' n) [ZMOD b] :=
begin
  conv { congr, skip, rw ←(of_digits_digits b' n) },
  rw coe_int_of_digits,
  apply of_digits_zmodeq' _ _ _ h,
end

lemma of_digits_neg_one : Π (L : list ℕ),
  of_digits (-1 : ℤ) L = (L.map (λ n : ℕ, (n : ℤ))).alternating_sum
| [] := rfl
| [n] := by simp [of_digits, list.alternating_sum]
| (a :: b :: t) :=
  begin
    simp only [of_digits, list.alternating_sum, list.map_cons, of_digits_neg_one t],
    ring,
  end

lemma modeq_eleven_digits_sum (n : ℕ) :
  n ≡ ((digits 10 n).map (λ n : ℕ, (n : ℤ))).alternating_sum [ZMOD 11] :=
begin
  have t := zmodeq_of_digits_digits 11 10 (-1 : ℤ) (by unfold int.modeq; norm_num) n,
  rwa of_digits_neg_one at t
end

/-! ## Divisibility  -/

lemma dvd_iff_dvd_digits_sum (b b' : ℕ) (h : b' % b = 1) (n : ℕ) :
  b ∣ n ↔ b ∣ (digits b' n).sum :=
begin
  rw ←of_digits_one,
  conv_lhs { rw ←(of_digits_digits b' n) },
  rw [nat.dvd_iff_mod_eq_zero, nat.dvd_iff_mod_eq_zero, of_digits_mod, h],
end

/-- **Divisibility by 3 Rule** -/
lemma three_dvd_iff (n : ℕ) : 3 ∣ n ↔ 3 ∣ (digits 10 n).sum :=
dvd_iff_dvd_digits_sum 3 10 (by norm_num) n

lemma nine_dvd_iff (n : ℕ) : 9 ∣ n ↔ 9 ∣ (digits 10 n).sum :=
dvd_iff_dvd_digits_sum 9 10 (by norm_num) n

lemma dvd_iff_dvd_of_digits (b b' : ℕ) (c : ℤ) (h : (b : ℤ) ∣ (b' : ℤ) - c) (n : ℕ) :
  b ∣ n ↔ (b : ℤ) ∣ of_digits c (digits b' n) :=
begin
  rw ←int.coe_nat_dvd,
  exact dvd_iff_dvd_of_dvd_sub
    (zmodeq_of_digits_digits b b' c (int.modeq_iff_dvd.2 h).symm _).symm.dvd,
end

lemma eleven_dvd_iff : 11 ∣ n ↔ (11 : ℤ) ∣ ((digits 10 n).map (λ n : ℕ, (n : ℤ))).alternating_sum :=
begin
  have t := dvd_iff_dvd_of_digits 11 10 (-1 : ℤ) (by norm_num) n,
  rw of_digits_neg_one at t,
  exact t,
end

lemma eleven_dvd_of_palindrome (p : (digits 10 n).palindrome) (h : even (digits 10 n).length) :
  11 ∣ n :=
begin
  let dig := (digits 10 n).map (coe : ℕ → ℤ),
  replace h : even dig.length := by rwa list.length_map,
  refine eleven_dvd_iff.2 ⟨0, (_ : dig.alternating_sum = 0)⟩,
  have := dig.alternating_sum_reverse,
  rw [(p.map _).reverse_eq, pow_succ, h.neg_one_pow, mul_one, neg_one_zsmul] at this,
  exact eq_zero_of_neg_eq this.symm,
end

/-! ### `norm_digits` tactic -/

namespace norm_digits

theorem digits_succ
  (b n m r l)
  (e : r + b * m = n)
  (hr : r < b)
  (h : nat.digits b m = l ∧ 2 ≤ b ∧ 0 < m) :
  nat.digits b n = r :: l ∧ 2 ≤ b ∧ 0 < n :=
begin
  rcases h with ⟨h, b2, m0⟩,
  have b0 : 0 < b := by linarith,
  have n0 : 0 < n := by linarith [mul_pos b0 m0],
  refine ⟨_, b2, n0⟩,
  obtain ⟨rfl, rfl⟩ := (nat.div_mod_unique b0).2 ⟨e, hr⟩,
  subst h, exact nat.digits_def' b2 n0,
end

theorem digits_one
  (b n) (n0 : 0 < n) (nb : n < b) :
  nat.digits b n = [n] ∧ 2 ≤ b ∧ 0 < n :=
begin
  have b2 : 2 ≤ b := by linarith,
  refine ⟨_, b2, n0⟩,
  rw [nat.digits_def' b2 n0, nat.mod_eq_of_lt nb,
    (nat.div_eq_zero_iff (by linarith : 0 < b)).2 nb, nat.digits_zero],
end

open tactic

/-- Helper function for the `norm_digits` tactic. -/
meta def eval_aux (eb : expr) (b : ℕ) :
  expr → ℕ → instance_cache → tactic (instance_cache × expr × expr)
| en n ic := do
  let m := n / b,
  let r := n % b,
  (ic, er) ← ic.of_nat r,
  (ic, pr) ← norm_num.prove_lt_nat ic er eb,
  if m = 0 then do
    (_, pn0) ← norm_num.prove_pos ic en,
    return (ic, `([%%en] : list nat), `(digits_one %%eb %%en %%pn0 %%pr))
  else do
    em ← expr.of_nat `(ℕ) m,
    (_, pe) ← norm_num.derive `(%%er + %%eb * %%em : ℕ),
    (ic, el, p) ← eval_aux em m ic,
    return (ic, `(@list.cons ℕ %%er %%el),
      `(digits_succ %%eb %%en %%em %%er %%el %%pe %%pr %%p))

/--
A tactic for normalizing expressions of the form `nat.digits a b = l` where
`a` and `b` are numerals.

```
example : nat.digits 10 123 = [3,2,1] := by norm_num
```
-/
@[norm_num] meta def eval : expr → tactic (expr × expr)
| `(nat.digits %%eb %%en) := do
  b ← expr.to_nat eb,
  n ← expr.to_nat en,
  if n = 0 then return (`([] : list ℕ), `(nat.digits_zero %%eb))
  else if b = 0 then do
    ic ← mk_instance_cache `(ℕ),
    (_, pn0) ← norm_num.prove_pos ic en,
    return (`([%%en] : list ℕ), `(@nat.digits_zero_succ' %%en %%pn0))
  else if b = 1 then do
    ic ← mk_instance_cache `(ℕ),
    (_, pn0) ← norm_num.prove_pos ic en,
    s ← simp_lemmas.add_simp simp_lemmas.mk `list.repeat,
    (rhs, p2, _) ← simplify s [] `(list.repeat 1 %%en),
    p ← mk_eq_trans `(nat.digits_one %%en) p2,
    return (rhs, p)
  else do
    ic ← mk_instance_cache `(ℕ),
    (_, l, p) ← eval_aux eb b en n ic,
    p ← mk_app ``and.left [p],
    return (l, p)
| _ := failed

end norm_digits

end nat


import Mathlib.Tactic.Basic
import Mathlib.Init.Data.Nat.Basic
/-!
## Dependent Inductive Types

The inductive types `List α` and `BTree α` fall within the simply typed fragment of
Lean. Inductive types may also depend on (non-type) terms. A typical example is
the type of lists of length _n_, or _vectors_:
-/
inductive Vec (α : Type u) : ℕ → Type u where
  | nil : Vec α 0
  | cons {n} (head : α) (tail : Vec α n) : Vec α (n+1)
  deriving Repr

/-!
Thus, the term `Vec.cons 3 (Vec.cons 1 Vec.nil)` has type `Vec ℕ 2`. By encoding the vector length
in the type, we can provide more precise information about the result of functions. A function such
as `Vec.reverse`, which reverses a vector, would map a value `Vec α n` to another value of the same
type, with the same `n`. And `Vec.zip` could require its two arguments to have the same length.
Fixed-length vectors and matrices are also useful in mathematics.

Unfortunately, this more precise information comes at a cost. Dependent inductive types cause
difficulties when the terms they depend on are provably equal but not syntactically equal up to
computation (e.g., `m + n` vs. `n + m`). In this guide, we will generally avoid dependent inductive
types. They are briefly covered in this section for completeness. To put it unambiguously: This is
not exam material.

The definitions below introduce conversions between lists and vectors:
-/
def listOfVec {α : Type} : ∀{n : ℕ}, Vec α n → List α
| _, Vec.nil => []
| _, (Vec.cons a v) => a :: listOfVec v

def vecOfList {α : Type} :
∀xs : List α, Vec α (List.length xs)
| [] => Vec.nil
| (x :: xs) => Vec.cons x (vecOfList xs)

#eval vecOfList [1,2,3] -- Vec.cons 1 (Vec.cons 2 (Vec.cons 3 Vec.nil))

#check vecOfList [1,2,3] -- Vec.cons 1 (Vec.cons 2 (Vec.cons 3 Vec.nil))

#eval listOfVec (vecOfList [1,2,3]) -- Vec ℕ (List.length [1, 2, 3])

/-!
The `listOfVec` conversion takes a type `α`, a length `n`, and a vector of length `n` over `α` as
arguments and returns a list over `α`. Although we do not care about the length `n`, it still needs
to be an argument because it appears in the type of the third argument. The first two arguments are
implicit, which is reasonable since they can be inferred from the type of the third argument.

The `vecOfList` conversion takes a type `α` and a list over `α` as arguments and returns a vector of
the same length as the list. Lean’s type checker is strong enough to determine that the two
right-hand sides have the desired type.

By the [Curry–Howard correspondence](../ForwardProofs/CurryHoward.lean.md), proofs are analogous to
function definitions. Let us verify that converting a vector to a list preserves its length:
-/
lemma lengthListOfVec {α : Type} :
  ∀{n : ℕ} (v : Vec α n), List.length (listOfVec v) = n
| _, Vec.nil => by rfl
| _, Vec.cons a v =>
  by simp [listOfVec, lengthListOfVec v]
/-!
To prove a goal `v : Vec α n ⊢ P[v]` by structural induction on `v`, we might
naively think that it suffices to show the following two sub-goals:
```
⊢ P[Vec.nil]
m : N, a : α, u : Vec α m, ih : P[u] ⊢ P[Vec.cons a u]
```
This is naive because the sub-goals are not even type-correct: The hole in `P[ ]` has
type `Vec α n` (the type of its original dweller, `v`), so we cannot simply plug `Vec.nil`,
`u`, or `Vec.cons a u`—which have types `Vec α 0`, `Vec α m`, and `Vec α (m + 1)`—into
that hole. We must massage `P` each time, replacing `n` with `0`, `m`, or `m + 1`. Using the
notation `Pₜ[ ]` for the variant of `P[ ]` where all occurrences of `n` are replaced by
term `t`, we get
```
⊢ P₀[Vec.nil]
m : N, a : α, u : Vec α m, ih : Pₘ[u] ⊢ Pₘ₊₁[Vec.cons a u]
```
Proofs by case distinction using the `cases'` tactic are similar, but without the
induction hypothesis. Often, the length `n` will be not a variable but a complex
term. Then the replacement of `n` in `P[ ]` might not be intuitively meaningful. With
`cases'`, the corresponding sub-goal is silently eliminated. Thus, a case distinction
on a value of type `Vec α 0` will yield only one sub-goal, of the form `⊢ P[Vec.nil]`,
since `0` could never be equal to a term of the form `m + 1`.

Dependently typed pattern matching is subtle, because the type of the value
we match on may change according to the constructor. Given `v : Vec α n`, we might
be tempted to write

```lean
match v with
| Vec.nil => . . .
| Vec.cons a u => . . .
```
but this is just as naive as our first induction proof attempt above. Because the
term `n` in the type `Vec α n` may change depending on the constructor, we must
pattern-match on it as well:

```lean
match n, v with
| 0, Vec.nil => . . .
| m + 1, Vec.cons a u => . . .
```
-/
def vecHead (v : Vec α n) : Option α :=
match n, v with
| 0, Vec.nil => Option.none
| m + 1, Vec.cons a u => Option.some a

#eval vecHead (vecOfList [5,6,7]) -- some 5

set_option pp.explicit true
#print vecHead

def vecHeadₑ : {α : Type u_1} → {n : ℕ} → Vec α n → Option α :=
fun {α} {n} v =>
  match n, v with
  | 0, @Vec.nil α => @none α
  | Nat.succ m, @Vec.cons α .(m) a u => @some α a

/-!
Lean reports a bunch of unused variables in `vecHead` and so you can put
placeholders (`_`) there instead:
-/
def vecHead₂ (v : Vec α n) : Option α :=
match n, v with
| _, Vec.nil => Option.none
| _, Vec.cons a _ => Option.some a

/-!
It may seem paradoxical to pattern-match on `n` only to ignore the result, but without it Lean cannot
infer the second implicit argument to `Vec.cons`. In this respect, `cases'` is more user-friendly than
match.

--BUGBUG - is this still true in Lean 4?

Incidentally, Lean’s core libraries define a type of fixed-length vectors, called
`Vector α n`, as a subtype of `List α`:

-- BUGBUG - does it? I'm not finding it...
-/
def Vector (α : Type) (n : ℕ) :=
  { xs : List α // List.length xs = n }
/-!
In other words, the type `Vector α n` consists of the values `xs` of type `List α` such
that `xs` is of length `n`. This makes it possible to reuse Lean’s extensive list library.
Subtyping will be explained in Chapter 11.
-/


/-
Copyright (c) 2022 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa

! This file was ported from Lean 3 source module ring_theory.polynomial.opposites
! leanprover-community/mathlib commit 932872382355f00112641d305ba0619305dc8642
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Polynomial.Degree.Definitions

/-!  #  Interactions between `R[X]` and `Rᵐᵒᵖ[X]`

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file contains the basic API for "pushing through" the isomorphism
`op_ring_equiv : R[X]ᵐᵒᵖ ≃+* Rᵐᵒᵖ[X]`.  It allows going back and forth between a polynomial ring
over a semiring and the polynomial ring over the opposite semiring. -/

open Polynomial

open Polynomial MulOpposite

variable {R : Type _} [Semiring R] {p q : R[X]}

noncomputable section

namespace Polynomial

/- warning: polynomial.op_ring_equiv -> Polynomial.opRingEquiv is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) [_inst_2 : Semiring.{u1} R], RingEquiv.{u1, u1} (MulOpposite.{u1} (Polynomial.{u1} R _inst_2)) (Polynomial.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2)) (MulOpposite.hasMul.{u1} (Polynomial.{u1} R _inst_2) (Polynomial.mul'.{u1} R _inst_2)) (MulOpposite.hasAdd.{u1} (Polynomial.{u1} R _inst_2) (Polynomial.add'.{u1} R _inst_2)) (Polynomial.mul'.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2)) (Polynomial.add'.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2))
but is expected to have type
  forall (R : Type.{u1}) [_inst_2 : Semiring.{u1} R], RingEquiv.{u1, u1} (MulOpposite.{u1} (Polynomial.{u1} R _inst_2)) (Polynomial.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2)) (MulOpposite.mul.{u1} (Polynomial.{u1} R _inst_2) (Polynomial.mul'.{u1} R _inst_2)) (Polynomial.mul'.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2)) (MulOpposite.add.{u1} (Polynomial.{u1} R _inst_2) (Polynomial.add'.{u1} R _inst_2)) (Polynomial.add'.{u1} (MulOpposite.{u1} R) (MulOpposite.semiring.{u1} R _inst_2))
Case conversion may be inaccurate. Consider using '#align polynomial.op_ring_equiv Polynomial.opRingEquivₓ'. -/
/-- Ring isomorphism between `R[X]ᵐᵒᵖ` and `Rᵐᵒᵖ[X]` sending each coefficient of a polynomial
to the corresponding element of the opposite ring. -/
def opRingEquiv (R : Type _) [Semiring R] : R[X]ᵐᵒᵖ ≃+* Rᵐᵒᵖ[X] :=
  ((toFinsuppIso R).op.trans AddMonoidAlgebra.opRingEquiv).trans (toFinsuppIso _).symm
#align polynomial.op_ring_equiv Polynomial.opRingEquiv

/-!  Lemmas to get started, using `op_ring_equiv R` on the various expressions of
`finsupp.single`: `monomial`, `C a`, `X`, `C a * X ^ n`. -/

#print Polynomial.opRingEquiv_op_monomial /-
-- for maintenance purposes: `by simp [op_ring_equiv]` proves this lemma
@[simp]
theorem opRingEquiv_op_monomial (n : ℕ) (r : R) :
    opRingEquiv R (op (monomial n r : R[X])) = monomial n (op r) := by
  simp only [op_ring_equiv, RingEquiv.trans_apply, RingEquiv.op_apply_apply,
    RingEquiv.toAddEquiv_eq_coe, AddEquiv.mulOp_apply, [anonymous], AddEquiv.coe_trans,
    op_add_equiv_apply, RingEquiv.coe_toAddEquiv, op_add_equiv_symm_apply, Function.comp_apply,
    unop_op, to_finsupp_iso_apply, to_finsupp_monomial, AddMonoidAlgebra.opRingEquiv_single,
    to_finsupp_iso_symm_apply, of_finsupp_single]
#align polynomial.op_ring_equiv_op_monomial Polynomial.opRingEquiv_op_monomial
-/

#print Polynomial.opRingEquiv_op_C /-
@[simp]
theorem opRingEquiv_op_C (a : R) : opRingEquiv R (op (C a)) = C (op a) :=
  opRingEquiv_op_monomial 0 a
#align polynomial.op_ring_equiv_op_C Polynomial.opRingEquiv_op_C
-/

#print Polynomial.opRingEquiv_op_X /-
@[simp]
theorem opRingEquiv_op_X : opRingEquiv R (op (X : R[X])) = X :=
  opRingEquiv_op_monomial 1 1
#align polynomial.op_ring_equiv_op_X Polynomial.opRingEquiv_op_X
-/

#print Polynomial.opRingEquiv_op_C_mul_X_pow /-
theorem opRingEquiv_op_C_mul_X_pow (r : R) (n : ℕ) :
    opRingEquiv R (op (C r * X ^ n : R[X])) = C (op r) * X ^ n := by
  simp only [X_pow_mul, op_mul, op_pow, map_mul, map_pow, op_ring_equiv_op_X, op_ring_equiv_op_C]
#align polynomial.op_ring_equiv_op_C_mul_X_pow Polynomial.opRingEquiv_op_C_mul_X_pow
-/

/-!  Lemmas to get started, using `(op_ring_equiv R).symm` on the various expressions of
`finsupp.single`: `monomial`, `C a`, `X`, `C a * X ^ n`. -/

#print Polynomial.opRingEquiv_symm_monomial /-
@[simp]
theorem opRingEquiv_symm_monomial (n : ℕ) (r : Rᵐᵒᵖ) :
    (opRingEquiv R).symm (monomial n r) = op (monomial n (unop r)) :=
  (opRingEquiv R).Injective (by simp)
#align polynomial.op_ring_equiv_symm_monomial Polynomial.opRingEquiv_symm_monomial
-/

#print Polynomial.opRingEquiv_symm_C /-
@[simp]
theorem opRingEquiv_symm_C (a : Rᵐᵒᵖ) : (opRingEquiv R).symm (C a) = op (C (unop a)) :=
  opRingEquiv_symm_monomial 0 a
#align polynomial.op_ring_equiv_symm_C Polynomial.opRingEquiv_symm_C
-/

#print Polynomial.opRingEquiv_symm_X /-
@[simp]
theorem opRingEquiv_symm_X : (opRingEquiv R).symm (X : Rᵐᵒᵖ[X]) = op X :=
  opRingEquiv_symm_monomial 1 1
#align polynomial.op_ring_equiv_symm_X Polynomial.opRingEquiv_symm_X
-/

#print Polynomial.opRingEquiv_symm_C_mul_X_pow /-
theorem opRingEquiv_symm_C_mul_X_pow (r : Rᵐᵒᵖ) (n : ℕ) :
    (opRingEquiv R).symm (C r * X ^ n : Rᵐᵒᵖ[X]) = op (C (unop r) * X ^ n) := by
  rw [C_mul_X_pow_eq_monomial, op_ring_equiv_symm_monomial, ← C_mul_X_pow_eq_monomial]
#align polynomial.op_ring_equiv_symm_C_mul_X_pow Polynomial.opRingEquiv_symm_C_mul_X_pow
-/

/-!  Lemmas about more global properties of polynomials and opposites. -/

#print Polynomial.coeff_opRingEquiv /-
@[simp]
theorem coeff_opRingEquiv (p : R[X]ᵐᵒᵖ) (n : ℕ) :
    (opRingEquiv R p).coeff n = op ((unop p).coeff n) :=
  by
  induction p using MulOpposite.rec'
  cases p
  rfl
#align polynomial.coeff_op_ring_equiv Polynomial.coeff_opRingEquiv
-/

#print Polynomial.support_opRingEquiv /-
@[simp]
theorem support_opRingEquiv (p : R[X]ᵐᵒᵖ) : (opRingEquiv R p).support = (unop p).support :=
  by
  induction p using MulOpposite.rec'
  cases p
  exact Finsupp.support_mapRange_of_injective _ _ op_injective
#align polynomial.support_op_ring_equiv Polynomial.support_opRingEquiv
-/

#print Polynomial.natDegree_opRingEquiv /-
@[simp]
theorem natDegree_opRingEquiv (p : R[X]ᵐᵒᵖ) : (opRingEquiv R p).natDegree = (unop p).natDegree :=
  by
  by_cases p0 : p = 0
  · simp only [p0, _root_.map_zero, nat_degree_zero, unop_zero]
  ·
    simp only [p0, nat_degree_eq_support_max', Ne.def, AddEquivClass.map_eq_zero_iff, not_false_iff,
      support_op_ring_equiv, unop_eq_zero_iff]
#align polynomial.nat_degree_op_ring_equiv Polynomial.natDegree_opRingEquiv
-/

#print Polynomial.leadingCoeff_opRingEquiv /-
@[simp]
theorem leadingCoeff_opRingEquiv (p : R[X]ᵐᵒᵖ) :
    (opRingEquiv R p).leadingCoeff = op (unop p).leadingCoeff := by
  rw [leading_coeff, coeff_op_ring_equiv, nat_degree_op_ring_equiv, leading_coeff]
#align polynomial.leading_coeff_op_ring_equiv Polynomial.leadingCoeff_opRingEquiv
-/

end Polynomial


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import topology.algebra.monoid
import algebra.group.pi
import topology.homeomorph

/-!
# Topological group with zero

In this file we define `has_continuous_inv'` to be a mixin typeclass a type with `has_inv` and
`has_zero` (e.g., a `group_with_zero`) such that `λ x, x⁻¹` is continuous at all nonzero points. Any
normed (semi)field has this property. Currently the only example of `has_continuous_inv'` in
`mathlib` which is not a normed field is the type `nnnreal` (a.k.a. `ℝ≥0`) of nonnegative real
numbers.

Then we prove lemmas about continuity of `x ↦ x⁻¹` and `f / g` providing dot-style `*.inv'` and
`*.div` operations on `filter.tendsto`, `continuous_at`, `continuous_within_at`, `continuous_on`,
and `continuous`. As a special case, we provide `*.div_const` operations that require only
`group_with_zero` and `has_continuous_mul` instances.

All lemmas about `(⁻¹)` use `inv'` in their names because lemmas without `'` are used for
`topological_group`s. We also use `'` in the typeclass name `has_continuous_inv'` for the sake of
consistency of notation.

On a `group_with_zero` with continuous multiplication, we also define left and right multiplication
as homeomorphisms.
-/

open_locale topological_space filter
open filter function

/-!
### A group with zero with continuous multiplication

If `G₀` is a group with zero with continuous `(*)`, then `(/y)` is continuous for any `y`. In this
section we prove lemmas that immediately follow from this fact providing `*.div_const` dot-style
operations on `filter.tendsto`, `continuous_at`, `continuous_within_at`, `continuous_on`, and
`continuous`.
-/

variables {α β G₀ : Type*}

section div_const

variables [group_with_zero G₀] [topological_space G₀] [has_continuous_mul G₀]
  {f : α → G₀} {s : set α} {l : filter α}

lemma filter.tendsto.div_const {x y : G₀} (hf : tendsto f l (𝓝 x)) :
  tendsto (λa, f a / y) l (𝓝 (x / y)) :=
by simpa only [div_eq_mul_inv] using hf.mul tendsto_const_nhds

variables [topological_space α]

lemma continuous_at.div_const {a : α} (hf : continuous_at f a) {y : G₀} :
  continuous_at (λ x, f x / y) a :=
by simpa only [div_eq_mul_inv] using hf.mul continuous_at_const

lemma continuous_within_at.div_const {a} (hf : continuous_within_at f s a) {y : G₀} :
  continuous_within_at (λ x, f x / y) s a :=
hf.div_const

lemma continuous_on.div_const (hf : continuous_on f s) {y : G₀} : continuous_on (λ x, f x / y) s :=
by simpa only [div_eq_mul_inv] using hf.mul continuous_on_const

@[continuity] lemma continuous.div_const (hf : continuous f) {y : G₀} :
  continuous (λ x, f x / y) :=
by simpa only [div_eq_mul_inv] using hf.mul continuous_const

end div_const

/-- A type with `0` and `has_inv` such that `λ x, x⁻¹` is continuous at all nonzero points. Any
normed (semi)field has this property. -/
class has_continuous_inv₀ (G₀ : Type*) [has_zero G₀] [has_inv G₀] [topological_space G₀] :=
(continuous_at_inv₀ : ∀ ⦃x : G₀⦄, x ≠ 0 → continuous_at has_inv.inv x)

export has_continuous_inv₀ (continuous_at_inv₀)

section inv₀

variables [has_zero G₀] [has_inv G₀] [topological_space G₀] [has_continuous_inv₀ G₀]
  {l : filter α} {f : α → G₀} {s : set α} {a : α}

/-!
### Continuity of `λ x, x⁻¹` at a non-zero point

We define `topological_group_with_zero` to be a `group_with_zero` such that the operation `x ↦ x⁻¹`
is continuous at all nonzero points. In this section we prove dot-style `*.inv'` lemmas for
`filter.tendsto`, `continuous_at`, `continuous_within_at`, `continuous_on`, and `continuous`.
-/

lemma tendsto_inv₀ {x : G₀}  (hx : x ≠ 0) : tendsto has_inv.inv (𝓝 x) (𝓝 x⁻¹) :=
continuous_at_inv₀ hx

lemma continuous_on_inv₀ : continuous_on (has_inv.inv : G₀ → G₀) {0}ᶜ :=
λ x hx, (continuous_at_inv₀ hx).continuous_within_at

/-- If a function converges to a nonzero value, its inverse converges to the inverse of this value.
We use the name `tendsto.inv₀` as `tendsto.inv` is already used in multiplicative topological
groups. -/
lemma filter.tendsto.inv₀ {a : G₀} (hf : tendsto f l (𝓝 a))
  (ha : a ≠ 0) :
  tendsto (λ x, (f x)⁻¹) l (𝓝 a⁻¹) :=
(tendsto_inv₀ ha).comp hf

variables [topological_space α]

lemma continuous_within_at.inv₀ (hf : continuous_within_at f s a) (ha : f a ≠ 0) :
  continuous_within_at (λ x, (f x)⁻¹) s a :=
hf.inv₀ ha

lemma continuous_at.inv₀ (hf : continuous_at f a) (ha : f a ≠ 0) :
  continuous_at (λ x, (f x)⁻¹) a :=
hf.inv₀ ha

@[continuity] lemma continuous.inv₀ (hf : continuous f) (h0 : ∀ x, f x ≠ 0) :
  continuous (λ x, (f x)⁻¹) :=
continuous_iff_continuous_at.2 $ λ x, (hf.tendsto x).inv₀ (h0 x)

lemma continuous_on.inv₀ (hf : continuous_on f s) (h0 : ∀ x ∈ s, f x ≠ 0) :
  continuous_on (λ x, (f x)⁻¹) s :=
λ x hx, (hf x hx).inv₀ (h0 x hx)

end inv₀

/-!
### Continuity of division

If `G₀` is a `group_with_zero` with `x ↦ x⁻¹` continuous at all nonzero points and `(*)`, then
division `(/)` is continuous at any point where the denominator is continuous.
-/

section div

variables [group_with_zero G₀] [topological_space G₀] [has_continuous_inv₀ G₀]
  [has_continuous_mul G₀] {f g : α → G₀}

lemma filter.tendsto.div {l : filter α} {a b : G₀} (hf : tendsto f l (𝓝 a))
  (hg : tendsto g l (𝓝 b)) (hy : b ≠ 0) :
  tendsto (f / g) l (𝓝 (a / b)) :=
by simpa only [div_eq_mul_inv] using hf.mul (hg.inv₀ hy)

variables [topological_space α] [topological_space β] {s : set α} {a : α}

lemma continuous_within_at.div (hf : continuous_within_at f s a) (hg : continuous_within_at g s a)
  (h₀ : g a ≠ 0) :
  continuous_within_at (f / g) s a :=
hf.div hg h₀

lemma continuous_on.div (hf : continuous_on f s) (hg : continuous_on g s) (h₀ : ∀ x ∈ s, g x ≠ 0) :
  continuous_on (f / g) s :=
λ x hx, (hf x hx).div (hg x hx) (h₀ x hx)

/-- Continuity at a point of the result of dividing two functions continuous at that point, where
the denominator is nonzero. -/
lemma continuous_at.div (hf : continuous_at f a) (hg : continuous_at g a) (h₀ : g a ≠ 0) :
  continuous_at (f / g) a :=
hf.div hg h₀

@[continuity] lemma continuous.div (hf : continuous f) (hg : continuous g) (h₀ : ∀ x, g x ≠ 0) :
  continuous (f / g) :=
by simpa only [div_eq_mul_inv] using hf.mul (hg.inv₀ h₀)

lemma continuous_on_div : continuous_on (λ p : G₀ × G₀, p.1 / p.2) {p | p.2 ≠ 0} :=
continuous_on_fst.div continuous_on_snd $ λ _, id

/-- The function `f x / g x` is discontinuous when `g x = 0`.
However, under appropriate conditions, `h x (f x / g x)` is still continuous.
The condition is that if `g a = 0` then `h x y` must tend to `h a 0` when `x` tends to `a`,
with no information about `y`. This is represented by the `⊤` filter.
Note: `filter.tendsto_prod_top_iff` characterizes this convergence in uniform spaces.
See also `filter.prod_top` and `filter.mem_prod_top`. -/
lemma continuous_at.comp_div_cases {f g : α → G₀} (h : α → G₀ → β)
  (hf : continuous_at f a) (hg : continuous_at g a)
  (hh : g a ≠ 0 → continuous_at ↿h (a, f a / g a))
  (h2h : g a = 0 → tendsto ↿h (𝓝 a ×ᶠ ⊤) (𝓝 (h a 0))) :
  continuous_at (λ x, h x (f x / g x)) a :=
begin
  show continuous_at (↿h ∘ (λ x, (x, f x / g x))) a,
  by_cases hga : g a = 0,
  { rw [continuous_at], simp_rw [comp_app, hga, div_zero],
    exact (h2h hga).comp (continuous_at_id.prod_mk tendsto_top) },
  { exact continuous_at.comp (hh hga) (continuous_at_id.prod (hf.div hg hga)) }
end

/-- `h x (f x / g x)` is continuous under certain conditions, even if the denominator is sometimes
  `0`. See docstring of `continuous_at.comp_div_cases`. -/
lemma continuous.comp_div_cases {f g : α → G₀} (h : α → G₀ → β)
  (hf : continuous f) (hg : continuous g)
  (hh : ∀ a, g a ≠ 0 → continuous_at ↿h (a, f a / g a))
  (h2h : ∀ a, g a = 0 → tendsto ↿h (𝓝 a ×ᶠ ⊤) (𝓝 (h a 0))) :
  continuous (λ x, h x (f x / g x)) :=
continuous_iff_continuous_at.mpr $
  λ a, hf.continuous_at.comp_div_cases _ hg.continuous_at (hh a) (h2h a)

end div

/-! ### Left and right multiplication as homeomorphisms -/

namespace homeomorph

variables [topological_space α] [group_with_zero α] [has_continuous_mul α]

/-- Left multiplication by a nonzero element in a `group_with_zero` with continuous multiplication
is a homeomorphism of the underlying type. -/
protected def mul_left₀ (c : α) (hc : c ≠ 0) : α ≃ₜ α :=
{ continuous_to_fun := continuous_mul_left _,
  continuous_inv_fun := continuous_mul_left _,
  .. equiv.mul_left₀ c hc }

/-- Right multiplication by a nonzero element in a `group_with_zero` with continuous multiplication
is a homeomorphism of the underlying type. -/
protected def mul_right₀ (c : α) (hc : c ≠ 0) : α ≃ₜ α :=
{ continuous_to_fun := continuous_mul_right _,
  continuous_inv_fun := continuous_mul_right _,
  .. equiv.mul_right₀ c hc }

@[simp] lemma coe_mul_left₀ (c : α) (hc : c ≠ 0) : ⇑(homeomorph.mul_left₀ c hc) = (*) c := rfl

@[simp] lemma mul_left₀_symm_apply (c : α) (hc : c ≠ 0) :
  ((homeomorph.mul_left₀ c hc).symm : α → α) = (*) c⁻¹ := rfl

@[simp] lemma coe_mul_right₀ (c : α) (hc : c ≠ 0) :
  ⇑(homeomorph.mul_right₀ c hc) = λ x, x * c := rfl

@[simp] lemma mul_right₀_symm_apply (c : α) (hc : c ≠ 0) :
  ((homeomorph.mul_right₀ c hc).symm : α → α) = λ x, x * c⁻¹ := rfl

end homeomorph

section zpow

variables [group_with_zero G₀] [topological_space G₀] [has_continuous_inv₀ G₀]
  [has_continuous_mul G₀]

lemma continuous_at_zpow (x : G₀) (m : ℤ) (h : x ≠ 0 ∨ 0 ≤ m) : continuous_at (λ x, x ^ m) x :=
begin
  cases m,
  { simpa only [zpow_of_nat] using continuous_at_pow x m },
  { simp only [zpow_neg_succ_of_nat],
    have hx : x ≠ 0, from h.resolve_right (int.neg_succ_of_nat_lt_zero m).not_le,
    exact (continuous_at_pow x (m + 1)).inv₀ (pow_ne_zero _ hx) }
end

lemma continuous_on_zpow (m : ℤ) : continuous_on (λ x : G₀, x ^ m) {0}ᶜ :=
λ x hx, (continuous_at_zpow _ _ (or.inl hx)).continuous_within_at

lemma filter.tendsto.zpow {f : α → G₀} {l : filter α} {a : G₀} (hf : tendsto f l (𝓝 a)) (m : ℤ)
  (h : a ≠ 0 ∨ 0 ≤ m) :
  tendsto (λ x, (f x) ^ m) l (𝓝 (a ^ m)) :=
(continuous_at_zpow _ m h).tendsto.comp hf

variables {X : Type*} [topological_space X] {a : X} {s : set X} {f : X → G₀}

lemma continuous_at.zpow (hf : continuous_at f a) (m : ℤ) (h : f a ≠ 0 ∨ 0 ≤ m) :
  continuous_at (λ x, (f x) ^ m) a :=
hf.zpow m h

lemma continuous_within_at.zpow (hf : continuous_within_at f s a) (m : ℤ) (h : f a ≠ 0 ∨ 0 ≤ m) :
  continuous_within_at (λ x, f x ^ m) s a :=
hf.zpow m h

lemma continuous_on.zpow (hf : continuous_on f s) (m : ℤ) (h : ∀ a ∈ s, f a ≠ 0 ∨ 0 ≤ m) :
  continuous_on (λ x, f x ^ m) s :=
λ a ha, (hf a ha).zpow m (h a ha)

@[continuity] lemma continuous.zpow (hf : continuous f) (m : ℤ) (h0 : ∀ a, f a ≠ 0 ∨ 0 ≤ m) :
  continuous (λ x, (f x) ^ m) :=
continuous_iff_continuous_at.2 $ λ x, (hf.tendsto x).zpow m (h0 x)

end zpow


/-
Copyright (c) 2022 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import algebra.lie.nilpotent
import algebra.lie.normalizer

/-!
# Engel's theorem

This file contains a proof of Engel's theorem providing necessary and sufficient conditions for Lie
algebras and Lie modules to be nilpotent.

The key result `lie_module.is_nilpotent_iff_forall` says that if `M` is a Lie module of a
Noetherian Lie algebra `L`, then `M` is nilpotent iff the image of `L → End(M)` consists of
nilpotent elements. In the special case that we have the adjoint representation `M = L`, this says
that a Lie algebra is nilpotent iff `ad x : End(L)` is nilpotent for all `x : L`.

Engel's theorem is true for any coefficients (i.e., it is really a theorem about Lie rings) and so
we work with coefficients in any commutative ring `R` throughout.

On the other hand, Engel's theorem is not true for infinite-dimensional Lie algebras and so a
finite-dimensionality assumption is required. We prove the theorem subject to the the assumption
that the Lie algebra is Noetherian as an `R`-module, though actually we only need the slightly
weaker property that the relation `>` is well-founded on the complete lattice of Lie subalgebras.

## Remarks about the proof

Engel's theorem is usually proved in the special case that the coefficients are a field, and uses
an inductive argument on the dimension of the Lie algebra. One begins by choosing either a maximal
proper Lie subalgebra (in some proofs) or a maximal nilpotent Lie subalgebra (in other proofs, at
the cost of obtaining a weaker end result).

Since we work with general coefficients, we cannot induct on dimension and an alternate approach
must be taken. The key ingredient is the concept of nilpotency, not just for Lie algebras, but for
Lie modules. Using this concept, we define an _Engelian Lie algebra_ `lie_algebra.is_engelian` to
be one for which a Lie module is nilpotent whenever the action consists of nilpotent endomorphisms.
The argument then proceeds by selecting a maximal Engelian Lie subalgebra and showing that it cannot
be proper.

The first part of the traditional statement of Engel's theorem consists of the statement that if `M`
is a non-trivial `R`-module and `L ⊆ End(M)` is a finite-dimensional Lie subalgebra of nilpotent
elements, then there exists a non-zero element `m : M` that is annihilated by every element of `L`.
This follows trivially from the result established here `lie_module.is_nilpotent_iff_forall`, that
`M` is a nilpotent Lie module over `L`, since the last non-zero term in the lower central series
will consist of such elements `m` (see: `lie_module.nontrivial_max_triv_of_is_nilpotent`). It seems
that this result has not previously been established at this level of generality.

The second part of the traditional statement of Engel's theorem concerns nilpotency of the Lie
algebra and a proof of this for general coefficients appeared in the literature as long ago
[as 1937](zorn1937). This also follows trivially from `lie_module.is_nilpotent_iff_forall` simply by
taking `M = L`.

It is pleasing that the two parts of the traditional statements of Engel's theorem are thus unified
into a single statement about nilpotency of Lie modules. This is not usually emphasised.

## Main definitions

  * `lie_algebra.is_engelian`
  * `lie_algebra.is_engelian_of_is_noetherian`
  * `lie_module.is_nilpotent_iff_forall`
  * `lie_algebra.is_nilpotent_iff_forall`

-/

universes u₁ u₂ u₃ u₄

variables {R : Type u₁} {L : Type u₂} {L₂ : Type u₃} {M : Type u₄}
variables [comm_ring R] [lie_ring L] [lie_algebra R L] [lie_ring L₂] [lie_algebra R L₂]
variables [add_comm_group M] [module R M] [lie_ring_module L M] [lie_module R L M]

include R L

namespace lie_submodule

open lie_module

variables {I : lie_ideal R L} {x : L} (hxI : (R ∙ x) ⊔ I = ⊤)

include hxI

lemma exists_smul_add_of_span_sup_eq_top (y : L) : ∃ (t : R) (z ∈ I), y = t • x + z :=
begin
  have hy : y ∈ (⊤ : submodule R L) := submodule.mem_top,
  simp only [← hxI, submodule.mem_sup, submodule.mem_span_singleton] at hy,
  obtain ⟨-, ⟨t, rfl⟩, z, hz, rfl⟩ := hy,
  exact ⟨t, z, hz, rfl⟩,
end

lemma lie_top_eq_of_span_sup_eq_top (N : lie_submodule R L M) :
  (↑⁅(⊤ : lie_ideal R L), N⁆ : submodule R M) =
  (N : submodule R M).map (to_endomorphism R L M x) ⊔ (↑⁅I, N⁆ : submodule R M) :=
begin
  simp only [lie_ideal_oper_eq_linear_span', submodule.sup_span, mem_top, exists_prop,
    exists_true_left, submodule.map_coe, to_endomorphism_apply_apply],
  refine le_antisymm (submodule.span_le.mpr _) (submodule.span_mono (λ z hz, _)),
  { rintros z ⟨y, n, hn : n ∈ N, rfl⟩,
    obtain ⟨t, z, hz, rfl⟩ := exists_smul_add_of_span_sup_eq_top hxI y,
    simp only [set_like.mem_coe, submodule.span_union, submodule.mem_sup],
    exact ⟨t • ⁅x, n⁆, submodule.subset_span ⟨t • n, N.smul_mem' t hn, lie_smul t x n⟩,
      ⁅z, n⁆, submodule.subset_span ⟨z, hz, n, hn, rfl⟩, by simp⟩, },
  { rcases hz with ⟨m, hm, rfl⟩ | ⟨y, hy, m, hm, rfl⟩,
    exacts [⟨x, m, hm, rfl⟩, ⟨y, m, hm, rfl⟩], },
end

lemma lcs_le_lcs_of_is_nilpotent_span_sup_eq_top {n i j : ℕ} (hxn : (to_endomorphism R L M x)^n = 0)
  (hIM : lower_central_series R L M i ≤ I.lcs M j) :
  lower_central_series R L M (i + n) ≤ I.lcs M (j + 1) :=
begin
  suffices : ∀ l, ((⊤ : lie_ideal R L).lcs M (i + l) : submodule R M) ≤
    (I.lcs M j : submodule R M).map
    ((to_endomorphism R L M x)^l) ⊔ (I.lcs M (j + 1) : submodule R M),
  { simpa only [bot_sup_eq, lie_ideal.incl_coe, submodule.map_zero, hxn] using this n, },
  intros l,
  induction l with l ih,
  { simp only [add_zero, lie_ideal.lcs_succ, pow_zero, linear_map.one_eq_id, submodule.map_id],
    exact le_sup_of_le_left hIM, },
  { simp only [lie_ideal.lcs_succ, i.add_succ l, lie_top_eq_of_span_sup_eq_top hxI, sup_le_iff],
    refine ⟨(submodule.map_mono ih).trans _, le_sup_of_le_right _⟩,
    { rw [submodule.map_sup, ← submodule.map_comp, ← linear_map.mul_eq_comp, ← pow_succ,
        ← I.lcs_succ],
      exact sup_le_sup_left coe_map_to_endomorphism_le _, },
    { refine le_trans (mono_lie_right _ _ I _) (mono_lie_right _ _ I hIM),
      exact antitone_lower_central_series R L M le_self_add, }, },
end

lemma is_nilpotent_of_is_nilpotent_span_sup_eq_top
  (hnp : is_nilpotent $ to_endomorphism R L M x) (hIM : is_nilpotent R I M) :
  is_nilpotent R L M :=
begin
  obtain ⟨n, hn⟩ := hnp,
  unfreezingI { obtain ⟨k, hk⟩ := hIM, },
  have hk' : I.lcs M k = ⊥,
  { simp only [← coe_to_submodule_eq_iff, I.coe_lcs_eq, hk, bot_coe_submodule], },
  suffices : ∀ l, lower_central_series R L M (l * n) ≤ I.lcs M l,
  { use k * n,
    simpa [hk'] using this k, },
  intros l,
  induction l with l ih,
  { simp, },
  { exact (l.succ_mul n).symm ▸ lcs_le_lcs_of_is_nilpotent_span_sup_eq_top hxI hn ih, },
end

end lie_submodule

section lie_algebra

open lie_module (hiding is_nilpotent)

variables (R L)

/-- A Lie algebra `L` is said to be Engelian if a sufficient condition for any `L`-Lie module `M` to
be nilpotent is that the image of the map `L → End(M)` consists of nilpotent elements.

Engel's theorem `lie_algebra.is_engelian_of_is_noetherian` states that any Noetherian Lie algebra is
Engelian. -/
def lie_algebra.is_engelian : Prop :=
  ∀ (M : Type u₄) [add_comm_group M], by exactI ∀ [module R M] [lie_ring_module L M], by exactI ∀
    [lie_module R L M], by exactI ∀ (h : ∀ (x : L), is_nilpotent (to_endomorphism R L M x)),
    lie_module.is_nilpotent R L M

variables {R L}

lemma lie_algebra.is_engelian_of_subsingleton [subsingleton L] : lie_algebra.is_engelian R L :=
begin
  intros M _i1 _i2 _i3 _i4 h,
  use 1,
  suffices : (⊤ : lie_ideal R L) = ⊥, { simp [this], },
  haveI := (lie_submodule.subsingleton_iff R L L).mpr infer_instance,
  apply subsingleton.elim,
end

lemma function.surjective.is_engelian
  {f : L →ₗ⁅R⁆ L₂} (hf : function.surjective f) (h : lie_algebra.is_engelian.{u₁ u₂ u₄} R L) :
  lie_algebra.is_engelian.{u₁ u₃ u₄} R L₂ :=
begin
  introsI M _i1 _i2 _i3 _i4 h',
  letI : lie_ring_module L M := lie_ring_module.comp_lie_hom M f,
  letI : lie_module R L M := comp_lie_hom M f,
  have hnp : ∀ x, is_nilpotent (to_endomorphism R L M x) := λ x, h' (f x),
  have surj_id : function.surjective (linear_map.id : M →ₗ[R] M) := function.surjective_id,
  haveI : lie_module.is_nilpotent R L M := h M hnp,
  apply hf.lie_module_is_nilpotent surj_id,
  simp,
end

lemma lie_equiv.is_engelian_iff (e : L ≃ₗ⁅R⁆ L₂) :
  lie_algebra.is_engelian.{u₁ u₂ u₄} R L ↔ lie_algebra.is_engelian.{u₁ u₃ u₄} R L₂ :=
⟨e.surjective.is_engelian, e.symm.surjective.is_engelian⟩

lemma lie_algebra.exists_engelian_lie_subalgebra_of_lt_normalizer
  {K : lie_subalgebra R L} (hK₁ : lie_algebra.is_engelian.{u₁ u₂ u₄} R K) (hK₂ : K < K.normalizer) :
  ∃ (K' : lie_subalgebra R L) (hK' : lie_algebra.is_engelian.{u₁ u₂ u₄} R K'), K < K' :=
begin
  obtain ⟨x, hx₁, hx₂⟩ := set_like.exists_of_lt hK₂,
  let K' : lie_subalgebra R L :=
  { lie_mem' := λ y z, lie_subalgebra.lie_mem_sup_of_mem_normalizer hx₁,
    .. (R ∙ x) ⊔ (K : submodule R L) },
  have hxK' : x ∈ K' := submodule.mem_sup_left (submodule.subset_span (set.mem_singleton _)),
  have hKK' : K ≤ K' := (lie_subalgebra.coe_submodule_le_coe_submodule K K').mp le_sup_right,
  have hK' : K' ≤ K.normalizer,
  { rw ← lie_subalgebra.coe_submodule_le_coe_submodule,
    exact sup_le ((submodule.span_singleton_le_iff_mem _ _).mpr hx₁) hK₂.le, },
  refine ⟨K', _, lt_iff_le_and_ne.mpr ⟨hKK', λ contra, hx₂ (contra.symm ▸ hxK')⟩⟩,
  introsI M _i1 _i2 _i3 _i4 h,
  obtain ⟨I, hI₁ : (I : lie_subalgebra R K') = lie_subalgebra.of_le hKK'⟩ :=
    lie_subalgebra.exists_nested_lie_ideal_of_le_normalizer hKK' hK',
  have hI₂ : (R ∙ (⟨x, hxK'⟩ : K')) ⊔ I = ⊤,
  { rw [← lie_ideal.coe_to_lie_subalgebra_to_submodule R K' I, hI₁],
    apply submodule.map_injective_of_injective (K' : submodule R L).injective_subtype,
    simpa, },
  have e : K ≃ₗ⁅R⁆ I := (lie_subalgebra.equiv_of_le hKK').trans
    (lie_equiv.of_eq _ _ ((lie_subalgebra.coe_set_eq _ _).mpr hI₁.symm)),
  have hI₃ : lie_algebra.is_engelian R I := e.is_engelian_iff.mp hK₁,
  exact lie_submodule.is_nilpotent_of_is_nilpotent_span_sup_eq_top hI₂ (h _) (hI₃ _ (λ x, h x)),
end

local attribute [instance] lie_subalgebra.subsingleton_bot

variables [is_noetherian R L]

/-- *Engel's theorem*.

Note that this implies all traditional forms of Engel's theorem via
`lie_module.nontrivial_max_triv_of_is_nilpotent`, `lie_module.is_nilpotent_iff_forall`,
`lie_algebra.is_nilpotent_iff_forall`. -/
lemma lie_algebra.is_engelian_of_is_noetherian : lie_algebra.is_engelian R L :=
begin
  introsI M _i1 _i2 _i3 _i4 h,
  rw ← is_nilpotent_range_to_endomorphism_iff,
  let L' := (to_endomorphism R L M).range,
  replace h : ∀ (y : L'), is_nilpotent (y : module.End R M),
  { rintros ⟨-, ⟨y, rfl⟩⟩,
    simp [h], },
  change lie_module.is_nilpotent R L' M,
  let s := { K : lie_subalgebra R L' | lie_algebra.is_engelian R K },
  have hs : s.nonempty := ⟨⊥, lie_algebra.is_engelian_of_subsingleton⟩,
  suffices : ⊤ ∈ s,
  { rw ← is_nilpotent_of_top_iff,
    apply this M,
    simp [lie_subalgebra.to_endomorphism_eq, h], },
  have : ∀ (K ∈ s), K ≠ ⊤ → ∃ (K' ∈ s), K < K',
  { rintros K (hK₁ : lie_algebra.is_engelian R K) hK₂,
    apply lie_algebra.exists_engelian_lie_subalgebra_of_lt_normalizer hK₁,
    apply lt_of_le_of_ne K.le_normalizer,
    rw [ne.def, eq_comm, K.normalizer_eq_self_iff, ← ne.def,
      ← lie_submodule.nontrivial_iff_ne_bot R K],
    haveI : nontrivial (L' ⧸ K.to_lie_submodule),
    { replace hK₂ : K.to_lie_submodule ≠ ⊤ :=
        by rwa [ne.def, ← lie_submodule.coe_to_submodule_eq_iff, K.coe_to_lie_submodule,
          lie_submodule.top_coe_submodule, ← lie_subalgebra.top_coe_submodule,
          K.coe_to_submodule_eq_iff],
      exact submodule.quotient.nontrivial_of_lt_top _ hK₂.lt_top, },
    haveI : lie_module.is_nilpotent R K (L' ⧸ K.to_lie_submodule),
    { refine hK₁ _ (λ x, _),
      have hx := lie_algebra.is_nilpotent_ad_of_is_nilpotent (h x),
      exact module.End.is_nilpotent.mapq _ hx, },
    exact nontrivial_max_triv_of_is_nilpotent R K (L' ⧸ K.to_lie_submodule), },
  haveI _i5 : is_noetherian R L' :=
    is_noetherian_of_surjective L _ (linear_map.range_range_restrict (to_endomorphism R L M)),
  obtain ⟨K, hK₁, hK₂⟩ := (lie_subalgebra.well_founded_of_noetherian R L').has_min s hs,
  have hK₃ : K = ⊤,
  { by_contra contra,
    obtain ⟨K', hK'₁, hK'₂⟩ := this K hK₁ contra,
    exact hK₂ K' hK'₁ hK'₂, },
  exact hK₃ ▸ hK₁,
end

/-- Engel's theorem. -/
lemma lie_module.is_nilpotent_iff_forall :
  lie_module.is_nilpotent R L M ↔ ∀ x, is_nilpotent $ to_endomorphism R L M x :=
⟨begin
  introsI h,
  obtain ⟨k, hk⟩ := nilpotent_endo_of_nilpotent_module R L M,
  exact λ x, ⟨k, hk x⟩,
end,
λ h, lie_algebra.is_engelian_of_is_noetherian M h⟩

/-- Engel's theorem. -/
lemma lie_algebra.is_nilpotent_iff_forall :
  lie_algebra.is_nilpotent R L ↔ ∀ x, is_nilpotent $ lie_algebra.ad R L x :=
lie_module.is_nilpotent_iff_forall

end lie_algebra


/-
Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis, Mario Carneiro, Johan Commelin
-/
import number_theory.padics.padic_numbers
import ring_theory.discrete_valuation_ring
import topology.metric_space.cau_seq_filter

/-!
# p-adic integers

This file defines the `p`-adic integers `ℤ_[p]` as the subtype of `ℚ_[p]` with norm `≤ 1`.
We show that `ℤ_[p]`
* is complete,
* is nonarchimedean,
* is a normed ring,
* is a local ring, and
* is a discrete valuation ring.

The relation between `ℤ_[p]` and `zmod p` is established in another file.

## Important definitions

* `padic_int` : the type of `p`-adic integers

## Notation

We introduce the notation `ℤ_[p]` for the `p`-adic integers.

## Implementation notes

Much, but not all, of this file assumes that `p` is prime. This assumption is inferred automatically
by taking `[fact p.prime]` as a type class argument.

Coercions into `ℤ_[p]` are set up to work with the `norm_cast` tactic.

## References

* [F. Q. Gouvêa, *p-adic numbers*][gouvea1997]
* [R. Y. Lewis, *A formal proof of Hensel's lemma over the p-adic integers*][lewis2019]
* <https://en.wikipedia.org/wiki/P-adic_number>

## Tags

p-adic, p adic, padic, p-adic integer
-/

open padic metric local_ring
noncomputable theory
open_locale classical

/-- The `p`-adic integers `ℤ_[p]` are the `p`-adic numbers with norm `≤ 1`. -/
def padic_int (p : ℕ) [fact p.prime] := {x : ℚ_[p] // ‖x‖ ≤ 1}

notation `ℤ_[`p`]` := padic_int p

namespace padic_int

/-! ### Ring structure and coercion to `ℚ_[p]` -/

variables {p : ℕ} [fact p.prime]

instance : has_coe ℤ_[p] ℚ_[p] := ⟨subtype.val⟩

lemma ext {x y : ℤ_[p]} : (x : ℚ_[p]) = y → x = y := subtype.ext

variables (p)

/-- The `p`-adic integers as a subring of `ℚ_[p]`. -/
def subring : subring (ℚ_[p]) :=
{ carrier := {x : ℚ_[p] | ‖x‖ ≤ 1},
  zero_mem' := by norm_num,
  one_mem' := by norm_num,
  add_mem' := λ x y hx hy, (padic_norm_e.nonarchimedean _ _).trans $ max_le_iff.2 ⟨hx, hy⟩,
  mul_mem' := λ x y hx hy, (padic_norm_e.mul _ _).trans_le $ mul_le_one hx (norm_nonneg _) hy,
  neg_mem' := λ x hx, (norm_neg _).trans_le hx }

@[simp] lemma mem_subring_iff {x : ℚ_[p]} : x ∈ subring p ↔ ‖x‖ ≤ 1 := iff.rfl

variables {p}

/-- Addition on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_add ℤ_[p] := (by apply_instance : has_add (subring p))

/-- Multiplication on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_mul ℤ_[p] := (by apply_instance : has_mul (subring p))

/-- Negation on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_neg ℤ_[p] := (by apply_instance : has_neg (subring p))

/-- Subtraction on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_sub ℤ_[p] := (by apply_instance : has_sub (subring p))

/-- Zero on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_zero ℤ_[p] := (by apply_instance : has_zero (subring p))

instance : inhabited ℤ_[p] := ⟨0⟩

/-- One on `ℤ_[p]` is inherited from `ℚ_[p]`. -/
instance : has_one ℤ_[p] := ⟨⟨1, by norm_num⟩⟩

@[simp] lemma mk_zero {h} : (⟨0, h⟩ : ℤ_[p]) = (0 : ℤ_[p]) := rfl

@[simp] lemma val_eq_coe (z : ℤ_[p]) : z.val = z := rfl

@[simp, norm_cast] lemma coe_add (z1 z2 : ℤ_[p]) : ((z1 + z2 : ℤ_[p]) : ℚ_[p]) = z1 + z2 := rfl
@[simp, norm_cast] lemma coe_mul (z1 z2 : ℤ_[p]) : ((z1 * z2 : ℤ_[p]) : ℚ_[p]) = z1 * z2 := rfl
@[simp, norm_cast] lemma coe_neg (z1 : ℤ_[p]) : ((-z1 : ℤ_[p]) : ℚ_[p]) = -z1 := rfl
@[simp, norm_cast] lemma coe_sub (z1 z2 : ℤ_[p]) : ((z1 - z2 : ℤ_[p]) : ℚ_[p]) = z1 - z2 := rfl
@[simp, norm_cast] lemma coe_one : ((1 : ℤ_[p]) : ℚ_[p]) = 1 := rfl
@[simp, norm_cast] lemma coe_zero : ((0 : ℤ_[p]) : ℚ_[p]) = 0 := rfl

lemma coe_eq_zero (z : ℤ_[p]) : (z : ℚ_[p]) = 0 ↔ z = 0 :=
by rw [← coe_zero, subtype.coe_inj]

lemma coe_ne_zero (z : ℤ_[p]) : (z : ℚ_[p]) ≠ 0 ↔ z ≠ 0 := z.coe_eq_zero.not

instance : add_comm_group ℤ_[p] :=
(by apply_instance : add_comm_group (subring p))

instance : comm_ring ℤ_[p] :=
(by apply_instance : comm_ring (subring p))

@[simp, norm_cast] lemma coe_nat_cast (n : ℕ) : ((n : ℤ_[p]) : ℚ_[p]) = n := rfl
@[simp, norm_cast] lemma coe_int_cast (z : ℤ) : ((z : ℤ_[p]) : ℚ_[p]) = z := rfl

/-- The coercion from `ℤ_[p]` to `ℚ_[p]` as a ring homomorphism. -/
def coe.ring_hom : ℤ_[p] →+* ℚ_[p] := (subring p).subtype

@[simp, norm_cast] lemma coe_pow (x : ℤ_[p]) (n : ℕ) : (↑(x^n) : ℚ_[p]) = (↑x : ℚ_[p])^n := rfl

@[simp] lemma mk_coe (k : ℤ_[p]) : (⟨k, k.2⟩ : ℤ_[p]) = k := subtype.coe_eta _ _

/-- The inverse of a `p`-adic integer with norm equal to `1` is also a `p`-adic integer.
Otherwise, the inverse is defined to be `0`. -/
def inv : ℤ_[p] → ℤ_[p]
| ⟨k, _⟩ := if h : ‖k‖ = 1 then ⟨k⁻¹, by simp [h]⟩ else 0

instance : char_zero ℤ_[p] :=
{ cast_injective :=
  λ m n h, nat.cast_injective $
  show (m:ℚ_[p]) = n, by { rw subtype.ext_iff at h, norm_cast at h, exact h } }

@[simp, norm_cast] lemma coe_int_eq (z1 z2 : ℤ) : (z1 : ℤ_[p]) = z2 ↔ z1 = z2 :=
suffices (z1 : ℚ_[p]) = z2 ↔ z1 = z2, from iff.trans (by norm_cast) this,
by norm_cast

/-- A sequence of integers that is Cauchy with respect to the `p`-adic norm converges to a `p`-adic
integer. -/
def of_int_seq (seq : ℕ → ℤ) (h : is_cau_seq (padic_norm p) (λ n, seq n)) : ℤ_[p] :=
⟨⟦⟨_, h⟩⟧,
 show ↑(padic_seq.norm _) ≤ (1 : ℝ), begin
   rw padic_seq.norm,
   split_ifs with hne; norm_cast,
   { exact zero_le_one },
   { apply padic_norm.of_int }
 end ⟩

end padic_int

namespace padic_int

/-! ### Instances

We now show that `ℤ_[p]` is a
* complete metric space
* normed ring
* integral domain
-/

variables (p : ℕ) [fact p.prime]

instance : metric_space ℤ_[p] := subtype.metric_space

instance complete_space : complete_space ℤ_[p] :=
have is_closed {x : ℚ_[p] | ‖x‖ ≤ 1}, from is_closed_le continuous_norm continuous_const,
this.complete_space_coe

instance : has_norm ℤ_[p] := ⟨λ z, ‖(z : ℚ_[p])‖⟩

variables {p}

lemma norm_def {z : ℤ_[p]} : ‖z‖ = ‖(z : ℚ_[p])‖ := rfl

variables (p)

instance : normed_comm_ring ℤ_[p] :=
{ dist_eq := λ ⟨_, _⟩ ⟨_, _⟩, rfl,
  norm_mul := by simp [norm_def],
  norm := norm, .. padic_int.comm_ring, .. padic_int.metric_space p }

instance : norm_one_class ℤ_[p] := ⟨norm_def.trans norm_one⟩

instance is_absolute_value : is_absolute_value (λ z : ℤ_[p], ‖z‖) :=
{ abv_nonneg := norm_nonneg,
  abv_eq_zero := λ ⟨_, _⟩, by simp [norm_eq_zero],
  abv_add := λ ⟨_,_⟩ ⟨_, _⟩, norm_add_le _ _,
  abv_mul := λ _ _, by simp only [norm_def, padic_norm_e.mul, padic_int.coe_mul] }

variables {p}

instance : is_domain ℤ_[p] := function.injective.is_domain (subring p).subtype subtype.coe_injective

end padic_int

namespace padic_int

/-! ### Norm -/

variables {p : ℕ} [fact p.prime]

lemma norm_le_one (z : ℤ_[p]) : ‖z‖ ≤ 1 := z.2

@[simp] lemma norm_mul (z1 z2 : ℤ_[p]) : ‖z1 * z2‖ = ‖z1‖ * ‖z2‖ := by simp [norm_def]

@[simp]

theorem nonarchimedean (q r : ℤ_[p]) : ‖q + r‖ ≤ max (‖q‖) (‖r‖) := padic_norm_e.nonarchimedean _ _

theorem norm_add_eq_max_of_ne {q r : ℤ_[p]} : ‖q‖ ≠ ‖r‖ → ‖q+r‖ = max (‖q‖) (‖r‖) :=
padic_norm_e.add_eq_max_of_ne

lemma norm_eq_of_norm_add_lt_right {z1 z2 : ℤ_[p]} (h : ‖z1 + z2‖ < ‖z2‖) : ‖z1‖ = ‖z2‖ :=
by_contradiction $ λ hne, not_lt_of_ge (by rw norm_add_eq_max_of_ne hne; apply le_max_right) h

lemma norm_eq_of_norm_add_lt_left {z1 z2 : ℤ_[p]} (h : ‖z1 + z2‖ < ‖z1‖) : ‖z1‖ = ‖z2‖ :=
by_contradiction $ λ hne, not_lt_of_ge (by rw norm_add_eq_max_of_ne hne; apply le_max_left) h

@[simp] lemma padic_norm_e_of_padic_int (z : ℤ_[p]) : ‖(z : ℚ_[p])‖ = ‖z‖ := by simp [norm_def]

lemma norm_int_cast_eq_padic_norm (z : ℤ) : ‖(z : ℤ_[p])‖ = ‖(z : ℚ_[p])‖ := by simp [norm_def]

@[simp] lemma norm_eq_padic_norm {q : ℚ_[p]} (hq : ‖q‖ ≤ 1) : @norm ℤ_[p] _ ⟨q, hq⟩ = ‖q‖ := rfl

@[simp] lemma norm_p : ‖(p : ℤ_[p])‖ = p⁻¹ := padic_norm_e.norm_p

@[simp] lemma norm_p_pow (n : ℕ) : ‖(p : ℤ_[p])^n‖ = p^(-n:ℤ) := padic_norm_e.norm_p_pow n

private def cau_seq_to_rat_cau_seq (f : cau_seq ℤ_[p] norm) : cau_seq ℚ_[p] (λ a, ‖a‖) :=
⟨ λ n, f n, λ _ hε, by simpa [norm, norm_def] using f.cauchy hε ⟩

variables (p)

instance complete : cau_seq.is_complete ℤ_[p] norm :=
⟨ λ f,
  have hqn : ‖cau_seq.lim (cau_seq_to_rat_cau_seq f)‖ ≤ 1,
    from padic_norm_e_lim_le zero_lt_one (λ _, norm_le_one _),
  ⟨⟨_, hqn⟩, λ ε, by simpa [norm, norm_def] using cau_seq.equiv_lim (cau_seq_to_rat_cau_seq f) ε⟩⟩

end padic_int

namespace padic_int

variables (p : ℕ) [hp : fact p.prime]

include hp

lemma exists_pow_neg_lt {ε : ℝ} (hε : 0 < ε) : ∃ k : ℕ, ↑p ^ -(k : ℤ) < ε :=
begin
  obtain ⟨k, hk⟩ := exists_nat_gt ε⁻¹,
  use k,
  rw ← inv_lt_inv hε (_root_.zpow_pos_of_pos _ _),
  { rw [zpow_neg, inv_inv, zpow_coe_nat],
    apply lt_of_lt_of_le hk,
    norm_cast,
    apply le_of_lt,
    convert nat.lt_pow_self _ _ using 1,
    exact hp.1.one_lt },
  { exact_mod_cast hp.1.pos }
end

lemma exists_pow_neg_lt_rat {ε : ℚ} (hε : 0 < ε) : ∃ k : ℕ, ↑p ^ -(k : ℤ) < ε :=
begin
  obtain ⟨k, hk⟩ := @exists_pow_neg_lt p _ ε (by exact_mod_cast hε),
  use k,
  rw (show (p : ℝ) = (p : ℚ), by simp) at hk,
  exact_mod_cast hk
end

variable {p}

lemma norm_int_lt_one_iff_dvd (k : ℤ) : ‖(k : ℤ_[p])‖ < 1 ↔ (p : ℤ) ∣ k :=
suffices ‖(k : ℚ_[p])‖ < 1 ↔ ↑p ∣ k, by rwa norm_int_cast_eq_padic_norm,
padic_norm_e.norm_int_lt_one_iff_dvd k

lemma norm_int_le_pow_iff_dvd {k : ℤ} {n : ℕ} : ‖(k : ℤ_[p])‖ ≤ p ^ (-n : ℤ) ↔ (p ^ n : ℤ) ∣ k :=
suffices ‖(k : ℚ_[p])‖ ≤ p ^ (-n : ℤ) ↔ ↑(p ^ n) ∣ k,
by simpa [norm_int_cast_eq_padic_norm], padic_norm_e.norm_int_le_pow_iff_dvd _ _

/-! ### Valuation on `ℤ_[p]` -/

/-- `padic_int.valuation` lifts the `p`-adic valuation on `ℚ` to `ℤ_[p]`.  -/
def valuation (x : ℤ_[p]) := padic.valuation (x : ℚ_[p])

lemma norm_eq_pow_val {x : ℤ_[p]} (hx : x ≠ 0) : ‖x‖ = (p : ℝ) ^ -x.valuation :=
begin
  convert padic.norm_eq_pow_val _,
  contrapose! hx,
  exact subtype.val_injective hx
end

@[simp] lemma valuation_zero : valuation (0 : ℤ_[p]) = 0 := padic.valuation_zero

@[simp] lemma valuation_one : valuation (1 : ℤ_[p]) = 0 := padic.valuation_one

@[simp] lemma valuation_p : valuation (p : ℤ_[p]) = 1 := by simp [valuation]

lemma valuation_nonneg (x : ℤ_[p]) : 0 ≤ x.valuation :=
begin
  by_cases hx : x = 0,
  { simp [hx] },
  have h : (1 : ℝ) < p := by exact_mod_cast hp.1.one_lt,
  rw [← neg_nonpos, ← (zpow_strict_mono h).le_iff_le],
  show (p : ℝ) ^ -valuation x ≤ p ^ (0 : ℤ),
  rw [← norm_eq_pow_val hx],
  simpa using x.property
end

@[simp] lemma valuation_p_pow_mul (n : ℕ) (c : ℤ_[p]) (hc : c ≠ 0) :
  (↑p ^ n * c).valuation = n + c.valuation :=
begin
  have : ‖(↑p ^ n * c)‖ = ‖(p ^ n : ℤ_[p])‖ * ‖c‖,
  { exact norm_mul _ _ },
  have aux : (↑p ^ n * c) ≠ 0,
  { contrapose! hc, rw mul_eq_zero at hc, cases hc,
    { refine (hp.1.ne_zero _).elim,
      exact_mod_cast (pow_eq_zero hc) },
    { exact hc } },
  rwa [norm_eq_pow_val aux, norm_p_pow, norm_eq_pow_val hc,
      ← zpow_add₀, ← neg_add, zpow_inj, neg_inj] at this,
  { exact_mod_cast hp.1.pos },
  { exact_mod_cast hp.1.ne_one },
  { exact_mod_cast hp.1.ne_zero }
end

section units

/-! ### Units of `ℤ_[p]` -/

local attribute [reducible] padic_int

lemma mul_inv : ∀ {z : ℤ_[p]}, ‖z‖ = 1 → z * z.inv = 1
| ⟨k, _⟩ h :=
  begin
    have hk : k ≠ 0, from λ h', zero_ne_one' ℚ_[p] (by simpa [h'] using h),
    unfold padic_int.inv,
    rw [norm_eq_padic_norm] at h,
    rw dif_pos h,
    apply subtype.ext_iff_val.2,
    simp [mul_inv_cancel hk]
  end

lemma inv_mul {z : ℤ_[p]} (hz : ‖z‖ = 1) : z.inv * z = 1 := by rw [mul_comm, mul_inv hz]

lemma is_unit_iff {z : ℤ_[p]} : is_unit z ↔ ‖z‖ = 1 :=
⟨λ h, begin
  rcases is_unit_iff_dvd_one.1 h with ⟨w, eq⟩,
  refine le_antisymm (norm_le_one _) _,
  have := mul_le_mul_of_nonneg_left (norm_le_one w) (norm_nonneg z),
  rwa [mul_one, ← norm_mul, ← eq, norm_one] at this
end, λ h, ⟨⟨z, z.inv, mul_inv h, inv_mul h⟩, rfl⟩⟩

lemma norm_lt_one_add {z1 z2 : ℤ_[p]} (hz1 : ‖z1‖ < 1) (hz2 : ‖z2‖ < 1) : ‖z1 + z2‖ < 1 :=
lt_of_le_of_lt (nonarchimedean _ _) (max_lt hz1 hz2)

lemma norm_lt_one_mul {z1 z2 : ℤ_[p]} (hz2 : ‖z2‖ < 1) : ‖z1 * z2‖ < 1 :=
calc ‖z1 * z2‖ = ‖z1‖ * ‖z2‖ : by simp
          ... < 1 : mul_lt_one_of_nonneg_of_lt_one_right (norm_le_one _) (norm_nonneg _) hz2

@[simp] lemma mem_nonunits {z : ℤ_[p]} : z ∈ nonunits ℤ_[p] ↔ ‖z‖ < 1 :=
by rw lt_iff_le_and_ne; simp [norm_le_one z, nonunits, is_unit_iff]

/-- A `p`-adic number `u` with `‖u‖ = 1` is a unit of `ℤ_[p]`. -/
def mk_units {u : ℚ_[p]} (h : ‖u‖ = 1) : ℤ_[p]ˣ :=
let z : ℤ_[p] := ⟨u, le_of_eq h⟩ in ⟨z, z.inv, mul_inv h, inv_mul h⟩

@[simp] lemma mk_units_eq {u : ℚ_[p]} (h : ‖u‖ = 1) : ((mk_units h : ℤ_[p]) : ℚ_[p]) = u := rfl

@[simp] lemma norm_units (u : ℤ_[p]ˣ) : ‖(u : ℤ_[p])‖ = 1 := is_unit_iff.mp $ by simp

/-- `unit_coeff hx` is the unit `u` in the unique representation `x = u * p ^ n`.
See `unit_coeff_spec`. -/
def unit_coeff {x : ℤ_[p]} (hx : x ≠ 0) : ℤ_[p]ˣ :=
let u : ℚ_[p] := x * p ^ -x.valuation in
have hu : ‖u‖ = 1,
by simp [hx, nat.zpow_ne_zero_of_pos (by exact_mod_cast hp.1.pos) x.valuation,
         norm_eq_pow_val, zpow_neg, inv_mul_cancel],
mk_units hu

@[simp] lemma unit_coeff_coe {x : ℤ_[p]} (hx : x ≠ 0) :
  (unit_coeff hx : ℚ_[p]) = x * p ^ -x.valuation := rfl

lemma unit_coeff_spec {x : ℤ_[p]} (hx : x ≠ 0) :
  x = (unit_coeff hx : ℤ_[p]) * p ^ int.nat_abs (valuation x) :=
begin
  apply subtype.coe_injective,
  push_cast,
  have repr : (x : ℚ_[p]) = (unit_coeff hx) * p ^ x.valuation,
  { rw [unit_coeff_coe, mul_assoc, ← zpow_add₀],
    { simp },
    { exact_mod_cast hp.1.ne_zero } },
  convert repr using 2,
  rw [← zpow_coe_nat, int.nat_abs_of_nonneg (valuation_nonneg x)]
end

end units

section norm_le_iff

/-! ### Various characterizations of open unit balls -/

lemma norm_le_pow_iff_le_valuation (x : ℤ_[p]) (hx : x ≠ 0) (n : ℕ) :
  ‖x‖ ≤ p ^ (-n : ℤ) ↔ ↑n ≤ x.valuation :=
begin
  rw norm_eq_pow_val hx,
  lift x.valuation to ℕ using x.valuation_nonneg with k hk,
  simp only [int.coe_nat_le, zpow_neg, zpow_coe_nat],
  have aux : ∀ n : ℕ, 0 < (p ^ n : ℝ),
  { apply pow_pos, exact_mod_cast hp.1.pos },
  rw [inv_le_inv (aux _) (aux _)],
  have : p ^ n ≤ p ^ k ↔ n ≤ k := (pow_strict_mono_right hp.1.one_lt).le_iff_le,
  rw [← this],
  norm_cast
end

lemma mem_span_pow_iff_le_valuation (x : ℤ_[p]) (hx : x ≠ 0) (n : ℕ) :
  x ∈ (ideal.span {p ^ n} : ideal ℤ_[p]) ↔ ↑n ≤ x.valuation :=
begin
  rw [ideal.mem_span_singleton],
  split,
  { rintro ⟨c, rfl⟩,
    suffices : c ≠ 0,
    { rw [valuation_p_pow_mul _ _ this, le_add_iff_nonneg_right], apply valuation_nonneg },
    contrapose! hx, rw [hx, mul_zero] },
  { rw [unit_coeff_spec hx] { occs := occurrences.pos [2] },
    lift x.valuation to ℕ using x.valuation_nonneg with k hk,
    simp only [int.nat_abs_of_nat, units.is_unit, is_unit.dvd_mul_left, int.coe_nat_le],
    intro H,
    obtain ⟨k, rfl⟩ := nat.exists_eq_add_of_le H,
    simp only [pow_add, dvd_mul_right] }
end

lemma norm_le_pow_iff_mem_span_pow (x : ℤ_[p]) (n : ℕ) :
  ‖x‖ ≤ p ^ (-n : ℤ) ↔ x ∈ (ideal.span {p ^ n} : ideal ℤ_[p]) :=
begin
  by_cases hx : x = 0,
  { subst hx,
    simp only [norm_zero, zpow_neg, zpow_coe_nat, inv_nonneg, iff_true, submodule.zero_mem],
    exact_mod_cast nat.zero_le _ },
  rw [norm_le_pow_iff_le_valuation x hx, mem_span_pow_iff_le_valuation x hx]
end

lemma norm_le_pow_iff_norm_lt_pow_add_one (x : ℤ_[p]) (n : ℤ) : ‖x‖ ≤ p ^ n ↔ ‖x‖ < p ^ (n + 1) :=
begin
  rw norm_def, exact padic.norm_le_pow_iff_norm_lt_pow_add_one _ _,
end

lemma norm_lt_pow_iff_norm_le_pow_sub_one (x : ℤ_[p]) (n : ℤ) : ‖x‖ < p ^ n ↔ ‖x‖ ≤ p ^ (n - 1) :=
by rw [norm_le_pow_iff_norm_lt_pow_add_one, sub_add_cancel]

lemma norm_lt_one_iff_dvd (x : ℤ_[p]) : ‖x‖ < 1 ↔ ↑p ∣ x :=
begin
  have := norm_le_pow_iff_mem_span_pow x 1,
  rw [ideal.mem_span_singleton, pow_one] at this,
  rw [← this, norm_le_pow_iff_norm_lt_pow_add_one],
  simp only [zpow_zero, int.coe_nat_zero, int.coe_nat_succ, add_left_neg, zero_add]
end

@[simp] lemma pow_p_dvd_int_iff (n : ℕ) (a : ℤ) : (p ^ n : ℤ_[p]) ∣ a ↔ ↑p ^ n ∣ a :=
by rw [← norm_int_le_pow_iff_dvd, norm_le_pow_iff_mem_span_pow, ideal.mem_span_singleton]

end norm_le_iff

section dvr

/-! ### Discrete valuation ring -/

instance : local_ring ℤ_[p] :=
local_ring.of_nonunits_add $ by simp only [mem_nonunits]; exact λ x y, norm_lt_one_add

lemma p_nonnunit : (p : ℤ_[p]) ∈ nonunits ℤ_[p] :=
have (p : ℝ)⁻¹ < 1, from inv_lt_one $ by exact_mod_cast hp.1.one_lt,
by simp [this]

lemma maximal_ideal_eq_span_p : maximal_ideal ℤ_[p] = ideal.span {p} :=
begin
  apply le_antisymm,
  { intros x hx,
    simp only [local_ring.mem_maximal_ideal, mem_nonunits] at hx,
    rwa [ideal.mem_span_singleton, ← norm_lt_one_iff_dvd] },
  { rw [ideal.span_le, set.singleton_subset_iff], exact p_nonnunit }
end

lemma prime_p : prime (p : ℤ_[p]) :=
begin
  rw [← ideal.span_singleton_prime, ← maximal_ideal_eq_span_p],
  { apply_instance },
  { exact_mod_cast hp.1.ne_zero }
end

lemma irreducible_p : irreducible (p : ℤ_[p]) := prime.irreducible prime_p

instance : discrete_valuation_ring ℤ_[p] :=
discrete_valuation_ring.of_has_unit_mul_pow_irreducible_factorization
⟨p, irreducible_p, λ x hx, ⟨x.valuation.nat_abs, unit_coeff hx,
  by rw [mul_comm, ← unit_coeff_spec hx]⟩⟩

lemma ideal_eq_span_pow_p {s : ideal ℤ_[p]} (hs : s ≠ ⊥) : ∃ n : ℕ, s = ideal.span {p ^ n} :=
discrete_valuation_ring.ideal_eq_span_pow_irreducible hs irreducible_p

open cau_seq

instance : is_adic_complete (maximal_ideal ℤ_[p]) ℤ_[p] :=
{ prec' := λ x hx,
  begin
    simp only [← ideal.one_eq_top, smul_eq_mul, mul_one, smodeq.sub_mem, maximal_ideal_eq_span_p,
      ideal.span_singleton_pow, ← norm_le_pow_iff_mem_span_pow] at hx ⊢,
    let x' : cau_seq ℤ_[p] norm := ⟨x, _⟩, swap,
    { intros ε hε, obtain ⟨m, hm⟩ := exists_pow_neg_lt p hε,
      refine ⟨m, λ n hn, lt_of_le_of_lt _ hm⟩, rw [← neg_sub, norm_neg], exact hx hn },
    { refine ⟨x'.lim, λ n, _⟩,
      have : (0:ℝ) < p ^ (-n : ℤ), { apply zpow_pos_of_pos, exact_mod_cast hp.1.pos },
      obtain ⟨i, hi⟩ := equiv_def₃ (equiv_lim x') this,
      by_cases hin : i ≤ n,
      { exact (hi i le_rfl n hin).le },
      { push_neg at hin, specialize hi i le_rfl i le_rfl, specialize hx hin.le,
        have := nonarchimedean (x n - x i) (x i - x'.lim),
        rw [sub_add_sub_cancel] at this,
        refine this.trans (max_le_iff.mpr ⟨hx, hi.le⟩) } }
  end }

end dvr

section fraction_ring

instance algebra : algebra ℤ_[p] ℚ_[p] := algebra.of_subring (subring p)

@[simp] lemma algebra_map_apply (x : ℤ_[p]) : algebra_map ℤ_[p] ℚ_[p] x = x := rfl

instance is_fraction_ring : is_fraction_ring ℤ_[p] ℚ_[p] :=
{ map_units := λ ⟨x, hx⟩,
  by rwa [set_like.coe_mk, algebra_map_apply, is_unit_iff_ne_zero, padic_int.coe_ne_zero,
      ←mem_non_zero_divisors_iff_ne_zero],
  surj := λ x,
  begin
    by_cases hx : ‖ x ‖ ≤ 1,
    { use (⟨x, hx⟩, 1),
      rw [submonoid.coe_one, map_one, mul_one, padic_int.algebra_map_apply, subtype.coe_mk] },
    { set n := int.to_nat(- x.valuation) with hn,
      have hn_coe : (n : ℤ) = -x.valuation,
      { rw [hn, int.to_nat_of_nonneg],
        rw right.nonneg_neg_iff,
        rw [padic.norm_le_one_iff_val_nonneg, not_le] at hx,
        exact hx.le },
      set a := x * p^n with ha,
      have ha_norm : ‖ a ‖ = 1,
      { have hx : x ≠ 0,
        { intro h0,
          rw [h0, norm_zero] at hx,
          exact hx (zero_le_one) },
        rw [ha, padic_norm_e.mul, padic_norm_e.norm_p_pow,
          padic.norm_eq_pow_val hx, ← zpow_add', hn_coe, neg_neg, add_left_neg, zpow_zero],
        exact or.inl (nat.cast_ne_zero.mpr (ne_zero.ne p)), },
      use (⟨a, le_of_eq ha_norm⟩,
        ⟨(p^n : ℤ_[p]), mem_non_zero_divisors_iff_ne_zero.mpr (ne_zero.ne _)⟩),
      simp only [set_like.coe_mk, map_pow, map_nat_cast, algebra_map_apply,
        padic_int.coe_pow, padic_int.coe_nat_cast, subtype.coe_mk] }
  end,
  eq_iff_exists := λ x y,
  begin
    rw [algebra_map_apply, algebra_map_apply, subtype.coe_inj],
    refine ⟨λ h, ⟨1, by rw h⟩, _⟩,
    rintro ⟨⟨c, hc⟩, h⟩,
    exact (mul_eq_mul_left_iff.mp h).resolve_right (mem_non_zero_divisors_iff_ne_zero.mp hc)
  end }

end fraction_ring

end padic_int


import data.nat.prime data.int.modeq data.complex.basic algebra.euclidean_domain number_theory.pell data.pnat

-- Questions:

structure gaussian_ints :=
(re : ℤ)
(im : ℤ)
-- this is the necessary structure for ℤ[i]
def gaussian_val (x : gaussian_ints) : ℕ := (x.re * x.re + x.im * x.im).nat_abs
-- and this is the valuation required.

-- 1.Give the prime factorisations, in ℤ[i], of the following elements of ℤ[i]. Be sure to justify that each of the factors is prime.
-- 221
--theorem q1a:

-- 7-9i
--theorem q1b:

-- 12-i
--theorem q1c:

-- 2. Find a greatest common divisor, in ℤ[i], of the following elements of ℤ[i]:
-- 37 and 5+7i
--theorem q2a:

-- 52 and 9+7i
--theorem q2b:

-- 3. Let n be an integer. Show that if 4n is the sum of three squares, then so is n.
-- [Hint: If 4n = a^2+b^2+c^2, show that all of a, b and c must be even.]
-- would it make more sense to wlog a,b,c,d,e,f ∈ ℕ?
theorem q3a (n a b c d e f : ℤ ) : (4*n = a^2 + b^2 + c^2) → (n = d^2 + e^2 + f^2) := sorry

-- Show that if n has the form 4^t(8k+7) for some nonnegative integer t and integer k, then n cannot be written as the sum of three squares.
-- (In fact, these are the only numbers that cannot be written as sum of three squares, but this is much harder.)
-- want t : pnat
theorem q3b (n k a b c : ℤ) (t : ℕ) : (n = (4^t)*(8*k+7)) → n ≠ a^2 + b^2 + c^2 := sorry

-- Use Fermat descent, starting with 557^2 + 55^2 = 26 . 12049 to write the prime 12049 as sum of two squares.
--theorem q4:

-- For each of the following n, either write n as the sum of two squares, or prove that it is not possible to do so:
-- 1865
-- ans: (29+32i)(29−32i) = 292 + 322.

--theorem q5a:

-- 77077
--ans: 77077 = (77·2)2 +(77·3)2.
--theorem q5b:

--609
-- ans: no.
theorem q5c (a b : ℕ) : 609 ≠ a^2 + b^2 := sorry

--7501
--ans: 7501 = (2+3i)(24+i)(2−3i)(24−i) = (45+74i)(45− 74i), so 7501 = 452 + 742.
--theorem q5d:

-- Let ζ = -1/2 + sqrt(-3)/2, and let ℤ[ζ] be the subset of ∁ consisting of all complex numbers of the form a+bζ, where a,b are integers.
-- Show that ℤ[ζ] is closed under addition and multiplication.
--theorem q6a:

-- Let N: ℤ[ζ] → ∁ be defined by N(z) = z(bar z). Show that if z ∈ ℤ[ζ], then N(z) is an integer.
--theorem q6b:

-- Show that for any a,b ∈ ℤ[ζ], with b ≠ 0, there exist a q, r in ℤ[ζ] such that a = bq+r and N(r) < N(b).
--theorem q6c:

--Conclude that for any a,b in ℤ[ζ], a greatest common divisor of a and b exists.
--theorem q6d:

-- Show that an integer prime p remains prime in ℤ[ζ] if and only if p ≡ 2 (mod 3).
-- [Hint: First show that if p ≡ 1 (mod 3), then there exists n ∈ ℤ such that p divides n^2+n+1.]
--theorem q6e:


--import tactic.ring

theorem m (a b c:ℕ) : (a+b) * c = a * c + b * c := begin
induction c with c H,
{
    refl
},
{
    have H2 := nat.mul_succ,
    rw H2, clear H2,
    have H2 := nat.mul_succ a c,
    rw H2, clear H2,
    have H2 := nat.mul_succ b c,
    rw H2, rw H, simp
}
end

theorem m2 (a b c:ℕ) : c * (a+b) = a * c + b * c := begin
have H := m a b c,
rw ←H,
apply nat.mul_comm
end

theorem m3 (n:ℕ) : 2*n = n+n :=begin

induction n,{refl},
{
    rw nat.mul_succ 2 n_n,
    rw n_ih,
    have H : nat.succ n_n = n_n + 1, {refl},
    rw H,simp
}

end

theorem a (n:ℕ) : n^2 = n*n := begin
unfold pow nat.pow, simp
end

theorem b (a b:ℕ) : (a+b)^2 = a^2 + b^2 + 2*a*b :=
begin
unfold pow nat.pow,
simp,

have H₁ := m  a b (a+b), rw H₁, clear H₁,
have H₁ := m2 a b a,     rw H₁, clear H₁,
have H₁ := m2 a b b,     rw H₁, clear H₁, simp,

have H₂ : a * b = b * a,
{
    exact nat.mul_comm a b
},

rw ←H₂,

have H₃ : 2 * a * b = a * b + a * b,
{
    have H₄ := m3 (a*b),
    have H₅ : 2 * (a * b) = 2 * a * b,
    {
        rw nat.mul_assoc 2 a b
    },
    {
        rw H₅ at H₄, clear H₅,
        rw H₄
    }
},

rw H₃,
simp
end

theorem c (a n:ℕ) : n^2 + 2*a*n = (n+a)^2 - a^2 := begin

have H := b n a,
rw H,
simp,
have H₂ := nat.add_sub_cancel (n ^ 2 + 2 * n * a) (a^2),
have H₃ :
a^2 + (n ^ 2 + 2 * n * a) - a^2 = (n ^ 2 + 2 * n * a) + a^2 - a^2,{simp},
rw H₃,
rw H₂,
have H₄ :  2*a*n = 2*n*a,
{
    have H₅ := m3 (a*n),
    clear H H₂ H₃,
    have H₆ : 2*a*n = 2*(a*n),
    {
        exact nat.mul_assoc 2 a n
    },
    rw H₆, rw H₅,

    have H₅ := m3 (n*a),
    have H₆ : 2*n*a = 2*(n*a),
    {
        exact nat.mul_assoc 2 n a
    },
    rw H₆, rw H₅,

    rw nat.mul_comm a n
},
rw H₄

end

-- ax^2 + bx + c = 0
-- x^2 + bx/a + c/a = 0
-- x^2 + bx/a = - c/a
-- (x + b/2a)^2 - b^2/4a^2 = -c/a
-- (x + b/2a)^2 = b^2/4a^2 - c/a
--              = b^2 - 4ac / 4a^2
-- x + b/2a     =  S ∨ -S
-- x            = -b/2a + S ∨ -b/2a - S

theorem quad (k : Type) [field k] (a b c x S : k)
(HS : S*S = b*b - 4*a*c) (char_not_2 : (2:k) ≠ 0) :
a * x*x + b * x + c = 0 ↔
(x = (-b + S ) / 2*a ∨
 x = (-b - S ) / 2*a ) := begin

split,
{
    intro H₁,
}

end


import data.set.basic

namespace xena -- hide

variable X : Type -- hide

-- begin hide
/- The first level, originally written by KMB. -/
-- end hide

/-
# Chapter 1 : Sets

## Level 1 : Introduction to sets.

This chapter assumes you are familiar with the following tactics: `intro`, `apply`, `exact`, `cases`, `split`, `left`, `right` and `use`.

If you are not, try playing Function World and Proposition World of the Natural Number Game.

In this level we will learn about the `change` tactic, and the idea of definitional equality.

For our initial examples, we'll use sets $A$ and $B$  with members of a generic type $X$. Lean defines $A \subseteq B$ to mean
$$\forall a, a \in A \implies a \in B.$$
Let's learn how to prove that $A \subseteq A$, indicated as our goal in the lemma below by `⊢ A ⊆ A`.

By *definition* our goal is equivalent to `∀ a : X, a ∈ A → a ∈ A` (that is, due to the actual definition of `⊆` in Lean).

Whenever two expressions are *definitionally equal* in this way, we can use the `change` tactic to replace one by the other in a goal.

For example, if `P` and `Q` are  equal by definition then we can use

`change Q,`

to change a goal `P` to a goal `Q` (remembering the comma!).
-/

/- Hint : Hint : If you are unsure how to prove A ⊆ A directly, try changing your goal.
If you start your proof with

`change ∀ a : X, a ∈ A → a ∈ A,`

then the goal should change to `⊢ ∀ (a : X), a ∈ A → a ∈ A`. You can change it back with

`change A ⊆ A,`

Note that you do not have to use the `change` tactic at all to complete this proof.

-/

/- Hint : Hint : To make progress with a goal of form `∀ a : X, ...`, introduce a term of type `X` by using a familiar tactic.

In this example, using

`intro a,`

will introduce an arbitrary term of type X.

Note that this is the tactic we use to assume a hypothesis (when proving an implication), or to choose an arbitrary element of some domain (when defining a function).

Use the same tactic to introduce an appropriately named hypothesis for an implication, and close the goal with the `exact` tactic.
-/

/-
If you get stuck, you can click on the hints for more details!
-/

/- Lemma
If $A$ is a set of elements of type X, then $A \subseteq A$.
-/
lemma subset.refl (A : set X) : A ⊆ A :=
begin
  change ∀ a : X, a ∈ A → a ∈ A,
  intro a,
  intro ha,
  exact ha,

end

end xena --hide


import category_theory.category.default

universes v u  -- The order in this declaration matters: v often needs to be explicitly specified while u often can be omitted

namespace category_theory

variables (C : Type u) [category.{v} C]

--rewrite this

/-
# Category world

## Level 7: Cancellations

With monomorphisms and epimorphisms, we get some new useful cancellation laws. You will notice that the following two lemmas are pretty similar to the lemma we had in level 5 of world 1. See if you can spot the difference.
-/

/- Lemma
If $$f : X ⟶ Y$$ and $$g : X ⟶ Y$$ are morphisms such that $$f = g$$, then $$f ≫ h = g ≫ h$$.
-/
lemma cancel_mono_id' (X Y : C) (f : X ⟶ Y) [mono f] {g : X ⟶ X} : (g ≫ f = f) ↔ g = 𝟙 X :=
begin
    split,

    intro hyp,
    rw ← category.id_comp f at hyp,
    rw ← category.assoc at hyp,
    rw category.comp_id at hyp,
    rw ← cancel_mono f,
    exact hyp,

    intro hyp,
    rw hyp,
    exact category.id_comp f,
end

end category_theory


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.basic
import Mathlib.data.finset.pi
import Mathlib.data.finset.powerset
import Mathlib.PostPort

universes u v u_1 u_2

namespace Mathlib

/-!
# Results about big operators with values in a (semi)ring

We prove results about big operators that involve some interaction between
multiplicative and additive structures on the values being combined.
-/

namespace finset

theorem sum_mul {α : Type u} {β : Type v} {s : finset α} {b : β} {f : α → β} [semiring β] : (finset.sum s fun (x : α) => f x) * b = finset.sum s fun (x : α) => f x * b :=
  Eq.symm (sum_hom s fun (x : β) => x * b)

theorem mul_sum {α : Type u} {β : Type v} {s : finset α} {b : β} {f : α → β} [semiring β] : (b * finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => b * f x :=
  Eq.symm (sum_hom s (Mul.mul b))

theorem sum_mul_boole {α : Type u} {β : Type v} [semiring β] [DecidableEq α] (s : finset α) (f : α → β) (a : α) : (finset.sum s fun (x : α) => f x * ite (a = x) 1 0) = ite (a ∈ s) (f a) 0 := sorry

theorem sum_boole_mul {α : Type u} {β : Type v} [semiring β] [DecidableEq α] (s : finset α) (f : α → β) (a : α) : (finset.sum s fun (x : α) => ite (a = x) 1 0 * f x) = ite (a ∈ s) (f a) 0 := sorry

theorem sum_div {α : Type u} {β : Type v} [division_ring β] {s : finset α} {f : α → β} {b : β} : (finset.sum s fun (x : α) => f x) / b = finset.sum s fun (x : α) => f x / b := sorry

/-- The product over a sum can be written as a sum over the product of sets, `finset.pi`.
  `finset.prod_univ_sum` is an alternative statement when the product is over `univ`. -/
theorem prod_sum {α : Type u} {β : Type v} [comm_semiring β] {δ : α → Type u_1} [DecidableEq α] [(a : α) → DecidableEq (δ a)] {s : finset α} {t : (a : α) → finset (δ a)} {f : (a : α) → δ a → β} : (finset.prod s fun (a : α) => finset.sum (t a) fun (b : δ a) => f a b) =
  finset.sum (pi s t)
    fun (p : (a : α) → a ∈ s → δ a) =>
      finset.prod (attach s)
        fun (x : Subtype fun (x : α) => x ∈ s) => f (subtype.val x) (p (subtype.val x) (subtype.property x)) := sorry

theorem sum_mul_sum {β : Type v} [comm_semiring β] {ι₁ : Type u_1} {ι₂ : Type u_2} (s₁ : finset ι₁) (s₂ : finset ι₂) (f₁ : ι₁ → β) (f₂ : ι₂ → β) : ((finset.sum s₁ fun (x₁ : ι₁) => f₁ x₁) * finset.sum s₂ fun (x₂ : ι₂) => f₂ x₂) =
  finset.sum (finset.product s₁ s₂) fun (p : ι₁ × ι₂) => f₁ (prod.fst p) * f₂ (prod.snd p) := sorry

/-- The product of `f a + g a` over all of `s` is the sum
  over the powerset of `s` of the product of `f` over a subset `t` times
  the product of `g` over the complement of `t`  -/
theorem prod_add {α : Type u} {β : Type v} [comm_semiring β] (f : α → β) (g : α → β) (s : finset α) : (finset.prod s fun (a : α) => f a + g a) =
  finset.sum (powerset s)
    fun (t : finset α) => (finset.prod t fun (a : α) => f a) * finset.prod (s \ t) fun (a : α) => g a := sorry

/--  Summing `a^s.card * b^(n-s.card)` over all finite subsets `s` of a `finset`
gives `(a + b)^s.card`.-/
theorem sum_pow_mul_eq_add_pow {α : Type u_1} {R : Type u_2} [comm_semiring R] (a : R) (b : R) (s : finset α) : (finset.sum (powerset s) fun (t : finset α) => a ^ card t * b ^ (card s - card t)) = (a + b) ^ card s := sorry

theorem prod_pow_eq_pow_sum {α : Type u} {β : Type v} [comm_semiring β] {x : β} {f : α → ℕ} {s : finset α} : (finset.prod s fun (i : α) => x ^ f i) = x ^ finset.sum s fun (x : α) => f x := sorry

theorem dvd_sum {α : Type u} {β : Type v} [comm_semiring β] {b : β} {s : finset α} {f : α → β} (h : ∀ (x : α), x ∈ s → b ∣ f x) : b ∣ finset.sum s fun (x : α) => f x := sorry

theorem prod_nat_cast {α : Type u} {β : Type v} [comm_semiring β] (s : finset α) (f : α → ℕ) : ↑(finset.prod s fun (x : α) => f x) = finset.prod s fun (x : α) => ↑(f x) :=
  ring_hom.map_prod (nat.cast_ring_hom β) f s

/-- A product over all subsets of `s ∪ {x}` is obtained by multiplying the product over all subsets
of `s`, and over all subsets of `s` to which one adds `x`. -/
theorem prod_powerset_insert {α : Type u} {β : Type v} [DecidableEq α] [comm_monoid β] {s : finset α} {x : α} (h : ¬x ∈ s) (f : finset α → β) : (finset.prod (powerset (insert x s)) fun (a : finset α) => f a) =
  (finset.prod (powerset s) fun (a : finset α) => f a) * finset.prod (powerset s) fun (t : finset α) => f (insert x t) := sorry