import algebra.periodic

/-!
# Big operators on periodic sequences indexed by ℕ

Here we just prove the following result.
Let `M` be a commutative monoid and `n : ℕ`.
Let `a : ℕ → M` be an `n`-periodic sequence.
Then `∏ [i < n] a(i) = ∏ [i < n] a(i + k)` for any `k : ℕ`.
Similar results hold when `M` is a commutative additive monoid with sums.
-/

namespace IMOSL
namespace extra

open function finset

lemma periodic_prod_const {M : Type*} [comm_monoid M] {a : ℕ → M} {n : ℕ}
  (h : periodic a n) (k : ℕ) : (range n).prod (λ m, a (m + k)) = (range n).prod a :=
begin
  induction k with k k_ih,
  simp only [add_zero],
  conv_lhs { congr, skip, funext, rw [nat.succ_eq_one_add, ← add_assoc] },
  cases n with n n,
  rw [prod_range_zero, prod_range_zero],
  rw [prod_range_succ, add_comm, h, ← k_ih, prod_range_succ', zero_add]
end

lemma periodic_sum_const {M : Type*} [add_comm_monoid M] {a : ℕ → M} {n : ℕ}
  (h : periodic a n) (k : ℕ) : (range n).sum (λ m, a (m + k)) = (range n).sum a :=
begin
  induction k with k k_ih,
  simp only [add_zero],
  conv_lhs { congr, skip, funext, rw [nat.succ_eq_one_add, ← add_assoc] },
  cases n with n n,
  rw [sum_range_zero, sum_range_zero],
  rw [sum_range_succ, add_comm (n + 1), h, ← k_ih, sum_range_succ', zero_add]
end

end extra
end IMOSL


variable {p : Prop}
variable {q : Prop}

section hrm
-- theorems involving only `→` can be proved w lambda
-- `theorem` command intrduces new theorem - basically a version of `def` command (kernel sees it the same)
  theorem t1 : p → q → p := fun hp : p => fun hq : q => hp
  #print t1

  -- this is like definition of constant function, but using args of `Prop` rather than `Type`
  -- intuitively, it assumes `p` and `q` are true, & uses first hypothesis (trivially) to establish conclusion `p` is true

-- `theorem` - basically a version of `def` command (kernel sees it the same)
-- but there's a few pragmatic diffs:
  -- in normal circumstances - not necessary to unfold def of a theorem
  -- lean tags proofs as irreducible - hint to not unfold
  -- lean also able to process & check proofs in parallel (since assissing correctness of proof does not require knowing details of another)

/- I THINK THIS IS THE IMPORTANT PART -/

-- lambda abstractions `hp : p` and `hq : q` can be viewed as temporary assumptions in proof of `t2`
  -- lean allows us to specify type of final term `hp` with `show` statement
    -- adding this extra info can improve clarity of a proof and help detect errors.
    -- `show` does nothing more than annotate the type
    -- internally, all presentations of `t2` produce same term

  theorem t2 : p → q → p :=
    fun hp : p =>
    fun hq : q =>
    show p from hp
  #print t2

-- can move lambda abs variables to left of the colon:
  theorem t3 (hp : p) (hq : q) : p := hp
  #print t3    -- p → q → p

  -- then apply theorem t4 as func application
  axiom hp : p
  theorem t4 : q → p := t1 hp
  -- here `axiom` declaration postulates existence of element of given type (& may compromise logical consistency)
    -- ie can use to postulate empty type `False` has an element

      axiom unsound : False
      -- Everything follows from false
      theorem ex : 1 = 0 :=
      False.elim unsound
  -- thus declaring an axiom `hp : p` is tantamount to declaring that `p` is true as witnessed by `hp`
  -- applying theorem `t4 : p → q → p` to fact that `hp : p` that `p` is ture yields `t1 hp : q → p`

  -- type of t1,2,3,4 is now `∀ {p q : Prop}, p → q → p`
    -- can read this as, "for every pair of props `p q`, we hv `p → q → p`"
    -- ie can move all parameters to right of colon
      theorem t5 : ∀ {p q : Prop}, p → q → p :=
        fun {p q : Prop} (hp : p) (hq : q) => hp

    -- (if p and q has been delcared as variables, lean can generalize)
      variable {p q : Prop}
      theorem t6 : p → q → p := fun (hp : p) (hq : q) => hp

    -- THEN by props-as-types correspondence, we can declare the assumption `hp` that `p` holds, as another variable
      -- lean detects it uses `hp` and automatically adds `hp : p` as premise
      variable {p q : Prop}
      variable (hp : p)
      theorem t7 : q → p := fun (hq : q) => hp
      #print t7
end hrm

/- generalizing t -/
-- applies to all variables
section bruv
theorem t0 (p q : Prop) (hp : p) (hq : q) : p := hp

variable (p q r s : Prop)

#check t0 p q                -- p → q → p
#check t0 r s                -- r → s → r
#check t0 (r → s) (s → r)    -- (r → s) → (s → r) → r → s

variable (h : r → s)
-- variable `h` of type `r → s` can be views as hypothesis or premise that `r → s` holds
#check t0 (r → s) (s → r) h  -- (s → r) → r → s

/- this is composition func from chapter 2 but using props instead of types-/
variable (p q r s : Prop)

theorem t01 (h₁ : q → r) (h₂ : p → q) : p → r :=
fun h₃ : p =>
show r from h₁ (h₂ h₃)
-- t01 says fun of h₁ and h₂ defined above composes idk

end bruv


/-
Copyright (c) 2021 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä
-/
import topology.instances.real
import topology.instances.ennreal
import portmanteau_comeonlean_lemmas

noncomputable theory
open set
open filter
open_locale topological_space

namespace portmanteau

abbreviation liminf_R (s : ℕ → ℝ) : ℝ := liminf at_top s

abbreviation limsup_R (s : ℕ → ℝ) : ℝ := limsup at_top s

abbreviation liminf_enn (s : ℕ → ennreal) : ennreal := liminf at_top s

abbreviation limsup_enn (s : ℕ → ennreal) : ennreal := limsup at_top s

abbreviation lim_R (s : ℕ → ℝ) (l : ℝ) : Prop := tendsto s at_top (𝓝 l)

abbreviation lim_enn (s : ℕ → ennreal) (l : ennreal) : Prop := tendsto s at_top (𝓝 l)

lemma lim_R_rw (s : ℕ → ℝ) (l : ℝ) : lim_R s l = tendsto s at_top (𝓝 l) := by refl

lemma liminf_enn_rw {s : ℕ → ennreal} : liminf_enn s = liminf at_top s := by refl

lemma limsup_enn_rw {s : ℕ → ennreal} : limsup_enn s = limsup at_top s := by refl

section portmanteau_limsup_liminf_lemmas

lemma liminf_le_limsup_enn (s : ℕ → ennreal) :
  (liminf_enn s) ≤ (limsup_enn s) :=
begin
  apply @liminf_le_limsup ennreal _ _ _ _ s ,
  exact at_top_ne_bot ,
end

lemma lim_eq_liminf_of_limsup_le_liminf_ennreal {s : ℕ → ennreal}
  (hle : limsup_enn s ≤ liminf_enn s ) :
    lim_enn (s) (liminf_enn s)  :=
begin
  have heq : limsup_enn s = liminf_enn s := le_antisymm hle (liminf_le_limsup_enn s) ,
  set l := liminf_enn s with hl,
  exact tendsto_of_liminf_eq_limsup hl heq ,
end

lemma lim_eq_limsup_of_limsup_le_liminf_ennreal {s : ℕ → ennreal}
  (hle : limsup_enn s ≤ liminf_enn s ) :
    lim_enn s (limsup_enn s) :=
begin
  rw le_antisymm hle (liminf_le_limsup_enn s) ,
  exact lim_eq_liminf_of_limsup_le_liminf_ennreal hle ,
end

lemma lim_eq_limsup_ennreal {s : ℕ → ennreal} {l : ennreal} (hlim : lim_enn s l ) :
  limsup_enn s = l := tendsto.limsup_eq hlim

lemma lim_eq_liminf_ennreal {s : ℕ → ennreal} {l : ennreal} (hlim : lim_enn s l ) :
  liminf_enn s = l := tendsto.liminf_eq hlim

lemma limsup_le_of_le_ennreal {s : ℕ → ennreal} {b : ennreal} (hb : ∀ n , s(n) ≤ b ) :
  limsup_enn s ≤ b :=
begin
  have key : s ≤ᶠ[at_top] (λ n , b) := eventually_of_forall hb ,
  have le := limsup_le_limsup key ,
  have lim : tendsto (λ (n:ℕ) , b) at_top (𝓝 b) := tendsto_const_nhds ,
  have eq : at_top.limsup (λ n , b) = b := lim_eq_limsup_ennreal lim ,
  rwa eq at le ,
end

lemma liminf_le_of_le_ennreal {s : ℕ → ennreal} {b : ennreal} (hb : ∀ n , s(n) ≤ b ) :
  liminf_enn s ≤ b :=
begin
  have key : s ≤ᶠ[at_top] (λ n , b) := eventually_of_forall hb ,
  have le := liminf_le_liminf key ,
  have lim : tendsto (λ (n:ℕ) , b) at_top (𝓝 b) := tendsto_const_nhds ,
  have eq : at_top.liminf (λ n , b) = b := lim_eq_liminf_ennreal lim ,
  rwa eq at le ,
end

-- TODO: Why does Limsup_le_Limsup_of_le not work?
lemma limsup_enn_mono {s t : ℕ → ennreal} (hst : ∀ (n : ℕ) , s(n) ≤ t(n)) :
    limsup_enn s ≤ limsup_enn t :=
begin
  have relax : s ≤ᶠ[at_top] t ,
  { apply eventually_of_mem (@univ_mem_sets ℕ at_top) ,
    intros n hn ,
    exact hst n , } ,
  exact limsup_le_limsup relax ,
end

-- TODO: Why does Liminf_le_Liminf_of_le not work?
lemma liminf_enn_mono {s t : ℕ → ennreal} (hst : ∀ (n : ℕ) , s(n) ≤ t(n)) :
    liminf_enn s ≤ liminf_enn t :=
begin
  have relax : s ≤ᶠ[at_top] t ,
  { apply eventually_of_mem (@univ_mem_sets ℕ at_top) ,
    intros n hn ,
    exact hst n , } ,
  exact liminf_le_liminf relax ,
end

lemma Sup_neg (A : set ℝ) : Sup {x : ℝ | -x ∈ A} = - Inf A :=
begin
  set mA := {x : ℝ | -x ∈ A} with hmA ,
  set z := Sup mA with hz ,
  have key := real.Inf_def A ,
  rw key ,
  simp ,
end

lemma Inf_neg (A : set ℝ) : Inf {x : ℝ | -x ∈ A} = - Sup A :=
begin
  set mA := {x : ℝ | -x ∈ A} with hmA ,
  have key := Sup_neg mA ,
  set mmA := {x : ℝ | -x ∈ mA} with hmmA ,
  have AmmA : A = mmA := by simp [hmA , hmmA],
  rw [AmmA, key] ,
  simp ,
end

lemma liminf_neg {s : ℕ → ℝ} :
    liminf_R (-s) = - limsup_R s :=
begin
  set A := { a : ℝ | ∃ (a_1 : ℕ) , ∀ (b : ℕ) , a_1 ≤ b → s b ≤ a } with hA ,
  set mA := { a : ℝ | -a ∈ A } with hmA ,
  have mA_rw : mA = { a : ℝ | ∃ (a_1 : ℕ) , ∀ (b : ℕ) , a_1 ≤ b → a ≤ - s b } ,
  { ext a ,
    rw mem_set_of_eq ,
    split ; -- hide in corners
    { intro h ,
      cases h with k hk ,
      use k ,
      intros l hl ,
      specialize hk l hl ,
      linarith , } ,
    } ,
  have ls_eq : limsup_R s = Inf A ,
  { change limsup at_top s = Inf A ,
    rw limsup_eq ,
    simp only [eventually_at_top] , } ,
  have li_eq : liminf_R (-s) = Sup mA ,
  { change liminf at_top (-s) = Sup mA ,
    rw liminf_eq ,
    simp only [mA_rw, pi.neg_apply, eventually_at_top] , } ,
  rw [li_eq, ls_eq] ,
  exact Sup_neg A ,
end

lemma limsup_neg {s : ℕ → ℝ} :
    limsup_R (-s) = - liminf_R s :=
begin
  set t := -s with ht ,
  have nt_rw : s = -t := by tidy ,
  have key := @liminf_neg t ,
  rw nt_rw ,
  linarith ,
end

-- TODO: should the following couple of lemmas be done in more generality?
-- Probably `complete_semilattice_Sup` and `order_topology` etc.
-- would be good general assumptions.
lemma f_Sup_eq_Sup_f_ennreal (f : ennreal → ennreal) (f_mono : monotone f) (f_cont : continuous f)
  (T : set ennreal) (nonemp_T : T.nonempty) :
    Sup (f '' T) = f(Sup T) :=
begin
  have ub : ∀ y ∈ f '' T , y ≤ f (Sup T) ,
  { intros y hy ,
    rcases hy with ⟨ x , hxT , fx_eq_y ⟩ ,
    rw ← fx_eq_y ,
    exact f_mono (le_Sup hxT) , } ,
  have lub : ∀ (b : ennreal) , (∀ y ∈ f '' T , y ≤ b) → f (Sup T) ≤ b ,
  { have tends : tendsto f (𝓝 (Sup T)) (𝓝 (f (Sup T))) := continuous.tendsto f_cont (Sup T) ,
    have tends' : tendsto f (𝓝[T] (Sup T)) (𝓝 (f (Sup T))) := tendsto_nhds_within_of_tendsto_nhds tends ,
    have tada : is_lub T (Sup T) := is_lub_Sup T ,
    have key := is_lub.is_lub_of_tendsto _ tada nonemp_T tends' ,
    { intros b hb ,
      exact key.2 hb , } ,
    { intros a haT b hbT hab ,
      exact f_mono hab , } ,
    } ,
  apply le_antisymm ,
  { apply Sup_le ub , } ,
  { apply le_Sup_iff.mpr lub , } ,
end

lemma f_Inf_eq_Inf_f_ennreal (f : ennreal → ennreal) (f_mono : monotone f) (f_cont : continuous f)
  (T : set ennreal) (nonemp_T : T.nonempty) :
    Inf (f '' T) = f(Inf T) :=
begin
  have lb : ∀ y ∈ f '' T , f (Inf T) ≤ y ,
  { intros y hy ,
    rcases hy with ⟨ x , hxT , fx_eq_y ⟩ ,
    rw ← fx_eq_y ,
    exact f_mono (Inf_le hxT) , } ,
  have glb : ∀ (b : ennreal) , (∀ y ∈ f '' T , b ≤ y) → b ≤ f (Inf T) ,
  { have tends : tendsto f (𝓝 (Inf T)) (𝓝 (f (Inf T))) := continuous.tendsto f_cont (Inf T) ,
    have tends' : tendsto f (𝓝[T] (Inf T)) (𝓝 (f (Inf T))) := tendsto_nhds_within_of_tendsto_nhds tends ,
    have inf_is_glb : is_glb T (Inf T) := is_glb_Inf T ,
    have key := is_glb.is_glb_of_tendsto _ inf_is_glb nonemp_T tends' ,
    { intros b hb ,
      exact key.2 hb , } ,
    { intros a haT b hbT hab ,
      exact f_mono hab , } ,
    } ,
  apply le_antisymm ,
  { apply Inf_le_iff.mpr glb , } ,
  { apply le_Inf lb , } ,
end

lemma f_Sup_eq_Inf_f_ennreal (f : ennreal → ennreal) (f_antimono : @monotone ennreal (order_dual(ennreal)) _ _ f) (f_cont : continuous f)
  (T : set ennreal) (nonemp_T : T.nonempty) : Inf (f '' T) = f(Sup T) :=
begin
  have lb : ∀ y ∈ f '' T , f (Sup T) ≤ y,
  { intros y hy ,
    rcases hy with ⟨ x , hxT , fx_eq_y ⟩ ,
    rw ← fx_eq_y ,
    exact f_antimono (le_Sup hxT) , } ,
  have glb : ∀ (b : ennreal) , (∀ y ∈ f '' T , b ≤ y ) → b ≤ f (Sup T) ,
  { have tends : tendsto f (𝓝 (Sup T)) (𝓝 (f (Sup T))) := continuous.tendsto f_cont (Sup T) ,
    have tends' : tendsto f (𝓝[T] (Sup T)) (𝓝 (f (Sup T))) := tendsto_nhds_within_of_tendsto_nhds tends ,
    have sup_is_lub : is_lub T (Sup T) := is_lub_Sup T ,
    have key := is_lub.is_glb_of_tendsto _ sup_is_lub nonemp_T tends' ,
    { intros b hb ,
      exact key.2 hb , } ,
    { intros a haT b hbT hab ,
      exact f_antimono hab , } ,
    } ,
  apply le_antisymm ,
  { exact Inf_le_iff.mpr glb , } ,
  { exact le_Inf lb , } ,
end

lemma f_Inf_eq_Sup_f_ennreal (f : ennreal → ennreal) (f_antimono : @monotone ennreal (order_dual(ennreal)) _ _ f) (f_cont : continuous f)
  (T : set ennreal) (nonemp_T : T.nonempty) : Sup (f '' T) = f(Inf T) :=
begin
  have ub : ∀ y ∈ f '' T , y ≤ f (Inf T) ,
  { intros y hy ,
    rcases hy with ⟨ x , hxT , fx_eq_y ⟩ ,
    rw ← fx_eq_y ,
    exact f_antimono (Inf_le hxT) , } ,
  have lub : ∀ (b : ennreal) , (∀ y ∈ f '' T , y ≤ b) → f (Inf T) ≤ b ,
  { have tends : tendsto f (𝓝 (Inf T)) (𝓝 (f (Inf T))) := continuous.tendsto f_cont (Inf T) ,
    have tends' : tendsto f (𝓝[T] (Inf T)) (𝓝 (f (Inf T))) := tendsto_nhds_within_of_tendsto_nhds tends ,
    have inf_is_glb : is_glb T (Inf T) := is_glb_Inf T ,
    have key := is_glb.is_lub_of_tendsto _ inf_is_glb nonemp_T tends' ,
    { intros b hb ,
      exact key.2 hb , } ,
    { intros a haT b hbT hab ,
      exact f_antimono hab , } ,
    } ,
  apply le_antisymm ,
  { apply Sup_le ub , } ,
  { apply le_Sup_iff.mpr lub , } ,
end

lemma image_range {α β γ : Type*} (g : β → γ) (f : α → β) :
  g '' (range f) = range (g ∘ f) :=
begin
  ext w ,
  simp only [mem_image, mem_range, exists_exists_eq_and] ,
end

lemma inf_tails_mono_ennreal (s : ℕ → ennreal) :
  monotone (λ (m : ℕ) , Inf (s '' (Ici m))) :=
begin
  intros n m hnm ,
  exact Inf_le_Inf (image_subset s (Ici_subset_Ici.mpr hnm)) ,
end

-- I did not manage to state this result correctly
-- using `@monotone` and `order_dual`...
lemma sup_tails_antimono_ennreal (s : ℕ → ennreal) :
  ∀ (k l : ℕ) , k ≤ l → (λ (m : ℕ) , Sup (s '' (Ici m))) l ≤ (λ (m : ℕ) , Sup (s '' (Ici m))) k :=
begin
  intros k l hkl ,
  exact Sup_le_Sup (image_subset s (Ici_subset_Ici.mpr hkl)) ,
end

lemma Sup_eq_Inf_upper_bounds (γ : Type) [complete_lattice γ] (A : set γ) :
  Sup A = Inf (upper_bounds(A)) :=
begin
  have key := is_lub_Sup A ,
  apply le_antisymm ,
  { cases key ,
    simp only [le_Inf_iff, Sup_le_iff] ,
    intros b hbub b' hb' ,
    solve_by_elim , } ,
  { cases key,
    exact Inf_le key_left , } ,
end

lemma Inf_eq_Sup_lower_bounds (γ : Type) [complete_lattice γ] (A : set γ) :
  Inf A = Sup (lower_bounds(A)) :=
begin
  have key := is_glb_Inf A ,
  apply le_antisymm ,
  { cases key,
    exact le_Sup key_left , } ,
  { cases key ,
    simp only [le_Inf_iff, Sup_le_iff] ,
    intros b hblb b' hb' ,
    solve_by_elim , } ,
end

lemma liminf_eq_Sup_Inf_ennreal (s : ℕ → ennreal) :
  liminf_enn s = Sup ( range( λ (m : ℕ) , Inf (s '' (Ici m))) ) :=
begin
  have mono := inf_tails_mono_ennreal s ,
  set s_inf_tail := λ (m : ℕ) , Inf (s '' (Ici m)) with h_s_inf_tail ,
  set A := { a : ennreal | ∃ (m : ℕ) , ∀ (n : ℕ) , m ≤ n → a ≤ s(n) } with hA ,
  have ls_eq : liminf_enn s = Sup A ,
  { change liminf at_top s = Sup A ,
    rw liminf_eq ,
    simp , } ,
  rw ls_eq ,
  have A_eq : A = { a : ennreal | ∃ (m : ℕ) , a ≤ s_inf_tail m} ,
  { simp only [mem_image, and_imp, mem_Ici, le_Inf_iff, forall_apply_eq_imp_iff₂, exists_imp_distrib] , } ,
  have ss : range(s_inf_tail) ⊆ { a : ennreal | ∃ (m : ℕ) , a ≤ s_inf_tail m } ,
  { intros x hx ,
    cases hx with n hn , -- corners
    use n ,
    rw hn ,
    exact le_refl x , } ,
  have key : upper_bounds { a : ennreal | ∃ (m : ℕ) , a ≤ s_inf_tail m } = upper_bounds (range(s_inf_tail)) ,
  { apply le_antisymm ,
    { intros b h_bub x hxran , -- more corners, safe place
      exact h_bub (ss hxran) , } ,
    { intros b h_bub x hx ,
      cases hx with m hm ,
      exact le_trans hm (h_bub (mem_range_self m)) , } ,
    } ,
  rw ← A_eq at key ,
  rw Sup_eq_Inf_upper_bounds ,
  rw Sup_eq_Inf_upper_bounds ,
  exact congr_arg Inf key ,
end

lemma limsup_eq_Inf_Sup_ennreal (s : ℕ → ennreal) :
  limsup_enn s = Inf ( range( λ (m : ℕ) , Sup (s '' (Ici m))) ) :=
begin
  have antimono := sup_tails_antimono_ennreal s ,
  set s_sup_tail := λ (m : ℕ) , Sup (s '' (Ici m)) with h_s_sup_tail ,
  set A := { a : ennreal | ∃ (m : ℕ) , ∀ (n : ℕ) , m ≤ n → s(n) ≤ a } with hA ,
  have ls_eq : limsup_enn s = Inf A ,
  { change limsup at_top s = Inf A ,
    rw limsup_eq ,
    simp , } ,
  rw ls_eq ,
  have A_eq : A = { a : ennreal | ∃ (m : ℕ) , s_sup_tail m ≤ a } ,
  { simp only [mem_image, and_imp, mem_Ici, Sup_le_iff, forall_apply_eq_imp_iff₂, exists_imp_distrib] , } ,
  have ss : range(s_sup_tail) ⊆ { a : ennreal | ∃ (m : ℕ) , s_sup_tail m ≤ a} ,
  { intros x hx ,
    cases hx with n hn ,
    use n ,
    rw hn , -- hide
    exact le_refl x , } ,
  have key : lower_bounds { a : ennreal | ∃ (m : ℕ) , s_sup_tail m ≤ a} = lower_bounds (range(s_sup_tail)) ,
  { apply le_antisymm ,
    { intros b h_blb x hxran ,
      exact h_blb (ss hxran) , } ,
    { intros b h_blb x hx , -- keep quiet
      cases hx with m hm ,
      exact le_trans (h_blb (mem_range_self m)) hm , } ,
    } ,
  rw ← A_eq at key ,
  rw Inf_eq_Sup_lower_bounds ,
  rw Inf_eq_Sup_lower_bounds ,
  exact congr_arg Sup key ,
end

lemma liminf_eq_supr_Inf_ennreal (s : ℕ → ennreal) :
  liminf_enn s = supr ( λ (m : ℕ) , Inf (s '' (Ici m))) :=
begin
  have eq : supr ( λ (m : ℕ) , Inf (s '' (Ici m))) = Sup ( range( λ (m : ℕ) , Inf (s '' (Ici m))) ) := by refl ,
  rw eq ,
  apply liminf_eq_Sup_Inf_ennreal ,
end

lemma liminf_apply_cont_mono_ennreal {s : ℕ → ennreal}
  (f : ennreal → ennreal) (f_cont : continuous f) (f_mono : monotone f) :
    liminf_enn (f ∘ s) = f ( liminf_enn s) :=
begin
  nth_rewrite 1 liminf_eq_Sup_Inf_ennreal ,
  rw ← f_Sup_eq_Sup_f_ennreal f f_mono f_cont (range (λ (m : ℕ) , Inf (s '' (Ici m)))) (range_nonempty _) ,
  rw image_range _ _ ,
  have eq : (f ∘ λ (m : ℕ), Inf (s '' Ici m)) = (λ (m : ℕ), Inf ((f ∘ s) '' Ici m)) ,
  { funext m ,
    dsimp ,
    rw ← f_Inf_eq_Inf_f_ennreal f f_mono f_cont (s '' Ici m) nonempty_of_nonempty_subtype ,
    have eq_im : (f '' (s '' (Ici m))) = ((f ∘ s) '' (Ici m)) ,
    { ext ,
      simp only [mem_image, exists_exists_and_eq_and] , } ,
    simp only [eq_im] , } ,
  rw [eq , liminf_eq_Sup_Inf_ennreal] ,
end

lemma liminf_apply_cont_antimono_ennreal {s : ℕ → ennreal}
  (f : ennreal → ennreal) (f_cont : continuous f) (f_antimono : @monotone ennreal (order_dual(ennreal)) _ _ f) :
    liminf_enn (f ∘ s) = f ( limsup_enn s) :=
begin
  rw limsup_eq_Inf_Sup_ennreal ,
  rw ← f_Inf_eq_Sup_f_ennreal f f_antimono f_cont (range (λ (m : ℕ) , Sup (s '' (Ici m)))) (range_nonempty _) ,
  rw image_range _ _ ,
  have eq : (f ∘ λ (m : ℕ), Sup (s '' Ici m)) = (λ (m : ℕ), Inf ((f ∘ s) '' Ici m)) ,
  { funext m ,
    dsimp ,
    rw ← f_Sup_eq_Inf_f_ennreal f f_antimono f_cont (s '' Ici m) nonempty_of_nonempty_subtype ,
    have eq_im : (f '' (s '' (Ici m))) = ((f ∘ s) '' (Ici m)) ,
    { ext ,
      simp only [mem_image, exists_exists_and_eq_and] , } ,
    simp only [eq_im] , } ,
  rw [eq , liminf_eq_Sup_Inf_ennreal] ,
end

lemma limsup_apply_cont_antimono_ennreal {s : ℕ → ennreal}
  (f : ennreal → ennreal) (f_cont : continuous f) (f_antimono : @monotone ennreal (order_dual(ennreal)) _ _ f) :
    limsup_enn (f ∘ s) = f ( liminf_enn s) :=
begin
  rw liminf_eq_Sup_Inf_ennreal ,
  rw ← f_Sup_eq_Inf_f_ennreal f f_antimono f_cont (range (λ (m : ℕ) , Inf (s '' (Ici m)))) (range_nonempty _) ,
  rw image_range _ _ ,
  have eq : (f ∘ λ (m : ℕ), Inf (s '' Ici m)) = (λ (m : ℕ), Sup ((f ∘ s) '' Ici m)) ,
  { funext m ,
    dsimp ,
    rw ← f_Inf_eq_Sup_f_ennreal f f_antimono f_cont (s '' Ici m) nonempty_of_nonempty_subtype ,
    have eq_im : (f '' (s '' (Ici m))) = ((f ∘ s) '' (Ici m)) ,
    { ext ,
      simp only [mem_image, exists_exists_and_eq_and] , } ,
    simp only [eq_im] , } ,
  rw [eq , limsup_eq_Inf_Sup_ennreal] ,
end

lemma liminf_const_sub (a : ennreal) (a_ne_top : a ≠ ⊤) (s : ℕ → ennreal) :
  liminf at_top (λ n , (a - s(n))) = a - limsup at_top s :=
begin
  have cont : continuous (λ (x : ennreal) , a-x ) := continuous_const_sub_ennreal a a_ne_top ,
  set f := λ (x : ennreal) , a-x with hf ,
  -- TODO: the following is repeated many times, but stating it as lemma
  -- gets messed up, because Lean doesn't know that in `order_dual(ennreal)`
  -- I want to use the same `-` (`sub`) as in `ennreal`...
  have antimono : @monotone ennreal (order_dual(ennreal)) _ _ f ,
  { intros x y hxy ,
    exact self_sub_le_self_sub_ennreal a y x hxy , } ,
  apply liminf_apply_cont_antimono_ennreal _ cont antimono ,
end

lemma limsup_const_sub (a : ennreal) (a_ne_top : a ≠ ⊤) (s : ℕ → ennreal) :
  limsup at_top (λ n , (a - s(n))) = a - liminf at_top s :=
begin
  have cont : continuous (λ (x : ennreal) , a-x ) := continuous_const_sub_ennreal a a_ne_top ,
  set f := λ (x : ennreal) , a-x with hf ,
  have antimono : @monotone ennreal (order_dual(ennreal)) _ _ f ,
  { intros x y hxy ,
    exact self_sub_le_self_sub_ennreal a y x hxy , } ,
  apply limsup_apply_cont_antimono_ennreal _ cont antimono ,
end

end portmanteau_limsup_liminf_lemmas

end portmanteau


/-
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.triv_sq_zero_ext

/-!
# Dual numbers

The dual numbers over `R` are of the form `a + bε`, where `a` and `b` are typically elements of a
commutative ring `R`, and `ε` is a symbol satisfying `ε^2 = 0`. They are a special case of
`triv_sq_zero_ext R M` with `M = R`.

## Notation

In the `dual_number` locale:

* `R[ε]` is a shorthand for `dual_number R`
* `ε` is a shorthand for `dual_number.eps`

## Main definitions

* `dual_number`
* `dual_number.eps`
* `dual_number.lift`

## Implementation notes

Rather than duplicating the API of `triv_sq_zero_ext`, this file reuses the functions there.

## References

* https://en.wikipedia.org/wiki/Dual_number
-/

variables {R : Type*}

/-- The type of dual numbers, numbers of the form $a + bε$ where $ε^2 = 0$.-/
abbreviation dual_number (R : Type*) : Type* := triv_sq_zero_ext R R

/-- The unit element $ε$ that squares to zero. -/
def dual_number.eps [has_zero R] [has_one R] : dual_number R := triv_sq_zero_ext.inr 1

localized "notation `ε` := dual_number.eps" in dual_number
localized "postfix `[ε]`:1025 := dual_number" in dual_number

open_locale dual_number

namespace dual_number

open triv_sq_zero_ext

@[simp] lemma fst_eps [has_zero R] [has_one R] : fst ε = (0 : R) := fst_inr _ _
@[simp] lemma snd_eps [has_zero R] [has_one R] : snd ε = (1 : R) := snd_inr _ _

/-- A version of `triv_sq_zero_ext.snd_mul` with `*` instead of `•`. -/
@[simp] lemma snd_mul [semiring R] (x y : R[ε]) : snd (x * y) = fst x * snd y + fst y * snd x :=
snd_mul _ _

@[simp] lemma eps_mul_eps [semiring R] : (ε * ε : R[ε]) = 0 := inr_mul_inr _ _ _

@[simp] lemma inr_eq_smul_eps [mul_zero_one_class R] (r : R) : inr r = (r • ε : R[ε]) :=
ext (mul_zero r).symm (mul_one r).symm

/-- For two algebra morphisms out of `R[ε]` to agree, it suffices for them to agree on `ε`. -/
@[ext] lemma alg_hom_ext {A} [comm_semiring R] [semiring A] [algebra R A]
  ⦃f g : R[ε] →ₐ[R] A⦄ (h : f ε = g ε) : f = g :=
alg_hom_ext' $ linear_map.ext_ring $ h

variables {A : Type*} [comm_semiring R] [semiring A] [algebra R A]

/-- A universal property of the dual numbers, providing a unique `R[ε] →ₐ[R] A` for every element
of `A` which squares to `0`.

This isomorphism is named to match the very similar `complex.lift`. -/
@[simps {attrs := []}]
def lift : {e : A // e * e = 0} ≃ (R[ε] →ₐ[R] A) :=
equiv.trans
  (show {e : A // e * e = 0} ≃ {f : R →ₗ[R] A // ∀ x y, f x * f y = 0}, from
    (linear_map.ring_lmap_equiv_self R ℕ A).symm.to_equiv.subtype_equiv $ λ a, begin
      dsimp,
      simp_rw smul_mul_smul,
      refine ⟨λ h x y, h.symm ▸ smul_zero _, λ h, by simpa using h 1 1⟩,
    end)
  triv_sq_zero_ext.lift

/- When applied to `ε`, `dual_number.lift` produces the element of `A` that squares to 0. -/
@[simp]
lemma lift_apply_eps (e : {e : A // e * e = 0}) : lift e (ε : R[ε]) = e :=
(triv_sq_zero_ext.lift_aux_apply_inr _ _ _).trans $ one_smul _ _

/- Lifting `dual_number.eps` itself gives the identity. -/
@[simp]
lemma lift_eps : lift ⟨ε, by exact eps_mul_eps⟩ = alg_hom.id R R[ε] :=
alg_hom_ext $ lift_apply_eps _

end dual_number


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

! This file was ported from Lean 3 source module number_theory.padics.padic_norm
! leanprover-community/mathlib commit cb3ceec8485239a61ed51d944cb9a95b68c6bafc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.Padics.PadicVal

/-!
# p-adic norm

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

This file defines the `p`-adic norm on `ℚ`.

The `p`-adic valuation on `ℚ` is the difference of the multiplicities of `p` in the numerator and
denominator of `q`. This function obeys the standard properties of a valuation, with the appropriate
assumptions on `p`.

The valuation induces a norm on `ℚ`. This norm is a nonarchimedean absolute value.
It takes values in {0} ∪ {1/p^k | k ∈ ℤ}.

## Notations

This file uses the local notation `/.` for `rat.mk`.

## Implementation notes

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

## References

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

## Tags

p-adic, p adic, padic, norm, valuation
-/

#print padicNorm /-
/-- If `q ≠ 0`, the `p`-adic norm of a rational `q` is `p ^ -padic_val_rat p q`.
If `q = 0`, the `p`-adic norm of `q` is `0`. -/
def padicNorm (p : ℕ) (q : ℚ) : ℚ :=
  if q = 0 then 0 else (p : ℚ) ^ (-padicValRat p q)
#align padic_norm padicNorm
-/

namespace padicNorm

open padicValRat

variable {p : ℕ}

/- warning: padic_norm.eq_zpow_of_nonzero -> padicNorm.eq_zpow_of_nonzero is a dubious translation:
lean 3 declaration is
  forall {p : Nat} {q : Rat}, (Ne.{1} Rat q (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero)))) -> (Eq.{1} Rat (padicNorm p q) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p) (Neg.neg.{0} Int Int.hasNeg (padicValRat p q))))
but is expected to have type
  forall {p : Nat} {q : Rat}, (Ne.{1} Rat q (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0))) -> (Eq.{1} Rat (padicNorm p q) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p) (Neg.neg.{0} Int Int.instNegInt (padicValRat p q))))
