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

! This file was ported from Lean 3 source module analysis.normed.group.add_circle
! leanprover-community/mathlib commit 084f76e20c88eae536222583331abd9468b08e1c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Normed.Group.Quotient
import Mathbin.Topology.Instances.AddCircle

/-!
# The additive circle as a normed group

We define the normed group structure on `add_circle p`, for `p : ℝ`. For example if `p = 1` then:
`‖(x : add_circle 1)‖ = |x - round x|` for any `x : ℝ` (see `unit_add_circle.norm_eq`).

## Main definitions:

 * `add_circle.norm_eq`: a characterisation of the norm on `add_circle p`

## TODO

 * The fact `inner_product_geometry.angle (real.cos θ) (real.sin θ) = ‖(θ : real.angle)‖`

-/

noncomputable section

open Set

open Int hiding mem_zmultiples_iff

open AddSubgroup

namespace AddCircle

variable (p : ℝ)

instance : NormedAddCommGroup (AddCircle p) :=
  AddSubgroup.normedAddCommGroupQuotient _

@[simp]
theorem norm_coe_mul (x : ℝ) (t : ℝ) :
    ‖(↑(t * x) : AddCircle (t * p))‖ = |t| * ‖(x : AddCircle p)‖ :=
  by
  have aux : ∀ {a b c : ℝ}, a ∈ zmultiples b → c * a ∈ zmultiples (c * b) := fun a b c h =>
    by
    simp only [mem_zmultiples_iff] at h⊢
    obtain ⟨n, rfl⟩ := h
    exact ⟨n, (mul_smul_comm n c b).symm⟩
  rcases eq_or_ne t 0 with (rfl | ht); · simp
  have ht' : |t| ≠ 0 := (not_congr abs_eq_zero).mpr ht
  simp only [quotient_norm_eq, Real.norm_eq_abs]
  conv_rhs => rw [← smul_eq_mul, ← Real.infₛ_smul_of_nonneg (abs_nonneg t)]
  simp only [QuotientAddGroup.mk'_apply, QuotientAddGroup.eq_iff_sub_mem]
  congr 1
  ext z
  rw [mem_smul_set_iff_inv_smul_mem₀ ht']
  show
    (∃ y, y - t * x ∈ zmultiples (t * p) ∧ |y| = z) ↔ ∃ w, w - x ∈ zmultiples p ∧ |w| = (|t|)⁻¹ * z
  constructor
  · rintro ⟨y, hy, rfl⟩
    refine' ⟨t⁻¹ * y, _, by rw [abs_mul, abs_inv]⟩
    rw [← inv_mul_cancel_left₀ ht x, ← inv_mul_cancel_left₀ ht p, ← mul_sub]
    exact aux hy
  · rintro ⟨w, hw, hw'⟩
    refine' ⟨t * w, _, by rw [← (eq_inv_mul_iff_mul_eq₀ ht').mp hw', abs_mul]⟩
    rw [← mul_sub]
    exact aux hw
#align add_circle.norm_coe_mul AddCircle.norm_coe_mul

theorem norm_neg_period (x : ℝ) : ‖(x : AddCircle (-p))‖ = ‖(x : AddCircle p)‖ :=
  by
  suffices ‖(↑(-1 * x) : AddCircle (-1 * p))‖ = ‖(x : AddCircle p)‖
    by
    rw [← this, neg_one_mul]
    simp
  simp only [norm_coe_mul, abs_neg, abs_one, one_mul]
#align add_circle.norm_neg_period AddCircle.norm_neg_period

@[simp]
theorem norm_eq_of_zero {x : ℝ} : ‖(x : AddCircle (0 : ℝ))‖ = |x| :=
  by
  suffices { y : ℝ | (y : AddCircle (0 : ℝ)) = (x : AddCircle (0 : ℝ)) } = {x} by
    rw [quotient_norm_eq, this, image_singleton, Real.norm_eq_abs, cinfₛ_singleton]
  ext y
  simp [QuotientAddGroup.eq_iff_sub_mem, mem_zmultiples_iff, sub_eq_zero]
#align add_circle.norm_eq_of_zero AddCircle.norm_eq_of_zero

theorem norm_eq {x : ℝ} : ‖(x : AddCircle p)‖ = |x - round (p⁻¹ * x) * p| :=
  by
  suffices ∀ x : ℝ, ‖(x : AddCircle (1 : ℝ))‖ = |x - round x|
    by
    rcases eq_or_ne p 0 with (rfl | hp)
    · simp
    intros
    have hx := norm_coe_mul p x p⁻¹
    rw [abs_inv, eq_inv_mul_iff_mul_eq₀ ((not_congr abs_eq_zero).mpr hp)] at hx
    rw [← hx, inv_mul_cancel hp, this, ← abs_mul, mul_sub, mul_inv_cancel_left₀ hp, mul_comm p]
  clear x p
  intros
  rw [quotient_norm_eq, abs_sub_round_eq_min]
  have h₁ : BddBelow (abs '' { m : ℝ | (m : AddCircle (1 : ℝ)) = x }) :=
    ⟨0, by simp [mem_lowerBounds]⟩
  have h₂ : (abs '' { m : ℝ | (m : AddCircle (1 : ℝ)) = x }).Nonempty := ⟨|x|, ⟨x, rfl, rfl⟩⟩
  apply le_antisymm
  · simp only [le_min_iff, Real.norm_eq_abs, cinfₛ_le_iff h₁ h₂]
    intro b h
    refine'
      ⟨mem_lowerBounds.1 h _ ⟨fract x, _, abs_fract⟩,
        mem_lowerBounds.1 h _ ⟨fract x - 1, _, by rw [abs_sub_comm, abs_one_sub_fract]⟩⟩
    ·
      simp only [mem_set_of_eq, fract, sub_eq_self, QuotientAddGroup.mk_sub,
        QuotientAddGroup.eq_zero_iff, int_cast_mem_zmultiples_one]
    ·
      simp only [mem_set_of_eq, fract, sub_eq_self, QuotientAddGroup.mk_sub,
        QuotientAddGroup.eq_zero_iff, int_cast_mem_zmultiples_one, sub_sub,
        (by norm_cast : (⌊x⌋ : ℝ) + 1 = (↑(⌊x⌋ + 1) : ℝ))]
  · simp only [QuotientAddGroup.mk'_apply, Real.norm_eq_abs, le_cinfₛ_iff h₁ h₂]
    rintro b' ⟨b, hb, rfl⟩
    simp only [mem_set_of_eq, QuotientAddGroup.eq_iff_sub_mem, mem_zmultiples_iff,
      smul_one_eq_coe] at hb
    obtain ⟨z, hz⟩ := hb
    rw [(by
        rw [hz]
        abel : x = b - z),
      fract_sub_int, ← abs_sub_round_eq_min]
    convert round_le b 0
    simp
#align add_circle.norm_eq AddCircle.norm_eq

theorem norm_eq' (hp : 0 < p) {x : ℝ} : ‖(x : AddCircle p)‖ = p * |p⁻¹ * x - round (p⁻¹ * x)| :=
  by
  conv_rhs =>
    congr
    rw [← abs_eq_self.mpr hp.le]
  rw [← abs_mul, mul_sub, mul_inv_cancel_left₀ hp.ne.symm, norm_eq, mul_comm p]
#align add_circle.norm_eq' AddCircle.norm_eq'

theorem norm_le_half_period {x : AddCircle p} (hp : p ≠ 0) : ‖x‖ ≤ |p| / 2 :=
  by
  obtain ⟨x⟩ := x
  change ‖(x : AddCircle p)‖ ≤ |p| / 2
  rw [norm_eq, ← mul_le_mul_left (abs_pos.mpr (inv_ne_zero hp)), ← abs_mul, mul_sub, mul_left_comm,
    ← mul_div_assoc, ← abs_mul, inv_mul_cancel hp, mul_one, abs_one]
  exact abs_sub_round (p⁻¹ * x)
#align add_circle.norm_le_half_period AddCircle.norm_le_half_period

@[simp]
theorem norm_half_period_eq : ‖(↑(p / 2) : AddCircle p)‖ = |p| / 2 :=
  by
  rcases eq_or_ne p 0 with (rfl | hp); · simp
  rw [norm_eq, ← mul_div_assoc, inv_mul_cancel hp, one_div, round_two_inv, algebraMap.coe_one,
    one_mul, (by linarith : p / 2 - p = -(p / 2)), abs_neg, abs_div, abs_two]
#align add_circle.norm_half_period_eq AddCircle.norm_half_period_eq

theorem norm_coe_eq_abs_iff {x : ℝ} (hp : p ≠ 0) : ‖(x : AddCircle p)‖ = |x| ↔ |x| ≤ |p| / 2 :=
  by
  refine' ⟨fun hx => hx ▸ norm_le_half_period p hp, fun hx => _⟩
  suffices ∀ p : ℝ, 0 < p → |x| ≤ p / 2 → ‖(x : AddCircle p)‖ = |x|
    by
    rcases lt_trichotomy 0 p with (hp | rfl | hp)
    · rw [abs_eq_self.mpr hp.le] at hx
      exact this p hp hx
    · contradiction
    · rw [← norm_neg_period]
      rw [abs_eq_neg_self.mpr hp.le] at hx
      exact this (-p) (neg_pos.mpr hp) hx
  clear hx
  intro p hp hx
  rcases eq_or_ne x (p / 2) with (rfl | hx')
  · simp [abs_div, abs_two]
  suffices round (p⁻¹ * x) = 0 by simp [norm_eq, this]
  rw [round_eq_zero_iff]
  obtain ⟨hx₁, hx₂⟩ := abs_le.mp hx
  replace hx₂ := Ne.lt_of_le hx' hx₂
  constructor
  ·
    rwa [← mul_le_mul_left hp, ← mul_assoc, mul_inv_cancel hp.ne.symm, one_mul, mul_neg, ←
      mul_div_assoc, mul_one]
  ·
    rwa [← mul_lt_mul_left hp, ← mul_assoc, mul_inv_cancel hp.ne.symm, one_mul, ← mul_div_assoc,
      mul_one]
#align add_circle.norm_coe_eq_abs_iff AddCircle.norm_coe_eq_abs_iff

open Metric

theorem closedBall_eq_univ_of_half_period_le (hp : p ≠ 0) (x : AddCircle p) {ε : ℝ}
    (hε : |p| / 2 ≤ ε) : closedBall x ε = univ :=
  eq_univ_iff_forall.mpr fun x => by
    simpa only [mem_closed_ball, dist_eq_norm] using (norm_le_half_period p hp).trans hε
#align add_circle.closed_ball_eq_univ_of_half_period_le AddCircle.closedBall_eq_univ_of_half_period_le

@[simp]
theorem coe_real_preimage_closedBall_period_zero (x ε : ℝ) :
    coe ⁻¹' closedBall (x : AddCircle (0 : ℝ)) ε = closedBall x ε := by
  ext y <;> simp [dist_eq_norm, ← QuotientAddGroup.mk_sub]
#align add_circle.coe_real_preimage_closed_ball_period_zero AddCircle.coe_real_preimage_closedBall_period_zero

theorem coe_real_preimage_closedBall_eq_unionᵢ (x ε : ℝ) :
    coe ⁻¹' closedBall (x : AddCircle p) ε = ⋃ z : ℤ, closedBall (x + z • p) ε :=
  by
  rcases eq_or_ne p 0 with (rfl | hp); · simp [Union_const]
  ext y
  simp only [dist_eq_norm, mem_preimage, mem_closed_ball, zsmul_eq_mul, mem_Union, Real.norm_eq_abs,
    ← QuotientAddGroup.mk_sub, norm_eq, ← sub_sub]
  refine' ⟨fun h => ⟨round (p⁻¹ * (y - x)), h⟩, _⟩
  rintro ⟨n, hn⟩
  rw [← mul_le_mul_left (abs_pos.mpr <| inv_ne_zero hp), ← abs_mul, mul_sub, mul_comm _ p,
    inv_mul_cancel_left₀ hp] at hn⊢
  exact (round_le (p⁻¹ * (y - x)) n).trans hn
#align add_circle.coe_real_preimage_closed_ball_eq_Union AddCircle.coe_real_preimage_closedBall_eq_unionᵢ

theorem coe_real_preimage_closedBall_inter_eq {x ε : ℝ} (s : Set ℝ)
    (hs : s ⊆ closedBall x (|p| / 2)) :
    coe ⁻¹' closedBall (x : AddCircle p) ε ∩ s = if ε < |p| / 2 then closedBall x ε ∩ s else s :=
  by
  cases' le_or_lt (|p| / 2) ε with hε hε
  · rcases eq_or_ne p 0 with (rfl | hp)
    · simp only [abs_zero, zero_div] at hε
      simp only [not_lt.mpr hε, coe_real_preimage_closed_ball_period_zero, abs_zero, zero_div,
        if_false, inter_eq_right_iff_subset]
      exact hs.trans (closed_ball_subset_closed_ball <| by simp [hε])
    simp [closed_ball_eq_univ_of_half_period_le p hp (↑x) hε, not_lt.mpr hε]
  · suffices ∀ z : ℤ, closed_ball (x + z • p) ε ∩ s = if z = 0 then closed_ball x ε ∩ s else ∅ by
      simp [-zsmul_eq_mul, ← QuotientAddGroup.mk_zero, coe_real_preimage_closed_ball_eq_Union,
        Union_inter, Union_ite, this, hε]
    intro z
    simp only [Real.closedBall_eq_Icc, zero_sub, zero_add] at hs⊢
    rcases eq_or_ne z 0 with (rfl | hz)
    · simp
    simp only [hz, zsmul_eq_mul, if_false, eq_empty_iff_forall_not_mem]
    rintro y ⟨⟨hy₁, hy₂⟩, hy₀⟩
    obtain ⟨hy₃, hy₄⟩ := hs hy₀
    rcases lt_trichotomy 0 p with (hp | rfl | hp)
    · cases' Int.cast_le_neg_one_or_one_le_cast_of_ne_zero ℝ hz with hz' hz'
      · have : ↑z * p ≤ -p
        nlinarith
        linarith [abs_eq_self.mpr hp.le]
      · have : p ≤ ↑z * p
        nlinarith
        linarith [abs_eq_self.mpr hp.le]
    · simp only [MulZeroClass.mul_zero, add_zero, abs_zero, zero_div] at hy₁ hy₂ hε
      linarith
    · cases' Int.cast_le_neg_one_or_one_le_cast_of_ne_zero ℝ hz with hz' hz'
      · have : -p ≤ ↑z * p
        nlinarith
        linarith [abs_eq_neg_self.mpr hp.le]
      · have : ↑z * p ≤ p
        nlinarith
        linarith [abs_eq_neg_self.mpr hp.le]
#align add_circle.coe_real_preimage_closed_ball_inter_eq AddCircle.coe_real_preimage_closedBall_inter_eq

section FiniteOrderPoints

variable {p} [hp : Fact (0 < p)]

include hp

theorem norm_div_nat_cast {m n : ℕ} :
    ‖(↑(↑m / ↑n * p) : AddCircle p)‖ = p * (↑(min (m % n) (n - m % n)) / n) :=
  by
  have : p⁻¹ * (↑m / ↑n * p) = ↑m / ↑n := by rw [mul_comm _ p, inv_mul_cancel_left₀ hp.out.ne.symm]
  rw [norm_eq' p hp.out, this, abs_sub_round_div_natCast_eq]
#align add_circle.norm_div_nat_cast AddCircle.norm_div_nat_cast

theorem exists_norm_eq_of_fin_add_order {u : AddCircle p} (hu : IsOfFinAddOrder u) :
    ∃ k : ℕ, ‖u‖ = p * (k / addOrderOf u) :=
  by
  let n := addOrderOf u
  change ∃ k : ℕ, ‖u‖ = p * (k / n)
  obtain ⟨m, -, -, hm⟩ := exists_gcd_eq_one_of_is_of_fin_add_order hu
  refine' ⟨min (m % n) (n - m % n), _⟩
  rw [← hm, norm_div_nat_cast]
#align add_circle.exists_norm_eq_of_fin_add_order AddCircle.exists_norm_eq_of_fin_add_order

theorem le_add_order_smul_norm_of_isOfFinAddOrder {u : AddCircle p} (hu : IsOfFinAddOrder u)
    (hu' : u ≠ 0) : p ≤ addOrderOf u • ‖u‖ :=
  by
  obtain ⟨n, hn⟩ := exists_norm_eq_of_fin_add_order hu
  replace hu : (addOrderOf u : ℝ) ≠ 0;
  · norm_cast
    exact (add_order_of_pos_iff.mpr hu).Ne.symm
  conv_lhs => rw [← mul_one p]
  rw [hn, nsmul_eq_mul, ← mul_assoc, mul_comm _ p, mul_assoc, mul_div_cancel' _ hu,
    mul_le_mul_left hp.out, Nat.one_le_cast, Nat.one_le_iff_ne_zero]
  contrapose! hu'
  simpa only [hu', algebraMap.coe_zero, zero_div, MulZeroClass.mul_zero, norm_eq_zero] using hn
#align add_circle.le_add_order_smul_norm_of_is_of_fin_add_order AddCircle.le_add_order_smul_norm_of_isOfFinAddOrder

end FiniteOrderPoints

end AddCircle

namespace UnitAddCircle

theorem norm_eq {x : ℝ} : ‖(x : UnitAddCircle)‖ = |x - round x| := by simp [AddCircle.norm_eq]
#align unit_add_circle.norm_eq UnitAddCircle.norm_eq

end UnitAddCircle


import set_theory.cardinal

-- import init.classical

variable α : Type*
variable β : Type*

-- variables 0 1 : ℕ
/--
inductive nat
| zero : nat
| succ (n : nat) : nat

inductive bool : Type
| ff : bool
| tt : bool
-/
-- the syntax of the following definition is wrong on {0 1} (at least)
-- noncomputable theory

-- to solve type error on "if .. then ..else.." we need the following two lines
-- `noncomuptable theory` is alread defined above to open_local cardinal
-- open_locale classical

--- wrong definition?
/- `indicator' s x` is `1` if `x ∈ s`, `0` otherwise. -/
--def indicator' (s : set α) (x : α) : ℕ := if x ∈ s then 1 else 0

-- The notation of power `^` is defined in set_theory/cardinal.lean

--  instance : has_pow cardinal cardinal := ⟨cardinal.power⟩
--  local infixr ^ := @has_pow.pow cardinal cardinal cardinal.has_pow

-- Exponentiation `c₁ ^ c₂` is defined by `cardinal.power_def α β : #α ^ #β = #(β → α)`.
-- in src/set_theory/cardinal.lean
-- https://leanprover-community.github.io/mathlib_docs/set_theory/cardinal.html

-- @[simp] theorem power_def (α β) : mk α ^ mk β = mk (β → α) := rfl

-- from data/set/basic.lean

variable {ι : Type*}
variable {α' : ι → Type*}
variables {s s₁ : set ι}
variables {t t₁ t₂ : Π i, set (α' i)}

/-- The cartesian product `prod s t` is the set of
 `(a, b)`
  such that `a ∈ s` and `b ∈ t`. -/
protected def prod' (s : set α) (t : set β) :
set (α × β) :=
{p | p.1 ∈ s ∧ p.2 ∈ t}

/-- Given an index set `ι` and a family of sets
  `t : Π i, set (α i)`, `pi s t`
  is the set of dependent functions
   `f : Πa, π a` such that `f a` belongs to `t a`
  whenever `a ∈ s`. -/
def pi (s : set ι) (t : Π i, set (α' i)) :
 set (Π i, α' i) := { f | ∀i ∈ s, f i ∈ t i }

@[simp] lemma mem_pi {f : Π i, α' i} :
f ∈ s.pi t ↔ ∀ i ∈ s, f i ∈ t i :=
by refl

#check α'

-- Define powerset
variable {U : Type}

def y2 : set ℕ := {1,2}
variable {y : set ℕ}
#check set ℕ
#reduce (set ℕ) -- ℕ → Prop
#print y

#reduce 2 ∈ y
#reduce 2 ∈ y2
#reduce y2 19
#print y2
#check y2

universe u
#check Type u -- Type (u+1) -- !! not Type u
#check Type -- Type 1
#check Prop -- Type

def v' := λ f: ℕ → ℕ, f 1

#check v'  -- v' : (ℕ → ℕ) → ℕ
#print v' -- def v' : (ℕ → ℕ) → ℕ := λ (f : ℕ → ℕ), f 1

def double' : ℕ → ℕ := λ x: ℕ , x+ x
def double'' (x: ℕ) : ℕ := x + x

-- double' and double'' are equivalent
#check double' -- double' : ℕ → ℕ
#check double'' -- double'' : ℕ → ℕ
#print double'  -- def double' : ℕ → ℕ := λ (x : ℕ), x + x
#print double'' --  def double'' : ℕ → ℕ := λ (x : ℕ), x + x

-- What is `set`?  `set α`?

#print set
/-
@[_ext_core id.{1} name set.ext]
def set : Type u → Type u :=
  λ (α : Type u), α → Prop
-/

#print set.mem
/-
protected def set.mem :
Π {α : Type u}, α → set α → Prop :=
λ {α : Type u} (a : α) (s : set α), s a
-/

#print set.univ
/-
def set.univ : Π {α : Type u}, set α :=
 λ {α : Type u} (a : α), true
-/

#print ∈
-- _ `∈`:50 _:50 := has_mem.mem #1 #0

#print set.has_mem
/-
 @[instance]
 protected def set.has_mem : Π {α : Type u}, has_mem α (set α) :=
 λ {α : Type u}, {mem := set.mem α}
-/

#print has_mem.mem
/-
@[reducible]
def has_mem.mem :
  Π {α : Type u} {γ : Type v} [self : has_mem α γ], α → γ → Prop :=
  λ {α : Type u} (γ : Type v) [self : has_mem α γ], [has_mem.mem self]
-/

#check Prop -- Prop: Type
#reduce Prop
#print Prop
#check y → Prop
#reduce y → Prop
#reduce y2 → Prop

#check list -- list: Type u_3 → Type u_3
#check vector -- vector: Type u_3 → ℕ → Type_u_3
#print vector
/-
 @[_ext_core id.{1} name vector.ext]
def vector : Type u → ℕ → Type u :=
λ (α : Type u) (n : ℕ), {l // l.length = n}
-/

#print list
/-
@[_ext_core id.{1} name list.ext]
inductive list : Type u → Type u
constructors:
list.nil : Π {T : Type u}, list T
list.cons : Π {T : Type u}, T → list T → list T
-/

#print set
/-
@[_ext_core id.{1} name set.ext]
def set : Type u → Type u :=
λ (α : Type u), α → Prop
-/

#print nat
/-
inductive nat : Type
constructors:
nat.zero : ℕ
nat.succ : ℕ → ℕ
-/

#check α
#check α × β

def tc2 : ℕ × ℕ := (1,2)

#check tc2

def is_function  (f: set (α × β)) (X:set α) (Y: set β): Prop :=
f ⊆ X.prod Y ∧ ∀x ∈ X,∃!y ∈ Y, (x,y) ∈ f

def Functions (X : set α) (Y: set β): set (set (α × β)) :=
{f | f ∈ 𝒫 (X.prod Y) ∧ (is_function f X Y)}

def Functions2 (X : set α) (Y: set β) :=
{f | f ∈ 𝒫 (X.prod Y) ∧ (is_function f X Y)}

#check Functions2
-- Functions2 : set α → set β → set (set (α × β))

-- set T :=  {x ∈ Univ | x : T}

-- set (ℕ × ℕ) := {x ∈ Univ | x : ℕ × ℕ} where ℕ is nat in Lean
--                ⇔ {x ∈ Univ | x ∈ (N × N)} where N is natural number
-- set (set ( ℕ × ℕ )) := {x ∈ Univ | x: set (ℕ × ℕ)}
--                       ⇔ {x ∈ Univ | x: (N × N)}  -- what is this ??

-- set ℕ := {x ∈ Univ | x : ℕ }
--        ⇔ {x ∈ Univ | x ∈ N}
-- => This is not true `set ℕ` is not a  mathematical set
/-
 set ℕ := λ x.
   | true if x : ℕ
   | false otherwise
  X := {x ∈ Univ | (set ℕ) x = true}

  set (ℕ × ℕ) := λ x.
    | true if x : ℕ × ℕ
    | false otherwise
  X := {x ∈ Univ | (set (ℕ × ℕ)) x = true}

  set (set (ℕ × ℕ)) := λ x.
    | true if x: set (ℕ × ℕ)
    | false otherwise

  set (set (ℕ × ℕ)) := λ x.
    | true if (set (ℕ × ℕ)) x = true
    | false otherwise
-/
#reduce (set ℕ)
-- {{1,..∞},{1,..,∞},..}
-- set (set ℕ) := {x ∈ Univ | x : set ℕ}
--              ⇔ {x ∈ Univ | x ∈ 𝒫 (N)} -- Is powerset ???

def setTc2 : set (ℕ × ℕ) := {(1,2),(3,4)}
-- setTc ⊂ ℕ × ℕ

def setSetTc2 : set (set (ℕ × ℕ)) := {{(1,2),(2,3)},{(4,5)}}

-- setSetTc ⊂ 𝒫 (N × N)

def powset2 (A : set Type) : set (set Type) := {x |x ⊂ A}

#check powset2

#check Prop -- Type
#check Type -- Type 1
#check Type 1 -- Type 2
#check Type u -- Type (u+1)
/-
set ℕ
  A ⊂ {x ∈ Univ | (set ℕ) x}
⇔ A ⊂ {x ∈ Univ | ∀y ∈ Univ. y ∈ ℕ}
⇔ A ⊂ {x ∈ Univ | x ∈ ℕ}
⇔ A ⊂ ℕ
--------------------------------
  set (set ℕ)
⇔ set (A ⊂ ℕ)
⇔ S ⊂ {x ∈ Univ | (set (set ℕ)) x}
⇔ S ⊂ {x ∈ Univ | x ∈ (A ⊂ ℕ)}
⇔
-/

open set

variable {α₁ : Type*}

#check univ
#check @univ
#check @univ α
variable {a1 : α₁}

--def a2 : α = x

-- if we
#check a1 ∈ (@univ α₁)
#reduce a1 ∈ (@univ α₁)  -- true

variables {X₁ Y₁: @univ α}

--
#check {n: ℕ | n ≤ 4} -- set ℕ (it is a subset of ℕ)
#check {n: ℕ // n ≤ 4} -- Type (it is called subtype)


import data.int.basic data.list.basic data.nat.gcd .tactics .nat .equiv

namespace int

open tactic

instance inh : inhabited int := ⟨0⟩

meta def int.reflect : Π (z : int), reflected z
| (int.of_nat n) := `(int.of_nat n)
| (int.neg_succ_of_nat n) := `(int.neg_succ_of_nat n)

meta instance int.has_reflect := int.reflect

lemma to_nat_to_int {z : int} :
0 ≤ z → z.to_nat.to_int = z :=
begin intro h, cases z with n, refl, cases h end

lemma eq_one_or_eq_neg_one_of_mul_eq_one_left {a b : ℤ} (h : a * b = 1) : b = 1 ∨ b = -1 :=
begin
  by_cases hb : 0 ≤ b,
  { apply or.inl (eq_one_of_mul_eq_one_left hb h) },
  { rw ← neg_mul_neg at h,
    apply or.inr (eq_neg_of_eq_neg _),
    apply (eq_one_of_mul_eq_one_left _ h).symm,
    simp at hb, simp [ge,le_neg], apply le_of_lt hb }
end

lemma eq_one_or_eq_neg_one_of_mul_eq_neg_one_left {a b : ℤ} (h : a * b = -1) : b = 1 ∨ b = -1 :=
begin
  rw [eq_neg_iff_eq_neg, neg_mul_eq_neg_mul] at h,
  apply eq_one_or_eq_neg_one_of_mul_eq_one_left h.symm,
end

lemma zero_ne_of_pos {z : int} : 0 < z → 0 ≠ z :=
begin intros h hc, rw hc at h, apply int.lt_irrefl z h end

lemma add_le_iff_le_sub {a b c : int} : a + b ≤ c ↔ a ≤ c - b :=
iff.intro (le_sub_right_of_add_le) (add_le_of_le_sub_right)

lemma le_iff_zero_le_sub (a b) : a ≤ b ↔ (0 : int) ≤ b - a :=
begin
  rewrite le_sub, simp
end

lemma neg_one_mul_eq_neg {z : int} : (-1) * z = -z :=
begin simp [neg_mul_eq_neg_mul_symm, one_mul] end

lemma abs_dvd (x y : int) : has_dvd.dvd (abs x) y ↔ has_dvd.dvd x y :=
begin rewrite abs_eq_nat_abs, apply nat_abs_dvd end

lemma mul_nonzero {z y : int} : z ≠ 0 → y ≠ 0 → z * y ≠ 0 :=
begin
  intros hm hn hc, apply hm,
  apply eq.trans, apply eq.symm,
  apply int.mul_div_cancel,
  apply hn, rewrite hc, apply int.zero_div
end

lemma div_nonzero (z y : int) : z ≠ 0 → has_dvd.dvd y z → (z / y) ≠ 0 :=
begin
  intros hz hy hc, apply hz,
  apply eq.trans, apply eq.symm,
  apply int.div_mul_cancel, apply hy,
  rewrite hc, apply zero_mul,
end

lemma nat_abs_nonzero (z : int) : z ≠ 0 → int.nat_abs z ≠ 0 :=
begin
  intro hz, cases z with n n; simp,
  { intro hc, subst hc, apply hz, refl },
  { intro hc, cases hc }
end

lemma dvd_iff_nat_abs_dvd_nat_abs {x y : int} :
  (has_dvd.dvd x y)
  ↔ (has_dvd.dvd (int.nat_abs x) (int.nat_abs y)) :=
begin
  rewrite iff.symm int.coe_nat_dvd,
  rewrite int.nat_abs_dvd, rewrite int.dvd_nat_abs
end

def lcm (x y : int) : int :=
  (nat.lcm (nat_abs x) (nat_abs y)).to_int

lemma lcm_dvd {x y z : int} (hx : has_dvd.dvd x z) (hy : y ∣ z) : lcm x y ∣ z :=
begin
  rewrite dvd_iff_nat_abs_dvd_nat_abs,
  rewrite dvd_iff_nat_abs_dvd_nat_abs at hx,
  rewrite dvd_iff_nat_abs_dvd_nat_abs at hy,
  unfold lcm, simp [nat.to_int], --rewrite nat_abs_of_nat,
  apply nat.lcm_dvd; assumption
end

lemma lcm_one_right (z : int) :
  lcm z 1 = abs z :=
begin
  unfold lcm, simp, rewrite nat.lcm_one_right,
  cases (classical.em (0 ≤ z)) with hz hz,
  simp [nat.to_int], rewrite abs_eq_nat_abs,
  rewrite not_le at hz,
  apply @eq.trans _ _ (↑(nat_abs z)), refl,
  rewrite (@of_nat_nat_abs_of_nonpos z _),
  rewrite abs_of_neg, apply hz,
  apply le_of_lt hz
end

def lcms : list int → int
| [] := 1
| (z::zs) := lcm z (lcms zs)

lemma dvd_lcm_left : ∀ (x y : int), has_dvd.dvd x (lcm x y) :=
begin
  intros x y, unfold lcm, rewrite iff.symm (abs_dvd _ _),
  rewrite abs_eq_nat_abs,
  simp [nat.to_int], rewrite coe_nat_dvd,
  apply nat.dvd_lcm_left
end

lemma dvd_lcm_right : ∀ (x y : int), has_dvd.dvd y (lcm x y) :=
begin
  intros x y, unfold lcm, rewrite iff.symm (abs_dvd _ _),
  rewrite abs_eq_nat_abs,simp [nat.to_int],  rewrite coe_nat_dvd,
  apply nat.dvd_lcm_right
end

lemma dvd_lcms {x : int} : ∀ {zs : list int}, x ∈ zs → has_dvd.dvd x (lcms zs)
| [] hm := by cases hm
| (z::zs) hm :=
  begin
    unfold lcms, rewrite list.mem_cons_iff at hm,
    cases hm with hm hm, subst hm,
    apply dvd_lcm_left,
    apply dvd_trans, apply @dvd_lcms zs hm,
    apply dvd_lcm_right,
  end

lemma nonzero_of_pos {z : int} : z > 0 → z ≠ 0 :=
begin intros hgt heq, subst heq, cases hgt end

lemma lcm_nonneg (x y : int) : lcm x y ≥ 0 :=
by unfold lcm

lemma lcms_nonneg : ∀ (zs : list int), lcms zs ≥ 0
| [] := by unfold lcms
| (z::zs) := by unfold lcms

lemma lcm_pos (x y : int) : x ≠ 0 → y ≠ 0 → lcm x y > 0 :=
begin
  intros hx hy, unfold lcm, unfold gt,
  simp [nat.to_int], --rewrite coe_nat_pos,
  let h := @nat.pos_iff_ne_zero,
  simp [gt] at h,
  rewrite h, apply nat.lcm_nonzero;
  apply nat_abs_nonzero; assumption
end

lemma lcms_pos : ∀ {zs : list int}, (∀ z : int, z ∈ zs → z ≠ 0) → lcms zs > 0
| [] _ := coe_succ_pos _
| (z::zs) hnzs :=
  begin
    unfold lcms, apply lcm_pos,
    apply hnzs _ (or.inl rfl),
    apply nonzero_of_pos, apply lcms_pos,
    apply list.forall_mem_of_forall_mem_cons hnzs
  end

lemma lcms_dvd {k : int} :
  ∀ {zs : list int}, (∀ z ∈ zs, has_dvd.dvd z k) → (has_dvd.dvd (lcms zs) k)
| [] hk := one_dvd _
| (z::zs) hk :=
  begin
    unfold lcms, apply lcm_dvd,
    apply hk _ (or.inl rfl),
    apply lcms_dvd,
    apply list.forall_mem_of_forall_mem_cons hk
  end

lemma lcms_distrib (xs ys zs : list int) :
  list.equiv zs (xs ∪ ys)
  → lcms zs = lcm (lcms xs) (lcms ys) :=
begin
  intro heqv, apply dvd_antisymm,
  apply lcms_nonneg, apply lcm_nonneg,
  apply lcms_dvd, intros z hz,
  rewrite (list.mem_iff_mem_of_equiv heqv) at hz,
  rewrite list.mem_union at hz, cases hz with hz hz,
  apply dvd_trans (dvd_lcms _) (dvd_lcm_left _ _);
  assumption,
  apply dvd_trans (dvd_lcms _) (dvd_lcm_right _ _);
  assumption,
  apply lcm_dvd; apply lcms_dvd; intros z hz;
  apply dvd_lcms; rewrite (list.mem_iff_mem_of_equiv heqv),
  apply list.mem_union_left hz,
  apply list.mem_union_right _ hz
end

lemma eq_zero_of_nonpos_of_nonzero (z : int) :
(¬ z < 0) → (¬ z > 0) → z = 0 :=
begin
  cases (lt_trichotomy z 0) with h h,
  intro hc, cases (hc h),
  cases h with h h, intros _ _, apply h,
  intros _ hc, cases (hc h)
end

lemma sign_trichotomy (z) :
  sign z = -1 ∨ sign z = 0 ∨ sign z = 1 :=
begin
  cases z with n n, cases n,
  apply or.inr (or.inl rfl),
  apply or.inr (or.inr rfl),
  apply or.inl rfl
end

lemma mul_le_mul_iff_le_of_pos_left (x y z : int) :
  z > 0 → (z * x ≤ z * y ↔ x ≤ y) :=
begin
  intro hz, apply iff.intro; intro h,
  let h' := @int.div_le_div _ _ z hz h,
  repeat {rewrite mul_comm z at h',
  rewrite int.mul_div_cancel _ (nonzero_of_pos hz) at h'},
  apply h', repeat {rewrite mul_comm z},
  apply int.mul_le_of_le_div hz,
  rewrite int.mul_div_cancel _ (nonzero_of_pos hz),
  apply h
end

lemma mul_le_mul_iff_le_of_neg_left (x y z : int) :
  z < 0 → (z * x ≤ z * y ↔ y ≤ x) :=
begin
  intros hz,
  rewrite eq.symm (neg_neg z),
  repeat {rewrite eq.symm (neg_mul_eq_neg_mul (-z) _)},
  rewrite neg_le_neg_iff,
  apply mul_le_mul_iff_le_of_pos_left,
  unfold gt, rewrite lt_neg, apply hz
end

lemma dvd_iff_exists (x y) :
  has_dvd.dvd x y ↔ ∃ (z : int), z * x = y :=
begin
  apply iff.intro; intro h,
  existsi (y / x), apply int.div_mul_cancel h,
  cases h with z hz, subst hz,
  apply dvd_mul_left
end

lemma div_mul_comm (x y z : int) : has_dvd.dvd y x → (x / y) * z = x * z / y :=
begin
  intro h, rewrite mul_comm x,
  rewrite int.mul_div_assoc _ h, rewrite mul_comm,
end

lemma nonneg_iff_exists (z : int) :
  0 ≤ z ↔ ∃ (n : nat), z = ↑n :=
begin
  cases z with m m,
  { constructor; intro h,
    existsi m, refl, exact_dec_trivial },
  { constructor; intro h, cases h,
    cases h with m hm, cases hm }
end

lemma exists_nat_diff (x y : int) (n : nat) :
  x ≤ y → y < x + ↑n → ∃ (m : nat), m < n ∧ y = x + ↑m :=
begin
  intros hxy hyx,
  rewrite iff.symm (sub_nonneg) at hxy,
  rewrite nonneg_iff_exists at hxy,
  cases hxy with m hm, existsi m,
  rewrite iff.symm sub_lt_iff_lt_add' at hyx,
  apply and.intro, rewrite iff.symm coe_nat_lt,
  rewrite eq.symm hm, apply hyx,
  rewrite eq.symm hm, rewrite add_sub, simp,
end

lemma exists_lt_and_lt (x y : int) :
  ∃ z, z < x ∧ z < y :=
begin
  cases (lt_trichotomy x y),
  existsi (pred x),
  apply and.intro (pred_self_lt _) (lt_trans (pred_self_lt _) h),
  cases h with h h, subst h,
  existsi (pred x), apply and.intro (pred_self_lt _) (pred_self_lt _),
  existsi (pred y),
  apply and.intro (lt_trans (pred_self_lt _) h) (pred_self_lt _)
end

lemma le_mul_of_pos_left : ∀ {x y : int}, y ≥ 0 → x > 0 → y ≤ x * y :=
begin
  intros x y hy hx,
  have hx' : x ≥ 1 := add_one_le_of_lt hx,
  let h := mul_le_mul hx' (le_refl y) hy _,
  rewrite one_mul at h, apply h,
  apply le_of_lt hx
end

lemma lt_mul_of_nonneg_right : ∀ {x y z : int}, x < y → y ≥ 0 → z > 0 → x < y * z :=
begin
  intros x y z hxy hy hz,
  have h := @mul_lt_mul _ _ x 1 y z hxy,
  rewrite mul_one at h, apply h,
  apply add_one_le_of_lt hz,
  apply int.zero_lt_one, apply hy
end

-- lemma zero_mul (z : int) :
--   int.of_nat 0 * z = 0 :=
-- eq.trans (refl _) (zero_mul _)
--
-- lemma mul_zero (z : int) :
--   z * int.of_nat 0 = 0 :=
-- eq.trans (refl _) (mul_zero _)
--
-- lemma one_mul (z : int) :
--   int.of_nat 1 * z = z :=
-- eq.trans (refl _) (one_mul _)

lemma neg_one_mul (z : int) :
  (int.neg_succ_of_nat 0) * z = -z :=
eq.trans (refl _) (neg_one_mul _)

-- lemma zero_add (z : int) :
--   int.of_nat 0 + z = z :=
-- eq.trans (refl _) (zero_add _)

lemma coe_eq_of_nat {n : nat} :
  ↑n = int.of_nat n := refl _

lemma coe_neg_succ_eq_neg_succ_of_nat {n : nat} :
  -↑(nat.succ n) = int.neg_succ_of_nat n := refl _

lemma exists_min_of_exists_lb_aux {P : int → Prop} {lb : int} (hlb : ∀ (x : int), x < lb → ¬P x) :
∀ (n : nat) {w}, w ≤ lb + n → P w → ∃ (y : int), P y ∧ ∀ (x : int), x < y → ¬P x
| 0 w :=
  begin
    intros hn hw, existsi w, constructor,
    apply hw, intros k hk, apply hlb, simp at hn,
    apply lt_of_lt_of_le hk hn
  end
| (n+1) w :=
  begin
    intros hn hw, cases lt_trichotomy w lb with hlt h,
    { exfalso, apply hlb _ hlt hw },
    cases h with heq hgt,
    { existsi w, constructor, apply hw, rw heq, apply hlb },
    {
      cases (classical.em (∃ x, (lb ≤ x ∧ x < w ∧ P x))) with hex hex,
      { cases hex with v hv, cases hv with hv1 hv, cases hv with hv2 hv3,
        apply @exists_min_of_exists_lb_aux n v _ hv3,
        rw [← lt_add_one_iff, add_assoc], rw int.coe_nat_add at hn,
        apply lt_of_lt_of_le hv2 hn },
      { existsi w, constructor, apply hw,
        intros k hk1 hk2,
        cases (lt_or_ge k lb) with hk3 hk3,
        { apply hlb k hk3 hk2 },
        { apply hex, existsi k, constructor,
          apply hk3, constructor; assumption } }
    }
  end

lemma exists_min_of_exists_lb {P : int → Prop} {w : int} {lb : int} :
P w → (∀ (x : int), x < lb → ¬P x) → ∃ (y : int), P y ∧ ∀ (x : int), x < y → ¬P x :=
begin
  intros hw hlb, apply exists_min_of_exists_lb_aux hlb (nat_abs (w - lb)) _ hw,
  rw [← @sub_le_iff_le_add' int _, ← abs_eq_nat_abs], apply le_abs_self
end

-- def exists_lt (p : int → Prop) := ∀ x, (p x → ∃ y, y < x ∧ p y)
--
-- lemma no_lb_of_exists_lt (p : int → Prop) :
-- (∃ x, p x) → exists_lt p → ∀ x, ∃ y, y < x ∧ p x := sorry

end int


/-
Copyright (c) 2019 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl

! This file was ported from Lean 3 source module topology.list
! leanprover-community/mathlib commit e97cf15cd1aec9bd5c193b2ffac5a6dc9118912b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Constructions
import Mathbin.Topology.Algebra.Monoid

/-!
# Topology on lists and vectors

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

-/

open TopologicalSpace Set Filter

open Topology Filter

variable {α : Type _} {β : Type _} [TopologicalSpace α] [TopologicalSpace β]

instance : TopologicalSpace (List α) :=
  TopologicalSpace.mkOfNhds (traverse nhds)

/- warning: nhds_list -> nhds_list is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (as : List.{u1} α), Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) as) (Traversable.traverse.{u1} List.{u1} List.traversable.{u1} Filter.{u1} Filter.applicative.{u1} α α (nhds.{u1} α _inst_1) as)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (as : List.{u1} α), Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) as) (Traversable.traverse.{u1} List.{u1} instTraversableList.{u1} Filter.{u1} (Alternative.toApplicative.{u1, u1} Filter.{u1} Filter.instAlternativeFilter.{u1}) α α (nhds.{u1} α _inst_1) as)
Case conversion may be inaccurate. Consider using '#align nhds_list nhds_listₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem nhds_list (as : List α) : 𝓝 as = traverse 𝓝 as :=
  by
  refine' nhds_mk_of_nhds _ _ _ _
  · intro l
    induction l
    case nil => exact le_rfl
    case
      cons a l ih =>
      suffices List.cons <$> pure a <*> pure l ≤ List.cons <$> 𝓝 a <*> traverse 𝓝 l by
        simpa only [functor_norm] using this
      exact Filter.seq_mono (Filter.map_mono <| pure_le_nhds a) ih
  · intro l s hs
    rcases(mem_traverse_iff _ _).1 hs with ⟨u, hu, hus⟩
    clear as hs
    have : ∃ v : List (Set α), l.forall₂ (fun a s => IsOpen s ∧ a ∈ s) v ∧ sequence v ⊆ s :=
      by
      induction hu generalizing s
      case nil hs this => exists ; simpa only [List.forall₂_nil_left_iff, exists_eq_left]
      case
        cons a s as ss ht h ih t hts =>
        rcases mem_nhds_iff.1 ht with ⟨u, hut, hu⟩
        rcases ih _ subset.rfl with ⟨v, hv, hvss⟩
        exact
          ⟨u::v, List.Forall₂.cons hu hv,
            subset.trans (Set.seq_mono (Set.image_subset _ hut) hvss) hts⟩
    rcases this with ⟨v, hv, hvs⟩
    refine' ⟨sequence v, mem_traverse _ _ _, hvs, _⟩
    · exact hv.imp fun a s ⟨hs, ha⟩ => IsOpen.mem_nhds hs ha
    · intro u hu
      have hu := (List.mem_traverse _ _).1 hu
      have : List.Forall₂ (fun a s => IsOpen s ∧ a ∈ s) u v :=
        by
        refine' List.Forall₂.flip _
        replace hv := hv.flip
        simp only [List.forall₂_and_left, flip] at hv⊢
        exact ⟨hv.1, hu.flip⟩
      refine' mem_of_superset _ hvs
      exact mem_traverse _ _ (this.imp fun a s ⟨hs, ha⟩ => IsOpen.mem_nhds hs ha)
#align nhds_list nhds_list

/- warning: nhds_nil -> nhds_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.nil.{u1} α)) (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} (List.{u1} α) (List.nil.{u1} α))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.nil.{u1} α)) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} (List.{u1} α) (List.nil.{u1} α))
Case conversion may be inaccurate. Consider using '#align nhds_nil nhds_nilₓ'. -/
@[simp]
theorem nhds_nil : 𝓝 ([] : List α) = pure [] := by
  rw [nhds_list, List.traverse_nil _] <;> infer_instance
#align nhds_nil nhds_nil

/- warning: nhds_cons -> nhds_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α) (l : List.{u1} α), Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.cons.{u1} α a l)) (Seq.seq.{u1, u1} Filter.{u1} Filter.hasSeq.{u1} (List.{u1} α) (List.{u1} α) (Functor.map.{u1, u1} Filter.{u1} Filter.functor.{u1} α ((List.{u1} α) -> (List.{u1} α)) (List.cons.{u1} α) (nhds.{u1} α _inst_1 a)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α) (l : List.{u1} α), Eq.{succ u1} (Filter.{u1} (List.{u1} α)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.cons.{u1} α a l)) (Seq.seq.{u1, u1} Filter.{u1} (Applicative.toSeq.{u1, u1} Filter.{u1} (Alternative.toApplicative.{u1, u1} Filter.{u1} Filter.instAlternativeFilter.{u1})) (List.{u1} α) (List.{u1} α) (Functor.map.{u1, u1} Filter.{u1} Filter.instFunctorFilter.{u1} α ((List.{u1} α) -> (List.{u1} α)) (List.cons.{u1} α) (nhds.{u1} α _inst_1 a)) (fun (x._@.Mathlib.Topology.List._hyg.621 : Unit) => nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l))
Case conversion may be inaccurate. Consider using '#align nhds_cons nhds_consₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem nhds_cons (a : α) (l : List α) : 𝓝 (a::l) = List.cons <$> 𝓝 a <*> 𝓝 l := by
  rw [nhds_list, List.traverse_cons _, ← nhds_list] <;> infer_instance
#align nhds_cons nhds_cons

/- warning: list.tendsto_cons -> List.tendsto_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {a : α} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p)) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.cons.{u1} α a l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {a : α} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p)) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.cons.{u1} α a l))
Case conversion may be inaccurate. Consider using '#align list.tendsto_cons List.tendsto_consₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem List.tendsto_cons {a : α} {l : List α} :
    Tendsto (fun p : α × List α => List.cons p.1 p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (a::l)) := by
  rw [nhds_cons, tendsto, Filter.map_prod] <;> exact le_rfl
