import borel_hierarchy
import topology.separation
import topology.bases

open topological_space

namespace borel_class

open set pointclass

variables {α : Type*} [topological_space α] [second_countable_topology α]

/-
def sigma0_pi0_rec : ordinal → bool → set α → Prop :=
  pointclass.sigma0_pi0_rec (countable_basis α)

def sigma0 : ordinal → set (set α) := pointclass.sigma0 (countable_basis α)

def pi0 : ordinal → set (set α) := pointclass.pi0 (countable_basis α)
-/

lemma sigma0_one : sigma0 (countable_basis α) 1 = {u : set α | is_open u} :=
begin
  ext z, refine ⟨λ hz, _, λ hz, _⟩,
  { rw pointclass.sigma0_one at hz,
    simp only [mem_range, mem_set_of_eq] at *,
    rcases hz with ⟨y, rfl⟩,
    apply is_open_Union,
    intro i,
    rcases (y i).property with h | h | h,
    { exact is_open_of_mem_countable_basis h },
    { rw h, exact is_open_empty },
    { simp only [mem_singleton_iff, subtype.val_eq_coe] at *,
      rw h, exact is_open_univ } },
  { rw [mem_set_of_eq,
      is_topological_basis.open_iff_eq_sUnion (is_basis_countable_basis α)] at hz,
    rcases hz with ⟨S, hS, hz⟩,
    rw pointclass.sigma0_one,
    rcases classical.em (S=∅) with rfl | nonemp,
    { existsi λ n, (⟨∅, _⟩ : ↥(countable_basis α ∪ {∅, univ})),
      { simp only [Union_empty],
        rw sUnion_empty at hz,
        exact hz.symm },
      { simp only [mem_insert_iff, true_or, eq_self_iff_true, union_insert, union_singleton] } },
    { rw sUnion_eq_Union at hz,
      have Sc : S.countable := countable.mono hS (countable_basis α).to_countable,
      have gsurj : ∃ (g : ℕ → S), g.surjective :=
        (set.countable_iff_exists_surjective (nonempty_iff_ne_empty.mpr nonemp)).mp Sc,
      cases gsurj with g hg,
      use inclusion (subset_union_of_subset_left hS {∅, univ}) ∘ g,
      simp only [subtype.val_eq_coe, coe_inclusion],
      rw ← Union_congr_of_surjective g hg at hz,
      swap 3,
      exacts [λ n, (g n).val, hz.symm, λ n, eq.refl (g n)] } }
end

lemma pi0_one : pi0 (countable_basis α) 1 = {u : set α | is_closed u} :=
begin
  rw [pi0_eq_compl_sigma0 _ _ (zero_ne_one).symm, sigma0_one],
  ext z, refine ⟨λ hz, _, λ hz, _⟩,
  { rcases hz with ⟨x, hx, co⟩,
    rw eq_compl_comm.mp (eq.symm co) at hx,
    exact ⟨hx⟩ },
  { change is_closed z at hz,
    change ∃ (x : set α), is_open x ∧ xᶜ = z,
    use zᶜ,
    simp only [compl_compl, is_open_compl_iff, and_true, eq_self_iff_true, hz] }
end

/-- The *ambiguous* Borel classes, comprising sets that are both `sigma0` and `pi0`. -/
def delta0 (i : ordinal) : set (set α) := sigma0 (countable_basis α) i ∩ pi0 (countable_basis α) i

lemma delta0_one : delta0 1 = {u : set α | is_clopen u} :=
by { rw [delta0, sigma0_one, pi0_one], tauto }

end borel_class

section zero_dim_space

variables (α : Type*) [topological_space α]

/-- The clopen subsets form a basis of the topology. -/
def has_clopen_basis : Prop := is_topological_basis {s : set α | is_clopen s}

/-- A space is zero-dimensional if it is T₂ and has a basis consisting of clopens. -/
class zero_dim_space (α : Type*) [topological_space α] [t2_space α] : Prop :=
(has_clopen_basis : has_clopen_basis α)

end zero_dim_space

variables {α : Type*} [topological_space α]

section profinite

variables [t2_space α]

/- Old mathlib lemma recovered using typeclass -/
lemma tot_sep_of_zero_dim [h : zero_dim_space α] :
  totally_separated_space α :=
  totally_separated_space_of_t1_of_basis_clopen h.has_clopen_basis

end profinite

section locally_compact

variables {H : Type*} [topological_space H] [locally_compact_space H] [t2_space H]

/- Application of the random lemma, now using an instance -/

instance zero_dim_space_of_totally_disconnected [totally_disconnected_space H] :
  zero_dim_space H := ⟨loc_compact_Haus_tot_disc_of_zero_dim⟩

theorem loc_compact_t2_tot_disc_iff_tot_sep' :
  totally_disconnected_space H ↔ totally_separated_space H :=
begin
  split,
  { introI h,
    exact tot_sep_of_zero_dim, },
  apply totally_separated_space.totally_disconnected_space,
end

end locally_compact


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.pullbacks
import Mathlib.category_theory.limits.preserves.basic
import Mathlib.PostPort

universes u₁ u₂ v

namespace Mathlib

/-!
# Preserving pullbacks

Constructions to relate the notions of preserving pullbacks and reflecting pullbacks to concrete
pullback cones.

In particular, we show that `pullback_comparison G f g` is an isomorphism iff `G` preserves
the pullback of `f` and `g`.

## TODO

* Dualise to pushouts
* Generalise to wide pullbacks

-/

namespace category_theory.limits

