import geometry.tarski_1
open classical set
namespace Euclidean_plane
variables {point : Type} [Euclidean_plane point]

local attribute [instance, priority 0] prop_decidable

theorem six9 {a b p : point} : b ∈ ray p a → ray p a = ray p b :=
begin
intro h,
ext,
split,
  intro h1,
  cases h with h h2,
  cases h2 with h2 h3,
  cases h1 with h1 h4,
  cases h4 with h4 h5,
  split,
    exact h2,
  split,
    exact h4,
  cases h3,
    cases h5,
      exact five1 h.symm h3 h5,
    right,
    exact three6b h5 h3,
  cases h5,
    left,
    exact three6b h3 h5,
  exact five3 h3 h5,
intro h1,
cases h with h h2,
cases h2 with h2 h3,
cases h1 with h1 h4,
cases h4 with h4 h5,
split,
  exact h,
split,
exact h4,
cases h3,
  cases h5,
    left,
    exact three6b h3 h5,
  exact five3 h3 h5,
cases h5,
  exact five1 h2.symm h3 h5,
right,
exact three6b h5 h3
end

theorem six10 {a b c p q r : point} : sided a b c → sided p q r → eqd a b p q → eqd a c p r → cong a b c p q r :=
begin
intro h,
revert p q r,
wlog h4 : distle a b a c,
    exact (five10 a b a c),
  introv h1 h2 h3,
  repeat {split};
  try {assumption},
  have h5 : distle p q p r,
    exact five6 h4 h2 h3,
  have h6 : B a b c,
    exact (six12 h).1 h4,
  have h7 : B p q r,
    exact (six12 h1).1 h5,
  exact (four3 h6.symm h7.symm h3.flip h2.flip).flip,
have h1 := this h.symm a_1.symm a_3 a_2,
repeat {split},
    exact h1.2.2,
  exact h1.2.1.flip,
exact h1.1
end