#align list.tendsto_cons List.tendsto_cons

/- warning: filter.tendsto.cons -> Filter.Tendsto.cons is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} β] {α : Type.{u2}} {f : α -> β} {g : α -> (List.{u1} β)} {a : Filter.{u2} α} {b : β} {l : List.{u1} β}, (Filter.Tendsto.{u2, u1} α β f a (nhds.{u1} β _inst_2 b)) -> (Filter.Tendsto.{u2, u1} α (List.{u1} β) g a (nhds.{u1} (List.{u1} β) (List.topologicalSpace.{u1} β _inst_2) l)) -> (Filter.Tendsto.{u2, u1} α (List.{u1} β) (fun (a : α) => List.cons.{u1} β (f a) (g a)) a (nhds.{u1} (List.{u1} β) (List.topologicalSpace.{u1} β _inst_2) (List.cons.{u1} β b l)))
but is expected to have type
  forall {β : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} β] {α : Type.{u2}} {f : α -> β} {g : α -> (List.{u1} β)} {a : Filter.{u2} α} {b : β} {l : List.{u1} β}, (Filter.Tendsto.{u2, u1} α β f a (nhds.{u1} β _inst_2 b)) -> (Filter.Tendsto.{u2, u1} α (List.{u1} β) g a (nhds.{u1} (List.{u1} β) (instTopologicalSpaceList.{u1} β _inst_2) l)) -> (Filter.Tendsto.{u2, u1} α (List.{u1} β) (fun (a : α) => List.cons.{u1} β (f a) (g a)) a (nhds.{u1} (List.{u1} β) (instTopologicalSpaceList.{u1} β _inst_2) (List.cons.{u1} β b l)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.cons Filter.Tendsto.consₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem Filter.Tendsto.cons {α : Type _} {f : α → β} {g : α → List β} {a : Filter α} {b : β}
    {l : List β} (hf : Tendsto f a (𝓝 b)) (hg : Tendsto g a (𝓝 l)) :
    Tendsto (fun a => List.cons (f a) (g a)) a (𝓝 (b::l)) :=
  List.tendsto_cons.comp (Tendsto.prod_mk hf hg)
#align filter.tendsto.cons Filter.Tendsto.cons

namespace List

/- warning: list.tendsto_cons_iff -> List.tendsto_cons_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {f : (List.{u1} α) -> β} {b : Filter.{u2} β} {a : α} {l : List.{u1} α}, Iff (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.cons.{u1} α a l)) b) (Filter.Tendsto.{u1, u2} (Prod.{u1, u1} α (List.{u1} α)) β (fun (p : Prod.{u1, u1} α (List.{u1} α)) => f (List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l)) b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {f : (List.{u1} α) -> β} {b : Filter.{u2} β} {a : α} {l : List.{u1} α}, Iff (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.cons.{u1} α a l)) b) (Filter.Tendsto.{u1, u2} (Prod.{u1, u1} α (List.{u1} α)) β (fun (p : Prod.{u1, u1} α (List.{u1} α)) => f (List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l)) b)
Case conversion may be inaccurate. Consider using '#align list.tendsto_cons_iff List.tendsto_cons_iffₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem tendsto_cons_iff {β : Type _} {f : List α → β} {b : Filter β} {a : α} {l : List α} :
    Tendsto f (𝓝 (a::l)) b ↔ Tendsto (fun p : α × List α => f (p.1::p.2)) (𝓝 a ×ᶠ 𝓝 l) b :=
  by
  have : 𝓝 (a::l) = (𝓝 a ×ᶠ 𝓝 l).map fun p : α × List α => p.1::p.2 :=
    by
    simp only [nhds_cons, Filter.prod_eq, (Filter.map_def _ _).symm,
      (Filter.seq_eq_filter_seq _ _).symm]
    simp [-Filter.seq_eq_filter_seq, -Filter.map_def, (· ∘ ·), functor_norm]
  rw [this, Filter.tendsto_map'_iff]
#align list.tendsto_cons_iff List.tendsto_cons_iff

/- warning: list.continuous_cons -> List.continuous_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α], Continuous.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (Prod.topologicalSpace.{u1, u1} α (List.{u1} α) _inst_1 (List.topologicalSpace.{u1} α _inst_1)) (List.topologicalSpace.{u1} α _inst_1) (fun (x : Prod.{u1, u1} α (List.{u1} α)) => List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) x) (Prod.snd.{u1, u1} α (List.{u1} α) x))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α], Continuous.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (instTopologicalSpaceProd.{u1, u1} α (List.{u1} α) _inst_1 (instTopologicalSpaceList.{u1} α _inst_1)) (instTopologicalSpaceList.{u1} α _inst_1) (fun (x : Prod.{u1, u1} α (List.{u1} α)) => List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) x) (Prod.snd.{u1, u1} α (List.{u1} α) x))
Case conversion may be inaccurate. Consider using '#align list.continuous_cons List.continuous_consₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem continuous_cons : Continuous fun x : α × List α => (x.1::x.2 : List α) :=
  continuous_iff_continuousAt.mpr fun ⟨x, y⟩ => continuousAt_fst.cons continuousAt_snd