Case conversion may be inaccurate. Consider using '#align padic_norm.eq_zpow_of_nonzero padicNorm.eq_zpow_of_nonzeroₓ'. -/
/-- Unfolds the definition of the `p`-adic norm of `q` when `q ≠ 0`. -/
@[simp]
protected theorem eq_zpow_of_nonzero {q : ℚ} (hq : q ≠ 0) :
    padicNorm p q = p ^ (-padicValRat p q) := by simp [hq, padicNorm]
#align padic_norm.eq_zpow_of_nonzero padicNorm.eq_zpow_of_nonzero

/- warning: padic_norm.nonneg -> padicNorm.nonneg is a dubious translation:
lean 3 declaration is
  forall {p : Nat} (q : Rat), LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) (padicNorm p q)
but is expected to have type
  forall {p : Nat} (q : Rat), LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) (padicNorm p q)
Case conversion may be inaccurate. Consider using '#align padic_norm.nonneg padicNorm.nonnegₓ'. -/
/-- The `p`-adic norm is nonnegative. -/
protected theorem nonneg (q : ℚ) : 0 ≤ padicNorm p q :=
  if hq : q = 0 then by simp [hq, padicNorm]
  else by
    unfold padicNorm <;> split_ifs
    apply zpow_nonneg
    exact_mod_cast Nat.zero_le _