theorem six10a {a b c a' b' c' : point} : sided a b c → cong a b c a' b' c' → sided a' b' c' :=
begin
intro h,
revert a' b' c',
wlog h1 : B a b c := (h.2.2) using b c,
  introv h2,
  split,
    intro h_1,
    subst b',
    exact h.1.symm (id_eqd h2.1),
  split,
    intro h_1,
    subst c',
    exact h.2.1.symm (id_eqd h2.2.2),
  exact or.inl (four6 h1 h2),
apply (this h.symm _).symm,
exact ⟨a_1.2.2, a_1.2.1.flip, a_1.1⟩
end

def l (a b : point) : set point := {x | col a b x}

class line (k : set point) : Prop :=
(hyp : ∃ a b, a ≠ b ∧ k = l a b)

lemma six14 {a b : point} (h : a ≠ b) : line (l a b) := ⟨⟨a, b, ⟨h, rfl⟩⟩⟩

theorem six15 {p q r : point} : p ≠ q → p ≠ r → B q p r → l p q = ray p q ∪ {p} ∪ ray p r :=
begin
intros h h1 h2,
ext,
split,
  intro h3,
  cases em (x = p),
    left, right,
    rw h_1,
    simp,
  cases h3,
    left, left,
    split,
      exact h.symm,
    split,
      exact h_1,
    left,
    exact h3,
    cases h3,
    left, left,
    split,
      exact h.symm,
    split,
      exact h_1,
    right,
    exact h3.symm,
  right,
  split,
    exact h1.symm,
  split,
    exact h_1,
  exact five2 h.symm h2 h3.symm,
intro h3,
cases h3,
  cases h3,
    cases h3 with h3 h4,
    cases h4 with h4 h5,
    cases h5,
      left,
      exact h5,
    right, left,
    exact h5.symm,
  have : x = p,
    simpa using h3,
  right, left,
  rw this,
  exact three1 q p,
cases h3 with h3 h4,
cases h4 with h4 h5,
right, right,
cases h5,
  exact (three7b h2 h5 h1).symm,
exact (three5a h2 h5).symm
end

theorem six16 {p q r : point} : p ≠ q → p ≠ r → r ∈ l p q → l p q = l p r :=
begin
intros h h1 h2,
ext,
split,
  intro h3,
  exact five4 h h2 h3,
intro h3,
have : col p r q,
  exact (four11 h2).1,
exact five4 h1 this h3
end

theorem six16a {p q r : point} : sided p q r → l p q = l p r :=
λ h, six16 h.1.symm h.2.1.symm (four11 (six4.1 h).1).2.1

theorem six17 (p q : point) : l p q = l q p :=
ext (λ x, ⟨λ h, (four11 h).2.1, λ h, (four11 h).2.1⟩)

theorem line.symm {a b : point} (h : line (l a b)) : line (l b a) := (six17 a b) ▸ h

@[simp] theorem six17a (p q : point) : p ∈ l p q := (four11 (four12 p q)).1

@[simp] theorem six17b (p q : point) : q ∈ l p q := (four11 (four12 q p)).2.2.2.1

theorem six18 {a b : point} {L : set point} : line L → a ≠ b → a ∈ L → b ∈ L → L = l a b :=
begin
intros h h1 h2 h3,
rcases h with ⟨p, q, hq⟩,
rw hq.2 at *,
cases em (a = p),
  rw h at *,
  exact six16 hq.1 h1 h3,
have ha : l p q = l p a,
  exact six16 hq.1 (ne.symm h) h2,
rw ha at *,
rw six17 p a at *,
exact six16 h h1 h3
end

theorem six18a {a p q : point} : a ∉ l p q → a ≠ p ∧ a ≠ q :=
begin
intro h,
split,
  intro h1,
  subst a,
  apply h,
  simp,
intro h1,
subst a,
simpa using h
end

theorem six19 {a b : point} : a ≠ b → ∃! L : set point, line L ∧ a ∈ L ∧ b ∈ L :=
λ h, ⟨l a b, ⟨six14 h, six17a a b, six17b a b⟩, λ Y hy, six18 hy.1 h hy.2.1 hy.2.2⟩

theorem six20 {a b c : point} {A : set point} : line A → a ∈ A → b ∈ A → a ≠ b → col a b c → c ∈ A :=
begin
intros h h1 h2 h3 h4,
suffices : A = l a b,
  subst A,
  exact h4,
exact six18 h h3 h1 h2
end

theorem six21 {a b : point} {A B : set point} : a ≠ b → line A → line B → a ∈ A → a ∈ B → b ∈ A → b ∈ B → A = B :=
begin
intros h h1 h2 h3 h4 h5 h6,
apply unique_of_exists_unique (six19 h),
  repeat {split, assumption},
  assumption,
assumption
end

def is (x : point) (A B : set point) : Prop := line A ∧ line B ∧ A ≠ B ∧ x ∈ A ∧ x ∈ B

theorem is.symm {x : point} (A B : set point) : is x A B → is x B A :=
λ h, ⟨h.2.1, h.1, h.2.2.1.symm, h.2.2.2.2, h.2.2.2.1⟩

theorem six21a {x y : point} {A B : set point} : line A → line B → A ≠ B → x ∈ A → x ∈ B → y ∈ A → y ∈ B → x = y :=
λ h h1 h2 h3 h4 h5 h6, classical.by_contradiction (λ h_1, h2 (six21 h_1 h h1 h3 h4 h5 h6))

theorem six21b {x y : point} {A B : set point} : is x A B → x ≠ y → y ∈ A → y ∉ B :=
λ h h1 h2 h3, h1 (six21a h.1 h.2.1 h.2.2.1 h.2.2.2.1 h.2.2.2.2 h2 h3)

theorem six22 {x : point} {A : set point} : line A → x ∈ A → ∃ y, x ≠ y ∧ A = l x y :=
begin
intros h h1,
rcases h with ⟨u, v, h⟩,
by_cases h_1 : x = u,
  subst u,
  exact ⟨v, h⟩,
rw h.2 at *,
exact ⟨u, h_1, six18 (six14 h.1) h_1 h1 (six17a u v)⟩
end

theorem six23 {a b c : point} : col a b c ↔ ∃ (L : set point), line L ∧ a ∈ L ∧ b ∈ L ∧ c ∈ L :=
begin
split,
  intro h,
  cases em (a = b),
    rw h_1 at *,
    cases em (b = c),
      rw h_2 at *,
      cases three14 c c with p hp,
      existsi l c p,
      split,
        exact six14 hp.2,
      simp,
    existsi l b c,
    split,
      exact six14 h_2,
    simp,
  existsi l a b,
  split,
    exact six14 h_1,
  simpa using h,
intro h,
cases h with L h,
cases em (a = b),
  rw h_1,
  exact four12 b c,
have : L = l a b,
  exact six18 h.1 h_1 h.2.1 h.2.2.1,
rw this at *,
exact h.2.2.2
end

theorem six24 : ¬col (P1 : point) P2 P3 :=
begin
have h := two_dim point,
intro h1,
cases h1,
  exact h.1 h1,
exact h1.elim h.2.1 h.2.2
end

theorem six25 {a b : point} : a ≠ b → ∃ c, ¬col a b c :=
begin
intro h1,
by_contradiction h2,
rw not_exists at h2,
simp at h2,
apply @six24 point,
apply six23.2,
exact ⟨l a b, six14 h1, h2 P1, h2 P2, h2 P3⟩
end

lemma six13a (a : point) : ¬line (l a a) :=
begin
intro h,
rcases h with ⟨u, v, h⟩,
cases six25 h.1 with r hr,
apply hr,
suffices : r ∈ l a a,
  rw h.2 at this,
  exact this,
left,
exact three3 a r
end

lemma six13 {a b : point} : line (l a b) → a ≠ b :=
begin
intros h h1,
subst b,
exact (six13a a) h
end

def tri (a b c : point) : Prop := a ≠ b ∧ b ≠ c ∧ a ≠ c

theorem six26 {a b c : point} : ¬col a b c → tri a b c :=
begin
intro h,
split,
  intro h,
  rw h at *,
  exact h (four12 b c),
split,
  intro h,
  rw h at *,
  exact h (four11 (four12 c a)).2.2.2.1,
intro h,
rw h at *,
exact h (four11 (four12 c b)).1
end

theorem six27 {a b c : point} {A : set point} : line A → a ∈ A → c ∈ A → B a b c → b ∈ A :=
begin
intros h h1 h2 h3,
cases em (a = c),
  rw h_1 at h3,
  have : c = b,
    exact bet_same h3,
  rwa this at h2,
have h4 := six18 h h_1 h1 h2,
rw h4,
right, left,
exact h3.symm
end

theorem six28 {a b c :point} : ¬col a b c → is a (l a b) (l a c) :=
begin
intro h,
split,
  exact six14 (six26 h).1,
split,
  exact six14 (six26 h).2.2,
split,
  intro h1,
  apply h,
  show c ∈ l a b,
  rw h1,
  simp,
simp
end

-- middle points

def M (a m b : point) : Prop := B a m b ∧ eqd m a m b

theorem M.symm {a b m : point} : M a m b → M b m a :=
λ h, ⟨h.1.symm, h.2.symm⟩

theorem seven3 {a m : point} : M a m a ↔ a = m :=
begin
split,
  intro h,
  exact bet_same h.1,
intro h,
rw h at *,
split,
  exact three1 m m,
exact eqd.refl m m
end

theorem seven4 {a m p q : point} : M a m p → M a m q → p = q :=
begin
intros h h1,
by_cases h_1 : a = m,
  subst m,
  exact (id_eqd h.2.symm).symm.trans (id_eqd h1.2.symm),
exact two12 h_1 h.1 h.2.symm h1.1 h1.2.symm
end

def S [decidable_eq point] (a p : point) : point :=
if a = p then a else (seg_cons a a p p).1

theorem seven5 (a p : point) : M p a (S a p) :=
begin
unfold S,
by_cases h : a = p,
  subst h,
  rw if_pos rfl,
  exact ⟨three1 a a, eqd.refl a a⟩,
rw if_neg h,
rcases seg_cons a a p p with ⟨q, hq⟩,
exact ⟨hq.1, hq.2.symm⟩
end

theorem seven6 {a p q : point} : M p a q → q = S a p :=
begin
intro h,
exact seven4 h (seven5 a p)
end

@[simp] theorem seven7 (a p : point) : S a (S a p) = p :=
begin
cases seven5 a p with h1 h2,
generalize hq : S a p = q,
rw hq at *,
cases seven5 a q with h3 h4,
generalize hr : S a q = r,
rw hr at *,
cases em (q = a),
  rw h at *,
  have ha : a = r,
    exact id_eqd h4.symm,
  rw ha at *,
  exact id_eqd h2,
exact two12 h h3 (eqd.trans h2 h4).symm h1.symm (eqd.refl a p)
end

theorem seven8 (a p : point) : ∃! q, S a q = p :=
begin
existsi S a p,
split,
  exact seven7 a p,
intros q h,
rw ←h,
exact (seven7 a q).symm
end

theorem seven9 {a p q : point} : S a p = S a q → p = q :=
begin
intro h,
apply unique_of_exists_unique (seven8 a (S a p)),
  simp,
rw ←h
end

theorem seven9a {a b : point} (p : point) : a ≠ b → S p a ≠ S p b :=
begin
intros h h1,
exact h (seven9 h1)
end

theorem seven10 {a p : point} : S a p = p ↔ a = p :=
begin
split,
  intro h,
  have : M p a (S a p),
    exact seven5 a p,
  rw h at *,
  exact (seven3.1 this).symm,
intro h,
subst p,
cases seven5 a a with h1 h2,
exact id_eqd h2.symm.flip
end

@[simp] theorem seven11 (a : point) : S a a = a :=
seven10.2 rfl

theorem seven12a {a p : point} : p ≠ a → S a p ≠ a :=
λ h h1, h (seven9 (h1.trans (seven11 a).symm))

theorem seven12b {a p : point} : p ≠ a → S a p ≠ p :=
λ h h1, h (seven10.1 h1).symm

theorem seven13 (a p q : point) : eqd p q (S a p) (S a q) :=
begin
have hp : M p a (S a p),
  exact seven5 a p,
have hq : M q a (S a q),
  exact seven5 a q,
generalize hp' : S a p = p',
generalize hq' : S a q = q',
rw hp' at *,
rw hq' at *,
cases em (p = a),
  have : S a p = p,
    exact seven10.2 h.symm,
  rw h at *,
  rw this at *,
  rw ←hp' at *,
  exact hq.2,
cases seg_cons p q a p' with x hx,
cases seg_cons p' q a x with x' hx',
cases seg_cons q p a q' with y hy,
cases seg_cons q' p a y with y' hy',
have h1 : B a p x,
  exact three6a hp.1.symm hx.1,
have h2 : B p p' x',
  exact three6a hx.1.symm hx'.1,
have h3 : B a p' x',
  exact three6a hp.1 h2,
have h4 : B a q y,
  exact three6a hq.1.symm hy.1,
have h5 : B q q' y',
  exact three6a hy.1.symm hy'.1,
have h6 : B a q' y',
  exact three6a hq.1 h5,
have h7 : eqd a x y a,
  exact two11 h1 h4.symm hy.2.symm.flip hx.2,
have h8 : eqd a x a x',
  exact two11 h1 h3 hp.2 (eqd.trans hx.2 hx'.2.symm),
have h9 : eqd a x y' a,
  exact two11 h1 h6.symm hy'.2.symm.flip (eqd.trans hx.2 hq.2.flip),
have h10 : B x a p',
      exact three5b hx.1.symm hp.1,
have h11 : B y a q',
      exact three5b hy.1.symm hq.1,
have h12 : afs x a x' y' y' a y x,
  repeat {split},
    exact three6b h10 hx'.1,
    exact (three6b h11 hy'.1).symm,
    exact two4 h9,
    exact two5 (eqd.trans h8.symm h7),
    exact two5 (eqd.refl x y'),
  exact two4 h9.symm,
have h13 : x ≠ a,
  intro h_1,
  rw h_1 at h1,
  exact h (bet_same h1).symm,
have h14 : eqd x' y' y x,
  exact afive_seg h12 h13,
have h15 : ifs y q a x y' q' a x',
  repeat {split},
    exact h4.symm,
    exact h6.symm,
    exact eqd.trans h7.symm h9,
    exact hq.2.flip,
    exact two5 h14.symm,
  exact h8,
have h16 := four2 h15,
have h17 : ifs x p a q x' p' a q',
  repeat {split},
    exact h1.symm,
    exact h3.symm,
    exact h8.flip,
    exact hp.2.flip,
    exact h16.flip,
  exact hq.2,
exact four2 h17
end

theorem seven15 (a : point) {p q r : point} : B p q r ↔ B (S a p) (S a q) (S a r) :=
begin
split,
  intro h,
  apply four6 h,
  repeat {split};
  exact seven13 a _ _,
intro h,
rw ←(seven7 a p),
rw ←(seven7 a q),
rw ←(seven7 a r),
apply four6 h,
repeat {split};
exact seven13 a _ _,
end

theorem seven16 (a : point) {p q r s : point}: eqd p q r s ↔ eqd (S a p) (S a q) (S a r) (S a s) :=
begin
split,
  intro h,
  exact (seven13 a p q).symm.trans (h.trans (seven13 a r s)),
intro h,
have h1 := eqd.trans (seven13 a (S a p) (S a q)).symm (eqd.trans h (seven13 a (S a r) (S a s))),
simpa using h1
end

theorem seven16a (a : point) {p q r : point} : cong p q r (S a p) (S a q) (S a r) :=
begin
repeat {split};
exact seven13 a _ _
end

theorem seven14 (a : point) {p q r : point} : M p q r ↔ M (S a p) (S a q) (S a r) :=
begin
split,
  intro h,
  cases h with h h1,
  split,
    exact (seven15 a).1 h,
  exact (seven16 a).1 h1,
intro h,
cases h with h h1,
split,
  exact (seven15 a).2 h,
exact (seven16 a).2 h1
end

theorem S_of_col {p q r : point} (a : point) : col p q r ↔ col (S a p) (S a q) (S a r) :=
begin
split,
  intro h,
  unfold col,
  repeat {cases h};
  simp [(seven15 a).1 h],
intro h,
unfold col,
repeat {cases h};
simp [(seven15 a).2 h]
end

theorem seven17 {a b p q : point} : M p a q → M p b q → a = b :=
begin
intros h h1,
have h2 := seven6 h,
have h3 := seven13 a q b,
have h4 := seven7 a p,
rwa ←h2 at h4,
rw h4 at h3,
have h6 : eqd p b p (S a b),
  exact eqd.trans h1.2.flip h3,
have h7 := seven13 a p b,
rw ←h2 at h7,
have h8 : eqd q b q (S a b),
  exact eqd.trans h1.2.symm.flip h7,
have h9 : b = (S a b),
  exact four19 h1.1 h6 h8.flip,
exact (seven10.1 h9.symm)
end

theorem seven18 {a b p : point} : S a p = S b p → a = b :=
begin
intro h,
generalize h1 : S a p = q,
have h2 := seven5 a p,
rw h1 at h2,
have h3 := seven5 b p,
rw (eq.trans h.symm h1) at h3,
exact seven17 h2 h3
end

theorem seven18a {a b : point} (p : point) : a ≠ b → S a p ≠ S b p :=
λ h h1, h (seven18 h1)

theorem seven19 {a b p : point} : S a (S b p) = S b (S a p) ↔ a = b :=
begin
split,
  intro h,
  generalize h1 : S a p = q,
  have h2 := seven5 a p,
  rw h1 at *,
  have h3 := seven5 a (S b p),
  rw h at h3,
  cases h3 with h3 h4,
  have h5 := (seven15 b).1 h3,
  have h6 := (seven16 b).1 h4,
  rw seven7 b p at *,
  rw seven7 b q at *,
  have h7 : M p (S b a) q,
    split;
    assumption,
  have h8 : a = (S b a),
    exact seven17 h2 h7,
  exact (seven10.1 h8.symm).symm,
intro h,
rw h
end

theorem seven20 {a m b : point} : col a m b → eqd m a m b → a = b ∨ M a m b :=
begin
intros h h1,
cases h,
  right,
  split;
  assumption,
cases h,
  left,
  have h2 := three3 b m,
  have h3 : eqd a b b b,
    exact four3 h.symm h2 h1.flip (eqd.refl b m),
  exact id_eqd h3,
left,
have h2 := three3 a m,
have h3 : eqd b a a a,
  exact four3 h h2 h1.symm.flip (eqd.refl a m),
exact id_eqd h3.flip
end

theorem seven21 {a b c d p : point} : ¬col a b c → b ≠ d → eqd a b c d → eqd b c d a →
col a p c → col b p d → M a p c ∧ M b p d :=
begin
intros h h1 h2 h3 h4 h5,
cases four14 (four11 h5).1 (two5 (eqd.refl b d)) with q hq,
have h6 := four13 (four11 h5).1 hq,
have h7 : fs b d p a d b q c,
  split,
    exact (four11 h5).1,
  split,
    exact hq,
  split,
    exact h2.flip,
  exact h3.symm,
have h8 : fs b d p c d b q a,
  split,
    exact (four11 h5).1,
  split,
    exact hq,
  split,
    exact h3,
  exact h2.symm.flip,
have h9 := four16 h7 h1,
have h10 := four16 h8 h1,
have h11 : cong a p c c q a,
split,
  exact h9.flip,
split,
  exact h10,
exact two5 (eqd.refl a c),
have h12 : p = q,
  suffices : l a c ≠ l b d,
    apply six21a (six14 (six26 h).2.2) (six14 h1) this (four11 h4).1 (four11 h5).1 (four11 (four13 h4 h11)).2.2.2.1 (four11 h6).2.1,
  intro h_1,
  suffices : b ∉ l a c,
    simpa [h_1, (six17a b d)] using this,
  exact (four10 h).1,
subst q,
split,
  cases seven20 h4 h9,
    exact ((six26 h).2.2 h_1).elim,
  assumption,
cases seven20 h5 (hq.2.2).flip,
  contradiction,
assumption
end

def hourglass (a b c p q m n : point) : Prop := B a c p ∧ B b c q ∧ eqd c a c b ∧ eqd c p c q ∧
M a m b ∧ M p n q

lemma seven23 {a b c p q m n : point} : hourglass a b c p q m n → distle c a c q → B m c n :=
begin
intros h h_1,
cases h with h h1,
cases h1 with h1 h2,
cases h2 with h2 h3,
cases h3 with h3 h4,
cases h4 with h4 h5,
cases em (p = c),
  rw h_2 at *,
  have h_3 : q = c,
    exact id_eqd h3.symm.flip,
  rw h_3 at *,
  have h_4 : c = n,
    exact seven3.1 h5,
  rw h_4 at *,
  exact three1 m n,
generalize h6 : S c p = a',
generalize h7 : S c q = b',
generalize h8 : S c n = m',
have h9 : M a' m' b',
  have h_3 := (seven14 c).1 h5,
  rwa [h6, h7, h8] at h_3,
have h10 : M p c a',
  have h10 := seven5 c p,
  rwa h6 at h10,
have h11 : M q c b',
  have h11 := seven5 c q,
  rwa h7 at h11,
have h12 : M n c m',
  have h12 := seven5 c n,
  rwa h8 at h12,
have h13 : distle c a c a',
  exact five6 h_1 (eqd.refl c a) (eqd.trans h3.symm h10.2),
cases em (a = c),
  rw h_3 at *,
  have : b = c,
    exact id_eqd h2.symm.flip,
  rw this at *,
  have : c = m,
    exact seven3.1 h4,
  rw this,
  exact three3 m n,
have h_4 : a' ≠ c,
  intro h_4,
  rw h_4 at *,
  have : eqd c a c c,
    exact five9 h13 (five11 c c a),
  exact h_3 (id_eqd this.flip),
have h14 : sided c a a',
  apply six3.2,
  split,
    exact h_3,
  split,
    exact h_4,
  existsi p,
  split,
    exact h_2,
  split,
    exact h,
  exact h10.1.symm,
have h15 : B c a a',
  exact (six12 h14).1 h13,
have h16 : distle c b c b',
  exact five6 h_1 h2 h11.2,
have h_5 : q ≠ c,
  intro h_5,
  rw h_5 at *,
  exact h_2 (id_eqd h3.flip),
cases em (b = c),
  rw h_6 at *,
  have : a = c,
   exact id_eqd h2.flip,
  rw this at *,
  have : c = m,
    exact seven3.1 h4,
  rw this,
  exact three3 m n,
have h_7 : b' ≠ c,
  intro h_7,
  rw h_7 at *,
  have : eqd c b c c,
    exact five9 h16 (five11 c c b),
  exact h_6 (id_eqd this.flip),
have h17 : sided c b b',
  apply six3.2,
  split,
    exact h_6,
  split,
    exact h_7,
  existsi q,
  split,
    exact h_5,
  split,
    exact h1,
  exact h11.1.symm,
have h18 : B c b b',
  exact (six12 h17).1 h16,
cases three17 h15.symm h18.symm h9.1 with r hr,
have h19 : B r c n,
  exact three6a hr.1 h12.1.symm,
suffices : r = m,
  rwa this at h19,
have h20 : ifs a' a c m' b' b c m',
  repeat {split},
    exact h15.symm,
    exact h18.symm,
    exact eqd.trans h10.2.symm.flip (eqd.trans h3.flip h11.2.flip),
    exact h2.flip,
    exact h9.2.flip,
  exact eqd.refl c m',
have h20 := four2 h20,
have h21 : col m' c r,
  right, left,
  exact hr.1.symm,
have h22 : eqd r a r b,
  cases em (m' = c),
    rw h_8 at *,
    have : c = r,
      exact bet_same hr.1,
    rw this at *,
    exact h20.flip,
  exact four17 h_8 h21 h20.flip h2,
have h23 : M a r b,
  split,
    exact hr.2,
  exact h22,
exact seven17 h23 h4
end

theorem seven22 {a b c p q m n : point} : hourglass a b c p q m n → B m c n :=
begin
intro h,
cases five10 c a c q,
  exact seven23 h h_1,
cases h with h h1,
cases h1 with h1 h2,
cases h2 with h2 h3,
cases h3 with h3 h4,
cases h4 with h4 h5,
have h6 : hourglass q p c b a n m,
  repeat {split},
    exact h1.symm,
    exact h.symm,
    exact h3.symm,
    exact h2.symm,
    exact h5.1.symm,
    exact h5.2.symm,
    exact h4.1.symm,
  exact h4.2.symm,
exact (seven23 h6 h_1).symm
end

theorem seven24 {a p : point} {A : set point} : line A → a ∈ A → (p ∈ A ↔ (S a p) ∈ A) :=
begin
intros h h1,
split,
  intro h2,
  cases em (a = p),
    have h3 := seven10.2 h_1,
    rwa h3,
  have h3 := six18 h h_1 h1 h2,
  rw h3,
  have h4 := seven5 a p,
  right, right,
  exact h4.1.symm,
intro h2,
cases em (a = (S a p)),
  have h3 := seven10.2 h_1,
  rw ←seven7 a p,
  rwa h3,
have h3 := six18 h h_1 h1 h2,
rw h3,
have h4 := seven5 a p,
right, right,
exact h4.1
end

theorem seven25 {a b c : point} : eqd c a c b → ∃ x, M a x b :=
begin
intro h,
cases em (col a c b),
  cases seven20 h_1 h,
    rw h_2 at *,
    existsi b,
    split,
      exact three1 b b,
    exact eqd.refl b b,
  constructor, exact h_2,
cases three14 c a with p hp,
cases seg_cons b a p c with q hq,
cases pasch hp.1.symm hq.1.symm with r hr,
cases pasch hp.1 hr.2 with x hx,
existsi x,
suffices : eqd x a x b,
  split,
    exact hx.1,
  exact this,
suffices : eqd r a r b,
  have h1 : col c r x,
    right, left,
    exact hx.2,
  cases em (c = r),
    rw h_2 at hx,
    have : r = x,
      exact bet_same hx.2,
    rw this at *,
    exact this,
  exact four17 h_2 h1 h this,
have h1 : afs c a p b c b q a,
  repeat {split},
    exact hp.1,
    exact hq.1,
    exact h,
    exact hq.2.symm,
    exact h.symm,
  exact two5 (eqd.refl a b),
have h2 : eqd p b q a,
  exact afive_seg h1 (six26 h_1).1.symm,
cases four5 hr.2 h2.flip with r' hr',
have h3 : ifs b r p a a r' q b,
  repeat {split},
    exact hr.2,
    exact hr'.1,
    exact hr'.2.2.2,
    exact hr'.2.2.1,
    exact two4 (eqd.refl a b),
  exact hq.2.symm.flip,
have h4 : ifs b r p q a r' q p,
  repeat {split},
    exact hr.2,
    exact hr'.1,
    exact hr'.2.2.2,
    exact hr'.2.2.1,
    exact hq.2,
  exact two5 (eqd.refl p q),
have h5 := four2 h3,
have h6 := four2 h4,
have h7 : cong a r q b r' p,
  split,
    exact h5.flip,
  split,
    exact h6,
  exact h2.symm.flip,
have h8 : col a r q,
  left, exact hr.1,
have h9 := four13 h8 h7,
have h_2 : a ≠ q,
  intro h_2,
  rw ←h_2 at *,
  have : col a c b,
    right, left,
    exact hq.1,
  exact h_1 this,
have h_3 : b ≠ p,
  intro h_3,
  rw ←h_3 at *,
  have : col a c b,
    right, right,
    exact hp.1.symm,
  exact h_1 this,
have h10 : l a q ≠ l b p,
  intro h_4,
  suffices : a ∈ l b p,
    have h_6 : col a p c,
      right, right,
      exact hp.1,
    exact h_1 (five4 hp.2 h_6 (four11 this).2.2.2.2),
  rw ←h_4,
  simp,
have h11 : col b p r,
  right, left,
  exact hr.2.symm,
  have h12 : col a q r',
    right, left,
    exact hr'.1.symm,
suffices : r' = r,
  rwa this at h5,
exact six21a (six14 h_2) (six14 h_3) h10 h12 (four11 h9).1 (four11 h8).1 h11
end

end Euclidean_plane


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura

! This file was ported from Lean 3 source module init.ite_simp
! leanprover-community/mathlib commit 4a03bdeb31b3688c31d02d7ff8e0ff2e5d6174db
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
prelude
import Leanbin.Init.Data.Bool.Default

/-!
# Simplification lemmas for ite.

We don't prove them at logic.lean because it is easier to prove them using
the tactic framework.
-/

@[simp]
theorem if_true_right_eq_or (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then q else True) = (¬p ∨ q) := by by_cases p <;> simp [h]
#align if_true_right_eq_or if_true_right_eq_or

@[simp]
theorem if_true_left_eq_or (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then True else q) = (p ∨ q) := by by_cases p <;> simp [h]
#align if_true_left_eq_or if_true_left_eq_or

@[simp]
theorem if_false_right_eq_and (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then q else False) = (p ∧ q) := by by_cases p <;> simp [h]
#align if_false_right_eq_and if_false_right_eq_and

@[simp]
theorem if_false_left_eq_and (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then False else q) = (¬p ∧ q) := by by_cases p <;> simp [h]
#align if_false_left_eq_and if_false_left_eq_and


-- import data.set
import tactic
import data.set
import data.real.basic

-- dEAduction imports
import structures2
import utils
import compute

-- General principles :
-- Type should be defined as parameters, in order to be implicit everywhere
-- other parameters are implicit in definitions, i.e. defined using '{}' (e.g. {A : set X} )
-- but explicit everywhere else, i.e. defined using '()' (e.g. (A : set X) )
-- each definition must be an iff statement (since it will be called with 'rw' or 'symp_rw')

-- no Magic button ("compute")
/- dEAduction
Title
    Tautologies (tutoriel)
Author
    Frédéric Le Roux
Institution
    Université du Monde
Description
    Ce fichier contient exercices très simples qui peuvent servir à tester ou illustrer
    les effets des différents boutons logiques.
DefaultAvailableProof
    NONE
DefaultAvailableMagic
    Assumption
-/

-- logic names ['and', 'or', 'negate', 'implies', 'iff', 'forall', 'exists']
-- proofs names ['proof_methods', 'new_object', 'apply']
-- magic names ['compute', 'assumption']

local attribute [instance] classical.prop_decidable
---------------------------------------------
-- global parameters = implicit variables --
---------------------------------------------
section course
variables (P Q R: Prop) -- NOT global
notation [parsing_only] P ` \and ` Q := P ∧ Q
notation [parsing_only]  P ` \or ` Q := P ∨ Q
notation [parsing_only]  ` \not ` P := ¬ P
notation [parsing_only]  P ` \implies ` Q := P → Q
notation [parsing_only]  P ` \iff ` Q := P ↔ Q

------------------
-- COURSE TITLE --
------------------
-- namespace Tautologies

lemma exercise.implication (H: P → Q):
P → Q
:=
/- dEAduction
PrettyName
    Implication
Description
    Utilisation et démonstration d'une implication.
AvailableLogic
    implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.et (H: P ∧ Q):
P ∧ Q
:=
/- dEAduction
PrettyName
    Conjonction
Description
    Utilisation et démonstration d'une conjonction.
AvailableLogic
    and
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_1 (H: P ∨ Q) (HP: P → R) (HQ: Q → R):
R
:=
/- dEAduction
PrettyName
    Disjonction I
Description
    Utilisation d'une disjonction.
AvailableLogic
    or implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_2 (H: P):
P ∨ Q
:=
/- dEAduction
PrettyName
    Disjonction II
Description
    Démonstration d'une disjonction.
AvailableLogic
    or
AvailableMagic
    assumption
-/
begin
    sorry
end

variable S: Prop

lemma exercise.ou_3 (H: P ∨ Q) (HP: P → R) (HQ: Q → S):
R ∨ S
:=
/- dEAduction
PrettyName
    Disjonction III
Description
    Utilisation et démonstration d'une disjonction.
AvailableLogic
    or implies
AvailableMagic
    assumption
-/
begin
    sorry
end

variables (X: Type)

lemma exercise.quel_que_soit (P Q: X → Prop) (H: ∀ x, P x) (H': ∀ x, (P x → Q x)):
∀ x, Q x
:=
/- dEAduction
PrettyName
    Quel que soit
Description
    Utilisation et démonstration d'une propriété universelle.
AvailableLogic
    forall implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.Existe_0 : (∃ x: ℝ, x >0) :=
/- dEAduction
PrettyName
    Existence (a)
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    exists
AvailableMagic
    assumption
-/
begin
    todo
end

lemma exercise.Existe_b : (∃ a b: ℝ, a < b) :=
/- dEAduction
PrettyName
    Existence (b)
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    exists
AvailableMagic
    assumption
-/
begin
    todo
end

lemma exercise.Existe_1 (P Q: X → Prop) (x: X) (H: P x) (H': (P x → Q x)):
∃ x, Q x
:=
/- dEAduction
PrettyName
    Existence I
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    forall exists implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.Existe_2 (P Q: X → Prop) (H: ∃ x, P x) (H': ∀ x, (P x → Q x)):
∃ x, Q x
:=
/- dEAduction
PrettyName
    Existence II
Description
    Utilisation et démonstration d'une propriété existentielle.
AvailableLogic
    forall exists implies
AvailableMagic
    assumption
-/
begin
    sorry
end

-- end Tautologies

end course


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import algebra.star.basic
import algebra.ring.pi
import algebra.module.pi

/-!
# `star` on pi types

We put a `has_star` structure on pi types that operates elementwise, such that it describes the
complex conjugation of vectors.
-/

universes u v w
variable {I : Type u}     -- The indexing type
variable {f : I → Type v} -- The family of types already equipped with instances

namespace pi

instance [Π i, has_star (f i)] : has_star (Π i, f i) :=
{ star := λ x i, star (x i) }

@[simp] lemma star_apply [Π i, has_star (f i)] (x : Π i, f i) (i : I) : star x i = star (x i) := rfl

lemma star_def [Π i, has_star (f i)] (x : Π i, f i) : star x = λ i, star (x i) := rfl

instance [Π i, has_involutive_star (f i)] : has_involutive_star (Π i, f i) :=
{ star_involutive := λ _, funext $ λ _, star_star _ }

instance [Π i, semigroup (f i)] [Π i, star_semigroup (f i)] : star_semigroup (Π i, f i) :=
{ star_mul := λ _ _, funext $ λ _, star_mul _ _ }

instance [Π i, add_monoid (f i)] [Π i, star_add_monoid (f i)] : star_add_monoid (Π i, f i) :=
{ star_add := λ _ _, funext $ λ _, star_add _ _ }

instance [Π i, non_unital_semiring (f i)] [Π i, star_ring (f i)] : star_ring (Π i, f i) :=
{ ..pi.star_add_monoid, ..(pi.star_semigroup : star_semigroup (Π i, f i)) }

instance {R : Type w}
  [Π i, has_scalar R (f i)] [has_star R] [Π i, has_star (f i)] [Π i, star_module R (f i)] :
  star_module R (Π i, f i) :=
{ star_smul := λ r x, funext $ λ i, star_smul r (x i) }

end pi


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

Additional theorems about the `vector` type.
-/
import data.vector data.list.basic data.sigma data.equiv.basic
       category.traversable

universes u
variables {n : ℕ}

namespace vector
variables {α : Type*}

attribute [simp] head_cons tail_cons

instance [inhabited α] : inhabited (vector α n) :=
⟨of_fn (λ _, default α)⟩

theorem to_list_injective : function.injective (@to_list α n) :=
subtype.val_injective

@[simp] theorem to_list_of_fn : ∀ {n} (f : fin n → α), to_list (of_fn f) = list.of_fn f
| 0     f := rfl
| (n+1) f := by rw [of_fn, list.of_fn_succ, to_list_cons, to_list_of_fn]

@[simp] theorem mk_to_list :
  ∀ (v : vector α n) h, (⟨to_list v, h⟩ : vector α n) = v
| ⟨l, h₁⟩ h₂ := rfl

theorem nth_eq_nth_le : ∀ (v : vector α n) (i),
  nth v i = v.to_list.nth_le i.1 (by rw to_list_length; exact i.2)
| ⟨l, h⟩ i := rfl

@[simp] theorem nth_of_fn {n} (f : fin n → α) (i) : nth (of_fn f) i = f i :=
by rw [nth_eq_nth_le, ← list.nth_le_of_fn f];
   congr; apply to_list_of_fn

@[simp] theorem of_fn_nth (v : vector α n) : of_fn (nth v) = v :=
begin
  rcases v with ⟨l, rfl⟩,
  apply to_list_injective,
  change nth ⟨l, eq.refl _⟩ with λ i, nth ⟨l, rfl⟩ i,
  simp [nth, list.of_fn_nth_le]
end

@[simp] theorem nth_tail : ∀ (v : vector α n.succ) (i : fin n),
  nth (tail v) i = nth v i.succ
| ⟨a::l, e⟩ ⟨i, h⟩ := by simp [nth_eq_nth_le]; refl

@[simp] theorem tail_of_fn {n : ℕ} (f : fin n.succ → α) :
  tail (of_fn f) = of_fn (λ i, f i.succ) :=
(of_fn_nth _).symm.trans $ by congr; funext i; simp

theorem head'_to_list : ∀ (v : vector α n.succ),
  (to_list v).head' = some (head v)
| ⟨a::l, e⟩ := rfl

def reverse (v : vector α n) : vector α n :=
⟨v.to_list.reverse, by simp⟩

@[simp] theorem nth_zero : ∀ (v : vector α n.succ), nth v 0 = head v
| ⟨a::l, e⟩ := rfl

@[simp] theorem head_of_fn
  {n : ℕ} (f : fin n.succ → α) : head (of_fn f) = f 0 :=
by rw [← nth_zero, nth_of_fn]

@[simp] theorem nth_cons_zero
  (a : α) (v : vector α n) : nth (a :: v) 0 = a :=
by simp [nth_zero]

@[simp] theorem nth_cons_succ
  (a : α) (v : vector α n) (i : fin n) : nth (a :: v) i.succ = nth v i :=
by rw [← nth_tail, tail_cons]

def m_of_fn {m} [monad m] {α : Type u} : ∀ {n}, (fin n → m α) → m (vector α n)
| 0     f := pure nil
| (n+1) f := do a ← f 0, v ← m_of_fn (λi, f i.succ), pure (a :: v)

theorem m_of_fn_pure {m} [monad m] [is_lawful_monad m] {α} :
  ∀ {n} (f : fin n → α), @m_of_fn m _ _ _ (λ i, pure (f i)) = pure (of_fn f)
| 0     f := rfl
| (n+1) f := by simp [m_of_fn, @m_of_fn_pure n, of_fn]

def mmap {m} [monad m] {α} {β : Type u} (f : α → m β) :
  ∀ {n}, vector α n → m (vector β n)
| _ ⟨[], rfl⟩   := pure nil
| _ ⟨a::l, rfl⟩ := do h' ← f a, t' ← mmap ⟨l, rfl⟩, pure (h' :: t')

@[simp] theorem mmap_nil {m} [monad m] {α β} (f : α → m β) :
  mmap f nil = pure nil := rfl

@[simp] theorem mmap_cons {m} [monad m] {α β} (f : α → m β) (a) :
  ∀ {n} (v : vector α n), mmap f (a::v) =
  do h' ← f a, t' ← mmap f v, pure (h' :: t')
| _ ⟨l, rfl⟩ := rfl

@[extensionality] theorem ext : ∀ {v w : vector α n}
  (h : ∀ m : fin n, vector.nth v m = vector.nth w m), v = w
| ⟨v, hv⟩ ⟨w, hw⟩ h := subtype.eq (list.ext_le (by rw [hv, hw])
  (λ m hm hn, h ⟨m, hv ▸ hm⟩))

instance zero_subsingleton : subsingleton (vector α 0) :=
⟨λ _ _, vector.ext (λ m, fin.elim0 m)⟩

def to_array : vector α n → array n α
| ⟨xs, h⟩ := cast (by rw h) xs.to_array

section insert_nth
variable {a : α}

def insert_nth (a : α) (i : fin (n+1)) (v : vector α n) : vector α (n+1) :=
⟨v.1.insert_nth i.1 a,
  begin
    rw [list.length_insert_nth, v.2],
    rw [v.2, ← nat.succ_le_succ_iff],
    exact i.2
  end⟩

lemma insert_nth_val {i : fin (n+1)} {v : vector α n} :
  (v.insert_nth a i).val = v.val.insert_nth i.1 a :=
rfl

@[simp] lemma remove_nth_val {i : fin n} :
  ∀{v : vector α n}, (remove_nth i v).val = v.val.remove_nth i.1
| ⟨l, hl⟩ := rfl

lemma remove_nth_insert_nth {v : vector α n} {i : fin (n+1)} : remove_nth i (insert_nth a i v) = v :=
subtype.eq $ list.remove_nth_insert_nth i.1 v.1

lemma remove_nth_insert_nth_ne {v : vector α (n+1)} :
  ∀{i j : fin (n+2)} (h : i ≠ j),
    remove_nth i (insert_nth a j v) = insert_nth a (i.pred_above j h.symm) (remove_nth (j.pred_above i h) v)
| ⟨i, hi⟩ ⟨j, hj⟩ ne :=
  begin
    have : i ≠ j := fin.vne_of_ne ne,
    refine subtype.eq _,
    dsimp [insert_nth, remove_nth, fin.pred_above, fin.cast_lt],
    rcases lt_trichotomy i j with h | h | h,
    { have h_nji : ¬ j < i := lt_asymm h,
      have j_pos : 0 < j := lt_of_le_of_lt (zero_le i) h,
      simp [h, h_nji, fin.lt_iff_val_lt_val],
      rw [show j.pred = j - 1, from rfl, list.insert_nth_remove_nth_of_ge, nat.sub_add_cancel j_pos],
      { rw [v.2], exact lt_of_lt_of_le h (nat.le_of_succ_le_succ hj) },
      { exact nat.le_sub_right_of_add_le h } },
    { exact (this h).elim },
    { have h_nij : ¬ i < j := lt_asymm h,
      have i_pos : 0 < i := lt_of_le_of_lt (zero_le j) h,
      simp [h, h_nij, fin.lt_iff_val_lt_val],
      rw [show i.pred = i - 1, from rfl, list.insert_nth_remove_nth_of_le, nat.sub_add_cancel i_pos],
      { show i - 1 + 1 ≤ v.val.length,
        rw [v.2, nat.sub_add_cancel i_pos],
        exact nat.le_of_lt_succ hi },
      { exact nat.le_sub_right_of_add_le h } }
  end

lemma insert_nth_comm (a b : α) (i j : fin (n+1)) (h : i ≤ j) :
  ∀(v : vector α n), (v.insert_nth a i).insert_nth b j.succ = (v.insert_nth b j).insert_nth a i.cast_succ
| ⟨l, hl⟩ :=
  begin
    refine subtype.eq _,
    simp [insert_nth_val, fin.succ_val, fin.cast_succ],
    apply list.insert_nth_comm,
    { assumption },
    { rw hl, exact nat.le_of_succ_le_succ j.2 }
  end

end insert_nth

end vector

namespace vector

section traverse

variables {F G : Type u → Type u}
variables [applicative F] [applicative G]

open applicative functor
open list (cons) nat

private def traverse_aux {α β : Type u} (f : α → F β) :
  Π (x : list α), F (vector β x.length)
| []      := pure vector.nil
| (x::xs) := vector.cons <$> f x <*> traverse_aux xs

protected def traverse {α β : Type u} (f : α → F β) : vector α n → F (vector β n)
| ⟨v, Hv⟩ := cast (by rw Hv) $ traverse_aux f v

variables [is_lawful_applicative F] [is_lawful_applicative G]
variables {α β γ : Type u}

@[simp] protected lemma traverse_def
  (f : α → F β) (x : α) : ∀ (xs : vector α n),
  (x :: xs).traverse f = cons <$> f x <*> xs.traverse f :=
by rintro ⟨xs, rfl⟩; refl

protected lemma id_traverse : ∀ (x : vector α n), x.traverse id.mk = x :=
begin
  rintro ⟨x, rfl⟩, dsimp [vector.traverse, cast],
  induction x with x xs IH, {refl},
  simp! [IH], refl
end

open function

protected lemma comp_traverse (f : β → F γ) (g : α → G β) : ∀ (x : vector α n),
  vector.traverse (comp.mk ∘ functor.map f ∘ g) x =
  comp.mk (vector.traverse f <$> vector.traverse g x) :=
by rintro ⟨x, rfl⟩; dsimp [vector.traverse, cast];
   induction x with x xs; simp! [cast, *] with functor_norm;
   [refl, simp [(∘)]]

protected lemma traverse_eq_map_id {α β} (f : α → β) : ∀ (x : vector α n),
  x.traverse (id.mk ∘ f) = id.mk (map f x) :=
by rintro ⟨x, rfl⟩; simp!;
   induction x; simp! * with functor_norm; refl

variable (η : applicative_transformation F G)

protected lemma naturality {α β : Type*}
  (f : α → F β) : ∀ (x : vector α n),
  η (x.traverse f) = x.traverse (@η _ ∘ f) :=
by rintro ⟨x, rfl⟩; simp! [cast];
   induction x with x xs IH; simp! * with functor_norm

end traverse

instance : traversable.{u} (flip vector n) :=
{ traverse := @vector.traverse n,
  map := λ α β, @vector.map.{u u} α β n }

instance : is_lawful_traversable.{u} (flip vector n) :=
{ id_traverse := @vector.id_traverse n,
  comp_traverse := @vector.comp_traverse n,
  traverse_eq_map_id := @vector.traverse_eq_map_id n,
  naturality := @vector.naturality n,
  id_map := by intros; cases x; simp! [(<$>)],
  comp_map := by intros; cases x; simp! [(<$>)] }

end vector


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

import data.set.pointwise.basic
import data.list.of_fn

/-!
# Pointwise operations with lists of sets

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

This file proves some lemmas about pointwise algebraic operations with lists of sets.
-/

namespace set

variables {F α β γ : Type*}
variables [monoid α] {s t : set α} {a : α} {m n : ℕ}

open_locale pointwise

@[to_additive] lemma mem_prod_list_of_fn {a : α} {s : fin n → set α} :
  a ∈ (list.of_fn s).prod ↔ ∃ f : (Π i : fin n, s i), (list.of_fn (λ i, (f i : α))).prod = a :=
begin
  induction n with n ih generalizing a,
  { simp_rw [list.of_fn_zero, list.prod_nil, fin.exists_fin_zero_pi, eq_comm, set.mem_one] },
  { simp_rw [list.of_fn_succ, list.prod_cons, fin.exists_fin_succ_pi, fin.cons_zero, fin.cons_succ,
      mem_mul, @ih, exists_and_distrib_left, exists_exists_eq_and, set_coe.exists, subtype.coe_mk,
      exists_prop] }
end

@[to_additive] lemma mem_list_prod {l : list (set α)} {a : α} :
  a ∈ l.prod ↔ ∃ l' : list (Σ s : set α, ↥s),
    list.prod (l'.map (λ x, (sigma.snd x : α))) = a ∧ l'.map sigma.fst = l :=
begin
  induction l using list.of_fn_rec with n f,
  simp_rw [list.exists_iff_exists_tuple, list.map_of_fn, list.of_fn_inj', and.left_comm,
    exists_and_distrib_left, exists_eq_left, heq_iff_eq, function.comp, mem_prod_list_of_fn],
  split,
  { rintros ⟨fi, rfl⟩,  exact ⟨λ i, ⟨_, fi i⟩, rfl, rfl⟩, },
  { rintros ⟨fi, rfl, rfl⟩, exact ⟨λ i, _, rfl⟩, },
end

@[to_additive] lemma mem_pow {a : α} {n : ℕ} :
  a ∈ s ^ n ↔ ∃ f : fin n → s, (list.of_fn (λ i, (f i : α))).prod = a :=
by rw [←mem_prod_list_of_fn, list.of_fn_const, list.prod_replicate]

end set


import morphisms.finite
import algebraic_geometry.finite_kScheme

open category_theory opposite

namespace algebraic_geometry

open_locale topological_space

universe u

variables {X Y : Scheme.{u}} (f : X ⟶ Y) (x : X.carrier)

-- def Scheme.hom.fiber_set (y : Y.carrier) : set X.carrier :=
-- { x | f.1.base x = y }

local notation `f⁻¹f` x:100 := f.fiber (f.1.base x)

@[derive is_iso]
noncomputable
def Scheme.hom.fiber_congr {x y : Y.carrier} (e : x = y) : f.fiber x ⟶ f.fiber y :=
limits.pullback.map _ _ _ _ (𝟙 _) (Scheme.Spec.map (Y.residue_field_of_eq e).op) (𝟙 _)
  (by rw [category.comp_id, category.id_comp])
  (by rw [category.comp_id, Scheme.residue_field_of_eq_from_Spec])

@[simp, reassoc]
lemma Scheme.hom.fiber_congr_ι {x y : Y.carrier} (e : x = y) :
  f.fiber_congr e ≫ f.fiber_ι y = f.fiber_ι x :=
(limits.pullback.lift_fst _ _ _).trans (category.comp_id _)

noncomputable
def Scheme.hom.as_fiber (x : X.carrier) : (f.fiber (f.1.base x)).carrier :=
(f.fiber_carrier (f.1.base x)).symm ⟨x, rfl⟩

lemma Scheme.hom.fiber_carrier_as_fiber (x : X.carrier) :
  ↑(f.fiber_carrier (f.1.base x) (f.as_fiber x)) = x :=
subtype.ext_iff.mp ((f.fiber_carrier (f.1.base x)).apply_symm_apply ⟨x, rfl⟩)

lemma Scheme.hom.as_fiber_fiber_carrier {y : Y.carrier} (x : (f.fiber y).carrier) :
  f.as_fiber (f.fiber_carrier y x) = (f.fiber_congr (f.fiber_carrier y x).prop.symm).1.base x :=
begin
  simp only [Scheme.hom.as_fiber, homeomorph.symm_apply_eq, Scheme.hom.fiber_carrier_apply,
    subtype.coe_mk, subtype.ext_iff, ← Scheme.comp_val_base_apply, Scheme.hom.fiber_congr_ι],
end

def Scheme.hom.quasi_finite_at (x : X.carrier) : Prop :=
𝓝[f.1.base ⁻¹' {f.1.base x} \ {x}] x = ⊥

lemma inducing.comap_nhds_within {α β : Type*} [topological_space α] [topological_space β]
  {f : α → β} (hf : inducing f) (x : α) (s : set β) :
    filter.comap f (𝓝[s] (f x)) = 𝓝[f ⁻¹' s] x :=
by rw [nhds_within, nhds_within, filter.comap_inf, hf.nhds_eq_comap, filter.comap_principal]

lemma embedding.comap_nhds_within_ne {α β : Type*} [topological_space α] [topological_space β]
  {f : α → β} (hf : embedding f) (x : α) : filter.comap f (𝓝[≠] (f x)) = 𝓝[≠] x :=
begin
  rw [hf.to_inducing.comap_nhds_within, set.preimage_compl],
  congr, ext, exact hf.inj.eq_iff
end

.
lemma Scheme.hom.quasi_finite_at_iff_is_open {x : X.carrier} :
  f.quasi_finite_at x ↔ is_open ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier) :=
begin
  change 𝓝[{ y | f.1.base y = f.1.base x } ∩ {x}ᶜ] x = ⊥ ↔ _,
  transitivity is_open (f.fiber_carrier (f.1.base x) '' {f.as_fiber x}),
  { rw [set.image_singleton, ← is_isolated_point_iff, set.inter_comm,
    nhds_within_inter', ← filter.subtype_coe_map_comap, filter.map_eq_bot_iff,
    ← embedding_subtype_coe.comap_nhds_within_ne, f.fiber_carrier_as_fiber] },
  { exact (open_embedding.open_iff_image_open $ homeomorph.open_embedding _).symm }
end

lemma Scheme.hom.quasi_finite_at_tfae [locally_of_finite_type f] (x : X.carrier) :
  tfae [f.quasi_finite_at x,
    is_open ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier),
    is_clopen ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier),
    (PresheafedSpace.stalk_map (f.fiber_to_residue_field (f.1.base x)).1 $ f.as_fiber x).finite,
    is_closed ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier) ∧
      stable_under_generalization ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier)] :=
begin
  rw list.tfae_cons_cons,
  split, { exact f.quasi_finite_at_iff_is_open },
  exact @is_open_singleton_tfae (f.fiber (f.1.base x))
    (local_ring.residue_field (Y.presheaf.stalk $ f.1.base x)) _
    (f.fiber_to_residue_field (f.1.base x))
    (category_theory.limits.pullback.snd.locally_of_finite_type _ _)
    (f.as_fiber x)
end

@[mk_iff]
class locally_quasi_finite extends locally_of_finite_type f : Prop :=
(quasi_finite_at : ∀ x, f.quasi_finite_at x)

lemma locally_quasi_finite_iff_fiber {f : X ⟶ Y} [locally_of_finite_type f] :
  locally_quasi_finite f ↔ ∀ y, discrete_topology (f.fiber y).carrier :=
begin
  simp_rw [discrete_topology_iff_forall_is_open_singleton, locally_quasi_finite_iff,
    and_iff_right ‹locally_of_finite_type f›, f.quasi_finite_at_iff_is_open],
  split,
  { intros H x y,
    have := H (f.fiber_carrier _ y),
    rwa [f.as_fiber_fiber_carrier, ← set.image_singleton,
      ← open_embedding.open_iff_image_open] at this,
    exact (Top.homeo_of_iso $
      Scheme.forget_to_Top.map_iso $ as_iso $ f.fiber_congr _).open_embedding },
  { intros H x, apply H }
end

instance locally_quasi_finite.discrete_fiber [locally_quasi_finite f] (y : Y.carrier) :
  discrete_topology (f.fiber y).carrier :=
locally_quasi_finite_iff_fiber.mp ‹locally_quasi_finite f› y

noncomputable
def _root_.category_theory.limits.pullback.map_iso {C} [category C] {X Y Z X' Y' Z' : C}
  (f : X ⟶ Z) (g : Y ⟶ Z) (f' : X' ⟶ Z') (g' : Y' ⟶ Z') (i₁ : X ≅ X')
  (i₂ : Y ≅ Y') (i₃ : Z ≅ Z') (h₁ : f ≫ i₃.hom = i₁.hom ≫ f')
  (h₂ : g ≫ i₃.hom = i₂.hom ≫ g') [limits.has_pullback f g] [limits.has_pullback f' g'] :
  limits.pullback f g ≅ limits.pullback f' g' :=
{ hom := limits.pullback.map f g f' g' i₁.hom i₂.hom i₃.hom h₁ h₂,
  inv := limits.pullback.map f' g' f g i₁.inv i₂.inv i₃.inv
    (by rw [iso.comp_inv_eq, category.assoc, h₁, iso.inv_hom_id_assoc])
    (by rw [iso.comp_inv_eq, category.assoc, h₂, iso.inv_hom_id_assoc]),
  hom_inv_id' := by { ext; simp },
  inv_hom_id' := by { ext; simp } }
.
open _root_.category_theory.limits

noncomputable
def fiber_fst_iso_pullback_fiber {X Y Z : Scheme} (f : X ⟶ Z) (g : Y ⟶ Z) (x : X.carrier) :
  (limits.pullback.fst : limits.pullback f g ⟶ _).fiber x ≅
    limits.pullback (g.fiber_to_residue_field (f.1.base x))
      (Scheme.Spec.map $ (f.map_residue_field x).op) :=
begin
  delta Scheme.hom.fiber Scheme.hom.fiber_ι,
  refine pullback_symmetry _ _ ≪≫ pullback_right_pullback_fst_iso _ _ _ ≪≫
    pullback.congr_hom (f.map_residue_field_from_Spec_residue_field x).symm rfl ≪≫
    (pullback_right_pullback_fst_iso _ _ _).symm ≪≫ pullback_symmetry _ _ ≪≫
    pullback.map_iso _ _ _ _ (pullback_symmetry _ _) (iso.refl _) (iso.refl _) _ _,
  { simp only [iso.refl_hom, category.comp_id, Scheme.hom.fiber_to_residue_field,
      pullback_symmetry_hom_comp_snd] },
  { simp only [iso.refl_hom, category.comp_id, category.id_comp] },
end

lemma Scheme.hom.quasi_finite_at.comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (hf : f.quasi_finite_at x) (hg : g.quasi_finite_at (f.1.base x)) :
  (f ≫ g).quasi_finite_at x :=
begin
  delta Scheme.hom.quasi_finite_at at hf hg ⊢,
  rw [eq_bot_iff, ← hf],
  refine le_inf inf_le_left (le_of_le_of_eq _ sup_bot_eq),
  rw [← @filter.comap_bot _ _ f.1.base, ← hg, nhds_within, nhds_within, filter.comap_inf,
    filter.comap_principal, sup_inf_left, filter.sup_principal],
  refine le_inf (inf_le_left.trans (le_trans _ le_sup_right)) (inf_le_right.trans _),
  { rw ← filter.tendsto_iff_comap, exact f.1.base.2.continuous_at },
  { rw filter.principal_mono, intros y hy,
    by_cases f.1.base y = f.1.base x, exacts [or.inl ⟨h, hy.2⟩, or.inr ⟨hy.1, h⟩] }
end

lemma Scheme.hom.quasi_finite_at.of_comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (h : (f ≫ g).quasi_finite_at x) :
  f.quasi_finite_at x :=
begin
  delta Scheme.hom.quasi_finite_at at h ⊢,
  simp_rw [eq_bot_iff, ← h, Scheme.comp_val_base, coe_comp],
  exact nhds_within_mono _ (λ x h, ⟨(congr_arg g.1.base h.1 : _), h.2⟩),
end

lemma Scheme.hom.quasi_finite_at.iff_comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (hg : g.quasi_finite_at (f.1.base x)) :
  (f ≫ g).quasi_finite_at x ↔ f.quasi_finite_at x :=
⟨Scheme.hom.quasi_finite_at.of_comp, λ h, Scheme.hom.quasi_finite_at.comp h hg⟩

lemma locally_quasi_finite_stable_under_composition :
  morphism_property.stable_under_composition @locally_quasi_finite :=
λ X Y Z f g hf hg, by exactI ⟨λ x, Scheme.hom.quasi_finite_at.comp (hf.2 _) (hg.2 _)⟩

lemma locally_quasi_finite_of_comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
  (hfg : locally_quasi_finite (f ≫ g)) : locally_quasi_finite f :=
@@locally_quasi_finite.mk (locally_of_finite_type_of_comp f g)
  (λ x, Scheme.hom.quasi_finite_at.of_comp (hfg.2 x))

lemma Scheme.hom.quasi_finite_at.of_injective
  (hf : function.injective f.1.base) (x) : f.quasi_finite_at x :=
begin
  rw [Scheme.hom.quasi_finite_at, eq_bot_iff, nhds_within],
  refine inf_le_right.trans_eq (filter.principal_eq_bot_iff.mpr _),
  rw set.eq_empty_iff_forall_not_mem,
  rintro y ⟨h₁, h₂⟩, exact h₂ (hf h₁)
end

lemma locally_quasi_finite_of_mono [mono f] [locally_of_finite_type f] :
  locally_quasi_finite f :=
⟨f^.quasi_finite_at.of_injective (injective_of_mono f)⟩

lemma locally_quasi_finite_respects_iso :
  morphism_property.respects_iso @locally_quasi_finite :=
locally_quasi_finite_stable_under_composition.respects_iso
  (λ X Y e, locally_quasi_finite_of_mono e.hom)

lemma locally_quasi_finite_stable_under_base_change :
  morphism_property.stable_under_base_change @locally_quasi_finite :=
morphism_property.stable_under_base_change.mk locally_quasi_finite_respects_iso
begin
  introsI X Y S f g H,
  rw locally_quasi_finite_iff_fiber,
  intro y,
  refine @@open_embedding.discrete_topology _ _ _ (Top.homeo_of_iso (Scheme.forget_to_Top.map_iso $
    fiber_fst_iso_pullback_fiber f g y)).open_embedding (show _, from _),
  apply_with discrete_topology_pullback_carrier_of_finite_type { instances := ff },
  { delta Scheme.hom.fiber_to_residue_field, apply_instance },
  { apply_instance }
end

instance locally_quasi_finite_comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
  [locally_quasi_finite f] [locally_quasi_finite g] : locally_quasi_finite (f ≫ g) :=
locally_quasi_finite_stable_under_composition _ _ infer_instance infer_instance

instance {X Y S : Scheme} (f : X ⟶ S) (g : Y ⟶ S) [locally_quasi_finite g] :
  locally_quasi_finite (pullback.fst : pullback f g ⟶ X) :=
locally_quasi_finite_stable_under_base_change (is_pullback.of_has_pullback f g).flip infer_instance

instance {X Y S : Scheme} (f : X ⟶ S) (g : Y ⟶ S) [locally_quasi_finite f] :
  locally_quasi_finite (pullback.snd : pullback f g ⟶ Y) :=
locally_quasi_finite_stable_under_base_change (is_pullback.of_has_pullback f g) infer_instance

local notation `𝖲𝗉𝖾𝖼` K := Scheme.Spec.obj (op $ CommRing.of $ K)

lemma finite_of_quasi_finite {K : Type*} [field K] (f : X ⟶ 𝖲𝗉𝖾𝖼 K)
  [locally_quasi_finite f] [quasi_compact f] : finite f :=
begin
  rw (finite_tfae_of_finite_type f).out 3 1,
  refine ((f.fiber_carrier (show prime_spectrum K, from ⊥)).trans ((homeomorph.set_congr _).trans
    (homeomorph.set.univ X.carrier))).symm.open_embedding.discrete_topology,
  exact set.eq_univ_iff_forall.mpr (λ x, @subsingleton.elim (prime_spectrum K) _ _ _)
end

lemma quasi_finite_tfae (f : X ⟶ Y) [locally_of_finite_type f] [quasi_compact f] :
  tfae [locally_quasi_finite f,
    ∀ x, _root_.finite (f.fiber x).carrier,
    ∀ x, finite (f.fiber_to_residue_field x),
    ∀ (K : Type u) [field K], by exactI ∀ (g : Scheme.Spec.obj (op $ CommRing.of K) ⟶ Y),
      finite (limits.pullback.snd : limits.pullback f g ⟶ _)] :=
begin
  have := λ x, @finite_tfae_of_finite_type (f.fiber x) _ _
    (f.fiber_to_residue_field x)
    (category_theory.limits.pullback.snd.locally_of_finite_type _ _)
    (category_theory.limits.pullback.snd.quasi_compact _ _),
  tfae_have : 1 → 4, { introsI, exact finite_of_quasi_finite _ },
  tfae_have : 4 → 3, { intros H x, apply H },
  tfae_have : 3 ↔ 2, { exact forall_congr (λ x, (this x).out 3 0) },
  tfae_have : 2 ↔ 1,
  { exact (forall_congr (λ x, (this x).out 0 1)).trans locally_quasi_finite_iff_fiber.symm },
  tfae_finish,
end

instance locally_quasi_finite.finite_fiber [locally_quasi_finite f] [quasi_compact f]
  (y : Y.carrier) :
  _root_.finite (f.fiber y).carrier :=
let H := (quasi_finite_tfae f).out 0 1 in H.mp infer_instance y

lemma locally_quasi_finite.set_finite_fiber [locally_quasi_finite f] [quasi_compact f]
  (y : Y.carrier) : { x | f.1.base x = y }.finite :=
begin
  apply_with set.to_finite { instances := ff },
  exact (f.fiber_carrier y).finite_iff.mp (locally_quasi_finite.finite_fiber f y),
end

instance finite.to_locally_quasi_finite [finite f] : locally_quasi_finite f :=
by { rw (quasi_finite_tfae f).out 0 3, intros, apply_instance }

instance is_immersion.to_locally_quasi_finite [is_immersion f] : locally_quasi_finite f :=
locally_quasi_finite_of_mono f

lemma Scheme.hom.quasi_finite_at.of_comp_is_open_immersion {X Y Z : Scheme} {f : X ⟶ Y}
  {g : Y ⟶ Z} [is_open_immersion f] {x : X.carrier} (h : (f ≫ g).quasi_finite_at x) :
  g.quasi_finite_at (f.1.base x) :=
begin
  delta Scheme.hom.quasi_finite_at at h ⊢,
  simp_rw [Scheme.comp_val_base, coe_comp] at h,
  rw [eq_bot_iff, ← @filter.map_bot _ _ f.1.base, ← h,
    (is_immersion.base_embedding f).map_nhds_within_eq, ← nhds_within_inter_of_mem],
  show set.range f.1.base ∈ nhds_within _ _,
  { apply nhds_within_le_nhds,
    exact is_open.mem_nhds (is_open_immersion.base_open f).2 (set.mem_range_self x) },
  apply nhds_within_mono,
  rintros _ ⟨⟨y, rfl⟩, h₁, h₂⟩,
  exact ⟨_, ⟨h₁, λ e, h₂ (congr_arg f.1.base e)⟩, rfl⟩,
end

lemma locally_quasi_finite_is_local_at_source :
  property_is_local_at_source @locally_quasi_finite :=
begin
  refine ⟨locally_quasi_finite_respects_iso, _, _⟩,
  { introsI, apply_instance },
  { introsI X Y f 𝒰 H,
    refine @@locally_quasi_finite.mk
      (locally_of_finite_type_is_local_at_source.3 f 𝒰 infer_instance) _,
    intro x,
    obtain ⟨y, hy⟩ := 𝒰.covers x,
    exact hy ▸ ((H $ 𝒰.f x).2 y).of_comp_is_open_immersion }
end

lemma locally_quasi_finite_is_local_at_target :
  property_is_local_at_target @locally_quasi_finite :=
begin
  refine ⟨locally_quasi_finite_respects_iso, _, _⟩,
  { introsI, delta morphism_restrict, apply_instance },
  { introsI X Y f 𝒰 H,
    apply locally_quasi_finite_is_local_at_source.3 f (𝒰.pullback_cover f),
    intro i,
    rw [Scheme.open_cover.pullback_cover_map, pullback.condition],
    apply_instance }
end

end algebraic_geometry


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Jeremy Avigad, Yury Kudryashov

! This file was ported from Lean 3 source module order.filter.cofinite
! leanprover-community/mathlib commit 4d392a6c9c4539cbeca399b3ee0afea398fbd2eb
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.Filter.AtTopBot
import Mathbin.Order.Filter.Pi

/-!
# The cofinite filter

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

In this file we define

`cofinite`: the filter of sets with finite complement

and prove its basic properties. In particular, we prove that for `ℕ` it is equal to `at_top`.

## TODO

Define filters for other cardinalities of the complement.
-/

open Set Function

open Classical

variable {ι α β : Type _} {l : Filter α}

namespace Filter

#print Filter.cofinite /-
/-- The cofinite filter is the filter of subsets whose complements are finite. -/
def cofinite : Filter α where
  sets := { s | sᶜ.Finite }
  univ_sets := by simp only [compl_univ, finite_empty, mem_set_of_eq]
  sets_of_superset s t (hs : sᶜ.Finite) (st : s ⊆ t) := hs.Subset <| compl_subset_compl.2 st
  inter_sets s t (hs : sᶜ.Finite) (ht : tᶜ.Finite) := by
    simp only [compl_inter, finite.union, ht, hs, mem_set_of_eq]
#align filter.cofinite Filter.cofinite
-/

/- warning: filter.mem_cofinite -> Filter.mem_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, Iff (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (Filter.cofinite.{u1} α)) (Set.Finite.{u1} α (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) s))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, Iff (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s (Filter.cofinite.{u1} α)) (Set.Finite.{u1} α (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) s))
Case conversion may be inaccurate. Consider using '#align filter.mem_cofinite Filter.mem_cofiniteₓ'. -/
@[simp]
theorem mem_cofinite {s : Set α} : s ∈ @cofinite α ↔ sᶜ.Finite :=
  Iff.rfl
#align filter.mem_cofinite Filter.mem_cofinite

#print Filter.eventually_cofinite /-
@[simp]
theorem eventually_cofinite {p : α → Prop} : (∀ᶠ x in cofinite, p x) ↔ { x | ¬p x }.Finite :=
  Iff.rfl
#align filter.eventually_cofinite Filter.eventually_cofinite
-/

/- warning: filter.has_basis_cofinite -> Filter.hasBasis_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Filter.HasBasis.{u1, succ u1} α (Set.{u1} α) (Filter.cofinite.{u1} α) (fun (s : Set.{u1} α) => Set.Finite.{u1} α s) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}}, Filter.HasBasis.{u1, succ u1} α (Set.{u1} α) (Filter.cofinite.{u1} α) (fun (s : Set.{u1} α) => Set.Finite.{u1} α s) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)))
Case conversion may be inaccurate. Consider using '#align filter.has_basis_cofinite Filter.hasBasis_cofiniteₓ'. -/
theorem hasBasis_cofinite : HasBasis cofinite (fun s : Set α => s.Finite) compl :=
  ⟨fun s =>
    ⟨fun h => ⟨sᶜ, h, (compl_compl s).Subset⟩, fun ⟨t, htf, hts⟩ =>
      htf.Subset <| compl_subset_comm.2 hts⟩⟩
#align filter.has_basis_cofinite Filter.hasBasis_cofinite

#print Filter.cofinite_neBot /-
instance cofinite_neBot [Infinite α] : NeBot (@cofinite α) :=
  hasBasis_cofinite.neBot_iff.2 fun s hs => hs.infinite_compl.Nonempty
#align filter.cofinite_ne_bot Filter.cofinite_neBot
-/

#print Filter.frequently_cofinite_iff_infinite /-
theorem frequently_cofinite_iff_infinite {p : α → Prop} :
    (∃ᶠ x in cofinite, p x) ↔ Set.Infinite { x | p x } := by
  simp only [Filter.Frequently, Filter.Eventually, mem_cofinite, compl_set_of, Classical.not_not,
    Set.Infinite]
#align filter.frequently_cofinite_iff_infinite Filter.frequently_cofinite_iff_infinite
-/

/- warning: set.finite.compl_mem_cofinite -> Set.Finite.compl_mem_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, (Set.Finite.{u1} α s) -> (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) s) (Filter.cofinite.{u1} α))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, (Set.Finite.{u1} α s) -> (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) s) (Filter.cofinite.{u1} α))
Case conversion may be inaccurate. Consider using '#align set.finite.compl_mem_cofinite Set.Finite.compl_mem_cofiniteₓ'. -/
theorem Set.Finite.compl_mem_cofinite {s : Set α} (hs : s.Finite) : sᶜ ∈ @cofinite α :=
  mem_cofinite.2 <| (compl_compl s).symm ▸ hs