#align list.continuous_cons List.continuous_cons

/- warning: list.tendsto_nhds -> List.tendsto_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {f : (List.{u1} α) -> β} {r : (List.{u1} α) -> (Filter.{u2} β)}, (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} (List.{u1} α) (List.nil.{u1} α)) (r (List.nil.{u1} α))) -> (forall (l : List.{u1} α) (a : α), (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l) (r l)) -> (Filter.Tendsto.{u1, u2} (Prod.{u1, u1} α (List.{u1} α)) β (fun (p : Prod.{u1, u1} α (List.{u1} α)) => f (List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l)) (r (List.cons.{u1} α a l)))) -> (forall (l : List.{u1} α), Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l) (r l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {f : (List.{u1} α) -> β} {r : (List.{u1} α) -> (Filter.{u2} β)}, (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} (List.{u1} α) (List.nil.{u1} α)) (r (List.nil.{u1} α))) -> (forall (l : List.{u1} α) (a : α), (Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l) (r l)) -> (Filter.Tendsto.{u1, u2} (Prod.{u1, u1} α (List.{u1} α)) β (fun (p : Prod.{u1, u1} α (List.{u1} α)) => f (List.cons.{u1} α (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l)) (r (List.cons.{u1} α a l)))) -> (forall (l : List.{u1} α), Filter.Tendsto.{u1, u2} (List.{u1} α) β f (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l) (r l))
Case conversion may be inaccurate. Consider using '#align list.tendsto_nhds List.tendsto_nhdsₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem tendsto_nhds {β : Type _} {f : List α → β} {r : List α → Filter β}
    (h_nil : Tendsto f (pure []) (r []))
    (h_cons :
      ∀ l a,
        Tendsto f (𝓝 l) (r l) →
          Tendsto (fun p : α × List α => f (p.1::p.2)) (𝓝 a ×ᶠ 𝓝 l) (r (a::l))) :
    ∀ l, Tendsto f (𝓝 l) (r l)
  | [] => by rwa [nhds_nil]
  | a::l => by rw [tendsto_cons_iff] <;> exact h_cons l a (tendsto_nhds l)