#align padic_norm.nonneg padicNorm.nonneg

#print padicNorm.zero /-
/-- The `p`-adic norm of `0` is `0`. -/
@[simp]
protected theorem zero : padicNorm p 0 = 0 := by simp [padicNorm]
#align padic_norm.zero padicNorm.zero
-/

#print padicNorm.one /-
/-- The `p`-adic norm of `1` is `1`. -/
@[simp]
protected theorem one : padicNorm p 1 = 1 := by simp [padicNorm]
#align padic_norm.one padicNorm.one
-/

/- warning: padic_norm.padic_norm_p -> padicNorm.padicNorm_p is a dubious translation:
lean 3 declaration is
  forall {p : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) p) -> (Eq.{1} Rat (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p)) (Inv.inv.{0} Rat Rat.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p)))
but is expected to have type
  forall {p : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) p) -> (Eq.{1} Rat (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)) (Inv.inv.{0} Rat Rat.instInvRat (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)))
Case conversion may be inaccurate. Consider using '#align padic_norm.padic_norm_p padicNorm.padicNorm_pₓ'. -/
/-- The `p`-adic norm of `p` is `p⁻¹` if `p > 1`.

See also `padic_norm.padic_norm_p_of_prime` for a version assuming `p` is prime. -/
theorem padicNorm_p (hp : 1 < p) : padicNorm p p = p⁻¹ := by
  simp [padicNorm, (pos_of_gt hp).ne', padicValNat.self hp]
#align padic_norm.padic_norm_p padicNorm.padicNorm_p

/- warning: padic_norm.padic_norm_p_of_prime -> padicNorm.padicNorm_p_of_prime is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [_inst_1 : Fact (Nat.Prime p)], Eq.{1} Rat (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p)) (Inv.inv.{0} Rat Rat.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p))
but is expected to have type
  forall {p : Nat} [_inst_1 : Fact (Nat.Prime p)], Eq.{1} Rat (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)) (Inv.inv.{0} Rat Rat.instInvRat (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p))