#align set.finite.compl_mem_cofinite Set.Finite.compl_mem_cofinite

#print Set.Finite.eventually_cofinite_nmem /-
theorem Set.Finite.eventually_cofinite_nmem {s : Set α} (hs : s.Finite) : ∀ᶠ x in cofinite, x ∉ s :=
  hs.compl_mem_cofinite
#align set.finite.eventually_cofinite_nmem Set.Finite.eventually_cofinite_nmem
-/

#print Finset.eventually_cofinite_nmem /-
theorem Finset.eventually_cofinite_nmem (s : Finset α) : ∀ᶠ x in cofinite, x ∉ s :=
  s.finite_toSet.eventually_cofinite_nmem
#align finset.eventually_cofinite_nmem Finset.eventually_cofinite_nmem
-/

#print Set.infinite_iff_frequently_cofinite /-
theorem Set.infinite_iff_frequently_cofinite {s : Set α} :
    Set.Infinite s ↔ ∃ᶠ x in cofinite, x ∈ s :=
  frequently_cofinite_iff_infinite.symm
#align set.infinite_iff_frequently_cofinite Set.infinite_iff_frequently_cofinite
-/

#print Filter.eventually_cofinite_ne /-
theorem eventually_cofinite_ne (x : α) : ∀ᶠ a in cofinite, a ≠ x :=
  (Set.finite_singleton x).eventually_cofinite_nmem