#align list.tendsto_nhds List.tendsto_nhds

/- warning: list.continuous_at_length -> List.continuousAt_length is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (l : List.{u1} α), ContinuousAt.{u1, 0} (List.{u1} α) Nat (List.topologicalSpace.{u1} α _inst_1) Nat.topologicalSpace (List.length.{u1} α) l
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (l : List.{u1} α), ContinuousAt.{u1, 0} (List.{u1} α) Nat (instTopologicalSpaceList.{u1} α _inst_1) instTopologicalSpaceNat (List.length.{u1} α) l
Case conversion may be inaccurate. Consider using '#align list.continuous_at_length List.continuousAt_lengthₓ'. -/
theorem continuousAt_length : ∀ l : List α, ContinuousAt List.length l :=
  by
  simp only [ContinuousAt, nhds_discrete]
  refine' tendsto_nhds _ _
  · exact tendsto_pure_pure _ _
  · intro l a ih
    dsimp only [List.length]
    refine' tendsto.comp (tendsto_pure_pure (fun x => x + 1) _) _
    refine' tendsto.comp ih tendsto_snd
#align list.continuous_at_length List.continuousAt_length

/- warning: list.tendsto_insert_nth' -> List.tendsto_insert_nth' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {a : α} {n : Nat} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.insertNth.{u1} α n (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p)) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l)) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.insertNth.{u1} α n a l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {a : α} {n : Nat} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.insertNth.{u1} α n (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p)) (Filter.prod.{u1, u1} α (List.{u1} α) (nhds.{u1} α _inst_1 a) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.insertNth.{u1} α n a l))
Case conversion may be inaccurate. Consider using '#align list.tendsto_insert_nth' List.tendsto_insert_nth'ₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem tendsto_insert_nth' {a : α} :
    ∀ {n : ℕ} {l : List α},
      Tendsto (fun p : α × List α => insertNth n p.1 p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (insertNth n a l))
  | 0, l => tendsto_cons
  | n + 1, [] => by simp
  | n + 1, a'::l =>
    by
    have :
      𝓝 a ×ᶠ 𝓝 (a'::l) = (𝓝 a ×ᶠ (𝓝 a' ×ᶠ 𝓝 l)).map fun p : α × α × List α => (p.1, p.2.1::p.2.2) :=
      by
      simp only [nhds_cons, Filter.prod_eq, ← Filter.map_def, ← Filter.seq_eq_filter_seq]
      simp [-Filter.seq_eq_filter_seq, -Filter.map_def, (· ∘ ·), functor_norm]
    rw [this, tendsto_map'_iff]
    exact
      (tendsto_fst.comp tendsto_snd).cons
        ((@tendsto_insert_nth' n l).comp <| tendsto_fst.prod_mk <| tendsto_snd.comp tendsto_snd)
#align list.tendsto_insert_nth' List.tendsto_insert_nth'

/- warning: list.tendsto_insert_nth -> List.tendsto_insertNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {n : Nat} {a : α} {l : List.{u1} α} {f : β -> α} {g : β -> (List.{u1} α)} {b : Filter.{u2} β}, (Filter.Tendsto.{u2, u1} β α f b (nhds.{u1} α _inst_1 a)) -> (Filter.Tendsto.{u2, u1} β (List.{u1} α) g b (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l)) -> (Filter.Tendsto.{u2, u1} β (List.{u1} α) (fun (b : β) => List.insertNth.{u1} α n (f b) (g b)) b (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.insertNth.{u1} α n a l)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {β : Type.{u2}} {n : Nat} {a : α} {l : List.{u1} α} {f : β -> α} {g : β -> (List.{u1} α)} {b : Filter.{u2} β}, (Filter.Tendsto.{u2, u1} β α f b (nhds.{u1} α _inst_1 a)) -> (Filter.Tendsto.{u2, u1} β (List.{u1} α) g b (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l)) -> (Filter.Tendsto.{u2, u1} β (List.{u1} α) (fun (b : β) => List.insertNth.{u1} α n (f b) (g b)) b (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.insertNth.{u1} α n a l)))
Case conversion may be inaccurate. Consider using '#align list.tendsto_insert_nth List.tendsto_insertNthₓ'. -/
theorem tendsto_insertNth {β} {n : ℕ} {a : α} {l : List α} {f : β → α} {g : β → List α}
    {b : Filter β} (hf : Tendsto f b (𝓝 a)) (hg : Tendsto g b (𝓝 l)) :
    Tendsto (fun b : β => insertNth n (f b) (g b)) b (𝓝 (insertNth n a l)) :=
  tendsto_insert_nth'.comp (Tendsto.prod_mk hf hg)
#align list.tendsto_insert_nth List.tendsto_insertNth

/- warning: list.continuous_insert_nth -> List.continuous_insertNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat}, Continuous.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (Prod.topologicalSpace.{u1, u1} α (List.{u1} α) _inst_1 (List.topologicalSpace.{u1} α _inst_1)) (List.topologicalSpace.{u1} α _inst_1) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.insertNth.{u1} α n (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat}, Continuous.{u1, u1} (Prod.{u1, u1} α (List.{u1} α)) (List.{u1} α) (instTopologicalSpaceProd.{u1, u1} α (List.{u1} α) _inst_1 (instTopologicalSpaceList.{u1} α _inst_1)) (instTopologicalSpaceList.{u1} α _inst_1) (fun (p : Prod.{u1, u1} α (List.{u1} α)) => List.insertNth.{u1} α n (Prod.fst.{u1, u1} α (List.{u1} α) p) (Prod.snd.{u1, u1} α (List.{u1} α) p))
Case conversion may be inaccurate. Consider using '#align list.continuous_insert_nth List.continuous_insertNthₓ'. -/
theorem continuous_insertNth {n : ℕ} : Continuous fun p : α × List α => insertNth n p.1 p.2 :=
  continuous_iff_continuousAt.mpr fun ⟨a, l⟩ => by
    rw [ContinuousAt, nhds_prod_eq] <;> exact tendsto_insert_nth'
#align list.continuous_insert_nth List.continuous_insertNth

/- warning: list.tendsto_remove_nth -> List.tendsto_removeNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (List.{u1} α) (List.{u1} α) (fun (l : List.{u1} α) => List.removeNth.{u1} α l n) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.removeNth.{u1} α l n))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (List.{u1} α) (List.{u1} α) (fun (l : List.{u1} α) => List.removeNth.{u1} α l n) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (List.removeNth.{u1} α l n))
Case conversion may be inaccurate. Consider using '#align list.tendsto_remove_nth List.tendsto_removeNthₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem tendsto_removeNth :
    ∀ {n : ℕ} {l : List α}, Tendsto (fun l => removeNth l n) (𝓝 l) (𝓝 (removeNth l n))
  | _, [] => by rw [nhds_nil] <;> exact tendsto_pure_nhds _ _
  | 0, a::l => by rw [tendsto_cons_iff] <;> exact tendsto_snd
  | n + 1, a::l => by
    rw [tendsto_cons_iff]
    dsimp [remove_nth]
    exact tendsto_fst.cons ((@tendsto_remove_nth n l).comp tendsto_snd)