Case conversion may be inaccurate. Consider using '#align padic_norm.padic_norm_p_of_prime padicNorm.padicNorm_p_of_primeₓ'. -/
/-- The `p`-adic norm of `p` is `p⁻¹` if `p` is prime.

See also `padic_norm.padic_norm_p` for a version assuming `1 < p`. -/
@[simp]
theorem padicNorm_p_of_prime [Fact p.Prime] : padicNorm p p = p⁻¹ :=
  padicNorm_p <| Nat.Prime.one_lt (Fact.out _)
#align padic_norm.padic_norm_p_of_prime padicNorm.padicNorm_p_of_prime

/- warning: padic_norm.padic_norm_of_prime_of_ne -> padicNorm.padicNorm_of_prime_of_ne is a dubious translation:
lean 3 declaration is
  forall {p : Nat} {q : Nat} [p_prime : Fact (Nat.Prime p)] [q_prime : Fact (Nat.Prime q)], (Ne.{1} Nat p q) -> (Eq.{1} Rat (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) q)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne))))
but is expected to have type
  forall {p : Nat} {q : Nat} [p_prime : Fact (Nat.Prime p)] [q_prime : Fact (Nat.Prime q)], (Ne.{1} Nat p q) -> (Eq.{1} Rat (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) q)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1)))
Case conversion may be inaccurate. Consider using '#align padic_norm.padic_norm_of_prime_of_ne padicNorm.padicNorm_of_prime_of_neₓ'. -/
/-- The `p`-adic norm of `q` is `1` if `q` is prime and not equal to `p`. -/
theorem padicNorm_of_prime_of_ne {q : ℕ} [p_prime : Fact p.Prime] [q_prime : Fact q.Prime]
    (neq : p ≠ q) : padicNorm p q = 1 :=
  by
  have p : padicValRat p q = 0 := by exact_mod_cast @padicValNat_primes p q p_prime q_prime neq
  simp [padicNorm, p, q_prime.1.1, q_prime.1.NeZero]
#align padic_norm.padic_norm_of_prime_of_ne padicNorm.padicNorm_of_prime_of_ne

/- warning: padic_norm.padic_norm_p_lt_one -> padicNorm.padicNorm_p_lt_one is a dubious translation:
lean 3 declaration is
  forall {p : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) p) -> (LT.lt.{0} Rat Rat.hasLt (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne))))
but is expected to have type
  forall {p : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) p) -> (LT.lt.{0} Rat Rat.instLTRat_1 (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1)))
Case conversion may be inaccurate. Consider using '#align padic_norm.padic_norm_p_lt_one padicNorm.padicNorm_p_lt_oneₓ'. -/
/-- The `p`-adic norm of `p` is less than `1` if `1 < p`.

See also `padic_norm.padic_norm_p_lt_one_of_prime` for a version assuming `p` is prime. -/
theorem padicNorm_p_lt_one (hp : 1 < p) : padicNorm p p < 1 :=
  by
  rw [padic_norm_p hp, inv_lt_one_iff]
  exact_mod_cast Or.inr hp
#align padic_norm.padic_norm_p_lt_one padicNorm.padicNorm_p_lt_one

/- warning: padic_norm.padic_norm_p_lt_one_of_prime -> padicNorm.padicNorm_p_lt_one_of_prime is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [_inst_1 : Fact (Nat.Prime p)], LT.lt.{0} Rat Rat.hasLt (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))
but is expected to have type
  forall {p : Nat} [_inst_1 : Fact (Nat.Prime p)], LT.lt.{0} Rat Rat.instLTRat_1 (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))
Case conversion may be inaccurate. Consider using '#align padic_norm.padic_norm_p_lt_one_of_prime padicNorm.padicNorm_p_lt_one_of_primeₓ'. -/
/-- The `p`-adic norm of `p` is less than `1` if `p` is prime.

See also `padic_norm.padic_norm_p_lt_one` for a version assuming `1 < p`. -/
theorem padicNorm_p_lt_one_of_prime [Fact p.Prime] : padicNorm p p < 1 :=
  padicNorm_p_lt_one <| Nat.Prime.one_lt (Fact.out _)
#align padic_norm.padic_norm_p_lt_one_of_prime padicNorm.padicNorm_p_lt_one_of_prime

/- warning: padic_norm.values_discrete -> padicNorm.values_discrete is a dubious translation:
lean 3 declaration is
  forall {p : Nat} {q : Rat}, (Ne.{1} Rat q (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero)))) -> (Exists.{1} Int (fun (z : Int) => Eq.{1} Rat (padicNorm p q) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p) (Neg.neg.{0} Int Int.hasNeg z))))
but is expected to have type
  forall {p : Nat} {q : Rat}, (Ne.{1} Rat q (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0))) -> (Exists.{1} Int (fun (z : Int) => Eq.{1} Rat (padicNorm p q) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p) (Neg.neg.{0} Int Int.instNegInt z))))
Case conversion may be inaccurate. Consider using '#align padic_norm.values_discrete padicNorm.values_discreteₓ'. -/
/-- `padic_norm p q` takes discrete values `p ^ -z` for `z : ℤ`. -/
protected theorem values_discrete {q : ℚ} (hq : q ≠ 0) : ∃ z : ℤ, padicNorm p q = p ^ (-z) :=
  ⟨padicValRat p q, by simp [padicNorm, hq]⟩
#align padic_norm.values_discrete padicNorm.values_discrete

#print padicNorm.neg /-
/-- `padic_norm p` is symmetric. -/
@[simp]
protected theorem neg (q : ℚ) : padicNorm p (-q) = padicNorm p q :=
  if hq : q = 0 then by simp [hq] else by simp [padicNorm, hq]
#align padic_norm.neg padicNorm.neg
-/

variable [hp : Fact p.Prime]

include hp

#print padicNorm.nonzero /-
/-- If `q ≠ 0`, then `padic_norm p q ≠ 0`. -/
protected theorem nonzero {q : ℚ} (hq : q ≠ 0) : padicNorm p q ≠ 0 :=
  by
  rw [padicNorm.eq_zpow_of_nonzero hq]
  apply zpow_ne_zero_of_ne_zero
  exact_mod_cast ne_of_gt hp.1.Pos
#align padic_norm.nonzero padicNorm.nonzero
-/

#print padicNorm.zero_of_padicNorm_eq_zero /-
/-- If the `p`-adic norm of `q` is 0, then `q` is `0`. -/
theorem zero_of_padicNorm_eq_zero {q : ℚ} (h : padicNorm p q = 0) : q = 0 :=
  by
  apply by_contradiction; intro hq
  unfold padicNorm at h; rw [if_neg hq] at h
  apply absurd h
  apply zpow_ne_zero_of_ne_zero
  exact_mod_cast hp.1.NeZero
#align padic_norm.zero_of_padic_norm_eq_zero padicNorm.zero_of_padicNorm_eq_zero
-/

#print padicNorm.mul /-
/-- The `p`-adic norm is multiplicative. -/
@[simp]
protected theorem mul (q r : ℚ) : padicNorm p (q * r) = padicNorm p q * padicNorm p r :=
  if hq : q = 0 then by simp [hq]
  else
    if hr : r = 0 then by simp [hr]
    else by
      have : q * r ≠ 0 := mul_ne_zero hq hr
      have : (p : ℚ) ≠ 0 := by simp [hp.1.NeZero]
      simp [padicNorm, *, padicValRat.mul, zpow_add₀ this, mul_comm]
#align padic_norm.mul padicNorm.mul
-/

#print padicNorm.div /-
/-- The `p`-adic norm respects division. -/
@[simp]
protected theorem div (q r : ℚ) : padicNorm p (q / r) = padicNorm p q / padicNorm p r :=
  if hr : r = 0 then by simp [hr]
  else eq_div_of_mul_eq (padicNorm.nonzero hr) (by rw [← padicNorm.mul, div_mul_cancel _ hr])
#align padic_norm.div padicNorm.div
-/

/- warning: padic_norm.of_int -> padicNorm.of_int is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (z : Int), LE.le.{0} Rat Rat.hasLe (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) z)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (z : Int), LE.le.{0} Rat Rat.instLERat (padicNorm p (Int.cast.{0} Rat Rat.instIntCastRat z)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))
Case conversion may be inaccurate. Consider using '#align padic_norm.of_int padicNorm.of_intₓ'. -/
/-- The `p`-adic norm of an integer is at most `1`. -/
protected theorem of_int (z : ℤ) : padicNorm p z ≤ 1 :=
  if hz : z = 0 then by simp [hz, zero_le_one]
  else by
    unfold padicNorm
    rw [if_neg _]
    · refine' zpow_le_one_of_nonpos _ _
      · exact_mod_cast le_of_lt hp.1.one_lt
      · rw [padicValRat.of_int, neg_nonpos]
        norm_cast
        simp
    exact_mod_cast hz
#align padic_norm.of_int padicNorm.of_int

private theorem nonarchimedean_aux {q r : ℚ} (h : padicValRat p q ≤ padicValRat p r) :
    padicNorm p (q + r) ≤ max (padicNorm p q) (padicNorm p r) :=
  have hnqp : padicNorm p q ≥ 0 := padicNorm.nonneg _
  have hnrp : padicNorm p r ≥ 0 := padicNorm.nonneg _
  if hq : q = 0 then by simp [hq, max_eq_right hnrp, le_max_right]
  else
    if hr : r = 0 then by simp [hr, max_eq_left hnqp, le_max_left]
    else
      if hqr : q + r = 0 then le_trans (by simpa [hqr] using hnqp) (le_max_left _ _)
      else by
        unfold padicNorm; split_ifs
        apply le_max_iff.2
        left
        apply zpow_le_of_le
        · exact_mod_cast le_of_lt hp.1.one_lt
        · apply neg_le_neg
          have : padicValRat p q = min (padicValRat p q) (padicValRat p r) := (min_eq_left h).symm
          rw [this]
          apply min_le_padic_val_rat_add <;> assumption
#align padic_norm.nonarchimedean_aux padic_norm.nonarchimedean_aux

/- warning: padic_norm.nonarchimedean -> padicNorm.nonarchimedean is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, LE.le.{0} Rat Rat.hasLe (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) q r)) (LinearOrder.max.{0} Rat Rat.linearOrder (padicNorm p q) (padicNorm p r))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, LE.le.{0} Rat Rat.instLERat (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) q r)) (Max.max.{0} Rat (LinearOrderedRing.toMax.{0} Rat Rat.instLinearOrderedRingRat) (padicNorm p q) (padicNorm p r))
Case conversion may be inaccurate. Consider using '#align padic_norm.nonarchimedean padicNorm.nonarchimedeanₓ'. -/
/-- The `p`-adic norm is nonarchimedean: the norm of `p + q` is at most the max of the norm of `p`
and the norm of `q`. -/
protected theorem nonarchimedean {q r : ℚ} :
    padicNorm p (q + r) ≤ max (padicNorm p q) (padicNorm p r) :=
  by
  wlog hle : padicValRat p q ≤ padicValRat p r generalizing q r
  · rw [add_comm, max_comm]
    exact this (le_of_not_le hle)
  exact nonarchimedean_aux hle
#align padic_norm.nonarchimedean padicNorm.nonarchimedean