#align filter.eventually_cofinite_ne Filter.eventually_cofinite_ne
-/

/- warning: filter.le_cofinite_iff_compl_singleton_mem -> Filter.le_cofinite_iff_compl_singleton_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) x)) l)
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) x)) l)
Case conversion may be inaccurate. Consider using '#align filter.le_cofinite_iff_compl_singleton_mem Filter.le_cofinite_iff_compl_singleton_memₓ'. -/
theorem le_cofinite_iff_compl_singleton_mem : l ≤ cofinite ↔ ∀ x, {x}ᶜ ∈ l :=
  by
  refine' ⟨fun h x => h (finite_singleton x).compl_mem_cofinite, fun h s (hs : sᶜ.Finite) => _⟩
  rw [← compl_compl s, ← bUnion_of_singleton (sᶜ), compl_Union₂, Filter.binterᵢ_mem hs]
  exact fun x _ => h x
#align filter.le_cofinite_iff_compl_singleton_mem Filter.le_cofinite_iff_compl_singleton_mem

/- warning: filter.le_cofinite_iff_eventually_ne -> Filter.le_cofinite_iff_eventually_ne is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Filter.Eventually.{u1} α (fun (y : α) => Ne.{succ u1} α y x) l)
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Filter.Eventually.{u1} α (fun (y : α) => Ne.{succ u1} α y x) l)
Case conversion may be inaccurate. Consider using '#align filter.le_cofinite_iff_eventually_ne Filter.le_cofinite_iff_eventually_neₓ'. -/
theorem le_cofinite_iff_eventually_ne : l ≤ cofinite ↔ ∀ x, ∀ᶠ y in l, y ≠ x :=
  le_cofinite_iff_compl_singleton_mem