#align list.tendsto_remove_nth List.tendsto_removeNth

/- warning: list.continuous_remove_nth -> List.continuous_removeNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat}, Continuous.{u1, u1} (List.{u1} α) (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) (List.topologicalSpace.{u1} α _inst_1) (fun (l : List.{u1} α) => List.removeNth.{u1} α l n)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat}, Continuous.{u1, u1} (List.{u1} α) (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) (instTopologicalSpaceList.{u1} α _inst_1) (fun (l : List.{u1} α) => List.removeNth.{u1} α l n)
Case conversion may be inaccurate. Consider using '#align list.continuous_remove_nth List.continuous_removeNthₓ'. -/
theorem continuous_removeNth {n : ℕ} : Continuous fun l : List α => removeNth l n :=
  continuous_iff_continuousAt.mpr fun a => tendsto_removeNth
#align list.continuous_remove_nth List.continuous_removeNth

/- warning: list.tendsto_prod -> List.tendsto_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : Monoid.{u1} α] [_inst_4 : ContinuousMul.{u1} α _inst_1 (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3))] {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (List.{u1} α) α (List.prod.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (MulOneClass.toHasOne.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3))) (nhds.{u1} (List.{u1} α) (List.topologicalSpace.{u1} α _inst_1) l) (nhds.{u1} α _inst_1 (List.prod.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (MulOneClass.toHasOne.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : Monoid.{u1} α] [_inst_4 : ContinuousMul.{u1} α _inst_1 (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3))] {l : List.{u1} α}, Filter.Tendsto.{u1, u1} (List.{u1} α) α (List.prod.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (Monoid.toOne.{u1} α _inst_3)) (nhds.{u1} (List.{u1} α) (instTopologicalSpaceList.{u1} α _inst_1) l) (nhds.{u1} α _inst_1 (List.prod.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (Monoid.toOne.{u1} α _inst_3) l))
Case conversion may be inaccurate. Consider using '#align list.tendsto_prod List.tendsto_prodₓ'. -/
@[to_additive]
theorem tendsto_prod [Monoid α] [ContinuousMul α] {l : List α} :
    Tendsto List.prod (𝓝 l) (𝓝 l.Prod) :=
  by
  induction' l with x l ih
  · simp (config := { contextual := true }) [nhds_nil, mem_of_mem_nhds, tendsto_pure_left]
  simp_rw [tendsto_cons_iff, prod_cons]
  have := continuous_iff_continuous_at.mp continuous_mul (x, l.prod)
  rw [ContinuousAt, nhds_prod_eq] at this
  exact this.comp (tendsto_id.prod_map ih)
#align list.tendsto_prod List.tendsto_prod
#align list.tendsto_sum List.tendsto_sum

/- warning: list.continuous_prod -> List.continuous_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : Monoid.{u1} α] [_inst_4 : ContinuousMul.{u1} α _inst_1 (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3))], Continuous.{u1, u1} (List.{u1} α) α (List.topologicalSpace.{u1} α _inst_1) _inst_1 (List.prod.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (MulOneClass.toHasOne.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : Monoid.{u1} α] [_inst_4 : ContinuousMul.{u1} α _inst_1 (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3))], Continuous.{u1, u1} (List.{u1} α) α (instTopologicalSpaceList.{u1} α _inst_1) _inst_1 (List.prod.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_3)) (Monoid.toOne.{u1} α _inst_3))
Case conversion may be inaccurate. Consider using '#align list.continuous_prod List.continuous_prodₓ'. -/
@[to_additive]
theorem continuous_prod [Monoid α] [ContinuousMul α] : Continuous (prod : List α → α) :=
  continuous_iff_continuousAt.mpr fun l => tendsto_prod
#align list.continuous_prod List.continuous_prod
#align list.continuous_sum List.continuous_sum

end List

namespace Vector

open List

instance (n : ℕ) : TopologicalSpace (Vector α n) := by unfold Vector <;> infer_instance