/- warning: padic_norm.triangle_ineq -> padicNorm.triangle_ineq is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (q : Rat) (r : Rat), LE.le.{0} Rat Rat.hasLe (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) q r)) (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) (padicNorm p q) (padicNorm p r))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (q : Rat) (r : Rat), LE.le.{0} Rat Rat.instLERat (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) q r)) (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) (padicNorm p q) (padicNorm p r))
Case conversion may be inaccurate. Consider using '#align padic_norm.triangle_ineq padicNorm.triangle_ineqₓ'. -/
/-- The `p`-adic norm respects the triangle inequality: the norm of `p + q` is at most the norm of
`p` plus the norm of `q`. -/
theorem triangle_ineq (q r : ℚ) : padicNorm p (q + r) ≤ padicNorm p q + padicNorm p r :=
  calc
    padicNorm p (q + r) ≤ max (padicNorm p q) (padicNorm p r) := padicNorm.nonarchimedean
    _ ≤ padicNorm p q + padicNorm p r :=
      max_le_add_of_nonneg (padicNorm.nonneg _) (padicNorm.nonneg _)

#align padic_norm.triangle_ineq padicNorm.triangle_ineq

/- warning: padic_norm.sub -> padicNorm.sub is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, LE.le.{0} Rat Rat.hasLe (padicNorm p (HSub.hSub.{0, 0, 0} Rat Rat Rat (instHSub.{0} Rat (SubNegMonoid.toHasSub.{0} Rat (AddGroup.toSubNegMonoid.{0} Rat Rat.addGroup))) q r)) (LinearOrder.max.{0} Rat Rat.linearOrder (padicNorm p q) (padicNorm p r))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, LE.le.{0} Rat Rat.instLERat (padicNorm p (HSub.hSub.{0, 0, 0} Rat Rat Rat (instHSub.{0} Rat Rat.instSubRat) q r)) (Max.max.{0} Rat (LinearOrderedRing.toMax.{0} Rat Rat.instLinearOrderedRingRat) (padicNorm p q) (padicNorm p r))
Case conversion may be inaccurate. Consider using '#align padic_norm.sub padicNorm.subₓ'. -/
/-- The `p`-adic norm of a difference is at most the max of each component. Restates the archimedean
property of the `p`-adic norm. -/
protected theorem sub {q r : ℚ} : padicNorm p (q - r) ≤ max (padicNorm p q) (padicNorm p r) := by
  rw [sub_eq_add_neg, ← padicNorm.neg r] <;> apply padicNorm.nonarchimedean
#align padic_norm.sub padicNorm.sub

/- warning: padic_norm.add_eq_max_of_ne -> padicNorm.add_eq_max_of_ne is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, (Ne.{1} Rat (padicNorm p q) (padicNorm p r)) -> (Eq.{1} Rat (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) q r)) (LinearOrder.max.{0} Rat Rat.linearOrder (padicNorm p q) (padicNorm p r)))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {q : Rat} {r : Rat}, (Ne.{1} Rat (padicNorm p q) (padicNorm p r)) -> (Eq.{1} Rat (padicNorm p (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) q r)) (Max.max.{0} Rat (LinearOrderedRing.toMax.{0} Rat Rat.instLinearOrderedRingRat) (padicNorm p q) (padicNorm p r)))
Case conversion may be inaccurate. Consider using '#align padic_norm.add_eq_max_of_ne padicNorm.add_eq_max_of_neₓ'. -/
/-- If the `p`-adic norms of `q` and `r` are different, then the norm of `q + r` is equal to the max
of the norms of `q` and `r`. -/
theorem add_eq_max_of_ne {q r : ℚ} (hne : padicNorm p q ≠ padicNorm p r) :
    padicNorm p (q + r) = max (padicNorm p q) (padicNorm p r) :=
  by
  wlog hlt : padicNorm p r < padicNorm p q
  · rw [add_comm, max_comm]
    exact this hne.symm (hne.lt_or_lt.resolve_right hlt)
  have : padicNorm p q ≤ max (padicNorm p (q + r)) (padicNorm p r) :=
    calc
      padicNorm p q = padicNorm p (q + r - r) := by congr <;> ring
      _ ≤ max (padicNorm p (q + r)) (padicNorm p (-r)) := padicNorm.nonarchimedean
      _ = max (padicNorm p (q + r)) (padicNorm p r) := by simp

  have hnge : padicNorm p r ≤ padicNorm p (q + r) :=
    by
    apply le_of_not_gt
    intro hgt
    rw [max_eq_right_of_lt hgt] at this
    apply not_lt_of_ge this
    assumption
  have : padicNorm p q ≤ padicNorm p (q + r) := by rwa [max_eq_left hnge] at this
  apply _root_.le_antisymm
  · apply padicNorm.nonarchimedean
  · rwa [max_eq_left_of_lt hlt]
#align padic_norm.add_eq_max_of_ne padicNorm.add_eq_max_of_ne

/-- The `p`-adic norm is an absolute value: positive-definite and multiplicative, satisfying the
triangle inequality. -/
instance : IsAbsoluteValue (padicNorm p)
    where
  abv_nonneg := padicNorm.nonneg
  abv_eq_zero _ := ⟨zero_of_padicNorm_eq_zero, fun hx => by simpa only [hx] ⟩
  abv_add := padicNorm.triangle_ineq
  abv_mul := padicNorm.mul

/- warning: padic_norm.dvd_iff_norm_le -> padicNorm.dvd_iff_norm_le is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {n : Nat} {z : Int}, Iff (Dvd.Dvd.{0} Int (semigroupDvd.{0} Int Int.semigroup) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p n)) z) (LE.le.{0} Rat Rat.hasLe (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) z)) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) p) (Neg.neg.{0} Int Int.hasNeg ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) n))))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {n : Nat} {z : Int}, Iff (Dvd.dvd.{0} Int Int.instDvdInt (Nat.cast.{0} Int instNatCastInt (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p n)) z) (LE.le.{0} Rat Rat.instLERat (padicNorm p (Int.cast.{0} Rat Rat.instIntCastRat z)) (HPow.hPow.{0, 0, 0} Rat Int Rat (instHPow.{0, 0} Rat Int (DivInvMonoid.Pow.{0} Rat (DivisionRing.toDivInvMonoid.{0} Rat Rat.divisionRing))) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) p) (Neg.neg.{0} Int Int.instNegInt (Nat.cast.{0} Int instNatCastInt n))))
Case conversion may be inaccurate. Consider using '#align padic_norm.dvd_iff_norm_le padicNorm.dvd_iff_norm_leₓ'. -/
theorem dvd_iff_norm_le {n : ℕ} {z : ℤ} : ↑(p ^ n) ∣ z ↔ padicNorm p z ≤ p ^ (-n : ℤ) :=
  by
  unfold padicNorm; split_ifs with hz
  · norm_cast  at hz
    have : 0 ≤ (p ^ n : ℚ) := by
      apply pow_nonneg
      exact_mod_cast le_of_lt hp.1.Pos
    simp [hz, this]
  · rw [zpow_le_iff_le, neg_le_neg_iff, padicValRat.of_int,
      padicValInt.of_ne_one_ne_zero hp.1.ne_one _]
    · norm_cast
      rw [← PartENat.coe_le_coe, PartENat.natCast_get, ← multiplicity.pow_dvd_iff_le_multiplicity]
      simp
    · exact_mod_cast hz
    · exact_mod_cast hp.1.one_lt
#align padic_norm.dvd_iff_norm_le padicNorm.dvd_iff_norm_le

/- warning: padic_norm.int_eq_one_iff -> padicNorm.int_eq_one_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Int), Iff (Eq.{1} Rat (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) m)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))) (Not (Dvd.Dvd.{0} Int (semigroupDvd.{0} Int Int.semigroup) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) p) m))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Int), Iff (Eq.{1} Rat (padicNorm p (Int.cast.{0} Rat Rat.instIntCastRat m)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))) (Not (Dvd.dvd.{0} Int Int.instDvdInt (Nat.cast.{0} Int instNatCastInt p) m))
Case conversion may be inaccurate. Consider using '#align padic_norm.int_eq_one_iff padicNorm.int_eq_one_iffₓ'. -/
/-- The `p`-adic norm of an integer `m` is one iff `p` doesn't divide `m`. -/
theorem int_eq_one_iff (m : ℤ) : padicNorm p m = 1 ↔ ¬(p : ℤ) ∣ m :=
  by
  nth_rw 2 [← pow_one p]
  simp only [dvd_iff_norm_le, Int.cast_ofNat, Nat.cast_one, zpow_neg, zpow_one, not_le]
  constructor
  · intro h
    rw [h, inv_lt_one_iff_of_pos] <;> norm_cast
    · exact Nat.Prime.one_lt (Fact.out _)
    · exact Nat.Prime.pos (Fact.out _)
  · simp only [padicNorm]
    split_ifs
    · rw [inv_lt_zero, ← Nat.cast_zero, Nat.cast_lt]
      intro h
      exact (Nat.not_lt_zero p h).elim
    · have : 1 < (p : ℚ) := by norm_cast <;> exact Nat.Prime.one_lt (Fact.out _ : Nat.Prime p)
      rw [← zpow_neg_one, zpow_lt_iff_lt this]
      have : 0 ≤ padicValRat p m
      simp only [of_int, Nat.cast_nonneg]
      intro h
      rw [← zpow_zero (p : ℚ), zpow_inj] <;> linarith
#align padic_norm.int_eq_one_iff padicNorm.int_eq_one_iff

/- warning: padic_norm.int_lt_one_iff -> padicNorm.int_lt_one_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Int), Iff (LT.lt.{0} Rat Rat.hasLt (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) m)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))) (Dvd.Dvd.{0} Int (semigroupDvd.{0} Int Int.semigroup) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) p) m)
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Int), Iff (LT.lt.{0} Rat Rat.instLTRat_1 (padicNorm p (Int.cast.{0} Rat Rat.instIntCastRat m)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))) (Dvd.dvd.{0} Int Int.instDvdInt (Nat.cast.{0} Int instNatCastInt p) m)
Case conversion may be inaccurate. Consider using '#align padic_norm.int_lt_one_iff padicNorm.int_lt_one_iffₓ'. -/
theorem int_lt_one_iff (m : ℤ) : padicNorm p m < 1 ↔ (p : ℤ) ∣ m :=
  by
  rw [← not_iff_not, ← int_eq_one_iff, eq_iff_le_not_lt]
  simp only [padicNorm.of_int, true_and_iff]
#align padic_norm.int_lt_one_iff padicNorm.int_lt_one_iff

/- warning: padic_norm.of_nat -> padicNorm.of_nat is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), LE.le.{0} Rat Rat.hasLe (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) m)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), LE.le.{0} Rat Rat.instLERat (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) m)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))
Case conversion may be inaccurate. Consider using '#align padic_norm.of_nat padicNorm.of_natₓ'. -/
theorem of_nat (m : ℕ) : padicNorm p m ≤ 1 :=
  padicNorm.of_int (m : ℤ)
#align padic_norm.of_nat padicNorm.of_nat

/- warning: padic_norm.nat_eq_one_iff -> padicNorm.nat_eq_one_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), Iff (Eq.{1} Rat (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) m)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))) (Not (Dvd.Dvd.{0} Nat Nat.hasDvd p m))
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), Iff (Eq.{1} Rat (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) m)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))) (Not (Dvd.dvd.{0} Nat Nat.instDvdNat p m))
Case conversion may be inaccurate. Consider using '#align padic_norm.nat_eq_one_iff padicNorm.nat_eq_one_iffₓ'. -/
/-- The `p`-adic norm of a natural `m` is one iff `p` doesn't divide `m`. -/
theorem nat_eq_one_iff (m : ℕ) : padicNorm p m = 1 ↔ ¬p ∣ m := by
  simp only [← Int.coe_nat_dvd, ← int_eq_one_iff, Int.cast_ofNat]
#align padic_norm.nat_eq_one_iff padicNorm.nat_eq_one_iff

/- warning: padic_norm.nat_lt_one_iff -> padicNorm.nat_lt_one_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), Iff (LT.lt.{0} Rat Rat.hasLt (padicNorm p ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.linearOrderedRing))))))))) m)) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))) (Dvd.Dvd.{0} Nat Nat.hasDvd p m)
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] (m : Nat), Iff (LT.lt.{0} Rat Rat.instLTRat_1 (padicNorm p (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) m)) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))) (Dvd.dvd.{0} Nat Nat.instDvdNat p m)
Case conversion may be inaccurate. Consider using '#align padic_norm.nat_lt_one_iff padicNorm.nat_lt_one_iffₓ'. -/
theorem nat_lt_one_iff (m : ℕ) : padicNorm p m < 1 ↔ p ∣ m := by
  simp only [← Int.coe_nat_dvd, ← int_lt_one_iff, Int.cast_ofNat]
#align padic_norm.nat_lt_one_iff padicNorm.nat_lt_one_iff

open BigOperators