#align filter.le_cofinite_iff_eventually_ne Filter.le_cofinite_iff_eventually_ne

/- warning: filter.at_top_le_cofinite -> Filter.atTop_le_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : NoMaxOrder.{u1} α (Preorder.toLT.{u1} α _inst_1)], LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.atTop.{u1} α _inst_1) (Filter.cofinite.{u1} α)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : NoMaxOrder.{u1} α (Preorder.toLT.{u1} α _inst_1)], LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (Filter.atTop.{u1} α _inst_1) (Filter.cofinite.{u1} α)
Case conversion may be inaccurate. Consider using '#align filter.at_top_le_cofinite Filter.atTop_le_cofiniteₓ'. -/
/-- If `α` is a preorder with no maximal element, then `at_top ≤ cofinite`. -/
theorem atTop_le_cofinite [Preorder α] [NoMaxOrder α] : (atTop : Filter α) ≤ cofinite :=
  le_cofinite_iff_eventually_ne.mpr eventually_ne_atTop
#align filter.at_top_le_cofinite Filter.atTop_le_cofinite

/- warning: filter.comap_cofinite_le -> Filter.comap_cofinite_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : α -> β), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.comap.{u1, u2} α β f (Filter.cofinite.{u2} β)) (Filter.cofinite.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : α -> β), LE.le.{u2} (Filter.{u2} α) (Preorder.toLE.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.instPartialOrderFilter.{u2} α))) (Filter.comap.{u2, u1} α β f (Filter.cofinite.{u1} β)) (Filter.cofinite.{u2} α)
Case conversion may be inaccurate. Consider using '#align filter.comap_cofinite_le Filter.comap_cofinite_leₓ'. -/
theorem comap_cofinite_le (f : α → β) : comap f cofinite ≤ cofinite :=
  le_cofinite_iff_eventually_ne.mpr fun x =>
    mem_comap.2 ⟨{f x}ᶜ, (finite_singleton _).compl_mem_cofinite, fun y => ne_of_apply_ne f⟩