/--
The map of a pullback cone is a limit iff the fork consisting of the mapped morphisms is a limit.
This essentially lets us commute `pullback_cone.mk` with `functor.map_cone`.
-/
def is_limit_map_cone_pullback_cone_equiv {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {W : C} {X : C} {Y : C} {Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} {h : W ⟶ X} {k : W ⟶ Y}
    (comm : h ≫ f = k ≫ g) :
    is_limit (functor.map_cone G (pullback_cone.mk h k comm)) ≃
        is_limit
          (pullback_cone.mk (functor.map G h) (functor.map G k)
            (is_limit_map_cone_pullback_cone_equiv._proof_1 G comm)) :=
  equiv.trans
    (equiv.symm
      (is_limit.postcompose_hom_equiv (diagram_iso_cospan (cospan f g ⋙ G))
        (functor.map_cone G (pullback_cone.mk h k comm))))
    (is_limit.equiv_iso_limit
      (cones.ext
        (iso.refl
          (cone.X
            (functor.obj (cones.postcompose (iso.hom (diagram_iso_cospan (cospan f g ⋙ G))))
              (functor.map_cone G (pullback_cone.mk h k comm)))))
        sorry))

/-- The property of preserving pullbacks expressed in terms of binary fans. -/
def is_limit_pullback_cone_map_of_is_limit {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {W : C} {X : C} {Y : C} {Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} {h : W ⟶ X} {k : W ⟶ Y}
    (comm : h ≫ f = k ≫ g) [preserves_limit (cospan f g) G]
    (l : is_limit (pullback_cone.mk h k comm)) :
    is_limit
        (pullback_cone.mk (functor.map G h) (functor.map G k)
          (is_limit_map_cone_pullback_cone_equiv._proof_1 G comm)) :=
  coe_fn (is_limit_map_cone_pullback_cone_equiv G comm) (preserves_limit.preserves l)

/-- The property of reflecting pullbacks expressed in terms of binary fans. -/
def is_limit_of_is_limit_pullback_cone_map {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {W : C} {X : C} {Y : C} {Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} {h : W ⟶ X} {k : W ⟶ Y}
    (comm : h ≫ f = k ≫ g) [reflects_limit (cospan f g) G]
    (l :
      is_limit
        (pullback_cone.mk (functor.map G h) (functor.map G k)
          (is_limit_map_cone_pullback_cone_equiv._proof_1 G comm))) :
    is_limit (pullback_cone.mk h k comm) :=
  reflects_limit.reflects (coe_fn (equiv.symm (is_limit_map_cone_pullback_cone_equiv G comm)) l)

/--
If `G` preserves pullbacks and `C` has them, then the pullback cone constructed of the mapped
morphisms of the pullback cone is a limit.
-/
def is_limit_of_has_pullback_of_preserves_limit {C : Type u₁} [category C] {D : Type u₂}
    [category D] (G : C ⥤ D) {X : C} {Y : C} {Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [has_pullback f g]
    [preserves_limit (cospan f g) G] :
    is_limit
        (pullback_cone.mk (functor.map G pullback.fst) (functor.map G pullback.snd)
          (is_limit_of_has_pullback_of_preserves_limit._proof_1 G f g)) :=
  is_limit_pullback_cone_map_of_is_limit G pullback.condition (pullback_is_pullback f g)

/--
If the pullback comparison map for `G` at `(f,g)` is an isomorphism, then `G` preserves the
pullback of `(f,g)`.
-/
def preserves_pullback.of_iso_comparison {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {X : C} {Y : C} {Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [has_pullback f g]
    [has_pullback (functor.map G f) (functor.map G g)] [i : is_iso (pullback_comparison G f g)] :
    preserves_limit (cospan f g) G :=
  preserves_limit_of_preserves_limit_cone (pullback_is_pullback f g)
    (coe_fn (equiv.symm (is_limit_map_cone_pullback_cone_equiv G pullback.condition))
      (is_limit.of_point_iso (limit.is_limit (cospan (functor.map G f) (functor.map G g)))))

/--
If `G` preserves the pullback of `(f,g)`, then the pullback comparison map for `G` at `(f,g)` is
an isomorphism.
-/
def preserves_pullback.iso {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D) {X : C}
    {Y : C} {Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [has_pullback f g]
    [has_pullback (functor.map G f) (functor.map G g)] [preserves_limit (cospan f g) G] :
    functor.obj G (pullback f g) ≅ pullback (functor.map G f) (functor.map G g) :=
  is_limit.cone_point_unique_up_to_iso (is_limit_of_has_pullback_of_preserves_limit G f g)
    (limit.is_limit (cospan (functor.map G f) (functor.map G g)))

@[simp] theorem preserves_pullback.iso_hom {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {X : C} {Y : C} {Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [has_pullback f g]
    [has_pullback (functor.map G f) (functor.map G g)] [preserves_limit (cospan f g) G] :
    iso.hom (preserves_pullback.iso G f g) = pullback_comparison G f g :=
  rfl

protected instance pullback_comparison.category_theory.is_iso {C : Type u₁} [category C]
    {D : Type u₂} [category D] (G : C ⥤ D) {X : C} {Y : C} {Z : C} (f : X ⟶ Z) (g : Y ⟶ Z)
    [has_pullback f g] [has_pullback (functor.map G f) (functor.map G g)]
    [preserves_limit (cospan f g) G] : is_iso (pullback_comparison G f g) :=
  eq.mpr sorry (is_iso.of_iso (preserves_pullback.iso G f g))

end Mathlib


/-                   2019-02-19, Oberharmersbach

Interactive theorem proving with a computer
— my experience

--                 _
--                | | ___  __ _ _ __
--                | |/ _ \/ _` | '_ \
--                | |  __| (_| | | | |
--                |_|\___|\__,_|_| |_|
--

A rose-colored introduction

by Johan Commelin -/

---------------------------------
-- Interactive theorem proving --
---------------------------------
--
--
---- Formal proof verification
--
--
---- Automatic theorem proving

import data.real.basic data.nat.prime nat.extra

definition hi := "hello, world!"

#eval hi

#check hi

#check 3

#check ℕ

#check real.sqrt

#check real.sqrt hi

#check (hi : ℝ)

#check 3 = 4

#check (nat.add_comm : ∀ (a b : ℕ), a + b = b + a)

example : ∀ (a b : ℕ), a + b = b + a :=
nat.add_comm

definition f (x : ℤ) := x^3 + 5 * x - 7

definition FLT : Prop :=
∀ n > 2, ∀ x y z, x^n + y^n = z^n → x = 0 ∨ y = 0

theorem Wiles : FLT :=
begin
  unfold FLT,
  intro n,
  intro hn,
  intros,
  sorry
end

open nat

theorem Euclid (n : ℕ) : ∃ p ≥ n, prime p :=
begin
  let N := n.fact + 1,
  let p := min_fac N,
  use_this p,
  have p_is_prime : prime p,
    apply min_fac_prime,
    have key_fact := n.fact_pos,
    have now : N > 1 := succ_lt_succ key_fact,
    apply ne_of_gt,
    assumption,
  split,
  { by_contradiction contra_hyp,
    have key_fact : p ∣ n.fact,
      apply dvd_fact,
      apply p_is_prime.pos,
      apply le_of_not_ge,
      assumption,
    have oops : p ∣ 1,
      have old_fact : p ∣ n.fact + 1,
        apply min_fac_dvd,
      have tada := p.dvd_add_iff_right_of_left,
      rw tada,
      assumption',
    have old_fact := p_is_prime.not_dvd_one,
    contradiction },
  assumption
end

theorem Euclid' (n : ℕ) : ∃ p ≥ n, prime p :=
begin
  let N := n.fact + 1,
  let p := min_fac N,
  use_this p,
  have p_is_prime : prime p := min_fac_prime _,
  split,
    show p ≥ n,
    by_contradiction,
    have key_fact : p ∣ n.fact := dvd_fact _ _,
    have oops : p ∣ 1,
  all_goals { auto },
end


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

! This file was ported from Lean 3 source module data.pfunctor.multivariate.M
! leanprover-community/mathlib commit 23aa88e32dcc9d2a24cca7bc23268567ed4cd7d6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Pfunctor.Multivariate.Basic
import Mathbin.Data.Pfunctor.Univariate.M

/-!
# The M construction as a multivariate polynomial functor.

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

M types are potentially infinite tree-like structures. They are defined
as the greatest fixpoint of a polynomial functor.

## Main definitions

 * `M.mk`     - constructor
 * `M.dest`   - destructor
 * `M.corec`  - corecursor: useful for formulating infinite, productive computations
 * `M.bisim`  - bisimulation: proof technique to show the equality of infinite objects

## Implementation notes

Dual view of M-types:

 * `Mp`: polynomial functor
 * `M`: greatest fixed point of a polynomial functor

Specifically, we define the polynomial functor `Mp` as:

 * A := a possibly infinite tree-like structure without information in the nodes
 * B := given the tree-like structure `t`, `B t` is a valid path
   from the root of `t` to any given node.

As a result `Mp.obj α` is made of a dataless tree and a function from
its valid paths to values of `α`

The difference with the polynomial functor of an initial algebra is
that `A` is a possibly infinite tree.

## Reference

 * Jeremy Avigad, Mario M. Carneiro and Simon Hudon.
   [*Data Types as Quotients of Polynomial Functors*][avigad-carneiro-hudon2019]
-/

universe u

open MvFunctor

namespace MvPFunctor

open TypeVec

variable {n : ℕ} (P : MvPFunctor.{u} (n + 1))

#print MvPFunctor.M.Path /-
/-- A path from the root of a tree to one of its node -/
inductive M.Path : P.getLast.M → Fin2 n → Type u
  |
  root (x : P.getLast.M) (a : P.A) (f : P.getLast.B a → P.getLast.M)
    (h : PFunctor.M.dest x = ⟨a, f⟩) (i : Fin2 n) (c : P.drop.B a i) : M.path x i
  |
  child (x : P.getLast.M) (a : P.A) (f : P.getLast.B a → P.getLast.M)
    (h : PFunctor.M.dest x = ⟨a, f⟩) (j : P.getLast.B a) (i : Fin2 n) (c : M.path (f j) i) :
    M.path x i
#align mvpfunctor.M.path MvPFunctor.M.Path
-/

#print MvPFunctor.M.Path.inhabited /-
instance M.Path.inhabited (x : P.getLast.M) {i} [Inhabited (P.drop.B x.headI i)] :
    Inhabited (M.Path P x i) :=
  ⟨M.Path.root _ (PFunctor.M.head x) (PFunctor.M.children x)
      (PFunctor.M.casesOn' x <| by
        intros <;> simp [PFunctor.M.dest_mk] <;> ext <;> rw [PFunctor.M.children_mk] <;> rfl)
      _ default⟩
#align mvpfunctor.M.path.inhabited MvPFunctor.M.Path.inhabited
-/

#print MvPFunctor.mp /-
/-- Polynomial functor of the M-type of `P`. `A` is a data-less
possibly infinite tree whereas, for a given `a : A`, `B a` is a valid
path in tree `a` so that `Wp.obj α` is made of a tree and a function
from its valid paths to the values it contains -/
def mp : MvPFunctor n where
  A := P.getLast.M
  B := M.Path P
#align mvpfunctor.Mp MvPFunctor.mp
-/

#print MvPFunctor.M /-
/-- `n`-ary M-type for `P` -/
def M (α : TypeVec n) : Type _ :=
  P.mp.Obj α
#align mvpfunctor.M MvPFunctor.M
-/

#print MvPFunctor.mvfunctorM /-
instance mvfunctorM : MvFunctor P.M := by delta M <;> infer_instance
#align mvpfunctor.mvfunctor_M MvPFunctor.mvfunctorM
-/

#print MvPFunctor.inhabitedM /-
instance inhabitedM {α : TypeVec _} [I : Inhabited P.A] [∀ i : Fin2 n, Inhabited (α i)] :
    Inhabited (P.M α) :=
  @Obj.inhabited _ (mp P) _ (@PFunctor.M.inhabited P.getLast I) _
#align mvpfunctor.inhabited_M MvPFunctor.inhabitedM
-/

#print MvPFunctor.M.corecShape /-
/-- construct through corecursion the shape of an M-type
without its contents -/
def M.corecShape {β : Type u} (g₀ : β → P.A) (g₂ : ∀ b : β, P.getLast.B (g₀ b) → β) :
    β → P.getLast.M :=
  PFunctor.M.corec fun b => ⟨g₀ b, g₂ b⟩
#align mvpfunctor.M.corec_shape MvPFunctor.M.corecShape
-/

#print MvPFunctor.castDropB /-
/-- Proof of type equality as an arrow -/
def castDropB {a a' : P.A} (h : a = a') : P.drop.B a ⟹ P.drop.B a' := fun i b => Eq.recOn h b
#align mvpfunctor.cast_dropB MvPFunctor.castDropB
-/

#print MvPFunctor.castLastB /-
/-- Proof of type equality as a function -/
def castLastB {a a' : P.A} (h : a = a') : P.getLast.B a → P.getLast.B a' := fun b => Eq.recOn h b
#align mvpfunctor.cast_lastB MvPFunctor.castLastB
-/

#print MvPFunctor.M.corecContents /-
/-- Using corecursion, construct the contents of an M-type -/
def M.corecContents {α : TypeVec.{u} n} {β : Type u} (g₀ : β → P.A)
    (g₁ : ∀ b : β, P.drop.B (g₀ b) ⟹ α) (g₂ : ∀ b : β, P.getLast.B (g₀ b) → β) :
    ∀ x b, x = M.corecShape P g₀ g₂ b → M.Path P x ⟹ α
  | _, b, h, _, M.path.root x a f h' i c =>
    have : a = g₀ b := by
      rw [h, M.corec_shape, PFunctor.M.dest_corec] at h'
      cases h'
      rfl
    g₁ b i (P.castDropB this i c)
  | _, b, h, _, M.path.child x a f h' j i c =>
    have h₀ : a = g₀ b := by
      rw [h, M.corec_shape, PFunctor.M.dest_corec] at h'
      cases h'
      rfl
    have h₁ : f j = M.corecShape P g₀ g₂ (g₂ b (castLastB P h₀ j)) :=
      by
      rw [h, M.corec_shape, PFunctor.M.dest_corec] at h'
      cases h'
      rfl
    M.corec_contents (f j) (g₂ b (P.castLastB h₀ j)) h₁ i c
#align mvpfunctor.M.corec_contents MvPFunctor.M.corecContents
-/

#print MvPFunctor.M.corec' /-
/-- Corecursor for M-type of `P` -/
def M.corec' {α : TypeVec n} {β : Type u} (g₀ : β → P.A) (g₁ : ∀ b : β, P.drop.B (g₀ b) ⟹ α)
    (g₂ : ∀ b : β, P.getLast.B (g₀ b) → β) : β → P.M α := fun b =>
  ⟨M.corecShape P g₀ g₂ b, M.corecContents P g₀ g₁ g₂ _ _ rfl⟩
#align mvpfunctor.M.corec' MvPFunctor.M.corec'
-/

#print MvPFunctor.M.corec /-
/-- Corecursor for M-type of `P` -/
def M.corec {α : TypeVec n} {β : Type u} (g : β → P.Obj (α.append1 β)) : β → P.M α :=
  M.corec' P (fun b => (g b).fst) (fun b => dropFun (g b).snd) fun b => lastFun (g b).snd
#align mvpfunctor.M.corec MvPFunctor.M.corec
-/

#print MvPFunctor.M.pathDestLeft /-
/-- Implementation of destructor for M-type of `P` -/
def M.pathDestLeft {α : TypeVec n} {x : P.getLast.M} {a : P.A} {f : P.getLast.B a → P.getLast.M}
    (h : PFunctor.M.dest x = ⟨a, f⟩) (f' : M.Path P x ⟹ α) : P.drop.B a ⟹ α := fun i c =>
  f' i (M.Path.root x a f h i c)
#align mvpfunctor.M.path_dest_left MvPFunctor.M.pathDestLeft
-/

#print MvPFunctor.M.pathDestRight /-
/-- Implementation of destructor for M-type of `P` -/
def M.pathDestRight {α : TypeVec n} {x : P.getLast.M} {a : P.A} {f : P.getLast.B a → P.getLast.M}
    (h : PFunctor.M.dest x = ⟨a, f⟩) (f' : M.Path P x ⟹ α) :
    ∀ j : P.getLast.B a, M.Path P (f j) ⟹ α := fun j i c => f' i (M.Path.child x a f h j i c)
#align mvpfunctor.M.path_dest_right MvPFunctor.M.pathDestRight
-/

#print MvPFunctor.M.dest' /-
/-- Destructor for M-type of `P` -/
def M.dest' {α : TypeVec n} {x : P.getLast.M} {a : P.A} {f : P.getLast.B a → P.getLast.M}
    (h : PFunctor.M.dest x = ⟨a, f⟩) (f' : M.Path P x ⟹ α) : P.Obj (α.append1 (P.M α)) :=
  ⟨a, splitFun (M.pathDestLeft P h f') fun x => ⟨f x, M.pathDestRight P h f' x⟩⟩
#align mvpfunctor.M.dest' MvPFunctor.M.dest'
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print MvPFunctor.M.dest /-
/-- Destructor for M-types -/
def M.dest {α : TypeVec n} (x : P.M α) : P.Obj (α ::: P.M α) :=
  M.dest' P (Sigma.eta <| PFunctor.M.dest x.fst).symm x.snd
#align mvpfunctor.M.dest MvPFunctor.M.dest
-/

#print MvPFunctor.M.mk /-
/-- Constructor for M-types -/
def M.mk {α : TypeVec n} : P.Obj (α.append1 (P.M α)) → P.M α :=
  M.corec _ fun i => appendFun id (M.dest P) <$$> i
#align mvpfunctor.M.mk MvPFunctor.M.mk
-/

#print MvPFunctor.M.dest'_eq_dest' /-
theorem M.dest'_eq_dest' {α : TypeVec n} {x : P.getLast.M} {a₁ : P.A}
    {f₁ : P.getLast.B a₁ → P.getLast.M} (h₁ : PFunctor.M.dest x = ⟨a₁, f₁⟩) {a₂ : P.A}
    {f₂ : P.getLast.B a₂ → P.getLast.M} (h₂ : PFunctor.M.dest x = ⟨a₂, f₂⟩) (f' : M.Path P x ⟹ α) :
    M.dest' P h₁ f' = M.dest' P h₂ f' := by cases h₁.symm.trans h₂ <;> rfl
#align mvpfunctor.M.dest'_eq_dest' MvPFunctor.M.dest'_eq_dest'
-/

#print MvPFunctor.M.dest_eq_dest' /-
theorem M.dest_eq_dest' {α : TypeVec n} {x : P.getLast.M} {a : P.A}
    {f : P.getLast.B a → P.getLast.M} (h : PFunctor.M.dest x = ⟨a, f⟩) (f' : M.Path P x ⟹ α) :
    M.dest P ⟨x, f'⟩ = M.dest' P h f' :=
  M.dest'_eq_dest' _ _ _ _
#align mvpfunctor.M.dest_eq_dest' MvPFunctor.M.dest_eq_dest'
-/

#print MvPFunctor.M.dest_corec' /-
theorem M.dest_corec' {α : TypeVec.{u} n} {β : Type u} (g₀ : β → P.A)
    (g₁ : ∀ b : β, P.drop.B (g₀ b) ⟹ α) (g₂ : ∀ b : β, P.getLast.B (g₀ b) → β) (x : β) :
    M.dest P (M.corec' P g₀ g₁ g₂ x) = ⟨g₀ x, splitFun (g₁ x) (M.corec' P g₀ g₁ g₂ ∘ g₂ x)⟩ :=
  rfl
#align mvpfunctor.M.dest_corec' MvPFunctor.M.dest_corec'
-/

#print MvPFunctor.M.dest_corec /-
theorem M.dest_corec {α : TypeVec n} {β : Type u} (g : β → P.Obj (α.append1 β)) (x : β) :
    M.dest P (M.corec P g x) = appendFun id (M.corec P g) <$$> g x :=
  by
  trans; apply M.dest_corec'
  cases' g x with a f; dsimp
  rw [MvPFunctor.map_eq]; congr
  conv =>
    rhs
    rw [← split_drop_fun_last_fun f, append_fun_comp_split_fun]
  rfl
#align mvpfunctor.M.dest_corec MvPFunctor.M.dest_corec
-/

#print MvPFunctor.M.bisim_lemma /-
theorem M.bisim_lemma {α : TypeVec n} {a₁ : (mp P).A} {f₁ : (mp P).B a₁ ⟹ α} {a' : P.A}
    {f' : (P.B a').drop ⟹ α} {f₁' : (P.B a').getLast → M P α}
    (e₁ : M.dest P ⟨a₁, f₁⟩ = ⟨a', splitFun f' f₁'⟩) :
    ∃ (g₁' : _)(e₁' : PFunctor.M.dest a₁ = ⟨a', g₁'⟩),
      f' = M.pathDestLeft P e₁' f₁ ∧
        f₁' = fun x : (last P).B a' => ⟨g₁' x, M.pathDestRight P e₁' f₁ x⟩ :=
  by
  generalize ef : @split_fun n _ (append1 α (M P α)) f' f₁' = ff at e₁
  cases' e₁' : PFunctor.M.dest a₁ with a₁' g₁'
  rw [M.dest_eq_dest' _ e₁'] at e₁
  cases e₁; exact ⟨_, e₁', split_fun_inj ef⟩
#align mvpfunctor.M.bisim_lemma MvPFunctor.M.bisim_lemma
-/

#print MvPFunctor.M.bisim /-
theorem M.bisim {α : TypeVec n} (R : P.M α → P.M α → Prop)
    (h :
      ∀ x y,
        R x y →
          ∃ a f f₁ f₂,
            M.dest P x = ⟨a, splitFun f f₁⟩ ∧
              M.dest P y = ⟨a, splitFun f f₂⟩ ∧ ∀ i, R (f₁ i) (f₂ i))
    (x y) (r : R x y) : x = y := by
  cases' x with a₁ f₁
  cases' y with a₂ f₂
  dsimp [Mp] at *
  have : a₁ = a₂ :=
    by
    refine'
      PFunctor.M.bisim (fun a₁ a₂ => ∃ x y, R x y ∧ x.1 = a₁ ∧ y.1 = a₂) _ _ _
        ⟨⟨a₁, f₁⟩, ⟨a₂, f₂⟩, r, rfl, rfl⟩
    rintro _ _ ⟨⟨a₁, f₁⟩, ⟨a₂, f₂⟩, r, rfl, rfl⟩
    rcases h _ _ r with ⟨a', f', f₁', f₂', e₁, e₂, h'⟩
    rcases M.bisim_lemma P e₁ with ⟨g₁', e₁', rfl, rfl⟩
    rcases M.bisim_lemma P e₂ with ⟨g₂', e₂', _, rfl⟩
    rw [e₁', e₂']
    exact ⟨_, _, _, rfl, rfl, fun b => ⟨_, _, h' b, rfl, rfl⟩⟩
  subst this
  congr with (i p)
  induction' p with x a f h' i c x a f h' i c p IH generalizing f₁ f₂ <;>
    try
      rcases h _ _ r with ⟨a', f', f₁', f₂', e₁, e₂, h''⟩
      rcases M.bisim_lemma P e₁ with ⟨g₁', e₁', rfl, rfl⟩
      rcases M.bisim_lemma P e₂ with ⟨g₂', e₂', e₃, rfl⟩
      cases h'.symm.trans e₁'
      cases h'.symm.trans e₂'
  · exact (congr_fun (congr_fun e₃ i) c : _)
  · exact IH _ _ (h'' _)
#align mvpfunctor.M.bisim MvPFunctor.M.bisim
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print MvPFunctor.M.bisim₀ /-
theorem M.bisim₀ {α : TypeVec n} (R : P.M α → P.M α → Prop) (h₀ : Equivalence R)
    (h : ∀ x y, R x y → (id ::: Quot.mk R) <$$> M.dest _ x = (id ::: Quot.mk R) <$$> M.dest _ y)
    (x y) (r : R x y) : x = y := by
  apply M.bisim P R _ _ _ r
  clear r x y
  introv Hr
  specialize h _ _ Hr
  clear Hr
  rcases M.dest P x with ⟨ax, fx⟩
  rcases M.dest P y with ⟨ay, fy⟩
  intro h
  rw [map_eq, map_eq] at h
  injection h with h₀ h₁
  subst ay
  simp at h₁
  clear h
  have Hdrop : drop_fun fx = drop_fun fy :=
    by
    replace h₁ := congr_arg drop_fun h₁
    simpa using h₁
  exists ax, drop_fun fx, last_fun fx, last_fun fy
  rw [split_drop_fun_last_fun, Hdrop, split_drop_fun_last_fun]
  simp
  intro i
  replace h₁ := congr_fun (congr_fun h₁ Fin2.fz) i
  simp [(· ⊚ ·), append_fun, split_fun] at h₁
  replace h₁ := Quot.exact _ h₁
  rw [h₀.eqv_gen_iff] at h₁
  exact h₁
#align mvpfunctor.M.bisim₀ MvPFunctor.M.bisim₀
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print MvPFunctor.M.bisim' /-
theorem M.bisim' {α : TypeVec n} (R : P.M α → P.M α → Prop)
    (h : ∀ x y, R x y → (id ::: Quot.mk R) <$$> M.dest _ x = (id ::: Quot.mk R) <$$> M.dest _ y)
    (x y) (r : R x y) : x = y := by
  have := M.bisim₀ P (EqvGen R) _ _
  · solve_by_elim [EqvGen.rel]
  · apply EqvGen.is_equivalence
  · clear r x y
    introv Hr
    have : ∀ x y, R x y → EqvGen R x y := @EqvGen.rel _ R
    induction Hr
    · rw [← Quot.factor_mk_eq R (EqvGen R) this]
      rwa [append_fun_comp_id, ← MvFunctor.map_map, ← MvFunctor.map_map, h]
    all_goals cc
#align mvpfunctor.M.bisim' MvPFunctor.M.bisim'
-/

#print MvPFunctor.M.dest_map /-
theorem M.dest_map {α β : TypeVec n} (g : α ⟹ β) (x : P.M α) :
    M.dest P (g <$$> x) = (appendFun g fun x => g <$$> x) <$$> M.dest P x :=
  by
  cases' x with a f
  rw [map_eq]
  conv =>
    rhs
    rw [M.dest, M.dest', map_eq, append_fun_comp_split_fun]
  rfl
#align mvpfunctor.M.dest_map MvPFunctor.M.dest_map
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print MvPFunctor.M.map_dest /-
theorem M.map_dest {α β : TypeVec n} (g : (α ::: P.M α) ⟹ (β ::: P.M β)) (x : P.M α)
    (h : ∀ x : P.M α, lastFun g x = (dropFun g <$$> x : P.M β)) :
    g <$$> M.dest P x = M.dest P (dropFun g <$$> x) :=
  by
  rw [M.dest_map]; congr
  apply eq_of_drop_last_eq <;> simp
  ext1; apply h
#align mvpfunctor.M.map_dest MvPFunctor.M.map_dest
-/

end MvPFunctor


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

! This file was ported from Lean 3 source module analysis.normed.order.upper_lower
! leanprover-community/mathlib commit 992efbda6f85a5c9074375d3c7cb9764c64d8f72
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Field.Pi
import Mathbin.Analysis.Normed.Group.Pointwise
import Mathbin.Analysis.Normed.Order.Basic
import Mathbin.Topology.Algebra.Order.UpperLower

/-!
# Upper/lower/order-connected sets in normed groups

The topological closure and interior of an upper/lower/order-connected set is an
upper/lower/order-connected set (with the notable exception of the closure of an order-connected
set).

We also prove lemmas specific to `ℝⁿ`. Those are helpful to prove that order-connected sets in `ℝⁿ`
are measurable.
-/

open Function Metric Set

variable {α ι : Type _}

section MetricSpace

variable [NormedOrderedGroup α] {s : Set α}

@[to_additive IsUpperSet.thickening]
protected theorem IsUpperSet.thickening' (hs : IsUpperSet s) (ε : ℝ) :
    IsUpperSet (thickening ε s) := by
  rw [← ball_mul_one]
  exact hs.mul_left
#align is_upper_set.thickening' IsUpperSet.thickening'
#align is_upper_set.thickening IsUpperSet.thickening

@[to_additive IsLowerSet.thickening]
protected theorem IsLowerSet.thickening' (hs : IsLowerSet s) (ε : ℝ) :
    IsLowerSet (thickening ε s) := by
  rw [← ball_mul_one]
  exact hs.mul_left
#align is_lower_set.thickening' IsLowerSet.thickening'
#align is_lower_set.thickening IsLowerSet.thickening

@[to_additive IsUpperSet.cthickening]
protected theorem IsUpperSet.cthickening' (hs : IsUpperSet s) (ε : ℝ) :
    IsUpperSet (cthickening ε s) :=
  by
  rw [cthickening_eq_Inter_thickening'']
  exact isUpperSet_interᵢ₂ fun δ hδ => hs.thickening' _
#align is_upper_set.cthickening' IsUpperSet.cthickening'
#align is_upper_set.cthickening IsUpperSet.cthickening

@[to_additive IsLowerSet.cthickening]
protected theorem IsLowerSet.cthickening' (hs : IsLowerSet s) (ε : ℝ) :
    IsLowerSet (cthickening ε s) :=
  by
  rw [cthickening_eq_Inter_thickening'']
  exact isLowerSet_interᵢ₂ fun δ hδ => hs.thickening' _
#align is_lower_set.cthickening' IsLowerSet.cthickening'
#align is_lower_set.cthickening IsLowerSet.cthickening

end MetricSpace

/-! ### `ℝⁿ` -/

section Finite

variable [Finite ι] {s : Set (ι → ℝ)} {x y : ι → ℝ} {δ : ℝ}

theorem IsUpperSet.mem_interior_of_forall_lt (hs : IsUpperSet s) (hx : x ∈ closure s)
    (h : ∀ i, x i < y i) : y ∈ interior s :=
  by
  cases nonempty_fintype ι
  obtain ⟨ε, hε, hxy⟩ := Pi.exists_forall_pos_add_lt h
  obtain ⟨z, hz, hxz⟩ := Metric.mem_closure_iff.1 hx _ hε
  rw [dist_pi_lt_iff hε] at hxz
  have hyz : ∀ i, z i < y i :=
    by
    refine' fun i => (hxy _).trans_le' (sub_le_iff_le_add'.1 <| (le_abs_self _).trans _)
    rw [← Real.norm_eq_abs, ← dist_eq_norm']
    exact (hxz _).le
  obtain ⟨δ, hδ, hyz⟩ := Pi.exists_forall_pos_add_lt hyz
  refine' mem_interior.2 ⟨ball y δ, _, is_open_ball, mem_ball_self hδ⟩
  rintro w hw
  refine' hs (fun i => _) hz
  simp_rw [ball_pi _ hδ, Real.ball_eq_Ioo] at hw
  exact ((lt_sub_iff_add_lt.2 <| hyz _).trans (hw _ <| mem_univ _).1).le
#align is_upper_set.mem_interior_of_forall_lt IsUpperSet.mem_interior_of_forall_lt

theorem IsLowerSet.mem_interior_of_forall_lt (hs : IsLowerSet s) (hx : x ∈ closure s)
    (h : ∀ i, y i < x i) : y ∈ interior s :=
  by
  cases nonempty_fintype ι
  obtain ⟨ε, hε, hxy⟩ := Pi.exists_forall_pos_add_lt h
  obtain ⟨z, hz, hxz⟩ := Metric.mem_closure_iff.1 hx _ hε
  rw [dist_pi_lt_iff hε] at hxz
  have hyz : ∀ i, y i < z i :=
    by
    refine' fun i =>
      (lt_sub_iff_add_lt.2 <| hxy _).trans_le (sub_le_comm.1 <| (le_abs_self _).trans _)
    rw [← Real.norm_eq_abs, ← dist_eq_norm]
    exact (hxz _).le
  obtain ⟨δ, hδ, hyz⟩ := Pi.exists_forall_pos_add_lt hyz
  refine' mem_interior.2 ⟨ball y δ, _, is_open_ball, mem_ball_self hδ⟩
  rintro w hw
  refine' hs (fun i => _) hz
  simp_rw [ball_pi _ hδ, Real.ball_eq_Ioo] at hw
  exact ((hw _ <| mem_univ _).2.trans <| hyz _).le
#align is_lower_set.mem_interior_of_forall_lt IsLowerSet.mem_interior_of_forall_lt

end Finite

section Fintype

variable [Fintype ι] {s : Set (ι → ℝ)} {x y : ι → ℝ} {δ : ℝ}

theorem IsUpperSet.exists_subset_ball (hs : IsUpperSet s) (hx : x ∈ closure s) (hδ : 0 < δ) :
    ∃ y, closedBall y (δ / 4) ⊆ closedBall x δ ∧ closedBall y (δ / 4) ⊆ interior s :=
  by
  refine' ⟨x + const _ (3 / 4 * δ), closed_ball_subset_closed_ball' _, _⟩
  · rw [dist_self_add_left]
    refine' (add_le_add_left (pi_norm_const_le <| 3 / 4 * δ) _).trans_eq _
    simp [Real.norm_of_nonneg, hδ.le, zero_le_three]
    ring_nf
  obtain ⟨y, hy, hxy⟩ := Metric.mem_closure_iff.1 hx _ (div_pos hδ zero_lt_four)
  refine' fun z hz => hs.mem_interior_of_forall_lt (subset_closure hy) fun i => _
  rw [mem_closed_ball, dist_eq_norm'] at hz
  rw [dist_eq_norm] at hxy
  replace hxy := (norm_le_pi_norm _ i).trans hxy.le
  replace hz := (norm_le_pi_norm _ i).trans hz
  dsimp at hxy hz
  rw [abs_sub_le_iff] at hxy hz
  linarith
#align is_upper_set.exists_subset_ball IsUpperSet.exists_subset_ball

theorem IsLowerSet.exists_subset_ball (hs : IsLowerSet s) (hx : x ∈ closure s) (hδ : 0 < δ) :
    ∃ y, closedBall y (δ / 4) ⊆ closedBall x δ ∧ closedBall y (δ / 4) ⊆ interior s :=
  by
  refine' ⟨x - const _ (3 / 4 * δ), closed_ball_subset_closed_ball' _, _⟩
  · rw [dist_self_sub_left]
    refine' (add_le_add_left (pi_norm_const_le <| 3 / 4 * δ) _).trans_eq _
    simp [Real.norm_of_nonneg, hδ.le, zero_le_three]
    ring_nf
  obtain ⟨y, hy, hxy⟩ := Metric.mem_closure_iff.1 hx _ (div_pos hδ zero_lt_four)
  refine' fun z hz => hs.mem_interior_of_forall_lt (subset_closure hy) fun i => _
  rw [mem_closed_ball, dist_eq_norm'] at hz
  rw [dist_eq_norm] at hxy
  replace hxy := (norm_le_pi_norm _ i).trans hxy.le
  replace hz := (norm_le_pi_norm _ i).trans hz
  dsimp at hxy hz
  rw [abs_sub_le_iff] at hxy hz
  linarith
#align is_lower_set.exists_subset_ball IsLowerSet.exists_subset_ball

end Fintype


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne, Benjamin Davidson
-/
import analysis.special_functions.trigonometric.arctan
import analysis.special_functions.trigonometric.complex_deriv

/-!
# Derivatives of the `tan` and `arctan` functions.

Continuity and derivatives of the tangent and arctangent functions.
-/

noncomputable theory

namespace real

open set filter
open_locale topological_space real

lemma has_strict_deriv_at_tan {x : ℝ} (h : cos x ≠ 0) :
  has_strict_deriv_at tan (1 / (cos x)^2) x :=
by exact_mod_cast (complex.has_strict_deriv_at_tan (by exact_mod_cast h)).real_of_complex

lemma has_deriv_at_tan {x : ℝ} (h : cos x ≠ 0) :
  has_deriv_at tan (1 / (cos x)^2) x :=
by exact_mod_cast (complex.has_deriv_at_tan (by exact_mod_cast h)).real_of_complex

lemma tendsto_abs_tan_of_cos_eq_zero {x : ℝ} (hx : cos x = 0) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] x) at_top :=
begin
  have hx : complex.cos x = 0, by exact_mod_cast hx,
  simp only [← complex.abs_of_real, complex.of_real_tan],
  refine (complex.tendsto_abs_tan_of_cos_eq_zero hx).comp _,
  refine tendsto.inf complex.continuous_of_real.continuous_at _,
  exact tendsto_principal_principal.2 (λ y, mt complex.of_real_inj.1)
end

lemma tendsto_abs_tan_at_top (k : ℤ) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] ((2 * k + 1) * π / 2)) at_top :=
tendsto_abs_tan_of_cos_eq_zero $ cos_eq_zero_iff.2 ⟨k, rfl⟩

lemma continuous_at_tan {x : ℝ} : continuous_at tan x ↔ cos x ≠ 0 :=
begin
  refine ⟨λ hc h₀, _, λ h, (has_deriv_at_tan h).continuous_at⟩,
  exact not_tendsto_nhds_of_tendsto_at_top (tendsto_abs_tan_of_cos_eq_zero h₀) _
    (hc.norm.tendsto.mono_left inf_le_left)
end

lemma differentiable_at_tan {x : ℝ} : differentiable_at ℝ tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at, λ h, (has_deriv_at_tan h).differentiable_at⟩

@[simp] lemma deriv_tan (x : ℝ) : deriv tan x = 1 / (cos x)^2 :=
if h : cos x = 0 then
  have ¬differentiable_at ℝ tan x := mt differentiable_at_tan.1 (not_not.2 h),
  by simp [deriv_zero_of_not_differentiable_at this, h, sq]
else (has_deriv_at_tan h).deriv

@[simp] lemma cont_diff_at_tan {n x} : cont_diff_at ℝ n tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at,
  λ h, (complex.cont_diff_at_tan.2 $ by exact_mod_cast h).real_of_complex⟩

lemma has_deriv_at_tan_of_mem_Ioo {x : ℝ} (h : x ∈ Ioo (-(π/2):ℝ) (π/2)) :
  has_deriv_at tan (1 / (cos x)^2) x :=
has_deriv_at_tan (cos_pos_of_mem_Ioo h).ne'

lemma differentiable_at_tan_of_mem_Ioo {x : ℝ} (h : x ∈ Ioo (-(π/2):ℝ) (π/2)) :
  differentiable_at ℝ tan x :=
(has_deriv_at_tan_of_mem_Ioo h).differentiable_at

lemma has_strict_deriv_at_arctan (x : ℝ) : has_strict_deriv_at arctan (1 / (1 + x^2)) x :=
have A : cos (arctan x) ≠ 0 := (cos_arctan_pos x).ne',
by simpa [cos_sq_arctan]
  using tan_local_homeomorph.has_strict_deriv_at_symm trivial (by simpa) (has_strict_deriv_at_tan A)

lemma has_deriv_at_arctan (x : ℝ) : has_deriv_at arctan (1 / (1 + x^2)) x :=
(has_strict_deriv_at_arctan x).has_deriv_at

lemma differentiable_at_arctan (x : ℝ) : differentiable_at ℝ arctan x :=
(has_deriv_at_arctan x).differentiable_at

lemma differentiable_arctan : differentiable ℝ arctan := differentiable_at_arctan

@[simp] lemma deriv_arctan : deriv arctan = (λ x, 1 / (1 + x^2)) :=
funext $ λ x, (has_deriv_at_arctan x).deriv

lemma cont_diff_arctan {n : with_top ℕ} : cont_diff ℝ n arctan :=
cont_diff_iff_cont_diff_at.2 $ λ x,
have cos (arctan x) ≠ 0 := (cos_arctan_pos x).ne',
tan_local_homeomorph.cont_diff_at_symm_deriv (by simpa) trivial (has_deriv_at_tan this)
  (cont_diff_at_tan.2 this)

end real

section
/-!
### Lemmas for derivatives of the composition of `real.arctan` with a differentiable function

In this section we register lemmas for the derivatives of the composition of `real.arctan` with a
differentiable function, for standalone use and use with `simp`. -/

open real

section deriv

variables {f : ℝ → ℝ} {f' x : ℝ} {s : set ℝ}

lemma has_strict_deriv_at.arctan (hf : has_strict_deriv_at f f' x) :
  has_strict_deriv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') x :=
(real.has_strict_deriv_at_arctan (f x)).comp x hf

lemma has_deriv_at.arctan (hf : has_deriv_at f f' x) :
  has_deriv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') x :=
(real.has_deriv_at_arctan (f x)).comp x hf

lemma has_deriv_within_at.arctan (hf : has_deriv_within_at f f' s x) :
  has_deriv_within_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') s x :=
(real.has_deriv_at_arctan (f x)).comp_has_deriv_within_at x hf

lemma deriv_within_arctan (hf : differentiable_within_at ℝ f s x)
  (hxs : unique_diff_within_at ℝ s x) :
  deriv_within (λ x, arctan (f x)) s x = (1 / (1 + (f x)^2)) * (deriv_within f s x) :=
hf.has_deriv_within_at.arctan.deriv_within hxs

@[simp] lemma deriv_arctan (hc : differentiable_at ℝ f x) :
  deriv (λ x, arctan (f x)) x = (1 / (1 + (f x)^2)) * (deriv f x) :=
hc.has_deriv_at.arctan.deriv

end deriv

section fderiv

variables {E : Type*} [normed_group E] [normed_space ℝ E] {f : E → ℝ} {f' : E →L[ℝ] ℝ} {x : E}
  {s : set E} {n : with_top ℕ}

lemma has_strict_fderiv_at.arctan (hf : has_strict_fderiv_at f f' x) :
  has_strict_fderiv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') x :=
(has_strict_deriv_at_arctan (f x)).comp_has_strict_fderiv_at x hf

lemma has_fderiv_at.arctan (hf : has_fderiv_at f f' x) :
  has_fderiv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') x :=
(has_deriv_at_arctan (f x)).comp_has_fderiv_at x hf

lemma has_fderiv_within_at.arctan (hf : has_fderiv_within_at f f' s x) :
  has_fderiv_within_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') s x :=
(has_deriv_at_arctan (f x)).comp_has_fderiv_within_at x hf

lemma fderiv_within_arctan (hf : differentiable_within_at ℝ f s x)
  (hxs : unique_diff_within_at ℝ s x) :
  fderiv_within ℝ (λ x, arctan (f x)) s x = (1 / (1 + (f x)^2)) • (fderiv_within ℝ f s x) :=
hf.has_fderiv_within_at.arctan.fderiv_within hxs

@[simp] lemma fderiv_arctan (hc : differentiable_at ℝ f x) :
  fderiv ℝ (λ x, arctan (f x)) x = (1 / (1 + (f x)^2)) • (fderiv ℝ f x) :=
hc.has_fderiv_at.arctan.fderiv

lemma differentiable_within_at.arctan (hf : differentiable_within_at ℝ f s x) :
  differentiable_within_at ℝ (λ x, real.arctan (f x)) s x :=
hf.has_fderiv_within_at.arctan.differentiable_within_at

@[simp] lemma differentiable_at.arctan (hc : differentiable_at ℝ f x) :
  differentiable_at ℝ (λ x, arctan (f x)) x :=
hc.has_fderiv_at.arctan.differentiable_at

lemma differentiable_on.arctan (hc : differentiable_on ℝ f s) :
  differentiable_on ℝ (λ x, arctan (f x)) s :=
λ x h, (hc x h).arctan

@[simp] lemma differentiable.arctan (hc : differentiable ℝ f) :
  differentiable ℝ (λ x, arctan (f x)) :=
λ x, (hc x).arctan

lemma cont_diff_at.arctan (h : cont_diff_at ℝ n f x) :
  cont_diff_at ℝ n (λ x, arctan (f x)) x :=
cont_diff_arctan.cont_diff_at.comp x h

lemma cont_diff.arctan (h : cont_diff ℝ n f) :
  cont_diff ℝ n (λ x, arctan (f x)) :=
cont_diff_arctan.comp h

lemma cont_diff_within_at.arctan (h : cont_diff_within_at ℝ n f s x) :
  cont_diff_within_at ℝ n (λ x, arctan (f x)) s x :=
cont_diff_arctan.comp_cont_diff_within_at h

lemma cont_diff_on.arctan (h : cont_diff_on ℝ n f s) :
  cont_diff_on ℝ n (λ x, arctan (f x)) s :=
cont_diff_arctan.comp_cont_diff_on h

end fderiv
end


/-
Copyright (c) 2018 Andreas Swerdlow. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andreas Swerdlow, Kenny Lau
-/
import algebra.ring.equiv
import algebra.ring.opposite

/-!
# Ring involutions

This file defines a ring involution as a structure extending `R ≃+* Rᵐᵒᵖ`,
with the additional fact `f.involution : (f (f x).unop).unop = x`.

## Notations

We provide a coercion to a function `R → Rᵐᵒᵖ`.

## References

* <https://en.wikipedia.org/wiki/Involution_(mathematics)#Ring_theory>

## Tags

Ring involution
-/

variables (R : Type*)

/-- A ring involution -/
structure ring_invo [semiring R] extends R ≃+* Rᵐᵒᵖ :=
(involution' : ∀ x, (to_fun (to_fun x).unop).unop = x)

namespace ring_invo
variables {R} [semiring R]

/-- Construct a ring involution from a ring homomorphism. -/
def mk' (f : R →+* Rᵐᵒᵖ) (involution : ∀ r, (f (f r).unop).unop = r) :
  ring_invo R :=
{ inv_fun := λ r, (f r.unop).unop,
  left_inv := λ r, involution r,
  right_inv := λ r, mul_opposite.unop_injective $ involution _,
  involution' := involution,
  .. f }

instance : has_coe_to_fun (ring_invo R) (λ _, R → Rᵐᵒᵖ) := ⟨λ f, f.to_ring_equiv.to_fun⟩

@[simp]
lemma to_fun_eq_coe (f : ring_invo R) : f.to_fun = f := rfl

@[simp]
lemma involution (f : ring_invo R) (x : R) : (f (f x).unop).unop = x :=
f.involution' x

instance has_coe_to_ring_equiv : has_coe (ring_invo R) (R ≃+* Rᵐᵒᵖ) :=
⟨ring_invo.to_ring_equiv⟩

@[norm_cast] lemma coe_ring_equiv (f : ring_invo R) (a : R) :
  (f : R ≃+* Rᵐᵒᵖ) a = f a := rfl

@[simp] lemma map_eq_zero_iff (f : ring_invo R) {x : R} : f x = 0 ↔ x = 0 :=
f.to_ring_equiv.map_eq_zero_iff

end ring_invo

open ring_invo

section comm_ring
variables [comm_ring R]

/-- The identity function of a `comm_ring` is a ring involution. -/
protected def ring_invo.id : ring_invo R :=
{ involution' := λ r, rfl,
  ..(ring_equiv.to_opposite R) }

instance : inhabited (ring_invo R) := ⟨ring_invo.id _⟩

end comm_ring


/-
Copyright (c) 2020 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import algebra.hom.group_action
import algebra.module.basic
import data.set_like.basic
import group_theory.group_action.basic
/-!

# Sets invariant to a `mul_action`

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

In this file we define `sub_mul_action R M`; a subset of a `mul_action R M` which is closed with
respect to scalar multiplication.

For most uses, typically `submodule R M` is more powerful.

## Main definitions

* `sub_mul_action.mul_action` - the `mul_action R M` transferred to the subtype.
* `sub_mul_action.mul_action'` - the `mul_action S M` transferred to the subtype when
  `is_scalar_tower S R M`.
* `sub_mul_action.is_scalar_tower` - the `is_scalar_tower S R M` transferred to the subtype.

## Tags

submodule, mul_action
-/

open function

universes u u' u'' v
variables {S : Type u'} {T : Type u''} {R : Type u} {M : Type v}

set_option old_structure_cmd true

/-- `smul_mem_class S R M` says `S` is a type of subsets `s ≤ M` that are closed under the
scalar action of `R` on `M`.

Note that only `R` is marked as an `out_param` here, since `M` is supplied by the `set_like`
class instead.
-/
class smul_mem_class (S : Type*) (R : out_param $ Type*) (M : Type*) [has_smul R M]
  [set_like S M] :=
(smul_mem : ∀ {s : S} (r : R) {m : M}, m ∈ s → r • m ∈ s)

/-- `vadd_mem_class S R M` says `S` is a type of subsets `s ≤ M` that are closed under the
additive action of `R` on `M`.

Note that only `R` is marked as an `out_param` here, since `M` is supplied by the `set_like`
class instead.
-/
class vadd_mem_class (S : Type*) (R : out_param $ Type*) (M : Type*) [has_vadd R M]
  [set_like S M] :=
(vadd_mem : ∀ {s : S} (r : R) {m : M}, m ∈ s → r +ᵥ m ∈ s)

attribute [to_additive] smul_mem_class

namespace set_like

variables [has_smul R M] [set_like S M] [hS : smul_mem_class S R M] (s : S)
include hS

open smul_mem_class

/-- A subset closed under the scalar action inherits that action. -/
@[to_additive "A subset closed under the additive action inherits that action.",
priority 900] -- lower priority so other instances are found first
instance has_smul : has_smul R s := ⟨λ r x, ⟨r • x.1, smul_mem r x.2⟩⟩

@[simp, norm_cast, to_additive, priority 900]
-- lower priority so later simp lemmas are used first; to appease simp_nf
protected lemma coe_smul (r : R) (x : s) : (↑(r • x) : M) = r • x := rfl

@[simp, to_additive, priority 900]
-- lower priority so later simp lemmas are used first; to appease simp_nf
lemma mk_smul_mk (r : R) (x : M) (hx : x ∈ s) :
  r • (⟨x, hx⟩ : s) = ⟨r • x, smul_mem r hx⟩ := rfl

@[to_additive] lemma smul_def (r : R) (x : s) : r • x = ⟨r • x, smul_mem r x.2⟩ := rfl

omit hS

@[simp] lemma forall_smul_mem_iff {R M S : Type*} [monoid R] [mul_action R M]
  [set_like S M] [smul_mem_class S R M] {N : S} {x : M} :
  (∀ (a : R), a • x ∈ N) ↔ x ∈ N :=
⟨λ h, by simpa using h 1, λ h a, smul_mem_class.smul_mem a h⟩

end set_like

/-- A sub_mul_action is a set which is closed under scalar multiplication.  -/
structure sub_mul_action (R : Type u) (M : Type v) [has_smul R M] : Type v :=
(carrier : set M)
(smul_mem' : ∀ (c : R) {x : M}, x ∈ carrier → c • x ∈ carrier)

namespace sub_mul_action

variables [has_smul R M]

instance : set_like (sub_mul_action R M) M :=
⟨sub_mul_action.carrier, λ p q h, by cases p; cases q; congr'⟩

instance : smul_mem_class (sub_mul_action R M) R M :=
{ smul_mem := smul_mem' }

@[simp] lemma mem_carrier {p : sub_mul_action R M} {x : M} : x ∈ p.carrier ↔ x ∈ (p : set M) :=
iff.rfl

@[ext] theorem ext {p q : sub_mul_action R M} (h : ∀ x, x ∈ p ↔ x ∈ q) : p = q := set_like.ext h

/-- Copy of a sub_mul_action with a new `carrier` equal to the old one. Useful to fix definitional
equalities.-/
protected def copy (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) : sub_mul_action R M :=
{ carrier := s,
  smul_mem' := hs.symm ▸ p.smul_mem' }

@[simp] lemma coe_copy (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) :
  (p.copy s hs : set M) = s := rfl

lemma copy_eq (p : sub_mul_action R M) (s : set M) (hs : s = ↑p) : p.copy s hs = p :=
set_like.coe_injective hs

instance : has_bot (sub_mul_action R M) :=
⟨{ carrier := ∅, smul_mem' := λ c, set.not_mem_empty}⟩

instance : inhabited (sub_mul_action R M) := ⟨⊥⟩

end sub_mul_action

namespace sub_mul_action

section has_smul

variables [has_smul R M]
variables (p : sub_mul_action R M)
variables {r : R} {x : M}

lemma smul_mem (r : R) (h : x ∈ p) : r • x ∈ p := p.smul_mem' r h

instance : has_smul R p :=
{ smul := λ c x, ⟨c • x.1, smul_mem _ c x.2⟩ }

variables {p}
@[simp, norm_cast] lemma coe_smul (r : R) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_mk (x : M) (hx : x ∈ p) : ((⟨x, hx⟩ : p) : M) = x := rfl

variables (p)

/-- Embedding of a submodule `p` to the ambient space `M`. -/
protected def subtype : p →[R] M :=
by refine {to_fun := coe, ..}; simp [coe_smul]

@[simp] theorem subtype_apply (x : p) : p.subtype x = x := rfl

lemma subtype_eq_val : ((sub_mul_action.subtype p) : p → M) = subtype.val := rfl

end has_smul

namespace smul_mem_class

variables [monoid R] [mul_action R M] {A : Type*} [set_like A M]
variables [hA : smul_mem_class A R M] (S' : A)

include hA
/-- A `sub_mul_action` of a `mul_action` is a `mul_action`.  -/
@[priority 75] -- Prefer subclasses of `mul_action` over `smul_mem_class`.
instance to_mul_action : mul_action R S' :=
subtype.coe_injective.mul_action coe (set_like.coe_smul S')

/-- The natural `mul_action_hom` over `R` from a `sub_mul_action` of `M` to `M`. -/
protected def subtype : S' →[R] M := ⟨coe,  λ _ _, rfl⟩

@[simp] protected theorem coe_subtype : (smul_mem_class.subtype S' : S' → M) = coe := rfl

end smul_mem_class

section mul_action_monoid

variables [monoid R] [mul_action R M]

section
variables [has_smul S R] [has_smul S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M)

lemma smul_of_tower_mem (s : S) {x : M} (h : x ∈ p) : s • x ∈ p :=
by { rw [←one_smul R x, ←smul_assoc], exact p.smul_mem _ h }

instance has_smul' : has_smul S p :=
{ smul := λ c x, ⟨c • x.1, smul_of_tower_mem _ c x.2⟩ }

instance : is_scalar_tower S R p :=
{ smul_assoc := λ s r x, subtype.ext $ smul_assoc s r ↑x }

instance is_scalar_tower' {S' : Type*} [has_smul S' R] [has_smul S' S]
  [has_smul S' M] [is_scalar_tower S' R M] [is_scalar_tower S' S M] :
  is_scalar_tower S' S p :=
{ smul_assoc := λ s r x, subtype.ext $ smul_assoc s r ↑x }

@[simp, norm_cast] lemma coe_smul_of_tower (s : S) (x : p) : ((s • x : p) : M) = s • ↑x := rfl

@[simp] lemma smul_mem_iff' {G} [group G] [has_smul G R] [mul_action G M]
  [is_scalar_tower G R M] (g : G) {x : M} :
  g • x ∈ p ↔ x ∈ p :=
⟨λ h, inv_smul_smul g x ▸ p.smul_of_tower_mem g⁻¹ h, p.smul_of_tower_mem g⟩

instance [has_smul Sᵐᵒᵖ R] [has_smul Sᵐᵒᵖ M] [is_scalar_tower Sᵐᵒᵖ R M]
  [is_central_scalar S M] : is_central_scalar S p :=
{ op_smul_eq_smul := λ r x, subtype.ext $ op_smul_eq_smul r x }

end

section

variables [monoid S] [has_smul S R] [mul_action S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M)

/-- If the scalar product forms a `mul_action`, then the subset inherits this action -/
instance mul_action' : mul_action S p :=
{ smul := (•),
  one_smul := λ x, subtype.ext $ one_smul _ x,
  mul_smul := λ c₁ c₂ x, subtype.ext $ mul_smul c₁ c₂ x }

instance : mul_action R p := p.mul_action'

end

/-- Orbits in a `sub_mul_action` coincide with orbits in the ambient space. -/
lemma coe_image_orbit {p : sub_mul_action R M} (m : p) :
  coe '' mul_action.orbit R m = mul_action.orbit R (m : M) := (set.range_comp _ _).symm

/- -- Previously, the relatively useless :
lemma orbit_of_sub_mul {p : sub_mul_action R M} (m : p) :
  (mul_action.orbit R m : set M) = mul_action.orbit R (m : M) := rfl
-/

/-- Stabilizers in monoid sub_mul_action coincide with stabilizers in the ambient space -/
lemma stabilizer_of_sub_mul.submonoid {p : sub_mul_action R M} (m : p) :
  mul_action.stabilizer.submonoid R m = mul_action.stabilizer.submonoid R (m : M) :=
begin
  ext,
  simp only [mul_action.mem_stabilizer_submonoid_iff,
      ← sub_mul_action.coe_smul, set_like.coe_eq_coe]
end

end mul_action_monoid

section mul_action_group

variables [group R] [mul_action R M]

/-- Stabilizers in group sub_mul_action coincide with stabilizers in the ambient space -/

end mul_action_group

section module

variables [semiring R] [add_comm_monoid M]
variables [module R M]
variables (p : sub_mul_action R M)

lemma zero_mem (h : (p : set M).nonempty) : (0 : M) ∈ p :=
let ⟨x, hx⟩ := h in zero_smul R (x : M) ▸ p.smul_mem 0 hx

/-- If the scalar product forms a `module`, and the `sub_mul_action` is not `⊥`, then the
subset inherits the zero. -/
instance [n_empty : nonempty p] : has_zero p :=
{ zero := ⟨0, n_empty.elim $ λ x, p.zero_mem ⟨x, x.prop⟩⟩ }

end module

section add_comm_group

variables [ring R] [add_comm_group M]
variables [module R M]
variables (p p' : sub_mul_action R M)
variables {r : R} {x y : M}

lemma neg_mem (hx : x ∈ p) : -x ∈ p := by { rw ← neg_one_smul R, exact p.smul_mem _ hx }

@[simp] lemma neg_mem_iff : -x ∈ p ↔ x ∈ p :=
⟨λ h, by { rw ←neg_neg x, exact neg_mem _ h}, neg_mem _⟩

instance : has_neg p := ⟨λx, ⟨-x.1, neg_mem _ x.2⟩⟩

@[simp, norm_cast] lemma coe_neg (x : p) : ((-x : p) : M) = -x := rfl

end add_comm_group

end sub_mul_action

namespace sub_mul_action

variables [group_with_zero S] [monoid R] [mul_action R M]
variables [has_smul S R] [mul_action S M] [is_scalar_tower S R M]
variables (p : sub_mul_action R M) {s : S} {x y : M}

theorem smul_mem_iff (s0 : s ≠ 0) : s • x ∈ p ↔ x ∈ p :=
p.smul_mem_iff' (units.mk0 s s0)

end sub_mul_action


/-
Copyright (c) 2020 Kevin Kappelmann. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Kappelmann

! This file was ported from Lean 3 source module algebra.continued_fractions.terminated_stable
! leanprover-community/mathlib commit a7e36e48519ab281320c4d192da6a7b348ce40ad
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.ContinuedFractions.Translations

/-!
# Stabilisation of gcf Computations Under Termination

## Summary

We show that the continuants and convergents of a gcf stabilise once the gcf terminates.
-/

namespace GeneralizedContinuedFraction

/- ./././Mathport/Syntax/Translate/Command.lean:224:11: unsupported: unusual advanced open style -/
variable {K : Type _} {g : GeneralizedContinuedFraction K} {n m : ℕ}

/-- If a gcf terminated at position `n`, it also terminated at `m ≥ n`.-/
theorem terminated_stable (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.TerminatedAt m :=
  g.s.terminated_stable n_le_m terminated_at_n
#align generalized_continued_fraction.terminated_stable GeneralizedContinuedFraction.terminated_stable

variable [DivisionRing K]

theorem continuantsAux_stable_step_of_terminated (terminated_at_n : g.TerminatedAt n) :
    g.continuantsAux (n + 2) = g.continuantsAux (n + 1) :=
  by
  rw [terminated_at_iff_s_none] at terminated_at_n
  simp only [terminated_at_n, continuants_aux]
#align generalized_continued_fraction.continuants_aux_stable_step_of_terminated GeneralizedContinuedFraction.continuantsAux_stable_step_of_terminated

theorem continuantsAux_stable_of_terminated (n_lt_m : n < m) (terminated_at_n : g.TerminatedAt n) :
    g.continuantsAux m = g.continuantsAux (n + 1) :=
  by
  refine' Nat.le_induction rfl (fun k hnk hk => _) _ n_lt_m
  rcases Nat.exists_eq_add_of_lt hnk with ⟨k, rfl⟩
  refine' (continuants_aux_stable_step_of_terminated _).trans hk
  exact terminated_stable (Nat.le_add_right _ _) terminated_at_n
#align generalized_continued_fraction.continuants_aux_stable_of_terminated GeneralizedContinuedFraction.continuantsAux_stable_of_terminated

theorem convergents'Aux_stable_step_of_terminated {s : Seq <| Pair K}
    (terminated_at_n : s.TerminatedAt n) : convergents'Aux s (n + 1) = convergents'Aux s n :=
  by
  change s.nth n = none at terminated_at_n
  induction' n with n IH generalizing s
  case zero => simp only [convergents'_aux, terminated_at_n, seq.head]
  case succ =>
    cases' s_head_eq : s.head with gp_head
    case none => simp only [convergents'_aux, s_head_eq]
    case
      some =>
      have : s.tail.terminated_at n := by simp only [seq.terminated_at, s.nth_tail, terminated_at_n]
      simp only [convergents'_aux, s_head_eq, IH this]
#align generalized_continued_fraction.convergents'_aux_stable_step_of_terminated GeneralizedContinuedFraction.convergents'Aux_stable_step_of_terminated

theorem convergents'Aux_stable_of_terminated {s : Seq <| Pair K} (n_le_m : n ≤ m)
    (terminated_at_n : s.TerminatedAt n) : convergents'Aux s m = convergents'Aux s n :=
  by
  induction' n_le_m with m n_le_m IH
  · rfl
  · refine' (convergents'_aux_stable_step_of_terminated _).trans IH
    exact s.terminated_stable n_le_m terminated_at_n
#align generalized_continued_fraction.convergents'_aux_stable_of_terminated GeneralizedContinuedFraction.convergents'Aux_stable_of_terminated

theorem continuants_stable_of_terminated (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.continuants m = g.continuants n := by
  simp only [nth_cont_eq_succ_nth_cont_aux,
    continuants_aux_stable_of_terminated (nat.pred_le_iff.elim_left n_le_m) terminated_at_n]
#align generalized_continued_fraction.continuants_stable_of_terminated GeneralizedContinuedFraction.continuants_stable_of_terminated

theorem numerators_stable_of_terminated (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.numerators m = g.numerators n := by
  simp only [num_eq_conts_a, continuants_stable_of_terminated n_le_m terminated_at_n]
#align generalized_continued_fraction.numerators_stable_of_terminated GeneralizedContinuedFraction.numerators_stable_of_terminated

theorem denominators_stable_of_terminated (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.denominators m = g.denominators n := by
  simp only [denom_eq_conts_b, continuants_stable_of_terminated n_le_m terminated_at_n]
#align generalized_continued_fraction.denominators_stable_of_terminated GeneralizedContinuedFraction.denominators_stable_of_terminated

theorem convergents_stable_of_terminated (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.convergents m = g.convergents n := by
  simp only [convergents, denominators_stable_of_terminated n_le_m terminated_at_n,
    numerators_stable_of_terminated n_le_m terminated_at_n]
#align generalized_continued_fraction.convergents_stable_of_terminated GeneralizedContinuedFraction.convergents_stable_of_terminated

theorem convergents'_stable_of_terminated (n_le_m : n ≤ m) (terminated_at_n : g.TerminatedAt n) :
    g.convergents' m = g.convergents' n := by
  simp only [convergents', convergents'_aux_stable_of_terminated n_le_m terminated_at_n]
#align generalized_continued_fraction.convergents'_stable_of_terminated GeneralizedContinuedFraction.convergents'_stable_of_terminated

end GeneralizedContinuedFraction