#print padicNorm.sum_lt /-
theorem sum_lt {α : Type _} {F : α → ℚ} {t : ℚ} {s : Finset α} :
    s.Nonempty → (∀ i ∈ s, padicNorm p (F i) < t) → padicNorm p (∑ i in s, F i) < t := by
  classical
    refine' s.induction_on (by rintro ⟨-, ⟨⟩⟩) _
    rintro a S haS IH - ht
    by_cases hs : S.nonempty
    · rw [Finset.sum_insert haS]
      exact
        lt_of_le_of_lt padicNorm.nonarchimedean
          (max_lt (ht a (Finset.mem_insert_self a S))
            (IH hs fun b hb => ht b (Finset.mem_insert_of_mem hb)))
    · simp_all
#align padic_norm.sum_lt padicNorm.sum_lt
-/

/- warning: padic_norm.sum_le -> padicNorm.sum_le is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {α : Type.{u1}} {F : α -> Rat} {t : Rat} {s : Finset.{u1} α}, (Finset.Nonempty.{u1} α s) -> (forall (i : α), (Membership.Mem.{u1, u1} α (Finset.{u1} α) (Finset.hasMem.{u1} α) i s) -> (LE.le.{0} Rat Rat.hasLe (padicNorm p (F i)) t)) -> (LE.le.{0} Rat Rat.hasLe (padicNorm p (Finset.sum.{0, u1} Rat α Rat.addCommMonoid s (fun (i : α) => F i))) t)
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {α : Type.{u1}} {F : α -> Rat} {t : Rat} {s : Finset.{u1} α}, (Finset.Nonempty.{u1} α s) -> (forall (i : α), (Membership.mem.{u1, u1} α (Finset.{u1} α) (Finset.instMembershipFinset.{u1} α) i s) -> (LE.le.{0} Rat Rat.instLERat (padicNorm p (F i)) t)) -> (LE.le.{0} Rat Rat.instLERat (padicNorm p (Finset.sum.{0, u1} Rat α Rat.addCommMonoid s (fun (i : α) => F i))) t)
Case conversion may be inaccurate. Consider using '#align padic_norm.sum_le padicNorm.sum_leₓ'. -/
theorem sum_le {α : Type _} {F : α → ℚ} {t : ℚ} {s : Finset α} :
    s.Nonempty → (∀ i ∈ s, padicNorm p (F i) ≤ t) → padicNorm p (∑ i in s, F i) ≤ t := by
  classical
    refine' s.induction_on (by rintro ⟨-, ⟨⟩⟩) _
    rintro a S haS IH - ht
    by_cases hs : S.nonempty
    · rw [Finset.sum_insert haS]
      exact
        padic_norm.nonarchimedean.trans
          (max_le (ht a (Finset.mem_insert_self a S))
            (IH hs fun b hb => ht b (Finset.mem_insert_of_mem hb)))
    · simp_all
#align padic_norm.sum_le padicNorm.sum_le

#print padicNorm.sum_lt' /-
theorem sum_lt' {α : Type _} {F : α → ℚ} {t : ℚ} {s : Finset α}
    (hF : ∀ i ∈ s, padicNorm p (F i) < t) (ht : 0 < t) : padicNorm p (∑ i in s, F i) < t :=
  by
  obtain rfl | hs := Finset.eq_empty_or_nonempty s
  · simp [ht]
  · exact sum_lt hs hF
#align padic_norm.sum_lt' padicNorm.sum_lt'
-/

/- warning: padic_norm.sum_le' -> padicNorm.sum_le' is a dubious translation:
lean 3 declaration is
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {α : Type.{u1}} {F : α -> Rat} {t : Rat} {s : Finset.{u1} α}, (forall (i : α), (Membership.Mem.{u1, u1} α (Finset.{u1} α) (Finset.hasMem.{u1} α) i s) -> (LE.le.{0} Rat Rat.hasLe (padicNorm p (F i)) t)) -> (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) t) -> (LE.le.{0} Rat Rat.hasLe (padicNorm p (Finset.sum.{0, u1} Rat α Rat.addCommMonoid s (fun (i : α) => F i))) t)
but is expected to have type
  forall {p : Nat} [hp : Fact (Nat.Prime p)] {α : Type.{u1}} {F : α -> Rat} {t : Rat} {s : Finset.{u1} α}, (forall (i : α), (Membership.mem.{u1, u1} α (Finset.{u1} α) (Finset.instMembershipFinset.{u1} α) i s) -> (LE.le.{0} Rat Rat.instLERat (padicNorm p (F i)) t)) -> (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) t) -> (LE.le.{0} Rat Rat.instLERat (padicNorm p (Finset.sum.{0, u1} Rat α Rat.addCommMonoid s (fun (i : α) => F i))) t)
Case conversion may be inaccurate. Consider using '#align padic_norm.sum_le' padicNorm.sum_le'ₓ'. -/
theorem sum_le' {α : Type _} {F : α → ℚ} {t : ℚ} {s : Finset α}
    (hF : ∀ i ∈ s, padicNorm p (F i) ≤ t) (ht : 0 ≤ t) : padicNorm p (∑ i in s, F i) ≤ t :=
  by
  obtain rfl | hs := Finset.eq_empty_or_nonempty s
  · simp [ht]
  · exact sum_le hs hF
#align padic_norm.sum_le' padicNorm.sum_le'

end padicNorm


/- # LoVe Homework 2: Backward Proofs
Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

namespace backward_proofs

/- ## Question 1 (4 points): Connectives and Quantifiers
1.1 (3 points). Complete the following proofs using basic tactics such as
`intro`, `apply`, and `exact`.
Hint: Some strategies for carrying out such proofs are described at the end of
Section 2.3 in the Hitchhiker's Guide. -/

lemma B (a b c : Prop) :
  (a → b) → (c → a) → c → b :=
begin
  assume hab,
  assume hca,
  assume c,
  exact (hab (hca c)),
end

lemma S (a b c : Prop) :
  (a → b → c) → (a → b) → a → c :=
begin
  assume h1,
  assume hab,
  assume a,
  exact (h1 a (hab a)),
end

lemma more_nonsense (a b c : Prop) :
  (c → (a → b) → a) → c → b → a :=
begin
  assume h1,
  assume hc,
  assume hb,
  apply h1,
  {
    exact hc,
  },
  {
    assume ha,
    exact hb,
  }
end

lemma even_more_nonsense (a b c : Prop) :
  (a → a → b) → (b → c) → a → b → c :=
begin
  assume haab,
  assume hbc,
  assume ha,
  assume hb,
  exact (hbc hb),
end

/- 1.2 (1 point). Prove the following lemma using basic tactics. -/

lemma weak_peirce (a b : Prop) :
  ((((a → b) → a) → a) → b) → b :=
begin
  assume h1,
  apply h1,
  assume h2,
  apply h2,
  assume ha,
  apply h1,
  assume h3,
  exact ha,
end

/- ## Question 2 (5 points): Logical Connectives
2.1 (1 point). Prove the following property about implication using basic
tactics.
Hints:
* Keep in mind that `¬ a` is the same as `a → false`. You can start by invoking
  `rw not_def` if this helps you.
* You will need to apply the elimination rules for `∨` and `false` at some
  point. -/

lemma about_implication (a b : Prop) :
  ¬ a ∨ b → a → b :=
begin
  assume h1,
  assume ha,
  apply or.elim (h1),
  {
    assume hnoa,
    apply false.elim,
    exact hnoa ha,
  },
  {
    assume hb,
    exact hb,
  }
end

namespace sorry_lemmas
/- ## Question 3 (**optional**): Intuitionistic Logic
Intuitionistic logic is extended to classical logic by assuming a classical
axiom. There are several possibilities for the choice of axiom. In this
question, we are concerned with the logical equivalence of three different
axioms: -/

def excluded_middle :=
∀a : Prop, a ∨ ¬ a

def peirce :=
∀a b : Prop, ((a → b) → a) → a

def double_negation :=
∀a : Prop, (¬¬ a) → a

/- For the proofs below, please avoid using lemmas from Lean's `classical`
namespace, because this would defeat the purpose of the exercise.
3.1 (**optional**). Prove the following implication using tactics.
Hint: You will need `or.elim` and `false.elim`. You can use
`rw excluded_middle` to unfold the definition of `excluded_middle`,
and similarly for `peirce`. -/

lemma peirce_of_em :
  excluded_middle → peirce :=
begin
  rw excluded_middle,
  rw peirce,
  assume exc,
  assume ha hb,
  assume h1,
  apply or.elim (exc ha),
  {
    assume a,
    exact a,
  },
  {
    assume noa,
    apply h1,
    assume a,
    apply false.elim,
    exact noa a,
  }
end

/- 3.2 (**optional**). Prove the following implication using tactics. -/

lemma dn_of_peirce :
  peirce → double_negation :=
begin
  rw peirce,
  rw double_negation,
  assume hpeir,
  assume a,
  assume hnonoa,
  apply hpeir a false,
  assume hnoa,
  apply false.elim,
  apply hnonoa,
  exact hnoa,
end

/- 2.2 (2 points). Prove the missing link in our chain of classical axiom
implications.
Hints:
* You can use `rw double_negation` to unfold the definition of
  `double_negation`, and similarly for the other definitions.
* You will need to apply the double negation hypothesis for `a ∨ ¬ a`. You will
  also need the left and right introduction rules for `∨` at some point. -/

#check excluded_middle
#check peirce
#check double_negation

lemma em_of_dn :
  double_negation → excluded_middle :=
begin
  rw double_negation,
  rw excluded_middle,
  assume hdn,
  assume a,
  apply hdn,
  assume hneg,
  apply hneg,
  apply or.intro_left,
  apply hdn,
  assume hna,
  apply hneg,
  apply or.intro_right,
  exact hna,
end

/- 2.3 (2 points). We have proved three of the six possible implications
between `excluded_middle`, `peirce`, and `double_negation`. State and prove the
three missing implications, exploiting the three theorems we already have. -/

#check peirce_of_em
#check dn_of_peirce
#check em_of_dn
.
lemma em_of_peirce:
  peirce → excluded_middle:=
begin
  assume hpeirce,
  exact (em_of_dn (dn_of_peirce (hpeirce))),
end

lemma peirce_of_dn:
  double_negation → peirce:=
begin
  assume hdn,
  exact (peirce_of_em (em_of_dn (hdn))),
end

lemma dn_of_em:
  excluded_middle → double_negation:=
begin
  assume hem,
  exact (dn_of_peirce (peirce_of_em (hem))),
end

end sorry_lemmas

end backward_proofs

end LoVe


-- Conjuntos con Lean
-- ==================

import data.set
open set

section Conjuntos

variable  {U : Type}
variable  {x : U}
variables {A B C : set U}

-- * Elementos básicos sobre conjuntos
-- ===================================

-- ** Pruebas de la reflexividad de la inclusión de conjuntos
-- ==========================================================

-- ----------------------------------------------------
-- Ej. 1. Demostrar
--    A ⊆ A
-- ----------------------------------------------------

-- #reduce x ∈ A
-- #reduce B ⊆ C

-- 1ª demostración
example : A ⊆ A :=
begin
  intros x h,
  exact h,
end

-- 2ª demostración
example : A ⊆ A :=
assume x,
assume h : x ∈ A,
show x ∈ A, from h

-- 3ª demostración
example : A ⊆ A :=
assume x,
assume h : x ∈ A,
h

-- 4ª demostración
example : A ⊆ A :=
assume x,
λ h : x ∈ A, h

-- 5ª demostración
example : A ⊆ A :=
assume x,
id

-- 6ª demostración
example : A ⊆ A :=
λ x, id

-- 7ª demostración
example : A ⊆ A :=
-- by library_search
set.subset.rfl

open set

-- 8ª demostración
example : A ⊆ A :=
subset.rfl

-- 9ª demostración
example : A ⊆ A :=
-- by hint
by tauto

-- 10ª demostración
example : A ⊆ A :=
by finish

-- 11ª demostración
example : A ⊆ A :=
by refl

-- ** Pruebas de la antisimetría de la inclusión de conjuntos
-- ==========================================================

-- ----------------------------------------------------
-- Ej. 2. Demostrar
--    A ⊆ B, B ⊆ A ⊢ A = B
-- ----------------------------------------------------

-- 1ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
begin
  ext,
  split,
  { intro h,
    exact h1 h, },
  { intro h,
    exact h2 h, },
end

-- 2ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
ext
( assume x,
  iff.intro
  ( assume h : x ∈ A,
    show x ∈ B, from h1 h)
  ( assume h : x ∈ B,
    show x ∈ A, from h2 h))

-- 3ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
ext
(λ x,
 iff.intro
 (λ h, h1 h)
 (λ h, h2 h))

-- 4ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
eq_of_subset_of_subset
  ( assume x,
    assume h : x ∈ A,
    show x ∈ B, from h1 h)
  ( assume x,
    assume h : x ∈ B,
    show x ∈ A, from h2 h)

-- 5ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
eq_of_subset_of_subset h1 h2

-- 6ª demostración
example
  (h1 : A ⊆ B)
  (h2 : B ⊆ A)
  : A = B :=