#align filter.comap_cofinite_le Filter.comap_cofinite_le

/- warning: filter.coprod_cofinite -> Filter.coprod_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β (Filter.cofinite.{u1} α) (Filter.cofinite.{u2} β)) (Filter.cofinite.{max u1 u2} (Prod.{u1, u2} α β))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.coprod.{u2, u1} α β (Filter.cofinite.{u2} α) (Filter.cofinite.{u1} β)) (Filter.cofinite.{max u2 u1} (Prod.{u2, u1} α β))
Case conversion may be inaccurate. Consider using '#align filter.coprod_cofinite Filter.coprod_cofiniteₓ'. -/
/-- The coproduct of the cofinite filters on two types is the cofinite filter on their product. -/
theorem coprod_cofinite : (cofinite : Filter α).coprod (cofinite : Filter β) = cofinite :=
  Filter.coext fun s => by
    simp only [compl_mem_coprod, mem_cofinite, compl_compl, finite_image_fst_and_snd_iff]
#align filter.coprod_cofinite Filter.coprod_cofinite

#print Filter.coprodᵢ_cofinite /-
/-- Finite product of finite sets is finite -/
theorem coprodᵢ_cofinite {α : ι → Type _} [Finite ι] :
    (Filter.coprodᵢ fun i => (cofinite : Filter (α i))) = cofinite :=
  Filter.coext fun s => by
    simp only [compl_mem_Coprod, mem_cofinite, compl_compl, forall_finite_image_eval_iff]