/- warning: vector.tendsto_cons -> Vector.tendsto_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {a : α} {l : Vector.{u1} α n}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (Nat.succ n)) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.cons.{u1} α n (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) (Prod.snd.{u1, u1} α (Vector.{u1} α n) p)) (Filter.prod.{u1, u1} α (Vector.{u1} α n) (nhds.{u1} α _inst_1 a) (nhds.{u1} (Vector.{u1} α n) (Vector.topologicalSpace.{u1} α _inst_1 n) l)) (nhds.{u1} (Vector.{u1} α (Nat.succ n)) (Vector.topologicalSpace.{u1} α _inst_1 (Nat.succ n)) (Vector.cons.{u1} α n a l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {a : α} {l : Vector.{u1} α n}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (Nat.succ n)) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.cons.{u1} α n (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) (Prod.snd.{u1, u1} α (Vector.{u1} α n) p)) (Filter.prod.{u1, u1} α (Vector.{u1} α n) (nhds.{u1} α _inst_1 a) (nhds.{u1} (Vector.{u1} α n) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 n) l)) (nhds.{u1} (Vector.{u1} α (Nat.succ n)) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (Nat.succ n)) (Vector.cons.{u1} α n a l))
Case conversion may be inaccurate. Consider using '#align vector.tendsto_cons Vector.tendsto_consₓ'. -/
theorem tendsto_cons {n : ℕ} {a : α} {l : Vector α n} :
    Tendsto (fun p : α × Vector α n => p.1 ::ᵥ p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (a ::ᵥ l)) :=
  by
  simp [tendsto_subtype_rng, ← Subtype.val_eq_coe, cons_val]
  exact tendsto_fst.cons (tendsto.comp continuousAt_subtype_val tendsto_snd)
#align vector.tendsto_cons Vector.tendsto_cons

/- warning: vector.tendsto_insert_nth -> Vector.tendsto_insertNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))} {a : α} {l : Vector.{u1} α n}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.insertNth.{u1} n α (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) i (Prod.snd.{u1, u1} α (Vector.{u1} α n) p)) (Filter.prod.{u1, u1} α (Vector.{u1} α n) (nhds.{u1} α _inst_1 a) (nhds.{u1} (Vector.{u1} α n) (Vector.topologicalSpace.{u1} α _inst_1 n) l)) (nhds.{u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.topologicalSpace.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.insertNth.{u1} n α a i l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))} {a : α} {l : Vector.{u1} α n}, Filter.Tendsto.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.insertNth.{u1} n α (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) i (Prod.snd.{u1, u1} α (Vector.{u1} α n) p)) (Filter.prod.{u1, u1} α (Vector.{u1} α n) (nhds.{u1} α _inst_1 a) (nhds.{u1} (Vector.{u1} α n) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 n) l)) (nhds.{u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.insertNth.{u1} n α a i l))
Case conversion may be inaccurate. Consider using '#align vector.tendsto_insert_nth Vector.tendsto_insertNthₓ'. -/
theorem tendsto_insertNth {n : ℕ} {i : Fin (n + 1)} {a : α} :
    ∀ {l : Vector α n},
      Tendsto (fun p : α × Vector α n => insertNth p.1 i p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (insertNth a i l))
  | ⟨l, hl⟩ => by
    rw [insert_nth, tendsto_subtype_rng]
    simp [insert_nth_val]
    exact List.tendsto_insertNth tendsto_fst (tendsto.comp continuousAt_subtype_val tendsto_snd : _)
#align vector.tendsto_insert_nth Vector.tendsto_insertNth

/- warning: vector.continuous_insert_nth' -> Vector.continuous_insert_nth' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))}, Continuous.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Prod.topologicalSpace.{u1, u1} α (Vector.{u1} α n) _inst_1 (Vector.topologicalSpace.{u1} α _inst_1 n)) (Vector.topologicalSpace.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.insertNth.{u1} n α (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) i (Prod.snd.{u1, u1} α (Vector.{u1} α n) p))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))}, Continuous.{u1, u1} (Prod.{u1, u1} α (Vector.{u1} α n)) (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instTopologicalSpaceProd.{u1, u1} α (Vector.{u1} α n) _inst_1 (Vector.instTopologicalSpaceVector.{u1} α _inst_1 n)) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (p : Prod.{u1, u1} α (Vector.{u1} α n)) => Vector.insertNth.{u1} n α (Prod.fst.{u1, u1} α (Vector.{u1} α n) p) i (Prod.snd.{u1, u1} α (Vector.{u1} α n) p))
Case conversion may be inaccurate. Consider using '#align vector.continuous_insert_nth' Vector.continuous_insert_nth'ₓ'. -/
theorem continuous_insert_nth' {n : ℕ} {i : Fin (n + 1)} :
    Continuous fun p : α × Vector α n => insertNth p.1 i p.2 :=
  continuous_iff_continuousAt.mpr fun ⟨a, l⟩ => by
    rw [ContinuousAt, nhds_prod_eq] <;> exact tendsto_insert_nth
#align vector.continuous_insert_nth' Vector.continuous_insert_nth'

/- warning: vector.continuous_insert_nth -> Vector.continuous_insertNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))} {f : β -> α} {g : β -> (Vector.{u1} α n)}, (Continuous.{u2, u1} β α _inst_2 _inst_1 f) -> (Continuous.{u2, u1} β (Vector.{u1} α n) _inst_2 (Vector.topologicalSpace.{u1} α _inst_1 n) g) -> (Continuous.{u2, u1} β (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) _inst_2 (Vector.topologicalSpace.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (b : β) => Vector.insertNth.{u1} n α (f b) i (g b)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))} {f : β -> α} {g : β -> (Vector.{u2} α n)}, (Continuous.{u1, u2} β α _inst_2 _inst_1 f) -> (Continuous.{u1, u2} β (Vector.{u2} α n) _inst_2 (Vector.instTopologicalSpaceVector.{u2} α _inst_1 n) g) -> (Continuous.{u1, u2} β (Vector.{u2} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) _inst_2 (Vector.instTopologicalSpaceVector.{u2} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (b : β) => Vector.insertNth.{u2} n α (f b) i (g b)))
Case conversion may be inaccurate. Consider using '#align vector.continuous_insert_nth Vector.continuous_insertNthₓ'. -/
theorem continuous_insertNth {n : ℕ} {i : Fin (n + 1)} {f : β → α} {g : β → Vector α n}
    (hf : Continuous f) (hg : Continuous g) : Continuous fun b => insertNth (f b) i (g b) :=
  continuous_insert_nth'.comp (hf.prod_mk hg : _)
#align vector.continuous_insert_nth Vector.continuous_insertNth

/- warning: vector.continuous_at_remove_nth -> Vector.continuousAt_removeNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))} {l : Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))}, ContinuousAt.{u1, u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.{u1} α (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.topologicalSpace.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.topologicalSpace.{u1} α _inst_1 (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.removeNth.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) i) l
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))} {l : Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))}, ContinuousAt.{u1, u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.{u1} α (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.removeNth.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) i) l
Case conversion may be inaccurate. Consider using '#align vector.continuous_at_remove_nth Vector.continuousAt_removeNthₓ'. -/
theorem continuousAt_removeNth {n : ℕ} {i : Fin (n + 1)} :
    ∀ {l : Vector α (n + 1)}, ContinuousAt (removeNth i) l
  | ⟨l, hl⟩ =>--  ∀{l:vector α (n+1)}, tendsto (remove_nth i) (𝓝 l) (𝓝 (remove_nth i l))
  --| ⟨l, hl⟩ :=
  by
    rw [ContinuousAt, remove_nth, tendsto_subtype_rng]
    simp only [← Subtype.val_eq_coe, Vector.removeNth_val]
    exact tendsto.comp List.tendsto_removeNth continuousAt_subtype_val
#align vector.continuous_at_remove_nth Vector.continuousAt_removeNth

/- warning: vector.continuous_remove_nth -> Vector.continuous_removeNth is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))}, Continuous.{u1, u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.{u1} α (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.topologicalSpace.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.topologicalSpace.{u1} α _inst_1 (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Vector.removeNth.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) i)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {n : Nat} {i : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))}, Continuous.{u1, u1} (Vector.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.{u1} α (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.instTopologicalSpaceVector.{u1} α _inst_1 (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Vector.removeNth.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) i)
Case conversion may be inaccurate. Consider using '#align vector.continuous_remove_nth Vector.continuous_removeNthₓ'. -/
theorem continuous_removeNth {n : ℕ} {i : Fin (n + 1)} :
    Continuous (removeNth i : Vector α (n + 1) → Vector α n) :=
  continuous_iff_continuousAt.mpr fun ⟨a, l⟩ => continuousAt_removeNth
#align vector.continuous_remove_nth Vector.continuous_removeNth

end Vector


variables (x y z : ℕ) (p : ℕ → Prop)

example (h : p ((x + 0) * (0 + y * 1 + z * 0))) : p (x * y) :=
  by { simp at h, assumption }


inductive time (D : Type) : Type
| start : time D
| after : (D → time D) → time D

def foo (C D : Type) : time D → (γ : Type) × (γ → C)
| time.start    => ⟨C, id⟩
| time.after ts => ⟨(∀ (i : D), (foo C D $ ts i).1) × D, λ p => (foo C D $ ts p.2).2 $ p.1 p.2⟩

theorem fooEq1 (C D) : foo C D time.start = ⟨C, id⟩ :=
  rfl
theorem fooEq2 (C D) (ts : D → time D): foo C D (time.after ts) = ⟨(∀ (i : D), (foo C D $ ts i).1) × D, λ p => (foo C D $ ts p.2).2 $ p.1 p.2⟩ :=
  rfl


/-
Copyright (c) 2019 Neil Strickland. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Neil Strickland
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.pnat.prime
import Mathlib.data.multiset.sort
import Mathlib.data.int.gcd
import Mathlib.algebra.group.default
import Mathlib.PostPort

namespace Mathlib

/-- The type of multisets of prime numbers.  Unique factorization
 gives an equivalence between this set and ℕ+, as we will formalize
 below. -/
def prime_multiset := multiset nat.primes

namespace prime_multiset

protected instance inhabited : Inhabited prime_multiset := eq.mpr sorry multiset.inhabited

protected instance has_repr : has_repr prime_multiset := id multiset.has_repr

protected instance canonically_ordered_add_monoid : canonically_ordered_add_monoid prime_multiset :=
  id multiset.canonically_ordered_add_monoid

protected instance distrib_lattice : distrib_lattice prime_multiset := id multiset.distrib_lattice

protected instance semilattice_sup_bot : semilattice_sup_bot prime_multiset :=
  id multiset.semilattice_sup_bot

protected instance has_sub : Sub prime_multiset := id multiset.has_sub

theorem add_sub_of_le {u : prime_multiset} {v : prime_multiset} : u ≤ v → u + (v - u) = v :=
  multiset.add_sub_of_le

/-- The multiset consisting of a single prime
-/
def of_prime (p : nat.primes) : prime_multiset := p ::ₘ 0

theorem card_of_prime (p : nat.primes) : coe_fn multiset.card (of_prime p) = 1 := rfl

/-- We can forget the primality property and regard a multiset
 of primes as just a multiset of positive integers, or a multiset
 of natural numbers.  In the opposite direction, if we have a
 multiset of positive integers or natural numbers, together with
 a proof that all the elements are prime, then we can regard it
 as a multiset of primes.  The next block of results records
 obvious properties of these coercions.
-/
def to_nat_multiset : prime_multiset → multiset ℕ :=
  fun (v : prime_multiset) => multiset.map (fun (p : nat.primes) => ↑p) v

protected instance coe_nat : has_coe prime_multiset (multiset ℕ) := has_coe.mk to_nat_multiset

protected instance coe_nat_hom : is_add_monoid_hom coe :=
  eq.mpr
    (id
      ((fun (f f_1 : prime_multiset → multiset ℕ) (e_3 : f = f_1) =>
          congr_arg is_add_monoid_hom e_3)
        coe to_nat_multiset
        (Eq.trans
          (Eq.trans (Eq.trans coe.equations._eqn_1 lift_t.equations._eqn_1) coe_t.equations._eqn_1)
          coe_b.equations._eqn_1)))
    (id (multiset.map.is_add_monoid_hom coe))

theorem coe_nat_injective : function.injective coe := multiset.map_injective nat.primes.coe_nat_inj

theorem coe_nat_of_prime (p : nat.primes) : ↑(of_prime p) = ↑p ::ₘ 0 := rfl

theorem coe_nat_prime (v : prime_multiset) (p : ℕ) (h : p ∈ ↑v) : nat.prime p := sorry

/-- Converts a `prime_multiset` to a `multiset ℕ+`. -/
def to_pnat_multiset : prime_multiset → multiset ℕ+ :=
  fun (v : prime_multiset) => multiset.map (fun (p : nat.primes) => ↑p) v

protected instance coe_pnat : has_coe prime_multiset (multiset ℕ+) := has_coe.mk to_pnat_multiset

protected instance coe_pnat_hom : is_add_monoid_hom coe :=
  eq.mpr
    (id
      ((fun (f f_1 : prime_multiset → multiset ℕ+) (e_3 : f = f_1) =>
          congr_arg is_add_monoid_hom e_3)
        coe to_pnat_multiset
        (Eq.trans
          (Eq.trans (Eq.trans coe.equations._eqn_1 lift_t.equations._eqn_1) coe_t.equations._eqn_1)
          coe_b.equations._eqn_1)))
    (id (multiset.map.is_add_monoid_hom coe))

theorem coe_pnat_injective : function.injective coe :=
  multiset.map_injective nat.primes.coe_pnat_inj

theorem coe_pnat_of_prime (p : nat.primes) : ↑(of_prime p) = ↑p ::ₘ 0 := rfl

theorem coe_pnat_prime (v : prime_multiset) (p : ℕ+) (h : p ∈ ↑v) : pnat.prime p := sorry

protected instance coe_multiset_pnat_nat : has_coe (multiset ℕ+) (multiset ℕ) :=
  has_coe.mk fun (v : multiset ℕ+) => multiset.map (fun (n : ℕ+) => ↑n) v

theorem coe_pnat_nat (v : prime_multiset) : ↑↑v = ↑v := sorry

/-- The product of a `prime_multiset`, as a `ℕ+`. -/
def prod (v : prime_multiset) : ℕ+ := multiset.prod ↑v

theorem coe_prod (v : prime_multiset) : ↑(prod v) = multiset.prod ↑v := sorry

theorem prod_of_prime (p : nat.primes) : prod (of_prime p) = ↑p := sorry

/-- If a `multiset ℕ` consists only of primes, it can be recast as a `prime_multiset`. -/
def of_nat_multiset (v : multiset ℕ) (h : ∀ (p : ℕ), p ∈ v → nat.prime p) : prime_multiset :=
  multiset.pmap (fun (p : ℕ) (hp : nat.prime p) => { val := p, property := hp }) v h

theorem to_of_nat_multiset (v : multiset ℕ) (h : ∀ (p : ℕ), p ∈ v → nat.prime p) :
    ↑(of_nat_multiset v h) = v :=
  sorry

theorem prod_of_nat_multiset (v : multiset ℕ) (h : ∀ (p : ℕ), p ∈ v → nat.prime p) :
    ↑(prod (of_nat_multiset v h)) = multiset.prod v :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (↑(prod (of_nat_multiset v h)) = multiset.prod v))
        (coe_prod (of_nat_multiset v h))))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (multiset.prod ↑(of_nat_multiset v h) = multiset.prod v))
          (to_of_nat_multiset v h)))
      (Eq.refl (multiset.prod v)))