-- by library_search
subset.antisymm h1 h2

-- ** Introducción de la intersección
-- ==================================

-- ----------------------------------------------------
-- Ej. 3. Demostrar
--    x ∈ A → x ∈ B → x ∈ A ∩ B
-- ----------------------------------------------------

-- #reduce x ∈ A ∩ B

-- 1ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
begin
  intros h1 h2,
  simp,
  split,
  { exact h1, },
  { exact h2, },
end

-- 2ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
begin
  intros h1 h2,
  split,
  { exact h1, },
  { exact h2, },
end

-- 3ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
assume h1 : x ∈ A,
assume h2 : x ∈ B,
show x ∈ A ∩ B, from and.intro h1 h2

-- 4ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
assume h1 : x ∈ A,
assume h2 : x ∈ B,
show x ∈ A ∩ B, from ⟨h1, h2⟩

-- 5ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
assume h1 : x ∈ A,
assume h2 : x ∈ B,
⟨h1, h2⟩

-- 6ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
λ h1 h2, ⟨h1, h2⟩

-- 7ª demostración
example : x ∈ A → x ∈ B → x ∈ A ∩ B :=
-- by library_search
mem_inter

-- *** Introducción de la unión
-- ============================

-- ----------------------------------------------------
-- Ej. 4. Demostrar
--    A ⊆ A ∪ B
-- ----------------------------------------------------

-- #reduce x ∈ A ∪ B

-- 1ª demostración
example : A ⊆ A ∪ B :=
begin
  intros x h,
  simp,
  left,
  exact h,
end

-- 2ª demostración
example : A ⊆ A ∪ B :=
begin
  intros x h,
  left,
  exact h,
end

-- 3ª demostración
example : A ⊆ A ∪ B :=
assume x,
assume h : x ∈ A,
show x ∈ A ∪ B, from or.inl h

-- 4ª demostración
example : A ⊆ A ∪ B :=
assume x,
assume h : x ∈ A,
or.inl h

-- 5ª demostración
example : A ⊆ A ∪ B :=
assume x,
λ h : x ∈ A, or.inl h

-- 6ª demostración
example : A ⊆ A ∪ B :=
assume x, or.inl

-- 7ª demostración
example : A ⊆ A ∪ B :=
λ x, or.inl

-- 8ª demostración
example : A ⊆ A ∪ B :=
-- by library_search
subset_union_left A B

-- 9ª demostración
example : A ⊆ A ∪ B :=
λ x, mem_union_left B

-- 10ª demostración
example : A ⊆ A ∪ B :=
-- by hint
by finish

-- 11ª demostración
example : A ⊆ A ∪ B :=
by simp

-- ** El conjunto vacío
-- ====================

-- ----------------------------------------------------
-- Ej. 5. Demostrar
--    ∅ ⊆ A
-- ----------------------------------------------------

-- #reduce (∅ : set U)
-- #reduce x ∈ (∅ : set U)

-- 1ª demostración
example : ∅ ⊆ A :=
begin
  intros x h,
  simp at h,
  exfalso,
  exact h,
end

-- 2ª demostración
example : ∅ ⊆ A :=
begin
  intros x h,
  exfalso,
  exact h,
end

-- 3ª demostración
example : ∅ ⊆ A :=
assume x,
assume h : x ∈ (∅ : set U),
show x ∈ A, from false.elim h

-- 4ª demostración
example : ∅ ⊆ A :=
λ x, λ h, false.elim h

-- 5ª demostración
example : ∅ ⊆ A :=
λ _, false.elim

-- 6ª demostración
example : ∅ ⊆ A :=
-- by library_search
empty_subset A

-- 7ª demostración
example : ∅ ⊆ A :=
assume x,
assume h : x ∈ (∅ : set U),
show x ∈ A, from absurd h (not_mem_empty x)

-- 8ª demostración
example : ∅ ⊆ A :=
λ x h, absurd h (not_mem_empty x)

-- 9ª demostración
example : ∅ ⊆ A :=
-- by hint
by tauto

-- 10ª demostración
example : ∅ ⊆ A :=
by finish

-- 11ª demostración
example : ∅ ⊆ A :=
by simp

-- ** Diferencia de conjuntos
-- ==========================

-- ----------------------------------------------------
-- Ej. 6. Demostrar
--    A \ B ⊆ A
-- ----------------------------------------------------

-- #reduce (A \ B)
-- #reduce x ∈ A \ B

-- 1ª demostración
example : A \ B ⊆ A :=
begin
  intros x h,
  simp at h,
  exact h.left,
end

-- 2ª demostración
example : A \ B ⊆ A :=
begin
  intros x h,
  exact h.left,
end

-- 3ª demostración
example : A \ B ⊆ A :=
assume x,
assume h : x ∈ A \ B,
show x ∈ A, from h.left

-- 4ª demostración
example : A \ B ⊆ A :=
assume x,
assume h : x ∈ A \ B,
and.left h

-- 5ª demostración
example : A \ B ⊆ A :=
assume x,
λ h, and.left h

-- 6ª demostración
example : A \ B ⊆ A :=
assume x, and.left

-- 7ª demostración
example : A \ B ⊆ A :=
λ _, and.left

-- 8ª demostración
example : A \ B ⊆ A :=
-- by library_search
diff_subset A B

-- 9ª demostración
example : A \ B ⊆ A :=
assume x,
assume h : x ∈ A \ B,
show x ∈ A, from mem_of_mem_diff h

-- 10ª demostración
example : A \ B ⊆ A :=
λ _, mem_of_mem_diff

-- 11ª demostración
example : A \ B ⊆ A :=
by finish [subset_def]

-- ** Complementario de un conjunto
-- ================================

-- ----------------------------------------------------
-- Ej. 7. Demostrar
--    A \ B ⊆ Bᶜ
-- ----------------------------------------------------

-- #reduce x ∈ Bᶜ
-- #reduce Bᶜ

-- 1ª demostración
example : A \ B ⊆ Bᶜ :=
begin
  intros x h,
  simp at *,
  exact h.right,
end

-- 2ª demostración
example : A \ B ⊆ Bᶜ :=
begin
  intros x h,
  exact h.right,
end

-- 3ª demostración
example : A \ B ⊆ Bᶜ :=
assume x,
assume h1 : x ∈ A \ B,
have h2 : x ∉ B, from and.right h1,
show x ∈ Bᶜ,     from h2

-- 4ª demostración
example : A \ B ⊆ Bᶜ :=
assume x,
assume h1 : x ∈ A \ B,
show x ∈ Bᶜ, from and.right h1

-- 5ª demostración
example : A \ B ⊆ Bᶜ :=
assume x,
λ h1, and.right h1

-- 6ª demostración
example : A \ B ⊆ Bᶜ :=
assume x,
and.right

-- 7ª demostración
example : A \ B ⊆ Bᶜ :=
λ _, and.right

-- 8ª demostración
example : A \ B ⊆ Bᶜ :=
λ _, not_mem_of_mem_diff

-- ** Pruebas de la conmutatividad de la intersección
-- ==================================================

-- ----------------------------------------------------
-- Ej. 8. Demostrar
--    A ∩ B ⊆ B ∩ A
-- ----------------------------------------------------

-- 1ª demostración
example : A ∩ B ⊆ B ∩ A :=
begin
  intros x h,
  simp at *,
  split,
  { exact h.right, },
  { exact h.left,  },
end

-- 2ª demostración
example : A ∩ B ⊆ B ∩ A :=
begin
  intros x h,
  split,
  { exact h.right, },
  { exact h.left,  },
end

-- 3ª demostración
example : A ∩ B ⊆ B ∩ A :=
begin
  rintros x ⟨h1, h2⟩,
  split,
  { exact h2, },
  { exact h1, },
end

-- 4ª demostración
example : A ∩ B ⊆ B ∩ A :=
begin
  rintros x ⟨h1, h2⟩,
  exact ⟨h2, h1⟩,
end

-- 5ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
assume h : x ∈ A ∩ B,
have h1 : x ∈ A, from and.left h,
have h2 : x ∈ B, from and.right h,
show x ∈ B ∩ A,  from and.intro h2 h1

-- 6ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
assume h : x ∈ A ∩ B,
have h1 : x ∈ A ∧ x ∈ B, from h,
have h2 : x ∈ B ∧ x ∈ A, from and.comm.mp h1,
show x ∈ B ∩ A,          from h2

-- 7ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
assume h : x ∈ A ∩ B,
show x ∈ B ∩ A, from and.comm.mp h

-- 8ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
assume h : x ∈ A ∩ B,
and.comm.mp h

-- 9ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
λ h, and.comm.mp h

-- 10ª demostración
example : A ∩ B ⊆ B ∩ A :=
assume x,
and.comm.mp

-- 10ª demostración
example : A ∩ B ⊆ B ∩ A :=
λ _, and.comm.mp

-- 11ª demostración
example : A ∩ B ⊆ B ∩ A :=
-- by hint
by finish

-- 12ª demostración
lemma aux : A ∩ B ⊆ B ∩ A :=
by simp

-- ----------------------------------------------------
-- Ej. 9. Demostrar
--    A ∩ B = B ∩ A
-- ----------------------------------------------------

-- 1ª demostración
example : A ∩ B = B ∩ A :=
begin
  apply eq_of_subset_of_subset,
  { exact aux, },
  { exact aux, },
end

-- 2ª demostración
example : A ∩ B = B ∩ A :=
eq_of_subset_of_subset aux aux

-- 3ª demostración
example : A ∩ B = B ∩ A :=
-- by library_search
inter_comm A B

-- 4ª demostración
example : A ∩ B = B ∩ A :=
-- by hint
by finish

-- * Identidades conjuntistas
-- ==========================

-- ** Distributiva de la intersección sobre la unión
-- =================================================

-- ----------------------------------------------------
-- Ej. 10. Demostrar
--    A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C)
-- ----------------------------------------------------

-- 1ª demostración
example :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
begin
  intros x h,
  cases h with ha hbc,
  cases hbc with hb hc,
  { left,
    split,
    { exact ha, },
    { exact hb, }},
  { right,
    split,
    { exact ha, },
    { exact hc, }},
end

-- 2ª demostración
example :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
begin
  intros x h,
  cases h with ha hbc,
  cases hbc with hb hc,
  { left,
    split,
    { assumption, },
    { assumption, }},
  { right,
    split,
    { assumption, },
    { assumption, }},
end

-- 3ª demostración
example :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
begin
  intros x h,
  cases h with ha hbc,
  cases hbc with hb hc,
  { left,
    split,
    assumption', },
  { right,
    split,
    assumption', },
end

-- 4ª demostración
example :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
begin
  rintros x ⟨ha, (hb | hc)⟩,
  { left,
    split,
    assumption', },
  { right,
    split,
    assumption', },
end

-- 5ª demostración
example :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
assume x,
assume h : x ∈ A ∩ (B ∪ C),
have x ∈ A, from and.left h,
have x ∈ B ∪ C, from and.right h,
or.elim (‹x ∈ B ∪ C›)
  ( assume : x ∈ B,
    have x ∈ A ∩ B, from and.intro ‹x ∈ A› ‹x ∈ B›,
    show x ∈ (A ∩ B) ∪ (A ∩ C), from or.inl this)
  ( assume : x ∈ C,
    have x ∈ A ∩ C, from and.intro ‹x ∈ A› ‹x ∈ C›,
    show x ∈ (A ∩ B) ∪ (A ∩ C), from or.inr this)

-- 6ª demostración
lemma inter_union_l1 :
  A ∩ (B ∪ C) ⊆ (A ∩ B) ∪ (A ∩ C) :=
assume x,
assume h : x ∈ A ∩ (B ∪ C),
have ha : x ∈ A, from and.left h,
have hbc : x ∈ B ∪ C, from and.right h,
or.elim hbc
  ( assume hb : x ∈ B,
    have hab: x ∈ A ∩ B, from and.intro ha hb,
    show x ∈ (A ∩ B) ∪ (A ∩ C), from or.inl hab)
  ( assume hc : x ∈ C,
    have hac : x ∈ A ∩ C, from and.intro ha hc,
    show x ∈ (A ∩ B) ∪ (A ∩ C), from or.inr hac)

-- ----------------------------------------------------
-- Ej. 11. Demostrar
--    (A ∩ B) ∪ (A ∩ C) ⊆ A ∩ (B ∪ C)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (A ∩ B) ∪ (A ∩ C) ⊆ A ∩ (B ∪ C) :=
begin
  intros x h,
  cases h with hab hac,
  { split,
    { exact hab.left, },
    { left,
      exact hab.right, }},
  { split,
    { exact hac.left, },
    { right,
      exact hac.right, }},
end

-- 2ª demostración
example :
  (A ∩ B) ∪ (A ∩ C) ⊆ A ∩ (B ∪ C) :=
begin
  rintros x (⟨ha, hb⟩ | ⟨ha, hc⟩),
  { split,
    { exact ha, },
    { left,
      exact hb, }},
  { split,
    { exact ha, },
    { right,
      exact hc, }},
end

-- 3ª demostración
lemma inter_union_l2 :
  (A ∩ B) ∪ (A ∩ C) ⊆ A ∩ (B ∪ C) :=
assume x,
assume : x ∈ (A ∩ B) ∪ (A ∩ C),
or.elim this
  ( assume h : x ∈ A ∩ B,
    have x ∈ A, from and.left h,
    have x ∈ B, from and.right h,
    have x ∈ B ∪ C, from or.inl this,
    show x ∈ A ∩ (B ∪ C), from and.intro ‹x ∈ A› this)
  ( assume h : x ∈ A ∩ C,
    have x ∈ A, from and.left h,
    have x ∈ C, from and.right h,
    have x ∈ B ∪ C, from or.inr this,
    show x ∈ A ∩ (B ∪ C), from and.intro ‹x ∈ A› this)

-- ----------------------------------------------------
-- Ej. 12. Demostrar
--    (A ∩ B) ∪ (A ∩ C) = A ∩ (B ∪ C)
-- ----------------------------------------------------

-- 1ª demostración
example :
  A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) :=
-- by library_search
inter_distrib_left A B C

-- 2ª demostración
theorem inter_union :
  A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) :=
eq_of_subset_of_subset inter_union_l1 inter_union_l2

-- 3ª demostración
example :
  A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) :=
begin
  ext,
  simp,
  exact and_or_distrib_left,
end

-- 4ª demostración
example :
  A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) :=