#align filter.Coprod_cofinite Filter.coprodᵢ_cofinite
-/

/- warning: filter.disjoint_cofinite_left -> Filter.disjoint_cofinite_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) (Filter.cofinite.{u1} α) l) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) => Set.Finite.{u1} α s)))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) (Filter.cofinite.{u1} α) l) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l) (Set.Finite.{u1} α s)))
Case conversion may be inaccurate. Consider using '#align filter.disjoint_cofinite_left Filter.disjoint_cofinite_leftₓ'. -/
@[simp]
theorem disjoint_cofinite_left : Disjoint cofinite l ↔ ∃ s ∈ l, Set.Finite s :=
  by
  simp only [has_basis_cofinite.disjoint_iff l.basis_sets, id, disjoint_compl_left_iff_subset]
  exact
    ⟨fun ⟨s, hs, t, ht, hts⟩ => ⟨t, ht, hs.Subset hts⟩, fun ⟨s, hs, hsf⟩ =>
      ⟨s, hsf, s, hs, subset.rfl⟩⟩
#align filter.disjoint_cofinite_left Filter.disjoint_cofinite_left

/- warning: filter.disjoint_cofinite_right -> Filter.disjoint_cofinite_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) l (Filter.cofinite.{u1} α)) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) => Set.Finite.{u1} α s)))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l) (Set.Finite.{u1} α s)))
Case conversion may be inaccurate. Consider using '#align filter.disjoint_cofinite_right Filter.disjoint_cofinite_rightₓ'. -/
@[simp]
theorem disjoint_cofinite_right : Disjoint l cofinite ↔ ∃ s ∈ l, Set.Finite s :=
  disjoint_comm.trans disjoint_cofinite_left