/-- If a `multiset ℕ+` consists only of primes, it can be recast as a `prime_multiset`. -/
def of_pnat_multiset (v : multiset ℕ+) (h : ∀ (p : ℕ+), p ∈ v → pnat.prime p) : prime_multiset :=
  multiset.pmap (fun (p : ℕ+) (hp : pnat.prime p) => { val := ↑p, property := hp }) v h

theorem to_of_pnat_multiset (v : multiset ℕ+) (h : ∀ (p : ℕ+), p ∈ v → pnat.prime p) :
    ↑(of_pnat_multiset v h) = v :=
  sorry

theorem prod_of_pnat_multiset (v : multiset ℕ+) (h : ∀ (p : ℕ+), p ∈ v → pnat.prime p) :
    prod (of_pnat_multiset v h) = multiset.prod v :=
  sorry

/-- Lists can be coerced to multisets; here we have some results
 about how this interacts with our constructions on multisets.
-/
def of_nat_list (l : List ℕ) (h : ∀ (p : ℕ), p ∈ l → nat.prime p) : prime_multiset :=
  of_nat_multiset (↑l) h

theorem prod_of_nat_list (l : List ℕ) (h : ∀ (p : ℕ), p ∈ l → nat.prime p) :
    ↑(prod (of_nat_list l h)) = list.prod l :=
  eq.mp
    (Eq._oldrec (Eq.refl (↑(prod (of_nat_multiset (↑l) h)) = multiset.prod ↑l))
      (multiset.coe_prod l))
    (prod_of_nat_multiset (↑l) h)

/-- If a `list ℕ+` consists only of primes, it can be recast as a `prime_multiset` with
  the coercion from lists to multisets. -/
def of_pnat_list (l : List ℕ+) (h : ∀ (p : ℕ+), p ∈ l → pnat.prime p) : prime_multiset :=
  of_pnat_multiset (↑l) h

theorem prod_of_pnat_list (l : List ℕ+) (h : ∀ (p : ℕ+), p ∈ l → pnat.prime p) :
    prod (of_pnat_list l h) = list.prod l :=
  eq.mp
    (Eq._oldrec (Eq.refl (prod (of_pnat_multiset (↑l) h) = multiset.prod ↑l)) (multiset.coe_prod l))
    (prod_of_pnat_multiset (↑l) h)

/-- The product map gives a homomorphism from the additive monoid
 of multisets to the multiplicative monoid ℕ+.
-/
theorem prod_zero : prod 0 = 1 := id multiset.prod_zero

theorem prod_add (u : prime_multiset) (v : prime_multiset) : prod (u + v) = prod u * prod v := sorry

theorem prod_smul (d : ℕ) (u : prime_multiset) : prod (d •ℕ u) = prod u ^ d := sorry

end prime_multiset

namespace pnat

/-- The prime factors of n, regarded as a multiset -/
def factor_multiset (n : ℕ+) : prime_multiset := prime_multiset.of_nat_list (nat.factors ↑n) sorry

/-- The product of the factors is the original number -/
theorem prod_factor_multiset (n : ℕ+) : prime_multiset.prod (factor_multiset n) = n := sorry

theorem coe_nat_factor_multiset (n : ℕ+) : ↑(factor_multiset n) = ↑(nat.factors ↑n) :=
  prime_multiset.to_of_nat_multiset (↑(nat.factors ↑n)) nat.mem_factors

end pnat

namespace prime_multiset

/-- If we start with a multiset of primes, take the product and
 then factor it, we get back the original multiset. -/
theorem factor_multiset_prod (v : prime_multiset) : pnat.factor_multiset (prod v) = v := sorry

end prime_multiset

namespace pnat

/-- Positive integers biject with multisets of primes. -/
def factor_multiset_equiv : ℕ+ ≃ prime_multiset :=
  equiv.mk factor_multiset prime_multiset.prod prod_factor_multiset
    prime_multiset.factor_multiset_prod

/-- Factoring gives a homomorphism from the multiplicative
 monoid ℕ+ to the additive monoid of multisets. -/
theorem factor_multiset_one : factor_multiset 1 = 0 := rfl

theorem factor_multiset_mul (n : ℕ+) (m : ℕ+) :
    factor_multiset (n * m) = factor_multiset n + factor_multiset m :=
  sorry

theorem factor_multiset_pow (n : ℕ+) (m : ℕ) : factor_multiset (n ^ m) = m •ℕ factor_multiset n :=
  sorry

/-- Factoring a prime gives the corresponding one-element multiset. -/
theorem factor_multiset_of_prime (p : nat.primes) :
    factor_multiset ↑p = prime_multiset.of_prime p :=
  sorry

/-- We now have four different results that all encode the
 idea that inequality of multisets corresponds to divisibility
 of positive integers. -/
theorem factor_multiset_le_iff {m : ℕ+} {n : ℕ+} : factor_multiset m ≤ factor_multiset n ↔ m ∣ n :=
  sorry

theorem factor_multiset_le_iff' {m : ℕ+} {v : prime_multiset} :
    factor_multiset m ≤ v ↔ m ∣ prime_multiset.prod v :=
  sorry

end pnat

namespace prime_multiset

theorem prod_dvd_iff {u : prime_multiset} {v : prime_multiset} : prod u ∣ prod v ↔ u ≤ v :=
  let h : pnat.factor_multiset (prod u) ≤ v ↔ prod u ∣ prod v := pnat.factor_multiset_le_iff';
  iff.symm
    (eq.mp
      (Eq._oldrec (Eq.refl (pnat.factor_multiset (prod u) ≤ v ↔ prod u ∣ prod v))
        (factor_multiset_prod u))
      h)

theorem prod_dvd_iff' {u : prime_multiset} {n : ℕ+} : prod u ∣ n ↔ u ≤ pnat.factor_multiset n :=
  sorry

end prime_multiset

namespace pnat

/-- The gcd and lcm operations on positive integers correspond
 to the inf and sup operations on multisets.
-/
theorem factor_multiset_gcd (m : ℕ+) (n : ℕ+) :
    factor_multiset (gcd m n) = factor_multiset m ⊓ factor_multiset n :=
  sorry

theorem factor_multiset_lcm (m : ℕ+) (n : ℕ+) :
    factor_multiset (lcm m n) = factor_multiset m ⊔ factor_multiset n :=
  sorry

/-- The number of occurrences of p in the factor multiset of m
 is the same as the p-adic valuation of m. -/
theorem count_factor_multiset (m : ℕ+) (p : nat.primes) (k : ℕ) :
    ↑p ^ k ∣ m ↔ k ≤ multiset.count p (factor_multiset m) :=
  sorry

end pnat

namespace prime_multiset

theorem prod_inf (u : prime_multiset) (v : prime_multiset) :
    prod (u ⊓ v) = pnat.gcd (prod u) (prod v) :=
  sorry

theorem prod_sup (u : prime_multiset) (v : prime_multiset) :
    prod (u ⊔ v) = pnat.lcm (prod u) (prod v) :=
  sorry

end Mathlib


-- This module defines
namespace Nat

-- `forallRange i n f` is true if f holds for all indices j from i to n-1.
def forallRange (i:Nat) (n:Nat) (f: ∀ (j:Nat), j < n → Bool) : Bool :=
  if h:i < n then
    f i h && forallRange (i+1) n f
  else
    true
  termination_by forallRange i n f => n-i

-- `forallRange` correctness theorem.
theorem forallRangeImplies'
  (n i j : Nat)
  (f  : ∀(k:Nat), k < n → Bool)
  (eq : i+j = n)
  (p  : forallRange i n f = true)
  (k  : Nat)
  (lb : i ≤ k)
  (ub : k < n)
  : f k ub = true := by
  induction j generalizing i with
  | zero =>
    simp at eq
    simp [eq] at lb
    have pr := Nat.not_le_of_gt ub
    contradiction
  | succ j ind =>
    have i_lt_n : i < n := Nat.le_trans (Nat.succ_le_succ lb) ub
    unfold forallRange at p
    simp [i_lt_n] at p
    cases Nat.eq_or_lt_of_le lb with
    | inl hEq =>
      subst hEq
      apply p.1
    | inr hLt =>
      have succ_i_add_j : succ i + j = n := by simp_arith [← eq]
      apply ind (succ i) succ_i_add_j p.2 hLt

-- Correctness theorem for `forallRange`
theorem forallRangeImplies (p:forallRange i n f = true) {j:Nat} (lb:i ≤ j) (ub : j < n)
  : f j ub = true :=
    have h : i+(n-i)=n := Nat.add_sub_of_le (Nat.le_trans lb (Nat.le_of_lt ub))
    forallRangeImplies' n i (n-i) f h p j lb ub

theorem lt_or_eq_of_succ {i j:Nat} (lt : i < Nat.succ j) : i < j ∨ i = j :=
  match lt with
  | Nat.le.step m => Or.inl m
  | Nat.le.refl => Or.inr rfl

-- Introduce strong induction principal for natural numbers.
theorem strong_induction_on {p : Nat → Prop} (n:Nat)
  (h:∀n, (∀ m, m < n → p m) → p n) : p n := by
    suffices ∀n m, m < n → p m from this (succ n) n (Nat.lt_succ_self _)
    intros n
    induction n with
    | zero =>
      intros m h
      contradiction
    | succ i ind =>
      intros m h1
      cases Nat.lt_or_eq_of_succ h1 with
      | inl is_lt =>
        apply ind _ is_lt
      | inr is_eq =>
        apply h
        rw [is_eq]
        apply ind

end Nat

-- Introduce strong induction principal for Fin.
theorem Fin.strong_induction_on {P : Fin w → Prop} (i:Fin w)
  (ind : ∀(i:Fin w), (∀(j:Fin w), j < i → P j) → P i)
 : P i := by
   cases i with
   | mk i i_lt =>
     revert i_lt
     apply @Nat.strong_induction_on (λi => ∀ (i_lt : i < w), P { val := i, isLt := i_lt })
     intros j p j_lt_w
     apply ind ⟨j, j_lt_w⟩
     intros z z_lt_j
     apply p _ z_lt_j

namespace PEG

inductive Expression (t : Type) (nt : Type) where
  | epsilon : Expression t nt
  | fail : Expression t nt
  | any : Expression t nt
  | terminal : t → Expression t nt
  | seq : (a b : nt) → Expression t nt
  | choice : (a b : nt) → Expression t nt
  | look : (a : nt) → Expression t nt
  | notP : (e : nt) → Expression t nt

def Grammar (t nt : Type _) := nt → Expression t nt

structure ProofRecord  (nt : Type) where
  leftnonterminal : nt
  success : Bool
  position : Nat
  lengthofspan : Nat
  subproof1index : Nat
  subproof2index : Nat

namespace ProofRecord

def endposition {nt:Type} (r:ProofRecord nt) : Nat := r.position + r.lengthofspan

inductive Result where
  | fail : Result
  | success : Nat → Result

def record_result (r:ProofRecord nt) : Result :=
  if r.success then
    Result.success r.lengthofspan
  else
    Result.fail

end ProofRecord

def PreProof (nt : Type) := Array (ProofRecord nt)

def record_match [dnt : DecidableEq nt] (r:ProofRecord nt) (n:nt) (i:Nat) : Bool :=
  r.leftnonterminal = n && r.position = i

open Expression

section well_formed

variable {t nt : Type}
variable [dt : DecidableEq t]
variable [dnt : DecidableEq nt]
variable (g : Grammar t nt)
variable (s : Array t)