begin
  ext,
  exact and_or_distrib_left,
end

-- 5ª demostración
example :
  A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) :=
ext (λ x, and_or_distrib_left)

-- ** (A ∩ Bᶜ) ∪ B = A ∪ B
-- =======================

-- ----------------------------------------------------
-- Ej. 1e. Demostrar
--    (A ∩ Bᶜ) ∪ B = A ∪ B
-- ----------------------------------------------------

-- 1ª demostración
-- ===============

example : (A ∩ Bᶜ) ∪ B = A ∪ B :=
calc
  (A ∩ Bᶜ) ∪ B = (A ∪ B) ∩ (Bᶜ ∪ B) : by rw union_distrib_right
           ... = (A ∪ B) ∩ univ     : by rw compl_union_self
           ... = A ∪ B              : by rw inter_univ

example : (A ∩ B) ∪ C = (A ∪ C) ∩ (B ∪ C) :=
-- by library_search
union_distrib_right A B C

example : Bᶜ ∪ B = univ :=
-- by library_search
compl_union_self B

example : A ∩ univ = A :=
-- by library_search
inter_univ A

-- 2ª demostración
-- ===============

example : (A ∩ Bᶜ) ∪ B = A ∪ B :=
begin
  rw union_distrib_right,
  rw compl_union_self,
  rw inter_univ,
end

-- 3ª demostración
-- ===============

example : (A ∩ Bᶜ) ∪ B = A ∪ B :=
by rw [union_distrib_right, compl_union_self, inter_univ]

-- 4ª demostración
-- ===============

example : (A ∩ Bᶜ) ∪ B = A ∪ B :=
by simp [union_distrib_right]

end Conjuntos

-- * Familias de conjuntos
-- =======================

section Familias

variables {I U : Type}
variable  {x : U}
variables {A B : I → set U}

-- ** Pertenencia a uniones e intersecciones de familias
-- =====================================================

-- ----------------------------------------------------
-- Ej. 14. Demostrar que
--    (x ∈ ⋃ i, A i) ↔ (∃ i, x ∈ A i)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (x ∈ ⋃ i, A i) ↔ (∃ i, x ∈ A i) :=
-- by library_search
mem_Union

-- 2ª demostración
example :
  (x ∈ ⋃ i, A i) ↔ (∃ i, x ∈ A i) :=
by simp

-- ----------------------------------------------------
-- Ej. 15. Demostrar que
--    (x ∈ ⋂ i, A i) ↔ (∀ i, x ∈ A i)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (x ∈ ⋂ i, A i) ↔ (∀ i, x ∈ A i) :=
-- by library_search
mem_Inter

-- 2ª demostración
example :
  (x ∈ ⋂ i, A i) ↔ (∀ i, x ∈ A i) :=
by simp

-- ** Distributiva de la intersección general sobre la intersección
-- ================================================================

-- 1ª demostración
example :
  (⋂ i, A i ∩ B i) = (⋂ i, A i) ∩ (⋂ i, B i) :=
begin
  ext,
  split,
  { intro h,
    rw mem_Inter at h,
    split,
    { rw mem_Inter,
      intro i,
      exact (h i).left, },
    { rw mem_Inter,
      intro i,
      exact (h i).right, }},
  { rintro ⟨h1, h2⟩,
    rw mem_Inter at *,
    intro i,
    exact ⟨h1 i, h2 i⟩, },
end

-- 2ª demostración
example :
  (⋂ i, A i ∩ B i) = (⋂ i, A i) ∩ (⋂ i, B i) :=
ext $
assume x : U,
iff.intro
( assume h : x ∈ ⋂ i, A i ∩ B i,
  have h1 : ∀ i, x ∈ A i ∩ B i,
    from mem_Inter.mp h,
  have h2 : ∀ i, x ∈ A i,
    from assume i, and.left (h1 i),
  have h3 : ∀ i, x ∈ B i,
    from assume i, and.right (h1 i),
  have h4 : x ∈ ⋂ i, A i,
    from mem_Inter.mpr h2,
  have h5 : x ∈ ⋂ i, B i,
    from mem_Inter.mpr h3,
  show x ∈ (⋂ i, A i) ∩ (⋂ i, B i),
    from and.intro h4 h5)
( assume h : x ∈ (⋂ i, A i) ∩ (⋂ i, B i),
  have h1 : ∀ i, x ∈ A i,
    from mem_Inter.mp (and.left h),
  have h2 : ∀ i, x ∈ B i,
    from mem_Inter.mp (and.right h),
  have h3 : ∀ i, x ∈ A i ∩ B i,
    from assume i, and.intro (h1 i) (h2 i),
  show x ∈ ⋂ i, A i ∩ B i,
    from mem_Inter.mpr h3)

-- 3ª demostración
example :
  (⋂ i, A i ∩ B i) = (⋂ i, A i) ∩ (⋂ i, B i) :=
-- by library_search
Inter_inter_distrib A B

-- 4ª demostración
example :
  (⋂ i, A i ∩ B i) = (⋂ i, A i) ∩ (⋂ i, B i) :=
ext (by finish)

-- ** Pruebas de intersección sobre unión general
-- ==============================================

variable  {C : set U}

-- ----------------------------------------------------
-- Ej. 16. Demostrar
--    C ∩ (⋃i, A i) ⊆ (⋃ i, C ∩ A i)
-- ----------------------------------------------------

-- 1ª demostración
example :
  C ∩ (⋃i, A i) ⊆ (⋃ i, C ∩ A i)  :=
begin
  rintros x ⟨hC, hU⟩,
  rw mem_Union at hU,
  cases hU with i hA,
  apply mem_Union.mpr,
  use i,
  split,
  assumption',
end

-- 2ª demostración
example :
  C ∩ (⋃i, A i) ⊆ (⋃ i, C ∩ A i)  :=
begin
  intros x h,
  simp * at *,
end

-- 3ª demostración
lemma inter_Uni_l1 :
  C ∩ (⋃i, A i) ⊆ (⋃ i, C ∩ A i)  :=
by {intros x h, simp * at *}

-- ----------------------------------------------------
-- Ej. 17. Demostrar
--    (⋃ i, C ∩ A i) ⊆ C ∩ (⋃i, A i)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (⋃ i, C ∩ A i) ⊆ C ∩ (⋃i, A i) :=
begin
  intros x h,
  rw mem_Union at h,
  cases h with i hi,
  cases hi with hC hA,
  split,
  { exact hC, },
  { apply mem_Union.mpr,
    use i,
    exact hA, },
end

-- 2ª demostración
example : (⋃ i, C ∩ A i) ⊆ C ∩ (⋃i, A i) :=
begin
  intros x h,
  rw mem_Union at h,
  rcases h with ⟨i, hC, hA⟩,
  split,
  { exact hC, },
  { apply mem_Union.mpr,
    use i,
    exact hA, },
end

-- 3ª demostración
example :
  (⋃ i, C ∩ A i) ⊆ C ∩ (⋃i, A i) :=
begin
  intros x h,
  simp * at *,
end

-- 4ª demostración
lemma inter_Uni_l2 :
  (⋃ i, C ∩ A i) ⊆ C ∩ (⋃i, A i) :=
by {intros x h, simp * at *}

-- ----------------------------------------------------
-- Ej. 18. Demostrar
--    C ∩ (⋃i, A i) = (⋃ i, C ∩ A i)
-- ----------------------------------------------------

-- 1ª demostración
example :
  C ∩ (⋃i, A i) = (⋃ i, C ∩ A i) :=
eq_of_subset_of_subset inter_Uni_l1 inter_Uni_l2

-- 2ª demostración
example :
  C ∩ (⋃i, A i) = (⋃ i, C ∩ A i) :=
-- by library_search
inter_Union C A

-- 3ª demostración
example :
  C ∩ (⋃i, A i) = (⋃ i, C ∩ A i) :=
ext $ by simp

-- 4ª demostración
example :
  C ∩ (⋃i, A i) = (⋃ i, C ∩ A i) :=
by {ext, simp}

end Familias

-- ** Pruebas de (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j)
-- ===============================================

section FamiliaDoble

variables {I J U : Type}
variables (A : I → J → set U)

-- ----------------------------------------------------
-- Ej. 19. Demostrar
--    (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j)
-- ----------------------------------------------------

-- 1ª demostración
example : (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j) :=
begin
  intros x h,
  rw mem_Union at h,
  cases h with i hi,
  rw mem_Inter at hi,
  apply mem_Inter.mpr,
  intro j,
  apply mem_Union.mpr,
  use i,
  exact (hi j),
end

-- 2ª demostración
example : (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j) :=
begin
  intros x h,
  simp * at *,
  cases h with i hi,
  intro j,
  use i,
  exact (hi j),
end

end FamiliaDoble

-- * Conjunto potencia
-- ===================

section Potencia

variable  {U : Type}
variables {A B C : set U}

-- ** Monotonía del conjunto potencia
-- ==================================

-- #reduce 𝒫 A
-- #reduce B ∈ 𝒫 A

-- ----------------------------------------------------
-- Ej. 20. Demostrar
--    𝒫 A ⊆ 𝒫 B → A ⊆ B
-- ----------------------------------------------------

-- 1ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  apply subset_of_mem_powerset,
  apply h,
  apply mem_powerset,
  exact subset.rfl,
end

-- 2ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  apply h,
  exact subset.rfl,
end

-- 3ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
begin
  intro h,
  exact (h subset.rfl),
end

-- 4ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
λ h, h subset.rfl

-- 5ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
have h3 : A ∈ 𝒫 A, from h2,
have h4 : A ∈ 𝒫 B, from h1 h3,
show A ⊆ B, from h4

-- 6ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
have h3 : A ∈ 𝒫 A, from h2,
h1 h3

-- 7ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
have h2 : A ⊆ A, from subset.rfl,
h1 h2

-- 8ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
assume h1 : 𝒫 A ⊆ 𝒫 B,
h1 subset.rfl

-- 9ª demostración
lemma aux1 : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
λ h, h subset.rfl

-- 10ª demostración
example : 𝒫 A ⊆ 𝒫 B → A ⊆ B :=
powerset_mono.mp

-- ----------------------------------------------------
-- Ej. 21. Demostrar
--    A ⊆ B → 𝒫 A ⊆ 𝒫 B
-- ----------------------------------------------------

-- 1ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
begin
  intro h,
  intros C hCA,
  apply mem_powerset,
  apply subset.trans hCA h,
end

-- 2ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
begin
  intros h C hCA,
  apply subset.trans hCA h,
end

-- 3ª demostración
lemma aux2 : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
λ h C hCA, subset.trans hCA h

-- 4ª demostración
example : A ⊆ B → 𝒫 A ⊆ 𝒫 B :=
powerset_mono.mpr

-- ----------------------------------------------------
-- Ej. 22. Demostrar
--    𝒫 A ⊆ 𝒫 B ↔ A ⊆ B
-- ----------------------------------------------------

-- 1ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
iff.intro aux1 aux2

-- 2ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
-- by library_search
powerset_mono

-- 3ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
-- by hint
by finish

-- 4ª demostración
example : 𝒫 A ⊆ 𝒫 B ↔ A ⊆ B :=
by simp

end Potencia