#align filter.disjoint_cofinite_right Filter.disjoint_cofinite_right

end Filter

open Filter

#print Nat.cofinite_eq_atTop /-
/-- For natural numbers the filters `cofinite` and `at_top` coincide. -/
theorem Nat.cofinite_eq_atTop : @cofinite ℕ = atTop :=
  by
  refine' le_antisymm _ at_top_le_cofinite
  refine' at_top_basis.ge_iff.2 fun N hN => _
  simpa only [mem_cofinite, compl_Ici] using finite_lt_nat N
#align nat.cofinite_eq_at_top Nat.cofinite_eq_atTop
-/

#print Nat.frequently_atTop_iff_infinite /-
theorem Nat.frequently_atTop_iff_infinite {p : ℕ → Prop} :
    (∃ᶠ n in atTop, p n) ↔ Set.Infinite { n | p n } := by
  rw [← Nat.cofinite_eq_atTop, frequently_cofinite_iff_infinite]
#align nat.frequently_at_top_iff_infinite Nat.frequently_atTop_iff_infinite
-/

/- warning: filter.tendsto.exists_within_forall_le -> Filter.Tendsto.exists_within_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atTop.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) => forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a₀) (f a))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {s : Set.{u2} α}, (Set.Nonempty.{u2} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atTop.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a₀ s) (forall (a : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a s) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a₀) (f a))))))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_within_forall_le Filter.Tendsto.exists_within_forall_leₓ'. -/
theorem Filter.Tendsto.exists_within_forall_le {α β : Type _} [LinearOrder β] {s : Set α}
    (hs : s.Nonempty) {f : α → β} (hf : Filter.Tendsto f Filter.cofinite Filter.atTop) :
    ∃ a₀ ∈ s, ∀ a ∈ s, f a₀ ≤ f a :=
  by
  rcases em (∃ y ∈ s, ∃ x, f y < x) with (⟨y, hys, x, hx⟩ | not_all_top)
  · -- the set of points `{y | f y < x}` is nonempty and finite, so we take `min` over this set
    have : { y | ¬x ≤ f y }.Finite := filter.eventually_cofinite.mp (tendsto_at_top.1 hf x)
    simp only [not_le] at this
    obtain ⟨a₀, ⟨ha₀ : f a₀ < x, ha₀s⟩, others_bigger⟩ :=
      exists_min_image _ f (this.inter_of_left s) ⟨y, hx, hys⟩
    refine' ⟨a₀, ha₀s, fun a has => (lt_or_le (f a) x).elim _ (le_trans ha₀.le)⟩
    exact fun h => others_bigger a ⟨h, has⟩
  · -- in this case, f is constant because all values are at top
    push_neg  at not_all_top
    obtain ⟨a₀, ha₀s⟩ := hs
    exact ⟨a₀, ha₀s, fun a ha => not_all_top a ha (f a₀)⟩
#align filter.tendsto.exists_within_forall_le Filter.Tendsto.exists_within_forall_le

/- warning: filter.tendsto.exists_forall_le -> Filter.Tendsto.exists_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Nonempty.{succ u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atTop.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => forall (a : α), LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2))))) (f a₀) (f a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Nonempty.{succ u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atTop.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => forall (a : α), LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)))))) (f a₀) (f a)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_forall_le Filter.Tendsto.exists_forall_leₓ'. -/
theorem Filter.Tendsto.exists_forall_le [Nonempty α] [LinearOrder β] {f : α → β}
    (hf : Tendsto f cofinite atTop) : ∃ a₀, ∀ a, f a₀ ≤ f a :=
  let ⟨a₀, _, ha₀⟩ := hf.exists_within_forall_le univ_nonempty
  ⟨a₀, fun a => ha₀ a (mem_univ _)⟩
#align filter.tendsto.exists_forall_le Filter.Tendsto.exists_forall_le

/- warning: filter.tendsto.exists_within_forall_ge -> Filter.Tendsto.exists_within_forall_ge is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) => forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f a₀))))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (DistribLattice.toLattice.{u2} β (instDistribLattice.{u2} β _inst_1))))))) -> (Exists.{succ u1} α (fun (a₀ : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a₀ s) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (DistribLattice.toLattice.{u2} β (instDistribLattice.{u2} β _inst_1)))))) (f a) (f a₀))))))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_within_forall_ge Filter.Tendsto.exists_within_forall_geₓ'. -/
theorem Filter.Tendsto.exists_within_forall_ge [LinearOrder β] {s : Set α} (hs : s.Nonempty)
    {f : α → β} (hf : Filter.Tendsto f Filter.cofinite Filter.atBot) :
    ∃ a₀ ∈ s, ∀ a ∈ s, f a ≤ f a₀ :=
  @Filter.Tendsto.exists_within_forall_le _ βᵒᵈ _ _ hs _ hf
#align filter.tendsto.exists_within_forall_ge Filter.Tendsto.exists_within_forall_ge

/- warning: filter.tendsto.exists_forall_ge -> Filter.Tendsto.exists_forall_ge is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Nonempty.{succ u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => forall (a : α), LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2))))) (f a) (f a₀)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Nonempty.{succ u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atBot.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => forall (a : α), LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)))))) (f a) (f a₀)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_forall_ge Filter.Tendsto.exists_forall_geₓ'. -/
theorem Filter.Tendsto.exists_forall_ge [Nonempty α] [LinearOrder β] {f : α → β}
    (hf : Tendsto f cofinite atBot) : ∃ a₀, ∀ a, f a ≤ f a₀ :=
  @Filter.Tendsto.exists_forall_le _ βᵒᵈ _ _ _ hf
#align filter.tendsto.exists_forall_ge Filter.Tendsto.exists_forall_ge

/- warning: function.injective.tendsto_cofinite -> Function.Injective.tendsto_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β}, (Function.Injective.{succ u1, succ u2} α β f) -> (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.cofinite.{u2} β))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β}, (Function.Injective.{succ u2, succ u1} α β f) -> (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.cofinite.{u1} β))
Case conversion may be inaccurate. Consider using '#align function.injective.tendsto_cofinite Function.Injective.tendsto_cofiniteₓ'. -/
/-- For an injective function `f`, inverse images of finite sets are finite. See also
`filter.comap_cofinite_le` and `function.injective.comap_cofinite_eq`. -/
theorem Function.Injective.tendsto_cofinite {f : α → β} (hf : Injective f) :
    Tendsto f cofinite cofinite := fun s h => h.Preimage (hf.InjOn _)
#align function.injective.tendsto_cofinite Function.Injective.tendsto_cofinite

/- warning: function.injective.comap_cofinite_eq -> Function.Injective.comap_cofinite_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β}, (Function.Injective.{succ u1, succ u2} α β f) -> (Eq.{succ u1} (Filter.{u1} α) (Filter.comap.{u1, u2} α β f (Filter.cofinite.{u2} β)) (Filter.cofinite.{u1} α))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β}, (Function.Injective.{succ u2, succ u1} α β f) -> (Eq.{succ u2} (Filter.{u2} α) (Filter.comap.{u2, u1} α β f (Filter.cofinite.{u1} β)) (Filter.cofinite.{u2} α))
Case conversion may be inaccurate. Consider using '#align function.injective.comap_cofinite_eq Function.Injective.comap_cofinite_eqₓ'. -/
/-- The pullback of the `filter.cofinite` under an injective function is equal to `filter.cofinite`.
See also `filter.comap_cofinite_le` and `function.injective.tendsto_cofinite`. -/
theorem Function.Injective.comap_cofinite_eq {f : α → β} (hf : Injective f) :
    comap f cofinite = cofinite :=
  (comap_cofinite_le f).antisymm hf.tendsto_cofinite.le_comap
#align function.injective.comap_cofinite_eq Function.Injective.comap_cofinite_eq

#print Function.Injective.nat_tendsto_atTop /-
/-- An injective sequence `f : ℕ → ℕ` tends to infinity at infinity. -/
theorem Function.Injective.nat_tendsto_atTop {f : ℕ → ℕ} (hf : Injective f) :
    Tendsto f atTop atTop :=
  Nat.cofinite_eq_atTop ▸ hf.tendsto_cofinite
#align function.injective.nat_tendsto_at_top Function.Injective.nat_tendsto_atTop
-/