def well_formed_record (p : PreProof nt) (i:Nat) (i_lt : i < p.size) (r : ProofRecord nt) : Bool :=
  let n := r.leftnonterminal
  match g n with
  | epsilon => r.success ∧ r.lengthofspan = 0
  | fail => ¬ r.success
  | any =>
    if r.position < s.size then
      r.success && r.lengthofspan = 1
    else
      ¬ r.success
  | terminal t =>
    if r.position < s.size && s.getD r.position t = t then
      r.success && r.lengthofspan = 1
    else
      ¬ r.success
  | seq a b =>
    r.subproof1index < i &&
      let r1 := p.getD r.subproof1index r
      record_match r1 a r.position &&
        if r1.success then
          r.subproof2index < i &&
            let r2 := p.getD r.subproof2index r
            record_match r2 b r1.endposition &&
              if r2.success then
                r.success && r.endposition = r2.endposition
              else
                ¬r.success
        else
          ¬r.success
  | choice a b =>
    r.subproof1index < i &&
      let r1 := p.getD r.subproof1index r
      record_match r1 a r.position &&
        if r1.success then
          r.success && r.lengthofspan = r1.lengthofspan
        else
          r.subproof2index < i &&
            let r2 := p.getD r.subproof2index r
            record_match r2 b r.position &&
              if r2.success then
                r.success && r.lengthofspan = r2.lengthofspan
              else
                ¬r.success
  | look a =>
    r.subproof1index < i &&
      let r1 := p.getD r.subproof1index r
      record_match r1 a r.position &&
        if r1.success then
          r.success && r.lengthofspan = 0
        else
          ¬r.success
  | notP a =>
    r.subproof1index < i &&
      let r1 := p.getD r.subproof1index r
      record_match r1 a r.position &&
        if r1.success then
          ¬r.success
        else
          r.success && r.lengthofspan = 0

def well_formed_proof (p : PreProof nt) : Bool :=
  Nat.forallRange 0 p.size (λi lt => well_formed_record g s p i lt (p.get ⟨i, lt⟩))

end well_formed

def Proof [DecidableEq t] [DecidableEq nt] (g:Grammar t nt) (s: Array t) :=
  { p:PreProof nt // well_formed_proof g s p }

namespace Proof

variable {g:Grammar t nt}
variable {s : Array t}
variable [DecidableEq t]
variable [DecidableEq nt]

def size (p:Proof g s) := p.val.size

def get (p:Proof g s) : Fin p.size → ProofRecord nt := p.val.get

instance : CoeFun (Proof g s) (fun p => Fin p.size → ProofRecord nt) :=
  ⟨fun p => p.get⟩

theorem has_well_formed_record (p:Proof g s) (i:Fin p.size) :
  well_formed_record g s p.val i.val i.isLt (p i) :=
    Nat.forallRangeImplies p.property (Nat.zero_le i.val) i.isLt

end Proof

section correctness

variable {g:Grammar t nt}
variable {s : Array t}
variable [h1:DecidableEq t]
variable [h2:DecidableEq nt]

-- Lemma to rewrite from dependent use of proof index to get-with-default
theorem proof_get_to_getD (r:ProofRecord nt) (p:Proof g s) (i:Fin p.size)  :
  p i = p.val.getD i.val r := by
  have isLt : i.val < Array.size p.val := i.isLt
  simp [Proof.get, Array.get, Array.getD, isLt ]
  apply congrArg
  apply Fin.eq_of_val_eq
  trivial

set_option tactic.dbg_cache true

theorem is_deterministic
  : forall (p q : Proof g s) (i: Fin p.size) (j: Fin q.size),
      (p i).leftnonterminal = (q j).leftnonterminal
      → (p i).position      = (q j).position
      → (p i).record_result = (q j).record_result := by
  intros p q i0
  induction i0 using Fin.strong_induction_on with
  | ind i ind =>
  intro j eq_nt p_pos_eq_q_pos
  have p_def := p.has_well_formed_record i
  have q_def := q.has_well_formed_record j
  simp only [well_formed_record, eq_nt, p_pos_eq_q_pos] at p_def q_def
  generalize q_j_eq : q j = q_j
  generalize e_eq : g (q_j.leftnonterminal) = e
  simp only [q_j_eq, e_eq] at p_def q_def p_pos_eq_q_pos
  simp only [ProofRecord.record_result]

  cases e
  case epsilon => simp_all
  case fail => simp_all
  case any => simp at q_def; split at q_def <;> simp_all
  case terminal t => simp at q_def; split at q_def <;> simp_all
  case seq a b =>
    simp [record_match, ProofRecord.endposition] at p_def q_def
    generalize p_sub1_eq : (p i).subproof1index = p_sub1
    generalize p_sub2_eq : (p i).subproof2index = p_sub2
    generalize q_sub1_eq : q_j.subproof1index = q_sub1
    generalize q_sub2_eq : q_j.subproof2index = q_sub2
    simp only [p_sub1_eq, p_sub2_eq] at p_def
    simp only [q_sub1_eq, q_sub2_eq] at q_def
    have ⟨p_sub1_bound, ⟨p_sub1_nt, p_sub1_pos⟩, p_def⟩ := p_def
    have ⟨q_sub1_bound, ⟨q_sub1_nt, q_sub1_pos⟩, q_def⟩ := q_def

    have ind1 := ind (Fin.mk p_sub1 (Nat.lt_trans p_sub1_bound i.isLt))
                      p_sub1_bound
                      (Fin.mk q_sub1 (Nat.lt_trans q_sub1_bound j.isLt))
    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind1
    simp [ProofRecord.record_result] at ind1

    split at p_def <;> split at q_def <;> simp [*] at ind1 p_def q_def <;> simp [*]

    have ⟨p_sub2_bound, ⟨p_sub2_nt, p_sub2_pos⟩, p_def⟩ := p_def
    have ⟨q_sub2_bound, ⟨q_sub2_nt, q_sub2_pos⟩, q_def⟩ := q_def

    -- Instantiate second invariant on subterm 2
    have ind2 :=
      ind (Fin.mk p_sub2 (Nat.lt_trans p_sub2_bound i.isLt))
          p_sub2_bound
          (Fin.mk q_sub2 (Nat.lt_trans q_sub2_bound j.isLt))
    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind2

    simp [ProofRecord.record_result] at ind2
    split at p_def <;> split at q_def <;>
      simp_arith [*] at ind2 p_def q_def <;>
      simp_arith [*]
  save
  case choice =>
    simp [record_match] at p_def q_def
    -- checkpoint simp
    generalize p_sub1_eq : (p i).subproof1index = p_sub1
    generalize p_sub2_eq : (p i).subproof2index = p_sub2
    simp only [p_sub1_eq, p_sub2_eq] at p_def

    generalize q_sub1_eq : q_j.subproof1index = q_sub1
    generalize q_sub2_eq : q_j.subproof2index = q_sub2
    simp only [q_sub1_eq, q_sub2_eq] at q_def
    have ⟨p_sub1_bound, ⟨p_sub1_nt, p_sub1_pos⟩, p_def⟩ := p_def
    have ⟨q_sub1_bound, ⟨q_sub1_nt, q_sub1_pos⟩, q_def⟩ := q_def
    have ind1 := ind (Fin.mk p_sub1 (Nat.lt_trans p_sub1_bound i.isLt))
                      p_sub1_bound
                      (Fin.mk q_sub1 (Nat.lt_trans q_sub1_bound j.isLt))

    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind1
    simp [ProofRecord.record_result] at ind1
    save
    trace "type here"
    stop
    split at p_def <;> split at q_def <;> simp_all

    have ⟨p_sub2_bound, ⟨p_sub2_nt, p_sub2_pos⟩, p_def⟩ := p_def
    have ⟨q_sub2_bound, ⟨q_sub2_nt, q_sub2_pos⟩, q_def⟩ := q_def
    -- Instantiate second invariant on subterm 2
    have ind2 :=
      ind (Fin.mk p_sub2 (Nat.lt_trans p_sub2_bound i.isLt))
          p_sub2_bound
          (Fin.mk q_sub2 (Nat.lt_trans q_sub2_bound j.isLt))
    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind2
    simp [ProofRecord.record_result] at ind2
    split at p_def <;> split at q_def <;> simp_all
  stop
  case look =>
    simp [record_match] at p_def q_def

    generalize p_sub1_eq : (p i).subproof1index = p_sub1
    simp only [p_sub1_eq] at p_def

    generalize q_sub1_eq : q_j.subproof1index = q_sub1
    simp only [q_sub1_eq] at q_def
    have ⟨p_sub1_bound, ⟨p_sub1_nt, p_sub1_pos⟩, p_def⟩ := p_def
    have ⟨q_sub1_bound, ⟨q_sub1_nt, q_sub1_pos⟩, q_def⟩ := q_def

    have ind1 := ind (Fin.mk p_sub1 (Nat.lt_trans p_sub1_bound i.isLt))
                      p_sub1_bound
                      (Fin.mk q_sub1 (Nat.lt_trans q_sub1_bound j.isLt))
    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind1
    simp [ProofRecord.record_result] at ind1
    split at p_def <;> split at q_def <;> simp_all
  case notP =>
    simp [record_match] at p_def q_def

    generalize p_sub1_eq : (p i).subproof1index = p_sub1
    simp only [p_sub1_eq] at p_def

    generalize q_sub1_eq : q_j.subproof1index = q_sub1
    simp only [q_sub1_eq] at q_def

    have ⟨p_sub1_bound, ⟨p_sub1_nt, p_sub1_pos⟩, p_def⟩ := p_def
    have ⟨q_sub1_bound, ⟨q_sub1_nt, q_sub1_pos⟩, q_def⟩ := q_def

    have ind1 := ind (Fin.mk p_sub1 (Nat.lt_trans p_sub1_bound i.isLt))
                      p_sub1_bound
                      (Fin.mk q_sub1 (Nat.lt_trans q_sub1_bound j.isLt))
    rw [proof_get_to_getD (p i) p, proof_get_to_getD q_j q] at ind1
    simp [ProofRecord.record_result] at ind1
    split at p_def <;> split at q_def <;> simp_all

end correctness

end PEG


import Lean4Axiomatic.Integer.Sign

/-! # Generic implementations of integer signedness properties -/

namespace Lean4Axiomatic.Integer.Impl.Generic

variable {ℕ : Type} [Natural ℕ]
variable {ℤ : Type} [Core (ℕ := ℕ) ℤ]
variable [Addition ℤ] [Multiplication ℤ] [Negation ℤ]

open Signed (Negative Positive)

/--
Definitions of signedness predicates on integers.

These definitions are generic in that they don't depend on the underlying
implementation of the integers (the type `ℤ`). This allows them to be used by
any integer implementation directly.

These definitions are in a separate `Ops` class so that subsequent results in
this file can use the canonical predicate names.
-/
instance signed_ops : Signed.Ops ℤ := {
  Positive := λ (a : ℤ) => NonzeroWithSign a 1
  Negative := λ (a : ℤ) => NonzeroWithSign a (-1)
  Nonzero := Nonzero
}

/--
An integer is positive iff it has sign `1` in signed-magnitude form.

**Property intuition**: Integers in signed-magnitude form are the product of an
integer sign and a positive natural number magnitude. Multiplying a positive
natural number by the integer `1` should produce a positive integer.

**Proof intuition**: Trivial because `Positive` is defined as this property.
-/
theorem positive_iff_sign_pos1 {a : ℤ} : Positive a ↔ NonzeroWithSign a 1 :=
  Iff.intro id id

/--
An integer is negative iff it has sign `-1` in signed-magnitude form.

**Property intuition**: Integers in signed-magnitude form are the product of an
integer sign and a positive natural number magnitude. Multiplying a positive
natural number by the integer `-1` should produce a negative integer.

**Proof intuition**: Trivial because `Negative` is defined as this property.
-/
theorem negative_iff_sign_neg1 {a : ℤ} : Negative a ↔ NonzeroWithSign a (-1) :=
  Iff.intro id id

/--
The `Signed.Nonzero` predicate is equivalent to the `Integer.Nonzero` class.

**Intuition**: Defined to be the case in `signed_ops`.
-/
theorem nonzero_iff_nonzero_impl
    {a : ℤ} : Signed.Nonzero a ↔ Integer.Nonzero a
    :=
  Iff.intro id id

end Lean4Axiomatic.Integer.Impl.Generic


import ring_theory.principal_ideal_domain ring_theory.matrix_algebra
import tactic

open_locale matrix

variables {m n o p R : Type*} [fintype m] [fintype n] [fintype o] [fintype p]

/-- A matrix with added rows is still a matrix over the same type.  -/
def matrix.reindex_or_zero [has_zero R] (A : matrix m n R) (fm : m ↪ o) (fn : n ↪ p)
  : matrix o p R := sorry

-- call this something different
/-- A smith normal form is a diagonal that doesn't have to be square and can have diagonal zeroes -/
def smith_normal [has_zero R] {r : ℕ} (a : fin r → R)
  (fm : fin r ↪ m) (fn : fin r ↪ n) : matrix m n R :=
(matrix.diagonal a).reindex_or_zero fm fn

variables [integral_domain R] [is_principal_ideal_ring R]

/-- Every matrix over a pid has a smith normal form. Lemma 3.8 -/
lemma pid_matrix_has_smith [decidable_eq n] [decidable_eq m] (A : matrix m n R) :
  ∃ (S : units (matrix m m R)) (T : units (matrix n n R))
    {r : ℕ} (a : fin r → R) (ha : sorry) -- the dvd condition
    (fm : fin r ↪ m)
    (fn : fin r ↪ n),
      ((S : matrix m m R) ⬝ A ⬝ (T : matrix n n R)) = smith_normal a fm fn :=
begin
  sorry
end

variables (r : ℕ) (i j : fin r) (a : fin r → R)

#exit

#check i ≤ j → (a i | a j)