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

import field_theory.subfield
import field_theory.tower
import ring_theory.algebraic

/-!
# Intermediate fields

Let `L / K` be a field extension, given as an instance `algebra K L`.
This file defines the type of fields in between `K` and `L`, `intermediate_field K L`.
An `intermediate_field K L` is a subfield of `L` which contains (the image of) `K`,
i.e. it is a `subfield L` and a `subalgebra K L`.

## Main definitions

 * `intermediate_field K L` : the type of intermediate fields between `K` and `L`.

 * `subalgebra.to_intermediate_field`: turns a subalgebra closed under `⁻¹`
   into an intermediate field

 * `subfield.to_intermediate_field`: turns a subfield containing the image of `K`
   into an intermediate field

* `intermediate_field.map`: map an intermediate field along an `alg_hom`

## Implementation notes

Intermediate fields are defined with a structure extending `subfield` and `subalgebra`.
A `subalgebra` is closed under all operations except `⁻¹`,

## Tags
intermediate field, field extension
-/

open finite_dimensional polynomial
open_locale big_operators

variables (K L : Type*) [field K] [field L] [algebra K L]

/-- `S : intermediate_field K L` is a subset of `L` such that there is a field
tower `L / S / K`. -/
structure intermediate_field extends subalgebra K L :=
(neg_mem' : ∀ x ∈ carrier, -x ∈ carrier)
(inv_mem' : ∀ x ∈ carrier, x⁻¹ ∈ carrier)

/-- Reinterpret an `intermediate_field` as a `subalgebra`. -/
add_decl_doc intermediate_field.to_subalgebra

variables {K L} (S : intermediate_field K L)

namespace intermediate_field

/-- Reinterpret an `intermediate_field` as a `subfield`. -/
def to_subfield : subfield L := { ..S.to_subalgebra, ..S }

instance : set_like (intermediate_field K L) L :=
⟨λ S, S.to_subalgebra.carrier, by { rintros ⟨⟨⟩⟩ ⟨⟨⟩⟩ ⟨h⟩, congr, }⟩

@[simp]
lemma mem_carrier {s : intermediate_field K L} {x : L} : x ∈ s.carrier ↔ x ∈ s := iff.rfl

/-- Two intermediate fields are equal if they have the same elements. -/
@[ext] theorem ext {S T : intermediate_field K L} (h : ∀ x, x ∈ S ↔ x ∈ T) : S = T :=
set_like.ext h

@[simp] lemma coe_to_subalgebra : (S.to_subalgebra : set L) = S := rfl

@[simp] lemma coe_to_subfield : (S.to_subfield : set L) = S := rfl

@[simp] lemma mem_mk (s : set L) (hK : ∀ x, algebra_map K L x ∈ s)
  (ho hm hz ha hn hi) (x : L) :
  x ∈ intermediate_field.mk (subalgebra.mk s ho hm hz ha hK) hn hi ↔ x ∈ s := iff.rfl

@[simp] lemma mem_to_subalgebra (s : intermediate_field K L) (x : L) :
  x ∈ s.to_subalgebra ↔ x ∈ s := iff.rfl

@[simp] lemma mem_to_subfield (s : intermediate_field K L) (x : L) :
  x ∈ s.to_subfield ↔ x ∈ s := iff.rfl

/-- An intermediate field contains the image of the smaller field. -/
theorem algebra_map_mem (x : K) : algebra_map K L x ∈ S :=
S.algebra_map_mem' x

/-- An intermediate field contains the ring's 1. -/
theorem one_mem : (1 : L) ∈ S := S.one_mem'

/-- An intermediate field contains the ring's 0. -/
theorem zero_mem : (0 : L) ∈ S := S.zero_mem'

/-- An intermediate field is closed under multiplication. -/
theorem mul_mem : ∀ {x y : L}, x ∈ S → y ∈ S → x * y ∈ S := S.mul_mem'

/-- An intermediate field is closed under scalar multiplication. -/

/-- An intermediate field is closed under addition. -/
theorem add_mem : ∀ {x y : L}, x ∈ S → y ∈ S → x + y ∈ S := S.add_mem'

/-- An intermediate field is closed under subtraction -/
theorem sub_mem {x y : L} (hx : x ∈ S) (hy : y ∈ S) : x - y ∈ S :=
S.to_subfield.sub_mem hx hy

/-- An intermediate field is closed under negation. -/
theorem neg_mem : ∀ {x : L}, x ∈ S → -x ∈ S := S.neg_mem'

/-- An intermediate field is closed under inverses. -/
theorem inv_mem : ∀ {x : L}, x ∈ S → x⁻¹ ∈ S := S.inv_mem'

/-- An intermediate field is closed under division. -/
theorem div_mem {x y : L} (hx : x ∈ S) (hy : y ∈ S) : x / y ∈ S :=
S.to_subfield.div_mem hx hy

/-- Copy of an intermediate field with a new `carrier` equal to the old one. Useful to fix
definitional equalities. -/
protected def copy (S : intermediate_field K L) (s : set L) (hs : s = ↑S) :
  intermediate_field K L :=
{ to_subalgebra := S.to_subalgebra.copy s (hs : s = (S.to_subalgebra).carrier),
  neg_mem' :=
    have hs' : (S.to_subalgebra.copy s hs).carrier = (S.to_subalgebra).carrier := hs,
    hs'.symm ▸ S.neg_mem',
  inv_mem' :=
    have hs' : (S.to_subalgebra.copy s hs).carrier = (S.to_subalgebra).carrier := hs,
    hs'.symm ▸ S.inv_mem' }

@[simp] lemma coe_copy (S : intermediate_field K L) (s : set L) (hs : s = ↑S) :
  (S.copy s hs : set L) = s := rfl

lemma copy_eq (S : intermediate_field K L) (s : set L) (hs : s = ↑S) : S.copy s hs = S :=
set_like.coe_injective hs

/-- Product of a list of elements in an intermediate_field is in the intermediate_field. -/
lemma list_prod_mem {l : list L} : (∀ x ∈ l, x ∈ S) → l.prod ∈ S :=
S.to_subfield.list_prod_mem

/-- Sum of a list of elements in an intermediate field is in the intermediate_field. -/
lemma list_sum_mem {l : list L} : (∀ x ∈ l, x ∈ S) → l.sum ∈ S :=
S.to_subfield.list_sum_mem

/-- Product of a multiset of elements in an intermediate field is in the intermediate_field. -/
lemma multiset_prod_mem (m : multiset L) :
  (∀ a ∈ m, a ∈ S) → m.prod ∈ S :=
S.to_subfield.multiset_prod_mem m

/-- Sum of a multiset of elements in a `intermediate_field` is in the `intermediate_field`. -/
lemma multiset_sum_mem (m : multiset L) :
  (∀ a ∈ m, a ∈ S) → m.sum ∈ S :=
S.to_subfield.multiset_sum_mem m

/-- Product of elements of an intermediate field indexed by a `finset` is in the intermediate_field.
-/
lemma prod_mem {ι : Type*} {t : finset ι} {f : ι → L} (h : ∀ c ∈ t, f c ∈ S) :
  ∏ i in t, f i ∈ S :=
S.to_subfield.prod_mem h

/-- Sum of elements in a `intermediate_field` indexed by a `finset` is in the `intermediate_field`.
-/
lemma sum_mem {ι : Type*} {t : finset ι} {f : ι → L} (h : ∀ c ∈ t, f c ∈ S) :
  ∑ i in t, f i ∈ S :=
S.to_subfield.sum_mem h

lemma pow_mem {x : L} (hx : x ∈ S) : ∀ (n : ℤ), x^n ∈ S
| (n : ℕ) := by { rw zpow_coe_nat, exact S.to_subfield.pow_mem hx _, }
| -[1+ n] := by { rw [zpow_neg_succ_of_nat],
    exact S.to_subfield.inv_mem (S.to_subfield.pow_mem hx _) }

lemma zsmul_mem {x : L} (hx : x ∈ S) (n : ℤ) :
  n • x ∈ S := S.to_subfield.zsmul_mem hx n

lemma coe_int_mem (n : ℤ) : (n : L) ∈ S :=
by simp only [← zsmul_one, zsmul_mem, one_mem]

end intermediate_field

/-- Turn a subalgebra closed under inverses into an intermediate field -/
def subalgebra.to_intermediate_field (S : subalgebra K L) (inv_mem : ∀ x ∈ S, x⁻¹ ∈ S) :
  intermediate_field K L :=
{ neg_mem' := λ x, S.neg_mem,
  inv_mem' := inv_mem,
  .. S }

@[simp] lemma to_subalgebra_to_intermediate_field
  (S : subalgebra K L) (inv_mem : ∀ x ∈ S, x⁻¹ ∈ S) :
  (S.to_intermediate_field inv_mem).to_subalgebra = S :=
by { ext, refl }

@[simp] lemma to_intermediate_field_to_subalgebra
  (S : intermediate_field K L) (inv_mem : ∀ x ∈ S.to_subalgebra, x⁻¹ ∈ S) :
  S.to_subalgebra.to_intermediate_field inv_mem = S :=
by { ext, refl }

/-- Turn a subfield of `L` containing the image of `K` into an intermediate field -/
def subfield.to_intermediate_field (S : subfield L)
  (algebra_map_mem : ∀ x, algebra_map K L x ∈ S) :
  intermediate_field K L :=
{ algebra_map_mem' := algebra_map_mem,
  .. S }

namespace intermediate_field

/-- An intermediate field inherits a field structure -/
instance to_field : field S :=
S.to_subfield.to_field

@[simp, norm_cast] lemma coe_add (x y : S) : (↑(x + y) : L) = ↑x + ↑y := rfl
@[simp, norm_cast] lemma coe_neg (x : S) : (↑(-x) : L) = -↑x := rfl
@[simp, norm_cast] lemma coe_mul (x y : S) : (↑(x * y) : L) = ↑x * ↑y := rfl
@[simp, norm_cast] lemma coe_inv (x : S) : (↑(x⁻¹) : L) = (↑x)⁻¹ := rfl
@[simp, norm_cast] lemma coe_zero : ((0 : S) : L) = 0 := rfl
@[simp, norm_cast] lemma coe_one : ((1 : S) : L) = 1 := rfl
@[simp, norm_cast] lemma coe_pow (x : S) (n : ℕ) : (↑(x ^ n) : L) = ↑x ^ n :=
begin
  induction n with n ih,
  { simp },
  { simp [pow_succ, ih] }
end

@[simp, norm_cast]
lemma coe_sum {ι : Type*} [fintype ι] (f : ι → S) : (↑∑ i, f i : L) = ∑ i, (f i : L) :=
begin
  classical,
  induction finset.univ using finset.induction_on with i s hi H,
  { simp },
  { rw [finset.sum_insert hi, coe_add, H, finset.sum_insert hi] }
end

@[simp, norm_cast]
lemma coe_prod {ι : Type*} [fintype ι] (f : ι → S) : (↑∏ i, f i : L) = ∏ i, (f i : L) :=
begin
  classical,
  induction finset.univ using finset.induction_on with i s hi H,
  { simp },
  { rw [finset.prod_insert hi, coe_mul, H, finset.prod_insert hi] }
end

/-! `intermediate_field`s inherit structure from their `subalgebra` coercions. -/

instance module' {R} [semiring R] [has_scalar R K] [module R L] [is_scalar_tower R K L] :
  module R S :=
S.to_subalgebra.module'
instance module : module K S := S.to_subalgebra.module

instance is_scalar_tower {R} [semiring R] [has_scalar R K] [module R L]
  [is_scalar_tower R K L] :
  is_scalar_tower R K S :=
S.to_subalgebra.is_scalar_tower

@[simp] lemma coe_smul {R} [semiring R] [has_scalar R K] [module R L] [is_scalar_tower R K L]
  (r : R) (x : S) :
  ↑(r • x) = (r • x : L) := rfl

instance algebra' {K'} [comm_semiring K'] [has_scalar K' K] [algebra K' L]
  [is_scalar_tower K' K L] :
  algebra K' S :=
S.to_subalgebra.algebra'
instance algebra : algebra K S := S.to_subalgebra.algebra

instance to_algebra {R : Type*} [semiring R] [algebra L R] : algebra S R :=
S.to_subalgebra.to_algebra

instance is_scalar_tower_bot {R : Type*} [semiring R] [algebra L R] :
  is_scalar_tower S L R :=
is_scalar_tower.subalgebra _ _ _ S.to_subalgebra

instance is_scalar_tower_mid {R : Type*} [semiring R] [algebra L R] [algebra K R]
  [is_scalar_tower K L R] : is_scalar_tower K S R :=
is_scalar_tower.subalgebra' _ _ _ S.to_subalgebra

/-- Specialize `is_scalar_tower_mid` to the common case where the top field is `L` -/
instance is_scalar_tower_mid' : is_scalar_tower K S L :=
S.is_scalar_tower_mid

variables {L' : Type*} [field L'] [algebra K L']

/-- If `f : L →+* L'` fixes `K`, `S.map f` is the intermediate field between `L'` and `K`
such that `x ∈ S ↔ f x ∈ S.map f`. -/
def map (f : L →ₐ[K] L') : intermediate_field K L' :=
{ inv_mem' := by { rintros _ ⟨x, hx, rfl⟩, exact ⟨x⁻¹, S.inv_mem hx, f.map_inv x⟩ },
  neg_mem' := λ x hx, (S.to_subalgebra.map f).neg_mem hx,
  .. S.to_subalgebra.map f}

lemma map_map {K L₁ L₂ L₃ : Type*} [field K] [field L₁] [algebra K L₁]
  [field L₂] [algebra K L₂] [field L₃] [algebra K L₃]
  (E : intermediate_field K L₁) (f : L₁ →ₐ[K] L₂) (g : L₂ →ₐ[K] L₃) :
  (E.map f).map g = E.map (g.comp f) :=
set_like.coe_injective $ set.image_image _ _ _

/-- Given an equivalence `e : L ≃ₐ[K] L'` of `K`-field extensions and an intermediate
field `E` of `L/K`, `intermediate_field_equiv_map e E` is the induced equivalence
between `E` and `E.map e` -/
@[simps] def intermediate_field_map (e : L ≃ₐ[K] L') (E : intermediate_field K L) :
  E ≃ₐ[K] (E.map e.to_alg_hom) :=
e.subalgebra_map E.to_subalgebra

/-- The embedding from an intermediate field of `L / K` to `L`. -/
def val : S →ₐ[K] L :=
S.to_subalgebra.val

@[simp] theorem coe_val : ⇑S.val = coe := rfl

@[simp] lemma val_mk {x : L} (hx : x ∈ S) : S.val ⟨x, hx⟩ = x := rfl

lemma range_val : S.val.range = S.to_subalgebra :=
S.to_subalgebra.range_val

lemma aeval_coe {R : Type*} [comm_ring R] [algebra R K] [algebra R L]
  [is_scalar_tower R K L] (x : S) (P : polynomial R) : aeval (x : L) P = aeval x P :=
begin
  refine polynomial.induction_on' P (λ f g hf hg, _) (λ n r, _),
  { rw [aeval_add, aeval_add, coe_add, hf, hg] },
  { simp only [coe_mul, aeval_monomial, coe_pow, mul_eq_mul_right_iff],
    left, refl }
end

lemma coe_is_integral_iff {R : Type*} [comm_ring R] [algebra R K] [algebra R L]
  [is_scalar_tower R K L] {x : S} : is_integral R (x : L) ↔ _root_.is_integral R x :=
begin
  refine ⟨λ h, _, λ h, _⟩,
  { obtain ⟨P, hPmo, hProot⟩ := h,
    refine ⟨P, hPmo, (ring_hom.injective_iff _).1 (algebra_map ↥S L).injective _ _⟩,
    letI : is_scalar_tower R S L := is_scalar_tower.of_algebra_map_eq (congr_fun rfl),
    rwa [eval₂_eq_eval_map, ← eval₂_at_apply, eval₂_eq_eval_map, polynomial.map_map,
      ← is_scalar_tower.algebra_map_eq, ← eval₂_eq_eval_map] },
  { obtain ⟨P, hPmo, hProot⟩ := h,
    refine ⟨P, hPmo, _⟩,
    rw [← aeval_def, aeval_coe, aeval_def, hProot, coe_zero] },
end

variables {S}

lemma to_subalgebra_injective {S S' : intermediate_field K L}
  (h : S.to_subalgebra = S'.to_subalgebra) : S = S' :=
by { ext, rw [← mem_to_subalgebra, ← mem_to_subalgebra, h] }

variables (S)

lemma set_range_subset : set.range (algebra_map K L) ⊆ S :=
S.to_subalgebra.range_subset

lemma field_range_le : (algebra_map K L).field_range ≤ S.to_subfield :=
λ x hx, S.to_subalgebra.range_subset (by rwa [set.mem_range, ← ring_hom.mem_field_range])

@[simp] lemma to_subalgebra_le_to_subalgebra {S S' : intermediate_field K L} :
  S.to_subalgebra ≤ S'.to_subalgebra ↔ S ≤ S' := iff.rfl

@[simp] lemma to_subalgebra_lt_to_subalgebra {S S' : intermediate_field K L} :
  S.to_subalgebra < S'.to_subalgebra ↔ S < S' := iff.rfl

variables {S}

section tower

/-- Lift an intermediate_field of an intermediate_field -/
def lift1 {F : intermediate_field K L} (E : intermediate_field K F) : intermediate_field K L :=
  map E (val F)

/-- Lift an intermediate_field of an intermediate_field -/
def lift2 {F : intermediate_field K L} (E : intermediate_field F L) : intermediate_field K L :=
{ carrier := E.carrier,
  zero_mem' := zero_mem E,
  add_mem' := λ x y, add_mem E,
  neg_mem' := λ x, neg_mem E,
  one_mem' := one_mem E,
  mul_mem' := λ x y, mul_mem E,
  inv_mem' := λ x, inv_mem E,
  algebra_map_mem' := λ x, algebra_map_mem E (algebra_map K F x) }

instance has_lift1 {F : intermediate_field K L} :
  has_lift_t (intermediate_field K F) (intermediate_field K L) := ⟨lift1⟩

instance has_lift2 {F : intermediate_field K L} :
  has_lift_t (intermediate_field F L) (intermediate_field K L) := ⟨lift2⟩

@[simp] lemma mem_lift2 {F : intermediate_field K L} {E : intermediate_field F L} {x : L} :
  x ∈ (↑E : intermediate_field K L) ↔ x ∈ E := iff.rfl

/-- This was formerly an instance called `lift2_alg`, but an instance above already provides it. -/
example {F : intermediate_field K L} {E : intermediate_field F L} : algebra K E :=
by apply_instance

lemma lift2_algebra_map {F : intermediate_field K L} {E : intermediate_field F L} :
  algebra_map K E = (algebra_map F E).comp (algebra_map K F) := rfl

instance lift2_tower {F : intermediate_field K L} {E : intermediate_field F L} :
  is_scalar_tower K F E :=
E.is_scalar_tower

/-- `lift2` is isomorphic to the original `intermediate_field`. -/
def lift2_alg_equiv {F : intermediate_field K L} (E : intermediate_field F L) :
  (↑E : intermediate_field K L) ≃ₐ[K] E :=
alg_equiv.refl

end tower

section finite_dimensional

variables (F E : intermediate_field K L)

instance finite_dimensional_left [finite_dimensional K L] : finite_dimensional K F :=
finite_dimensional.finite_dimensional_submodule F.to_subalgebra.to_submodule

instance finite_dimensional_right [finite_dimensional K L] : finite_dimensional F L :=
right K F L

@[simp] lemma dim_eq_dim_subalgebra :
  module.rank K F.to_subalgebra = module.rank K F := rfl

@[simp] lemma finrank_eq_finrank_subalgebra :
  finrank K F.to_subalgebra = finrank K F := rfl

variables {F} {E}

@[simp] lemma to_subalgebra_eq_iff : F.to_subalgebra = E.to_subalgebra ↔ F = E :=
by { rw [set_like.ext_iff, set_like.ext'_iff, set.ext_iff], refl }

lemma eq_of_le_of_finrank_le [finite_dimensional K L] (h_le : F ≤ E)
  (h_finrank : finrank K E ≤ finrank K F) : F = E :=
to_subalgebra_injective $ subalgebra.to_submodule_injective $ eq_of_le_of_finrank_le h_le h_finrank

lemma eq_of_le_of_finrank_eq [finite_dimensional K L] (h_le : F ≤ E)
  (h_finrank : finrank K F = finrank K E) : F = E :=
eq_of_le_of_finrank_le h_le h_finrank.ge

lemma eq_of_le_of_finrank_le' [finite_dimensional K L] (h_le : F ≤ E)
  (h_finrank : finrank F L ≤ finrank E L) : F = E :=
begin
  apply eq_of_le_of_finrank_le h_le,
  have h1 := finrank_mul_finrank K F L,
  have h2 := finrank_mul_finrank K E L,
  have h3 : 0 < finrank E L := finrank_pos,
  nlinarith,
end

lemma eq_of_le_of_finrank_eq' [finite_dimensional K L] (h_le : F ≤ E)
  (h_finrank : finrank F L = finrank E L) : F = E :=
eq_of_le_of_finrank_le' h_le h_finrank.le

end finite_dimensional

end intermediate_field

/-- If `L/K` is algebraic, the `K`-subalgebras of `L` are all fields.  -/
def subalgebra_equiv_intermediate_field (alg : algebra.is_algebraic K L) :
  subalgebra K L ≃o intermediate_field K L :=
{ to_fun := λ S, S.to_intermediate_field (λ x hx, S.inv_mem_of_algebraic (alg (⟨x, hx⟩ : S))),
  inv_fun := λ S, S.to_subalgebra,
  left_inv := λ S, to_subalgebra_to_intermediate_field _ _,
  right_inv := λ S, to_intermediate_field_to_subalgebra _ _,
  map_rel_iff' := λ S S', iff.rfl }

@[simp] lemma mem_subalgebra_equiv_intermediate_field (alg : algebra.is_algebraic K L)
  {S : subalgebra K L} {x : L} :
  x ∈ subalgebra_equiv_intermediate_field alg S ↔ x ∈ S :=
iff.rfl

@[simp] lemma mem_subalgebra_equiv_intermediate_field_symm (alg : algebra.is_algebraic K L)
  {S : intermediate_field K L} {x : L} :
  x ∈ (subalgebra_equiv_intermediate_field alg).symm S ↔ x ∈ S :=
iff.rfl


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

! This file was ported from Lean 3 source module data.finset.powerset
! leanprover-community/mathlib commit cc70d9141824ea8982d1562ce009952f2c3ece30
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.Lattice
import Mathbin.Data.Multiset.Powerset

/-!
# The powerset of a finset

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

namespace Finset

open Function Multiset

variable {α : Type _} {s t : Finset α}

/-! ### powerset -/

section Powerset

#print Finset.powerset /-
/-- When `s` is a finset, `s.powerset` is the finset of all subsets of `s` (seen as finsets). -/
def powerset (s : Finset α) : Finset (Finset α) :=
  ⟨s.1.powerset.pmap Finset.mk fun t h => nodup_of_le (mem_powerset.1 h) s.Nodup,
    s.Nodup.powerset.pmap fun a ha b hb => congr_arg Finset.val⟩
#align finset.powerset Finset.powerset
-/

#print Finset.mem_powerset /-
@[simp]
theorem mem_powerset {s t : Finset α} : s ∈ powerset t ↔ s ⊆ t := by
  cases s <;> simp only [powerset, mem_mk, mem_pmap, mem_powerset, exists_prop, exists_eq_right] <;>
    rw [← val_le_iff]
#align finset.mem_powerset Finset.mem_powerset
-/

#print Finset.coe_powerset /-
@[simp, norm_cast]
theorem coe_powerset (s : Finset α) :
    (s.powerset : Set (Finset α)) = coe ⁻¹' (s : Set α).powerset :=
  by
  ext
  simp
#align finset.coe_powerset Finset.coe_powerset
-/

#print Finset.empty_mem_powerset /-
@[simp]
theorem empty_mem_powerset (s : Finset α) : ∅ ∈ powerset s :=
  mem_powerset.2 (empty_subset _)
#align finset.empty_mem_powerset Finset.empty_mem_powerset
-/

#print Finset.mem_powerset_self /-
@[simp]
theorem mem_powerset_self (s : Finset α) : s ∈ powerset s :=
  mem_powerset.2 Subset.rfl
#align finset.mem_powerset_self Finset.mem_powerset_self
-/

#print Finset.powerset_nonempty /-
theorem powerset_nonempty (s : Finset α) : s.powerset.Nonempty :=
  ⟨∅, empty_mem_powerset _⟩
#align finset.powerset_nonempty Finset.powerset_nonempty
-/

#print Finset.powerset_mono /-
@[simp]
theorem powerset_mono {s t : Finset α} : powerset s ⊆ powerset t ↔ s ⊆ t :=
  ⟨fun h => mem_powerset.1 <| h <| mem_powerset_self _, fun st u h =>
    mem_powerset.2 <| Subset.trans (mem_powerset.1 h) st⟩
#align finset.powerset_mono Finset.powerset_mono
-/

#print Finset.powerset_injective /-
theorem powerset_injective : Injective (powerset : Finset α → Finset (Finset α)) :=
  injective_of_le_imp_le _ fun s t => powerset_mono.1
#align finset.powerset_injective Finset.powerset_injective
-/

#print Finset.powerset_inj /-
@[simp]
theorem powerset_inj : powerset s = powerset t ↔ s = t :=
  powerset_injective.eq_iff
#align finset.powerset_inj Finset.powerset_inj
-/

#print Finset.powerset_empty /-
@[simp]
theorem powerset_empty : (∅ : Finset α).powerset = {∅} :=
  rfl
#align finset.powerset_empty Finset.powerset_empty
-/

#print Finset.powerset_eq_singleton_empty /-
@[simp]
theorem powerset_eq_singleton_empty : s.powerset = {∅} ↔ s = ∅ := by
  rw [← powerset_empty, powerset_inj]
#align finset.powerset_eq_singleton_empty Finset.powerset_eq_singleton_empty
-/

#print Finset.card_powerset /-
/-- **Number of Subsets of a Set** -/
@[simp]
theorem card_powerset (s : Finset α) : card (powerset s) = 2 ^ card s :=
  (card_pmap _ _ _).trans (card_powerset s.1)
#align finset.card_powerset Finset.card_powerset
-/

#print Finset.not_mem_of_mem_powerset_of_not_mem /-
theorem not_mem_of_mem_powerset_of_not_mem {s t : Finset α} {a : α} (ht : t ∈ s.powerset)
    (h : a ∉ s) : a ∉ t := by
  apply mt _ h
  apply mem_powerset.1 ht
#align finset.not_mem_of_mem_powerset_of_not_mem Finset.not_mem_of_mem_powerset_of_not_mem
-/

#print Finset.powerset_insert /-
theorem powerset_insert [DecidableEq α] (s : Finset α) (a : α) :
    powerset (insert a s) = s.powerset ∪ s.powerset.image (insert a) :=
  by
  ext t
  simp only [exists_prop, mem_powerset, mem_image, mem_union, subset_insert_iff]
  by_cases h : a ∈ t
  · constructor
    · exact fun H => Or.inr ⟨_, H, insert_erase h⟩
    · intro H
      cases H
      · exact subset.trans (erase_subset a t) H
      · rcases H with ⟨u, hu⟩
        rw [← hu.2]
        exact subset.trans (erase_insert_subset a u) hu.1
  · have : ¬∃ u : Finset α, u ⊆ s ∧ insert a u = t := by simp [Ne.symm (ne_insert_of_not_mem _ _ h)]
    simp [Finset.erase_eq_of_not_mem h, this]
#align finset.powerset_insert Finset.powerset_insert
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊆ » s) -/
#print Finset.decidableExistsOfDecidableSubsets /-
/-- For predicate `p` decidable on subsets, it is decidable whether `p` holds for any subset. -/
instance decidableExistsOfDecidableSubsets {s : Finset α} {p : ∀ (t) (_ : t ⊆ s), Prop}
    [∀ (t) (h : t ⊆ s), Decidable (p t h)] : Decidable (∃ (t : _)(h : t ⊆ s), p t h) :=
  decidable_of_iff (∃ (t : _)(hs : t ∈ s.powerset), p t (mem_powerset.1 hs))
    ⟨fun ⟨t, _, hp⟩ => ⟨t, _, hp⟩, fun ⟨t, hs, hp⟩ => ⟨t, mem_powerset.2 hs, hp⟩⟩
#align finset.decidable_exists_of_decidable_subsets Finset.decidableExistsOfDecidableSubsets
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊆ » s) -/
#print Finset.decidableForallOfDecidableSubsets /-
/-- For predicate `p` decidable on subsets, it is decidable whether `p` holds for every subset. -/
instance decidableForallOfDecidableSubsets {s : Finset α} {p : ∀ (t) (_ : t ⊆ s), Prop}
    [∀ (t) (h : t ⊆ s), Decidable (p t h)] : Decidable (∀ (t) (h : t ⊆ s), p t h) :=
  decidable_of_iff (∀ (t) (h : t ∈ s.powerset), p t (mem_powerset.1 h))
    ⟨fun h t hs => h t (mem_powerset.2 hs), fun h _ _ => h _ _⟩
#align finset.decidable_forall_of_decidable_subsets Finset.decidableForallOfDecidableSubsets
-/

#print Finset.decidableExistsOfDecidableSubsets' /-
/-- A version of `finset.decidable_exists_of_decidable_subsets` with a non-dependent `p`.
Typeclass inference cannot find `hu` here, so this is not an instance. -/
def decidableExistsOfDecidableSubsets' {s : Finset α} {p : Finset α → Prop}
    (hu : ∀ (t) (h : t ⊆ s), Decidable (p t)) : Decidable (∃ (t : _)(h : t ⊆ s), p t) :=
  @Finset.decidableExistsOfDecidableSubsets _ _ _ hu
#align finset.decidable_exists_of_decidable_subsets' Finset.decidableExistsOfDecidableSubsets'
-/

#print Finset.decidableForallOfDecidableSubsets' /-
/-- A version of `finset.decidable_forall_of_decidable_subsets` with a non-dependent `p`.
Typeclass inference cannot find `hu` here, so this is not an instance. -/
def decidableForallOfDecidableSubsets' {s : Finset α} {p : Finset α → Prop}
    (hu : ∀ (t) (h : t ⊆ s), Decidable (p t)) : Decidable (∀ (t) (h : t ⊆ s), p t) :=
  @Finset.decidableForallOfDecidableSubsets _ _ _ hu
#align finset.decidable_forall_of_decidable_subsets' Finset.decidableForallOfDecidableSubsets'
-/

end Powerset

section Ssubsets

variable [DecidableEq α]

#print Finset.ssubsets /-
/-- For `s` a finset, `s.ssubsets` is the finset comprising strict subsets of `s`. -/
def ssubsets (s : Finset α) : Finset (Finset α) :=
  erase (powerset s) s
#align finset.ssubsets Finset.ssubsets
-/

#print Finset.mem_ssubsets /-
@[simp]
theorem mem_ssubsets {s t : Finset α} : t ∈ s.ssubsets ↔ t ⊂ s := by
  rw [ssubsets, mem_erase, mem_powerset, ssubset_iff_subset_ne, and_comm]
#align finset.mem_ssubsets Finset.mem_ssubsets
-/

#print Finset.empty_mem_ssubsets /-
theorem empty_mem_ssubsets {s : Finset α} (h : s.Nonempty) : ∅ ∈ s.ssubsets :=
  by
  rw [mem_ssubsets, ssubset_iff_subset_ne]
  exact ⟨empty_subset s, h.ne_empty.symm⟩
#align finset.empty_mem_ssubsets Finset.empty_mem_ssubsets
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊂ » s) -/
#print Finset.decidableExistsOfDecidableSsubsets /-
/-- For predicate `p` decidable on ssubsets, it is decidable whether `p` holds for any ssubset. -/
instance decidableExistsOfDecidableSsubsets {s : Finset α} {p : ∀ (t) (_ : t ⊂ s), Prop}
    [∀ (t) (h : t ⊂ s), Decidable (p t h)] : Decidable (∃ t h, p t h) :=
  decidable_of_iff (∃ (t : _)(hs : t ∈ s.ssubsets), p t (mem_ssubsets.1 hs))
    ⟨fun ⟨t, _, hp⟩ => ⟨t, _, hp⟩, fun ⟨t, hs, hp⟩ => ⟨t, mem_ssubsets.2 hs, hp⟩⟩
#align finset.decidable_exists_of_decidable_ssubsets Finset.decidableExistsOfDecidableSsubsets
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊂ » s) -/
#print Finset.decidableForallOfDecidableSsubsets /-
/-- For predicate `p` decidable on ssubsets, it is decidable whether `p` holds for every ssubset. -/
instance decidableForallOfDecidableSsubsets {s : Finset α} {p : ∀ (t) (_ : t ⊂ s), Prop}
    [∀ (t) (h : t ⊂ s), Decidable (p t h)] : Decidable (∀ t h, p t h) :=
  decidable_of_iff (∀ (t) (h : t ∈ s.ssubsets), p t (mem_ssubsets.1 h))
    ⟨fun h t hs => h t (mem_ssubsets.2 hs), fun h _ _ => h _ _⟩
#align finset.decidable_forall_of_decidable_ssubsets Finset.decidableForallOfDecidableSsubsets
-/

#print Finset.decidableExistsOfDecidableSsubsets' /-
/-- A version of `finset.decidable_exists_of_decidable_ssubsets` with a non-dependent `p`.
Typeclass inference cannot find `hu` here, so this is not an instance. -/
def decidableExistsOfDecidableSsubsets' {s : Finset α} {p : Finset α → Prop}
    (hu : ∀ (t) (h : t ⊂ s), Decidable (p t)) : Decidable (∃ (t : _)(h : t ⊂ s), p t) :=
  @Finset.decidableExistsOfDecidableSsubsets _ _ _ _ hu
#align finset.decidable_exists_of_decidable_ssubsets' Finset.decidableExistsOfDecidableSsubsets'
-/

#print Finset.decidableForallOfDecidableSsubsets' /-
/-- A version of `finset.decidable_forall_of_decidable_ssubsets` with a non-dependent `p`.
Typeclass inference cannot find `hu` here, so this is not an instance. -/
def decidableForallOfDecidableSsubsets' {s : Finset α} {p : Finset α → Prop}
    (hu : ∀ (t) (h : t ⊂ s), Decidable (p t)) : Decidable (∀ (t) (h : t ⊂ s), p t) :=
  @Finset.decidableForallOfDecidableSsubsets _ _ _ _ hu
#align finset.decidable_forall_of_decidable_ssubsets' Finset.decidableForallOfDecidableSsubsets'
-/

end Ssubsets

section PowersetLen

#print Finset.powersetLen /-
/-- Given an integer `n` and a finset `s`, then `powerset_len n s` is the finset of subsets of `s`
of cardinality `n`. -/
def powersetLen (n : ℕ) (s : Finset α) : Finset (Finset α) :=
  ⟨(s.1.powersetLen n).pmap Finset.mk fun t h => nodup_of_le (mem_powersetLen.1 h).1 s.2,
    s.2.powersetLen.pmap fun a ha b hb => congr_arg Finset.val⟩
#align finset.powerset_len Finset.powersetLen
-/

#print Finset.mem_powersetLen /-
/-- **Formula for the Number of Combinations** -/
theorem mem_powersetLen {n} {s t : Finset α} : s ∈ powersetLen n t ↔ s ⊆ t ∧ card s = n := by
  cases s <;> simp [powerset_len, val_le_iff.symm] <;> rfl
#align finset.mem_powerset_len Finset.mem_powersetLen
-/

#print Finset.powersetLen_mono /-
@[simp]
theorem powersetLen_mono {n} {s t : Finset α} (h : s ⊆ t) : powersetLen n s ⊆ powersetLen n t :=
  fun u h' => mem_powersetLen.2 <| And.imp (fun h₂ => Subset.trans h₂ h) id (mem_powersetLen.1 h')
#align finset.powerset_len_mono Finset.powersetLen_mono
-/

#print Finset.card_powersetLen /-
/-- **Formula for the Number of Combinations** -/
@[simp]
theorem card_powersetLen (n : ℕ) (s : Finset α) : card (powersetLen n s) = Nat.choose (card s) n :=
  (card_pmap _ _ _).trans (card_powersetLen n s.1)
#align finset.card_powerset_len Finset.card_powersetLen
-/

#print Finset.powersetLen_zero /-
@[simp]
theorem powersetLen_zero (s : Finset α) : Finset.powersetLen 0 s = {∅} :=
  by
  ext; rw [mem_powerset_len, mem_singleton, card_eq_zero]
  refine'
    ⟨fun h => h.2, fun h => by
      rw [h]
      exact ⟨empty_subset s, rfl⟩⟩
#align finset.powerset_len_zero Finset.powersetLen_zero
-/

#print Finset.powersetLen_empty /-
@[simp]
theorem powersetLen_empty (n : ℕ) {s : Finset α} (h : s.card < n) : powersetLen n s = ∅ :=
  Finset.card_eq_zero.mp (by rw [card_powerset_len, Nat.choose_eq_zero_of_lt h])
#align finset.powerset_len_empty Finset.powersetLen_empty
-/

/- warning: finset.powerset_len_eq_filter -> Finset.powersetLen_eq_filter is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} {s : Finset.{u1} α}, Eq.{succ u1} (Finset.{u1} (Finset.{u1} α)) (Finset.powersetLen.{u1} α n s) (Finset.filter.{u1} (Finset.{u1} α) (fun (x : Finset.{u1} α) => Eq.{1} Nat (Finset.card.{u1} α x) n) (fun (a : Finset.{u1} α) => Nat.decidableEq (Finset.card.{u1} α a) n) (Finset.powerset.{u1} α s))
but is expected to have type
  forall {α : Type.{u1}} {n : Nat} {s : Finset.{u1} α}, Eq.{succ u1} (Finset.{u1} (Finset.{u1} α)) (Finset.powersetLen.{u1} α n s) (Finset.filter.{u1} (Finset.{u1} α) (fun (x : Finset.{u1} α) => Eq.{1} Nat (Finset.card.{u1} α x) n) (fun (a : Finset.{u1} α) => instDecidableEqNat (Finset.card.{u1} α a) n) (Finset.powerset.{u1} α s))
Case conversion may be inaccurate. Consider using '#align finset.powerset_len_eq_filter Finset.powersetLen_eq_filterₓ'. -/
theorem powersetLen_eq_filter {n} {s : Finset α} :
    powersetLen n s = (powerset s).filterₓ fun x => x.card = n :=
  by
  ext
  simp [mem_powerset_len]
#align finset.powerset_len_eq_filter Finset.powersetLen_eq_filter

#print Finset.powersetLen_succ_insert /-
theorem powersetLen_succ_insert [DecidableEq α] {x : α} {s : Finset α} (h : x ∉ s) (n : ℕ) :
    powersetLen n.succ (insert x s) = powersetLen n.succ s ∪ (powersetLen n s).image (insert x) :=
  by
  rw [powerset_len_eq_filter, powerset_insert, filter_union, ← powerset_len_eq_filter]
  congr
  rw [powerset_len_eq_filter, image_filter]
  congr 1
  ext t
  simp only [mem_powerset, mem_filter, Function.comp_apply, and_congr_right_iff]
  intro ht
  have : x ∉ t := fun H => h (ht H)
  simp [card_insert_of_not_mem this, Nat.succ_inj']
#align finset.powerset_len_succ_insert Finset.powersetLen_succ_insert
-/

#print Finset.powersetLen_nonempty /-
theorem powersetLen_nonempty {n : ℕ} {s : Finset α} (h : n ≤ s.card) : (powersetLen n s).Nonempty :=
  by
  classical
    induction' s using Finset.induction_on with x s hx IH generalizing n
    · rw [card_empty, le_zero_iff] at h
      rw [h, powerset_len_zero]
      exact Finset.singleton_nonempty _
    · cases n
      · simp
      · rw [card_insert_of_not_mem hx, Nat.succ_le_succ_iff] at h
        rw [powerset_len_succ_insert hx]
        refine' nonempty.mono _ ((IH h).image (insert x))
        convert subset_union_right _ _
#align finset.powerset_len_nonempty Finset.powersetLen_nonempty
-/

#print Finset.powersetLen_self /-
@[simp]
theorem powersetLen_self (s : Finset α) : powersetLen s.card s = {s} :=
  by
  ext
  rw [mem_powerset_len, mem_singleton]
  constructor
  · exact fun ⟨hs, hc⟩ => eq_of_subset_of_card_le hs hc.ge
  · rintro rfl
    simp
#align finset.powerset_len_self Finset.powersetLen_self
-/

/- warning: finset.pairwise_disjoint_powerset_len -> Finset.pairwise_disjoint_powersetLen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Finset.{u1} α), Pairwise.{0} Nat (fun (i : Nat) (j : Nat) => Disjoint.{u1} (Finset.{u1} (Finset.{u1} α)) (Finset.partialOrder.{u1} (Finset.{u1} α)) (Finset.orderBot.{u1} (Finset.{u1} α)) (Finset.powersetLen.{u1} α i s) (Finset.powersetLen.{u1} α j s))
but is expected to have type
  forall {α : Type.{u1}} (s : Finset.{u1} α), Pairwise.{0} Nat (fun (i : Nat) (j : Nat) => Disjoint.{u1} (Finset.{u1} (Finset.{u1} α)) (Finset.partialOrder.{u1} (Finset.{u1} α)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} (Finset.{u1} α)) (Finset.powersetLen.{u1} α i s) (Finset.powersetLen.{u1} α j s))
Case conversion may be inaccurate. Consider using '#align finset.pairwise_disjoint_powerset_len Finset.pairwise_disjoint_powersetLenₓ'. -/
theorem pairwise_disjoint_powersetLen (s : Finset α) :
    Pairwise fun i j => Disjoint (s.powersetLen i) (s.powersetLen j) := fun i j hij =>
  Finset.disjoint_left.mpr fun x hi hj =>
    hij <| (mem_powersetLen.mp hi).2.symm.trans (mem_powersetLen.mp hj).2
#align finset.pairwise_disjoint_powerset_len Finset.pairwise_disjoint_powersetLen

#print Finset.powerset_card_disjUnionᵢ /-
theorem powerset_card_disjUnionᵢ (s : Finset α) :
    Finset.powerset s =
      (range (s.card + 1)).disjUnionₓ (fun i => powersetLen i s)
        (s.pairwise_disjoint_powersetLen.set_pairwise _) :=
  by
  refine' ext fun a => ⟨fun ha => _, fun ha => _⟩
  · rw [mem_disj_Union]
    exact
      ⟨a.card, mem_range.mpr (Nat.lt_succ_of_le (card_le_of_subset (mem_powerset.mp ha))),
        mem_powerset_len.mpr ⟨mem_powerset.mp ha, rfl⟩⟩
  · rcases mem_disj_Union.mp ha with ⟨i, hi, ha⟩
    exact mem_powerset.mpr (mem_powerset_len.mp ha).1
#align finset.powerset_card_disj_Union Finset.powerset_card_disjUnionᵢ
-/

#print Finset.powerset_card_bunionᵢ /-
theorem powerset_card_bunionᵢ [DecidableEq (Finset α)] (s : Finset α) :
    Finset.powerset s = (range (s.card + 1)).bunionᵢ fun i => powersetLen i s := by
  simpa only [disj_Union_eq_bUnion] using powerset_card_disj_Union s
#align finset.powerset_card_bUnion Finset.powerset_card_bunionᵢ
-/

/- warning: finset.powerset_len_sup -> Finset.powerset_len_sup is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (u : Finset.{u1} α) (n : Nat), (LT.lt.{0} Nat Nat.hasLt n (Finset.card.{u1} α u)) -> (Eq.{succ u1} (Finset.{u1} α) (Finset.sup.{u1, u1} (Finset.{u1} α) (Finset.{u1} α) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b))) (Finset.orderBot.{u1} α) (Finset.powersetLen.{u1} α (Nat.succ n) u) (id.{succ u1} (Finset.{u1} α))) u)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (u : Finset.{u1} α) (n : Nat), (LT.lt.{0} Nat instLTNat n (Finset.card.{u1} α u)) -> (Eq.{succ u1} (Finset.{u1} α) (Finset.sup.{u1, u1} (Finset.{u1} α) (Finset.{u1} α) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) (Finset.powersetLen.{u1} α (Nat.succ n) u) (id.{succ u1} (Finset.{u1} α))) u)
Case conversion may be inaccurate. Consider using '#align finset.powerset_len_sup Finset.powerset_len_supₓ'. -/
theorem powerset_len_sup [DecidableEq α] (u : Finset α) (n : ℕ) (hn : n < u.card) :
    (powersetLen n.succ u).sup id = u := by
  apply le_antisymm
  · simp_rw [Finset.sup_le_iff, mem_powerset_len]
    rintro x ⟨h, -⟩
    exact h
  · rw [sup_eq_bUnion, le_iff_subset, subset_iff]
    cases' (Nat.succ_le_of_lt hn).eq_or_lt with h' h'
    · simp [h']
    · intro x hx
      simp only [mem_bUnion, exists_prop, id.def]
      obtain ⟨t, ht⟩ : ∃ t, t ∈ powerset_len n (u.erase x) := powerset_len_nonempty _
      · refine' ⟨insert x t, _, mem_insert_self _ _⟩
        rw [← insert_erase hx, powerset_len_succ_insert (not_mem_erase _ _)]
        exact mem_union_right _ (mem_image_of_mem _ ht)
      · rw [card_erase_of_mem hx]
        exact Nat.le_pred_of_lt hn
#align finset.powerset_len_sup Finset.powerset_len_sup

#print Finset.powersetLen_card_add /-
@[simp]
theorem powersetLen_card_add (s : Finset α) {i : ℕ} (hi : 0 < i) : s.powersetLen (s.card + i) = ∅ :=
  Finset.powersetLen_empty _ (lt_add_of_pos_right (Finset.card s) hi)
#align finset.powerset_len_card_add Finset.powersetLen_card_add
-/

#print Finset.map_val_val_powersetLen /-
@[simp]
theorem map_val_val_powersetLen (s : Finset α) (i : ℕ) :
    (s.powersetLen i).val.map Finset.val = s.1.powersetLen i := by
  simp [Finset.powersetLen, map_pmap, pmap_eq_map, map_id']
#align finset.map_val_val_powerset_len Finset.map_val_val_powersetLen
-/

#print Finset.powersetLen_map /-
theorem powersetLen_map {β : Type _} (f : α ↪ β) (n : ℕ) (s : Finset α) :
    powersetLen n (s.map f) = (powersetLen n s).map (mapEmbedding f).toEmbedding :=
  eq_of_veq <|
    Multiset.map_injective (@eq_of_veq _) <| by
      simp_rw [map_val_val_powerset_len, map_val, Multiset.map_map, Function.comp,
        RelEmbedding.coeFn_toEmbedding, map_embedding_apply, map_val, ← Multiset.map_map _ val,
        map_val_val_powerset_len, Multiset.powersetLen_map]
#align finset.powerset_len_map Finset.powersetLen_map
-/

end PowersetLen

end Finset


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Floris van Doorn
-/

import order.bounded
import set_theory.ordinal.principal
import tactic.linarith

/-!
# Cardinals and ordinals

Relationships between cardinals and ordinals, properties of cardinals that are proved
using ordinals.

## Main definitions

* The function `cardinal.aleph'` gives the cardinals listed by their ordinal
  index, and is the inverse of `cardinal.aleph_idx`.
  `aleph' n = n`, `aleph' ω = ℵ₀`, `aleph' (ω + 1) = succ ℵ₀`, etc.
  It is an order isomorphism between ordinals and cardinals.
* The function `cardinal.aleph` gives the infinite cardinals listed by their
  ordinal index. `aleph 0 = ℵ₀`, `aleph 1 = succ ℵ₀` is the first
  uncountable cardinal, and so on.

## Main Statements

* `cardinal.mul_eq_max` and `cardinal.add_eq_max` state that the product (resp. sum) of two infinite
  cardinals is just their maximum. Several variations around this fact are also given.
* `cardinal.mk_list_eq_mk` : when `α` is infinite, `α` and `list α` have the same cardinality.
* simp lemmas for inequalities between `bit0 a` and `bit1 b` are registered, making `simp`
  able to prove inequalities about numeral cardinals.

## Tags

cardinal arithmetic (for infinite cardinals)
-/

noncomputable theory

open function cardinal set equiv order
open_locale classical cardinal ordinal

universes u v w

namespace cardinal
section using_ordinals
open ordinal

theorem ord_is_limit {c} (co : ℵ₀ ≤ c) : (ord c).is_limit :=
begin
  refine ⟨λ h, aleph_0_ne_zero _, λ a, lt_imp_lt_of_le_imp_le (λ h, _)⟩,
  { rw [←ordinal.le_zero, ord_le] at h,
    simpa only [card_zero, nonpos_iff_eq_zero] using co.trans h },
  { rw ord_le at h ⊢,
    rwa [←@add_one_of_aleph_0_le (card a), ←card_succ],
    rw [←ord_le, ←le_succ_of_is_limit, ord_le],
    { exact co.trans h },
    { rw ord_aleph_0, exact omega_is_limit } }
end

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ω = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  In this definition, we register additionally that this function is an initial segment,
  i.e., it is order preserving and its range is an initial segment of the ordinals.
  For the basic function version, see `aleph_idx`.
  For an upgraded version stating that the range is everything, see `aleph_idx.rel_iso`. -/
def aleph_idx.initial_seg : @initial_seg cardinal ordinal (<) (<) :=
@rel_embedding.collapse cardinal ordinal (<) (<) _ cardinal.ord.order_embedding.lt_embedding

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ω = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  For an upgraded version stating that the range is everything, see `aleph_idx.rel_iso`. -/
def aleph_idx : cardinal → ordinal := aleph_idx.initial_seg

@[simp] theorem aleph_idx.initial_seg_coe :
  (aleph_idx.initial_seg : cardinal → ordinal) = aleph_idx := rfl

@[simp] theorem aleph_idx_lt {a b} : aleph_idx a < aleph_idx b ↔ a < b :=
aleph_idx.initial_seg.to_rel_embedding.map_rel_iff

@[simp] theorem aleph_idx_le {a b} : aleph_idx a ≤ aleph_idx b ↔ a ≤ b :=
by rw [← not_lt, ← not_lt, aleph_idx_lt]

theorem aleph_idx.init {a b} : b < aleph_idx a → ∃ c, aleph_idx c = b :=
aleph_idx.initial_seg.init _ _

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ℵ₀ = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  In this version, we register additionally that this function is an order isomorphism
  between cardinals and ordinals.
  For the basic function version, see `aleph_idx`. -/
def aleph_idx.rel_iso : @rel_iso cardinal.{u} ordinal.{u} (<) (<) :=
@rel_iso.of_surjective cardinal.{u} ordinal.{u} (<) (<) aleph_idx.initial_seg.{u} $
(initial_seg.eq_or_principal aleph_idx.initial_seg.{u}).resolve_right $
λ ⟨o, e⟩, begin
  have : ∀ c, aleph_idx c < o := λ c, (e _).2 ⟨_, rfl⟩,
  refine ordinal.induction_on o _ this, introsI α r _ h,
  let s := ⨆ a, inv_fun aleph_idx (ordinal.typein r a),
  apply (lt_succ s).not_le,
  have I : injective aleph_idx := aleph_idx.initial_seg.to_embedding.injective,
  simpa only [typein_enum, left_inverse_inv_fun I (succ s)] using le_csupr
    (cardinal.bdd_above_range.{u u} (λ a : α, inv_fun aleph_idx (ordinal.typein r a)))
    (ordinal.enum r _ (h (succ s)))
end

@[simp] theorem aleph_idx.rel_iso_coe :
  (aleph_idx.rel_iso : cardinal → ordinal) = aleph_idx := rfl

@[simp] theorem type_cardinal : @type cardinal (<) _ = ordinal.univ.{u (u+1)} :=
by rw ordinal.univ_id; exact quotient.sound ⟨aleph_idx.rel_iso⟩

@[simp] theorem mk_cardinal : #cardinal = univ.{u (u+1)} :=
by simpa only [card_type, card_univ] using congr_arg card type_cardinal

/-- The `aleph'` function gives the cardinals listed by their ordinal
  index, and is the inverse of `aleph_idx`.
  `aleph' n = n`, `aleph' ω = ω`, `aleph' (ω + 1) = succ ℵ₀`, etc.
  In this version, we register additionally that this function is an order isomorphism
  between ordinals and cardinals.
  For the basic function version, see `aleph'`. -/
def aleph'.rel_iso := cardinal.aleph_idx.rel_iso.symm

/-- The `aleph'` function gives the cardinals listed by their ordinal
  index, and is the inverse of `aleph_idx`.
  `aleph' n = n`, `aleph' ω = ω`, `aleph' (ω + 1) = succ ℵ₀`, etc. -/
def aleph' : ordinal → cardinal := aleph'.rel_iso

@[simp] theorem aleph'.rel_iso_coe :
  (aleph'.rel_iso : ordinal → cardinal) = aleph' := rfl

@[simp] theorem aleph'_lt {o₁ o₂ : ordinal} : aleph' o₁ < aleph' o₂ ↔ o₁ < o₂ :=
aleph'.rel_iso.map_rel_iff

@[simp] theorem aleph'_le {o₁ o₂ : ordinal} : aleph' o₁ ≤ aleph' o₂ ↔ o₁ ≤ o₂ :=
le_iff_le_iff_lt_iff_lt.2 aleph'_lt

@[simp] theorem aleph'_aleph_idx (c : cardinal) : aleph' c.aleph_idx = c :=
cardinal.aleph_idx.rel_iso.to_equiv.symm_apply_apply c

@[simp] theorem aleph_idx_aleph' (o : ordinal) : (aleph' o).aleph_idx = o :=
cardinal.aleph_idx.rel_iso.to_equiv.apply_symm_apply o

@[simp] theorem aleph'_zero : aleph' 0 = 0 :=
by { rw [← nonpos_iff_eq_zero, ← aleph'_aleph_idx 0, aleph'_le], apply ordinal.zero_le }

@[simp] theorem aleph'_succ {o : ordinal} : aleph' (succ o) = succ (aleph' o) :=
begin
  apply (succ_le_of_lt $ aleph'_lt.2 $ lt_succ o).antisymm' (cardinal.aleph_idx_le.1 $ _),
  rw [aleph_idx_aleph', succ_le_iff, ← aleph'_lt, aleph'_aleph_idx],
  apply lt_succ
end

@[simp] theorem aleph'_nat : ∀ n : ℕ, aleph' n = n
| 0     := aleph'_zero
| (n+1) := show aleph' (succ n) = n.succ,
           by rw [aleph'_succ, aleph'_nat, nat_succ]

theorem aleph'_le_of_limit {o : ordinal} (l : o.is_limit) {c} :
  aleph' o ≤ c ↔ ∀ o' < o, aleph' o' ≤ c :=
⟨λ h o' h', (aleph'_le.2 $ h'.le).trans h,
 λ h, begin
  rw [←aleph'_aleph_idx c, aleph'_le, limit_le l],
  intros x h',
  rw [←aleph'_le, aleph'_aleph_idx],
  exact h _ h'
end⟩

@[simp] theorem aleph'_omega : aleph' ω = ℵ₀ :=
eq_of_forall_ge_iff $ λ c, begin
  simp only [aleph'_le_of_limit omega_is_limit, lt_omega, exists_imp_distrib, aleph_0_le],
  exact forall_swap.trans (forall_congr $ λ n, by simp only [forall_eq, aleph'_nat]),
end

/-- `aleph'` and `aleph_idx` form an equivalence between `ordinal` and `cardinal` -/
@[simp] def aleph'_equiv : ordinal ≃ cardinal :=
⟨aleph', aleph_idx, aleph_idx_aleph', aleph'_aleph_idx⟩

/-- The `aleph` function gives the infinite cardinals listed by their
  ordinal index. `aleph 0 = ℵ₀`, `aleph 1 = succ ℵ₀` is the first
  uncountable cardinal, and so on. -/
def aleph (o : ordinal) : cardinal := aleph' (ω + o)

@[simp] theorem aleph_lt {o₁ o₂ : ordinal} : aleph o₁ < aleph o₂ ↔ o₁ < o₂ :=
aleph'_lt.trans (add_lt_add_iff_left _)

@[simp] theorem aleph_le {o₁ o₂ : ordinal} : aleph o₁ ≤ aleph o₂ ↔ o₁ ≤ o₂ :=
le_iff_le_iff_lt_iff_lt.2 aleph_lt

@[simp] theorem max_aleph_eq (o₁ o₂ : ordinal) : max (aleph o₁) (aleph o₂) = aleph (max o₁ o₂) :=
begin
  cases le_total (aleph o₁) (aleph o₂) with h h,
  { rw [max_eq_right h, max_eq_right (aleph_le.1 h)] },
  { rw [max_eq_left h, max_eq_left (aleph_le.1 h)] }
end

@[simp] theorem aleph_succ {o : ordinal} : aleph (succ o) = succ (aleph o) :=
by { rw [aleph, add_succ, aleph'_succ], refl }

@[simp] theorem aleph_zero : aleph 0 = ℵ₀ :=
by simp only [aleph, add_zero, aleph'_omega]

theorem aleph_0_le_aleph' {o : ordinal} : ℵ₀ ≤ aleph' o ↔ ω ≤ o :=
by rw [← aleph'_omega, aleph'_le]

theorem aleph_0_le_aleph (o : ordinal) : ℵ₀ ≤ aleph o :=
by { rw [aleph, aleph_0_le_aleph'], apply ordinal.le_add_right }

theorem aleph'_pos {o : ordinal} (ho : 0 < o) : 0 < aleph' o :=
by rwa [←aleph'_zero, aleph'_lt]

theorem aleph_pos (o : ordinal) : 0 < aleph o :=
aleph_0_pos.trans_le (aleph_0_le_aleph o)

instance nonempty_out_aleph (o : ordinal) : nonempty (aleph o).ord.out.α :=
begin
  rw [out_nonempty_iff_ne_zero, ←ord_zero],
  exact λ h, (ord_injective h).not_gt (aleph_pos o)
end

theorem ord_aleph_is_limit (o : ordinal) : is_limit (aleph o).ord :=
ord_is_limit $ aleph_0_le_aleph _

instance (o : ordinal) : no_max_order (aleph o).ord.out.α :=
out_no_max_of_succ_lt (ord_aleph_is_limit o).2

theorem exists_aleph {c : cardinal} : ℵ₀ ≤ c ↔ ∃ o, c = aleph o :=
⟨λ h, ⟨aleph_idx c - ω,
  by { rw [aleph, ordinal.add_sub_cancel_of_le, aleph'_aleph_idx],
       rwa [← aleph_0_le_aleph', aleph'_aleph_idx] }⟩,
 λ ⟨o, e⟩, e.symm ▸ aleph_0_le_aleph _⟩

theorem aleph'_is_normal : is_normal (ord ∘ aleph') :=
⟨λ o, ord_lt_ord.2 $ aleph'_lt.2 $ lt_succ o,
 λ o l a, by simp only [ord_le, aleph'_le_of_limit l]⟩

theorem aleph_is_normal : is_normal (ord ∘ aleph) :=
aleph'_is_normal.trans $ add_is_normal ω

theorem succ_aleph_0 : succ ℵ₀ = aleph 1 :=
by rw [←aleph_zero, ←aleph_succ, ordinal.succ_zero]

lemma aleph_0_lt_aleph_one : ℵ₀ < aleph 1 :=
by { rw ←succ_aleph_0, apply lt_succ }

lemma countable_iff_lt_aleph_one {α : Type*} (s : set α) : s.countable ↔ #s < aleph 1 :=
by rw [←succ_aleph_0, lt_succ_iff, mk_set_le_aleph_0]

/-- Ordinals that are cardinals are unbounded. -/
theorem ord_card_unbounded : unbounded (<) {b : ordinal | b.card.ord = b} :=
unbounded_lt_iff.2 $ λ a, ⟨_, ⟨(by { dsimp, rw card_ord }), (lt_ord_succ_card a).le⟩⟩

theorem eq_aleph'_of_eq_card_ord {o : ordinal} (ho : o.card.ord = o) : ∃ a, (aleph' a).ord = o :=
⟨cardinal.aleph_idx.rel_iso o.card, by simpa using ho⟩

/-- `ord ∘ aleph'` enumerates the ordinals that are cardinals. -/
theorem ord_aleph'_eq_enum_card : ord ∘ aleph' = enum_ord {b : ordinal | b.card.ord = b} :=
begin
  rw [←eq_enum_ord _ ord_card_unbounded, range_eq_iff],
  exact ⟨aleph'_is_normal.strict_mono, ⟨(λ a, (by { dsimp, rw card_ord })),
    λ b hb, eq_aleph'_of_eq_card_ord hb⟩⟩
end

/-- Infinite ordinals that are cardinals are unbounded. -/
theorem ord_card_unbounded' : unbounded (<) {b : ordinal | b.card.ord = b ∧ ω ≤ b} :=
(unbounded_lt_inter_le ω).2 ord_card_unbounded

theorem eq_aleph_of_eq_card_ord {o : ordinal} (ho : o.card.ord = o) (ho' : ω ≤ o) :
  ∃ a, (aleph a).ord = o :=
begin
  cases eq_aleph'_of_eq_card_ord ho with a ha,
  use a - ω,
  unfold aleph,
  rwa ordinal.add_sub_cancel_of_le,
  rwa [←aleph_0_le_aleph', ←ord_le_ord, ha, ord_aleph_0]
end

/-- `ord ∘ aleph` enumerates the infinite ordinals that are cardinals. -/
theorem ord_aleph_eq_enum_card :
  ord ∘ aleph = enum_ord {b : ordinal | b.card.ord = b ∧ ω ≤ b} :=
begin
  rw ←eq_enum_ord _ ord_card_unbounded',
  use aleph_is_normal.strict_mono,
  rw range_eq_iff,
  refine ⟨(λ a, ⟨_, _⟩), λ b hb, eq_aleph_of_eq_card_ord hb.1 hb.2⟩,
  { rw card_ord },
  { rw [←ord_aleph_0, ord_le_ord],
    exact aleph_0_le_aleph _ }
end

/-! ### Properties of `mul` -/

/-- If `α` is an infinite type, then `α × α` and `α` have the same cardinality. -/
theorem mul_eq_self {c : cardinal} (h : ℵ₀ ≤ c) : c * c = c :=
begin
  refine le_antisymm _
    (by simpa only [mul_one] using
      mul_le_mul_left' (one_le_aleph_0.trans h) c),
  -- the only nontrivial part is `c * c ≤ c`. We prove it inductively.
  refine acc.rec_on (cardinal.lt_wf.apply c) (λ c _,
    quotient.induction_on c $ λ α IH ol, _) h,
  -- consider the minimal well-order `r` on `α` (a type with cardinality `c`).
  rcases ord_eq α with ⟨r, wo, e⟩, resetI,
  letI := linear_order_of_STO' r,
  haveI : is_well_order α (<) := wo,
  -- Define an order `s` on `α × α` by writing `(a, b) < (c, d)` if `max a b < max c d`, or
  -- the max are equal and `a < c`, or the max are equal and `a = c` and `b < d`.
  let g : α × α → α := λ p, max p.1 p.2,
  let f : α × α ↪ ordinal × (α × α) :=
    ⟨λ p:α×α, (typein (<) (g p), p), λ p q, congr_arg prod.snd⟩,
  let s := f ⁻¹'o (prod.lex (<) (prod.lex (<) (<))),
  -- this is a well order on `α × α`.
  haveI : is_well_order _ s := (rel_embedding.preimage _ _).is_well_order,
  /- it suffices to show that this well order is smaller than `r`
     if it were larger, then `r` would be a strict prefix of `s`. It would be contained in
    `β × β` for some `β` of cardinality `< c`. By the inductive assumption, this set has the
    same cardinality as `β` (or it is finite if `β` is finite), so it is `< c`, which is a
    contradiction. -/
  suffices : type s ≤ type r, {exact card_le_card this},
  refine le_of_forall_lt (λ o h, _),
  rcases typein_surj s h with ⟨p, rfl⟩,
  rw [← e, lt_ord],
  refine lt_of_le_of_lt
    (_ : _ ≤ card (succ (typein (<) (g p))) * card (succ (typein (<) (g p)))) _,
  { have : {q | s q p} ⊆ insert (g p) {x | x < g p} ×ˢ insert (g p) {x | x < g p},
    { intros q h,
      simp only [s, embedding.coe_fn_mk, order.preimage, typein_lt_typein, prod.lex_def, typein_inj]
        at h,
      exact max_le_iff.1 (le_iff_lt_or_eq.2 $ h.imp_right and.left) },
    suffices H : (insert (g p) {x | r x (g p)} : set α) ≃ ({x | r x (g p)} ⊕ punit),
    { exact ⟨(set.embedding_of_subset _ _ this).trans
        ((equiv.set.prod _ _).trans (H.prod_congr H)).to_embedding⟩ },
    refine (equiv.set.insert _).trans
      ((equiv.refl _).sum_congr punit_equiv_punit),
    apply @irrefl _ r },
  cases lt_or_le (card (succ (typein (<) (g p)))) ℵ₀ with qo qo,
  { exact (mul_lt_aleph_0 qo qo).trans_le ol },
  { suffices, {exact (IH _ this qo).trans_lt this},
    rw ← lt_ord, apply (ord_is_limit ol).2,
    rw [mk_def, e], apply typein_lt_type }
end

end using_ordinals

/-- If `α` and `β` are infinite types, then the cardinality of `α × β` is the maximum
of the cardinalities of `α` and `β`. -/
theorem mul_eq_max {a b : cardinal} (ha : ℵ₀ ≤ a) (hb : ℵ₀ ≤ b) : a * b = max a b :=
le_antisymm
  (mul_eq_self (ha.trans (le_max_left a b)) ▸
    mul_le_mul' (le_max_left _ _) (le_max_right _ _)) $
max_le
  (by simpa only [mul_one] using
    mul_le_mul_left' (one_le_aleph_0.trans hb) a)
  (by simpa only [one_mul] using
    mul_le_mul_right' (one_le_aleph_0.trans ha) b)

@[simp] theorem mul_mk_eq_max {α β : Type*} [infinite α] [infinite β] : #α * #β = max (#α) (#β) :=
mul_eq_max (aleph_0_le_mk α) (aleph_0_le_mk β)

@[simp] theorem aleph_mul_aleph (o₁ o₂ : ordinal) : aleph o₁ * aleph o₂ = aleph (max o₁ o₂) :=
by rw [cardinal.mul_eq_max (aleph_0_le_aleph o₁) (aleph_0_le_aleph o₂), max_aleph_eq]

@[simp] theorem aleph_0_mul_eq {a : cardinal} (ha : ℵ₀ ≤ a) : ℵ₀ * a = a :=
(mul_eq_max le_rfl ha).trans (max_eq_right ha)

@[simp] theorem mul_aleph_0_eq {a : cardinal} (ha : ℵ₀ ≤ a) : a * ℵ₀ = a :=
(mul_eq_max ha le_rfl).trans (max_eq_left ha)

@[simp] theorem aleph_0_mul_mk_eq {α : Type*} [infinite α] : ℵ₀ * #α = #α :=
aleph_0_mul_eq (aleph_0_le_mk α)

@[simp] theorem mk_mul_aleph_0_eq {α : Type*} [infinite α] : #α * ℵ₀ = #α :=
mul_aleph_0_eq (aleph_0_le_mk α)

@[simp] theorem aleph_0_mul_aleph (o : ordinal) : ℵ₀ * aleph o = aleph o :=
aleph_0_mul_eq (aleph_0_le_aleph o)

@[simp] theorem aleph_mul_aleph_0 (o : ordinal) : aleph o * ℵ₀ = aleph o :=
mul_aleph_0_eq (aleph_0_le_aleph o)

theorem mul_lt_of_lt {a b c : cardinal} (hc : ℵ₀ ≤ c) (h1 : a < c) (h2 : b < c) : a * b < c :=
(mul_le_mul' (le_max_left a b) (le_max_right a b)).trans_lt $
(lt_or_le (max a b) ℵ₀).elim
  (λ h, (mul_lt_aleph_0 h h).trans_le hc)
  (λ h, by { rw mul_eq_self h, exact max_lt h1 h2 })

lemma mul_le_max_of_aleph_0_le_left {a b : cardinal} (h : ℵ₀ ≤ a) : a * b ≤ max a b :=
begin
  convert mul_le_mul' (le_max_left a b) (le_max_right a b),
  rw mul_eq_self,
  refine h.trans (le_max_left a b)
end

lemma mul_eq_max_of_aleph_0_le_left {a b : cardinal} (h : ℵ₀ ≤ a) (h' : b ≠ 0) : a * b = max a b :=
begin
  cases le_or_lt ℵ₀ b with hb hb, { exact mul_eq_max h hb },
  refine (mul_le_max_of_aleph_0_le_left h).antisymm _,
  have : b ≤ a, from hb.le.trans h,
  rw [max_eq_left this],
  convert mul_le_mul_left' (one_le_iff_ne_zero.mpr h') _, rw [mul_one],
end

lemma mul_eq_max_of_aleph_0_le_right {a b : cardinal} (h' : a ≠ 0) (h : ℵ₀ ≤ b) : a * b = max a b :=
begin
  rw [mul_comm, max_comm],
  exact mul_eq_max_of_aleph_0_le_left h h'
end

lemma mul_eq_max' {a b : cardinal} (h : ℵ₀ ≤ a * b) : a * b = max a b :=
begin
  rcases aleph_0_le_mul_iff.mp h with ⟨ha, hb, ha' | hb'⟩,
  { exact mul_eq_max_of_aleph_0_le_left ha' hb },
  { exact mul_eq_max_of_aleph_0_le_right ha hb' }
end

theorem mul_le_max (a b : cardinal) : a * b ≤ max (max a b) ℵ₀ :=
begin
  rcases eq_or_ne a 0 with rfl | ha0, { simp },
  rcases eq_or_ne b 0 with rfl | hb0, { simp },
  cases le_or_lt ℵ₀ a with ha ha,
  { rw [mul_eq_max_of_aleph_0_le_left ha hb0],
    exact le_max_left _ _ },
  { cases le_or_lt ℵ₀ b with hb hb,
    { rw [mul_comm, mul_eq_max_of_aleph_0_le_left hb ha0, max_comm],
      exact le_max_left _ _ },
    { exact le_max_of_le_right (mul_lt_aleph_0 ha hb).le } }
end

lemma mul_eq_left {a b : cardinal} (ha : ℵ₀ ≤ a) (hb : b ≤ a) (hb' : b ≠ 0) : a * b = a :=
by { rw [mul_eq_max_of_aleph_0_le_left ha hb', max_eq_left hb] }

lemma mul_eq_right {a b : cardinal} (hb : ℵ₀ ≤ b) (ha : a ≤ b) (ha' : a ≠ 0) : a * b = b :=
by { rw [mul_comm, mul_eq_left hb ha ha'] }

lemma le_mul_left {a b : cardinal} (h : b ≠ 0) : a ≤ b * a :=
by { convert mul_le_mul_right' (one_le_iff_ne_zero.mpr h) _,
  rw [one_mul] }

lemma le_mul_right {a b : cardinal} (h : b ≠ 0) : a ≤ a * b :=
by { rw [mul_comm], exact le_mul_left h }

lemma mul_eq_left_iff {a b : cardinal} : a * b = a ↔ ((max ℵ₀ b ≤ a ∧ b ≠ 0) ∨ b = 1 ∨ a = 0) :=
begin
  rw max_le_iff,
  refine ⟨λ h, _, _⟩,
  { cases le_or_lt ℵ₀ a with ha ha,
    { have : a ≠ 0, { rintro rfl, exact ha.not_lt aleph_0_pos },
      left, use ha,
      { rw ←not_lt, exact λ hb, ne_of_gt (hb.trans_le (le_mul_left this)) h },
      { rintro rfl, apply this, rw mul_zero at h, exact h.symm }},
    right, by_cases h2a : a = 0, { exact or.inr h2a },
    have hb : b ≠ 0, { rintro rfl, apply h2a, rw mul_zero at h, exact h.symm },
    left, rw [←h, mul_lt_aleph_0_iff, lt_aleph_0, lt_aleph_0] at ha,
    rcases ha with rfl|rfl|⟨⟨n, rfl⟩, ⟨m, rfl⟩⟩, contradiction, contradiction,
    rw ←ne at h2a, rw ←one_le_iff_ne_zero at h2a hb, norm_cast at h2a hb h ⊢,
    apply le_antisymm _ hb, rw ←not_lt,
    apply λ h2b, ne_of_gt _ h, conv_lhs { rw ←mul_one n },
    rwa mul_lt_mul_left, apply nat.lt_of_succ_le h2a },
  { rintro (⟨⟨ha, hab⟩, hb⟩|rfl|rfl),
    { rw [mul_eq_max_of_aleph_0_le_left ha hb, max_eq_left hab] },
    all_goals { simp }}
end

/-! ### Properties of `add` -/

/-- If `α` is an infinite type, then `α ⊕ α` and `α` have the same cardinality. -/
theorem add_eq_self {c : cardinal} (h : ℵ₀ ≤ c) : c + c = c :=
le_antisymm
  (by simpa only [nat.cast_bit0, nat.cast_one, mul_eq_self h, two_mul] using
     mul_le_mul_right' ((nat_lt_aleph_0 2).le.trans h) c)
  (self_le_add_left c c)

/-- If `α` is an infinite type, then the cardinality of `α ⊕ β` is the maximum
of the cardinalities of `α` and `β`. -/
theorem add_eq_max {a b : cardinal} (ha : ℵ₀ ≤ a) : a + b = max a b :=
le_antisymm
  (add_eq_self (ha.trans (le_max_left a b)) ▸
    add_le_add (le_max_left _ _) (le_max_right _ _)) $
max_le (self_le_add_right _ _) (self_le_add_left _ _)

theorem add_eq_max' {a b : cardinal} (ha : ℵ₀ ≤ b) : a + b = max a b :=
by rw [add_comm, max_comm, add_eq_max ha]

@[simp] theorem add_mk_eq_max {α β : Type*} [infinite α] : #α + #β = max (#α) (#β) :=
add_eq_max (aleph_0_le_mk α)

@[simp] theorem add_mk_eq_max' {α β : Type*} [infinite β] : #α + #β = max (#α) (#β) :=
add_eq_max' (aleph_0_le_mk β)

theorem add_le_max (a b : cardinal) : a + b ≤ max (max a b) ℵ₀ :=
begin
  cases le_or_lt ℵ₀ a with ha ha,
  { rw [add_eq_max ha],
    exact le_max_left _ _ },
  { cases le_or_lt ℵ₀ b with hb hb,
    { rw [add_comm, add_eq_max hb, max_comm],
      exact le_max_left _ _ },
    { exact le_max_of_le_right (add_lt_aleph_0 ha hb).le } }
end

theorem add_le_of_le {a b c : cardinal} (hc : ℵ₀ ≤ c) (h1 : a ≤ c) (h2 : b ≤ c) : a + b ≤ c :=
(add_le_add h1 h2).trans $ le_of_eq $ add_eq_self hc

theorem add_lt_of_lt {a b c : cardinal} (hc : ℵ₀ ≤ c) (h1 : a < c) (h2 : b < c) : a + b < c :=
(add_le_add (le_max_left a b) (le_max_right a b)).trans_lt $
(lt_or_le (max a b) ℵ₀).elim
  (λ h, (add_lt_aleph_0 h h).trans_le hc)
  (λ h, by rw add_eq_self h; exact max_lt h1 h2)

lemma eq_of_add_eq_of_aleph_0_le {a b c : cardinal} (h : a + b = c) (ha : a < c) (hc : ℵ₀ ≤ c) :
  b = c :=
begin
  apply le_antisymm,
  { rw [← h], apply self_le_add_left },
  rw[← not_lt], intro hb,
  have : a + b < c := add_lt_of_lt hc ha hb,
  simpa [h, lt_irrefl] using this
end

lemma add_eq_left {a b : cardinal} (ha : ℵ₀ ≤ a) (hb : b ≤ a) : a + b = a :=
by { rw [add_eq_max ha, max_eq_left hb] }

lemma add_eq_right {a b : cardinal} (hb : ℵ₀ ≤ b) (ha : a ≤ b) : a + b = b :=
by { rw [add_comm, add_eq_left hb ha] }

lemma add_eq_left_iff {a b : cardinal} : a + b = a ↔ (max ℵ₀ b ≤ a ∨ b = 0) :=
begin
  rw max_le_iff,
  refine ⟨λ h, _, _⟩,
  { cases (le_or_lt ℵ₀ a) with ha ha,
    { left, use ha, rw ←not_lt, apply λ hb, ne_of_gt _ h,
      exact hb.trans_le (self_le_add_left b a) },
    right, rw [←h, add_lt_aleph_0_iff, lt_aleph_0, lt_aleph_0] at ha,
    rcases ha with ⟨⟨n, rfl⟩, ⟨m, rfl⟩⟩, norm_cast at h ⊢,
    rw [←add_right_inj, h, add_zero] },
  { rintro (⟨h1, h2⟩|h3),
    { rw [add_eq_max h1, max_eq_left h2] },
    { rw [h3, add_zero] } }
end

lemma add_eq_right_iff {a b : cardinal} : a + b = b ↔ (max ℵ₀ a ≤ b ∨ a = 0) :=
by { rw [add_comm, add_eq_left_iff] }

lemma add_one_eq {a : cardinal} (ha : ℵ₀ ≤ a) : a + 1 = a :=
add_eq_left ha (one_le_aleph_0.trans ha)

@[simp] lemma mk_add_one_eq {α : Type*} [infinite α] : #α + 1 = #α :=
add_one_eq (aleph_0_le_mk α)

protected lemma eq_of_add_eq_add_left {a b c : cardinal} (h : a + b = a + c) (ha : a < ℵ₀) :
  b = c :=
begin
  cases le_or_lt ℵ₀ b with hb hb,
  { have : a < b := ha.trans_le hb,
    rw [add_eq_right hb this.le, eq_comm] at h,
    rw [eq_of_add_eq_of_aleph_0_le h this hb] },
  { have hc : c < ℵ₀,
    { rw ←not_le, intro hc,
      apply lt_irrefl ℵ₀, apply (hc.trans (self_le_add_left _ a)).trans_lt,
      rw ←h, apply add_lt_aleph_0 ha hb },
    rw lt_aleph_0 at *,
    rcases ha with ⟨n, rfl⟩, rcases hb with ⟨m, rfl⟩, rcases hc with ⟨k, rfl⟩,
    norm_cast at h ⊢, apply add_left_cancel h }
end

protected lemma eq_of_add_eq_add_right {a b c : cardinal} (h : a + b = c + b) (hb : b < ℵ₀) :
  a = c :=
by { rw [add_comm a b, add_comm c b] at h, exact cardinal.eq_of_add_eq_add_left h hb }

@[simp] theorem aleph_add_aleph (o₁ o₂ : ordinal) : aleph o₁ + aleph o₂ = aleph (max o₁ o₂) :=
by rw [cardinal.add_eq_max (aleph_0_le_aleph o₁), max_aleph_eq]

theorem principal_add_ord {c : cardinal} (hc : ℵ₀ ≤ c) : ordinal.principal (+) c.ord :=
λ a b ha hb, by { rw [lt_ord, ordinal.card_add] at *, exact add_lt_of_lt hc ha hb }

theorem principal_add_aleph (o : ordinal) : ordinal.principal (+) (aleph o).ord :=
principal_add_ord $ aleph_0_le_aleph o

/-! ### Properties about power -/

theorem pow_le {κ μ : cardinal.{u}} (H1 : ℵ₀ ≤ κ) (H2 : μ < ℵ₀) : κ ^ μ ≤ κ :=
let ⟨n, H3⟩ := lt_aleph_0.1 H2 in
H3.symm ▸ (quotient.induction_on κ (λ α H1, nat.rec_on n
  (lt_of_lt_of_le (by { rw [nat.cast_zero, power_zero], exact one_lt_aleph_0 }) H1).le
  (λ n ih, trans_rel_left _
    (by { rw [nat.cast_succ, power_add, power_one], exact mul_le_mul_right' ih _ })
    (mul_eq_self H1))) H1)

theorem pow_eq {κ μ : cardinal.{u}} (H1 : ℵ₀ ≤ κ) (H2 : 1 ≤ μ) (H3 : μ < ℵ₀) : κ ^ μ = κ :=
(pow_le H1 H3).antisymm $ self_le_power κ H2

lemma power_self_eq {c : cardinal} (h : ℵ₀ ≤ c) : c ^ c = 2 ^ c :=
begin
  apply ((power_le_power_right $ (cantor c).le).trans _).antisymm,
  { convert power_le_power_right ((nat_lt_aleph_0 2).le.trans h), apply nat.cast_two.symm },
  { rw [←power_mul, mul_eq_self h] }
end

lemma prod_eq_two_power {ι : Type u} [infinite ι] {c : ι → cardinal.{v}} (h₁ : ∀ i, 2 ≤ c i)
  (h₂ : ∀ i, lift.{u} (c i) ≤ lift.{v} (#ι)) :
  prod c = 2 ^ lift.{v} (#ι) :=
begin
  rw [← lift_id' (prod c), lift_prod, ← lift_two_power],
  apply le_antisymm,
  { refine (prod_le_prod _ _ h₂).trans_eq _,
    rw [prod_const, lift_lift, ← lift_power, power_self_eq (aleph_0_le_mk ι), lift_umax.{u v}] },
  { rw [← prod_const', lift_prod],
    refine prod_le_prod _ _ (λ i, _),
    rw [lift_two, ← lift_two.{u v}, lift_le],
    exact h₁ i }
end

lemma power_eq_two_power {c₁ c₂ : cardinal} (h₁ : ℵ₀ ≤ c₁) (h₂ : 2 ≤ c₂) (h₂' : c₂ ≤ c₁) :
  c₂ ^ c₁ = 2 ^ c₁ :=
le_antisymm (power_self_eq h₁ ▸ power_le_power_right h₂') (power_le_power_right h₂)

lemma nat_power_eq {c : cardinal.{u}} (h : ℵ₀ ≤ c) {n : ℕ} (hn : 2 ≤ n) :
  (n : cardinal.{u}) ^ c = 2 ^ c :=
power_eq_two_power h (by assumption_mod_cast) ((nat_lt_aleph_0 n).le.trans h)

lemma power_nat_le {c : cardinal.{u}} {n : ℕ} (h : ℵ₀ ≤ c) : c ^ n ≤ c :=
pow_le h (nat_lt_aleph_0 n)

lemma power_nat_eq {c : cardinal.{u}} {n : ℕ} (h1 : ℵ₀ ≤ c) (h2 : 1 ≤ n) : c ^ n = c :=
pow_eq h1 (by exact_mod_cast h2) (nat_lt_aleph_0 n)

lemma power_nat_le_max {c : cardinal.{u}} {n : ℕ} : c ^ (n : cardinal.{u}) ≤ max c ℵ₀ :=
begin
  cases le_or_lt ℵ₀ c with hc hc,
  { exact le_max_of_le_left (power_nat_le hc) },
  { exact le_max_of_le_right ((power_lt_aleph_0 hc (nat_lt_aleph_0 _)).le) }
end

lemma powerlt_aleph_0 {c : cardinal} (h : ℵ₀ ≤ c) : c ^< ℵ₀ = c :=
begin
  apply le_antisymm,
  { rw powerlt_le, intro c', rw lt_aleph_0, rintro ⟨n, rfl⟩, apply power_nat_le h },
  convert le_powerlt c one_lt_aleph_0, rw power_one
end

lemma powerlt_aleph_0_le (c : cardinal) : c ^< ℵ₀ ≤ max c ℵ₀ :=
begin
  cases le_or_lt ℵ₀ c,
  { rw powerlt_aleph_0 h, apply le_max_left },
  rw powerlt_le,
  exact λ c' hc', (power_lt_aleph_0 h hc').le.trans (le_max_right _ _)
end

/-! ### Computing cardinality of various types -/

theorem mk_list_eq_mk (α : Type u) [infinite α] : #(list α) = #α :=
have H1 : ℵ₀ ≤ #α := aleph_0_le_mk α,
eq.symm $ le_antisymm ⟨⟨λ x, [x], λ x y H, (list.cons.inj H).1⟩⟩ $
calc  #(list α)
    = sum (λ n : ℕ, #α ^ (n : cardinal.{u})) : mk_list_eq_sum_pow α
... ≤ sum (λ n : ℕ, #α) : sum_le_sum _ _ $ λ n, pow_le H1 $ nat_lt_aleph_0 n
... = #α : by simp [H1]

theorem mk_list_eq_aleph_0 (α : Type u) [encodable α] [nonempty α] : #(list α) = ℵ₀ :=
mk_le_aleph_0.antisymm (aleph_0_le_mk _)

theorem mk_list_eq_max_mk_aleph_0 (α : Type u) [nonempty α] : #(list α) = max (#α) ℵ₀ :=
begin
  casesI fintype_or_infinite α,
  { haveI : encodable α := fintype.to_encodable α,
    rw [mk_list_eq_aleph_0, eq_comm, max_eq_right],
    exact mk_le_aleph_0 },
  { rw [mk_list_eq_mk, eq_comm, max_eq_left],
    exact aleph_0_le_mk α }
end

theorem mk_list_le_max (α : Type u) : #(list α) ≤ max ℵ₀ (#α) :=
begin
  casesI fintype_or_infinite α,
  { haveI := fintype.to_encodable α,
    exact mk_le_aleph_0.trans (le_max_left _ _) },
  { rw mk_list_eq_mk,
    apply le_max_right }
end

theorem mk_finset_eq_mk (α : Type u) [infinite α] : #(finset α) = #α :=
eq.symm $ le_antisymm (mk_le_of_injective (λ x y, finset.singleton_inj.1)) $
calc #(finset α) ≤ #(list α) : mk_le_of_surjective list.to_finset_surjective
... = #α : mk_list_eq_mk α

lemma mk_bounded_set_le_of_infinite (α : Type u) [infinite α] (c : cardinal) :
  #{t : set α // #t ≤ c} ≤ #α ^ c :=
begin
  refine le_trans _ (by rw [←add_one_eq (aleph_0_le_mk α)]),
  induction c using cardinal.induction_on with β,
  fapply mk_le_of_surjective,
  { intro f, use sum.inl ⁻¹' range f,
    refine le_trans (mk_preimage_of_injective _ _ (λ x y, sum.inl.inj)) _,
    apply mk_range_le },
  rintro ⟨s, ⟨g⟩⟩,
  use λ y, if h : ∃(x : s), g x = y then sum.inl (classical.some h).val else sum.inr ⟨⟩,
  apply subtype.eq, ext,
  split,
  { rintro ⟨y, h⟩, dsimp only at h, by_cases h' : ∃ (z : s), g z = y,
    { rw [dif_pos h'] at h, cases sum.inl.inj h, exact (classical.some h').2 },
    { rw [dif_neg h'] at h, cases h }},
  { intro h, have : ∃(z : s), g z = g ⟨x, h⟩, exact ⟨⟨x, h⟩, rfl⟩,
    use g ⟨x, h⟩, dsimp only, rw [dif_pos this], congr',
    suffices : classical.some this = ⟨x, h⟩, exact congr_arg subtype.val this,
    apply g.2, exact classical.some_spec this }
end

lemma mk_bounded_set_le (α : Type u) (c : cardinal) :
  #{t : set α // #t ≤ c} ≤ max (#α) ℵ₀ ^ c :=
begin
  transitivity #{t : set (ulift.{u} ℕ ⊕ α) // #t ≤ c},
  { refine ⟨embedding.subtype_map _ _⟩, apply embedding.image,
    use sum.inr, apply sum.inr.inj, intros s hs, exact mk_image_le.trans hs },
  apply (mk_bounded_set_le_of_infinite (ulift.{u} ℕ ⊕ α) c).trans,
  rw [max_comm, ←add_eq_max]; refl
end

lemma mk_bounded_subset_le {α : Type u} (s : set α) (c : cardinal.{u}) :
  #{t : set α // t ⊆ s ∧ #t ≤ c} ≤ max (#s) ℵ₀ ^ c :=
begin
  refine le_trans _ (mk_bounded_set_le s c),
  refine ⟨embedding.cod_restrict _ _ _⟩,
  use λ t, coe ⁻¹' t.1,
  { rintros ⟨t, ht1, ht2⟩ ⟨t', h1t', h2t'⟩ h, apply subtype.eq, dsimp only at h ⊢,
    refine (preimage_eq_preimage' _ _).1 h; rw [subtype.range_coe]; assumption },
  rintro ⟨t, h1t, h2t⟩, exact (mk_preimage_of_injective _ _ subtype.val_injective).trans h2t
end

/-! ### Properties of `compl` -/

lemma mk_compl_of_infinite {α : Type*} [infinite α] (s : set α) (h2 : #s < #α) :
  #(sᶜ : set α) = #α :=
by { refine eq_of_add_eq_of_aleph_0_le _ h2 (aleph_0_le_mk α), exact mk_sum_compl s }

lemma mk_compl_finset_of_infinite {α : Type*} [infinite α] (s : finset α) :
  #((↑s)ᶜ : set α) = #α :=
by { apply mk_compl_of_infinite, exact (finset_card_lt_aleph_0 s).trans_le (aleph_0_le_mk α) }

lemma mk_compl_eq_mk_compl_infinite {α : Type*} [infinite α] {s t : set α} (hs : #s < #α)
  (ht : #t < #α) : #(sᶜ : set α) = #(tᶜ : set α) :=
by { rw [mk_compl_of_infinite s hs, mk_compl_of_infinite t ht] }

lemma mk_compl_eq_mk_compl_finite_lift {α : Type u} {β : Type v} [fintype α]
  {s : set α} {t : set β} (h1 : lift.{max v w} (#α) = lift.{max u w} (#β))
  (h2 : lift.{max v w} (#s) = lift.{max u w} (#t)) :
  lift.{max v w} (#(sᶜ : set α)) = lift.{max u w} (#(tᶜ : set β)) :=
begin
  rcases lift_mk_eq.1 h1 with ⟨e⟩, letI : fintype β := fintype.of_equiv α e,
  replace h1 : fintype.card α = fintype.card β := (fintype.of_equiv_card _).symm,
  classical,
  lift s to finset α using finite.of_fintype s,
  lift t to finset β using finite.of_fintype t,
  simp only [finset.coe_sort_coe, mk_finset, lift_nat_cast, nat.cast_inj] at h2,
  simp only [← finset.coe_compl, finset.coe_sort_coe, mk_finset, finset.card_compl,
    lift_nat_cast, nat.cast_inj, h1, h2]
end

lemma mk_compl_eq_mk_compl_finite {α β : Type u} [fintype α] {s : set α} {t : set β}
  (h1 : #α = #β) (h : #s = #t) : #(sᶜ : set α) = #(tᶜ : set β) :=
by { rw ← lift_inj, apply mk_compl_eq_mk_compl_finite_lift; rwa [lift_inj] }

lemma mk_compl_eq_mk_compl_finite_same {α : Type*} [fintype α] {s t : set α}
  (h : #s = #t) : #(sᶜ : set α) = #(tᶜ : set α) :=
mk_compl_eq_mk_compl_finite rfl h

/-! ### Extending an injection to an equiv -/

theorem extend_function {α β : Type*} {s : set α} (f : s ↪ β)
  (h : nonempty ((sᶜ : set α) ≃ ((range f)ᶜ : set β))) :
  ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  intros, have := h, cases this with g,
  let h : α ≃ β := (set.sum_compl (s : set α)).symm.trans
    ((sum_congr (equiv.of_injective f f.2) g).trans
    (set.sum_compl (range f))),
  refine ⟨h, _⟩, rintro ⟨x, hx⟩, simp [set.sum_compl_symm_apply_of_mem, hx]
end

theorem extend_function_finite {α β : Type*} [fintype α] {s : set α} (f : s ↪ β)
  (h : nonempty (α ≃ β)) : ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  apply extend_function f,
  cases id h with g,
  rw [← lift_mk_eq] at h,
  rw [←lift_mk_eq, mk_compl_eq_mk_compl_finite_lift h],
  rw [mk_range_eq_lift], exact f.2
end

theorem extend_function_of_lt {α β : Type*} {s : set α} (f : s ↪ β) (hs : #s < #α)
  (h : nonempty (α ≃ β)) : ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  casesI fintype_or_infinite α,
  { exact extend_function_finite f h },
  { apply extend_function f, cases id h with g, haveI := infinite.of_injective _ g.injective,
    rw [← lift_mk_eq'] at h ⊢,
    rwa [mk_compl_of_infinite s hs, mk_compl_of_infinite],
    rwa [← lift_lt, mk_range_eq_of_injective f.injective, ← h, lift_lt] },
end

section bit
/-!
This section proves inequalities for `bit0` and `bit1`, enabling `simp` to solve inequalities
for numeral cardinals. The complexity of the resulting algorithm is not good, as in some cases
`simp` reduces an inequality to a disjunction of two situations, depending on whether a cardinal
is finite or infinite. Since the evaluation of the branches is not lazy, this is bad. It is good
enough for practical situations, though.

For specific numbers, these inequalities could also be deduced from the corresponding
inequalities of natural numbers using `norm_cast`:
```
example : (37 : cardinal) < 42 :=
by { norm_cast, norm_num }
```
-/

lemma bit0_ne_zero (a : cardinal) : ¬bit0 a = 0 ↔ ¬a = 0 :=
by simp [bit0]

@[simp] lemma bit1_ne_zero (a : cardinal) : ¬bit1 a = 0 :=
by simp [bit1]

@[simp] lemma zero_lt_bit0 (a : cardinal) : 0 < bit0 a ↔ 0 < a :=
by { rw ←not_iff_not, simp [bit0], }

@[simp] lemma zero_lt_bit1 (a : cardinal) : 0 < bit1 a :=
zero_lt_one.trans_le (self_le_add_left _ _)

@[simp] lemma one_le_bit0 (a : cardinal) : 1 ≤ bit0 a ↔ 0 < a :=
⟨λ h, (zero_lt_bit0 a).mp (zero_lt_one.trans_le h),
 λ h, (one_le_iff_pos.mpr h).trans (self_le_add_left a a)⟩

@[simp] lemma one_le_bit1 (a : cardinal) : 1 ≤ bit1 a :=
self_le_add_left _ _

theorem bit0_eq_self {c : cardinal} (h : ℵ₀ ≤ c) : bit0 c = c :=
add_eq_self h

@[simp] theorem bit0_lt_aleph_0 {c : cardinal} : bit0 c < ℵ₀ ↔ c < ℵ₀ :=
by simp [bit0, add_lt_aleph_0_iff]

@[simp] theorem aleph_0_le_bit0 {c : cardinal} : ℵ₀ ≤ bit0 c ↔ ℵ₀ ≤ c :=
by { rw ←not_iff_not, simp }

@[simp] theorem bit1_eq_self_iff {c : cardinal} : bit1 c = c ↔ ℵ₀ ≤ c :=
begin
  by_cases h : ℵ₀ ≤ c,
  { simp only [bit1, bit0_eq_self h, h, eq_self_iff_true, add_one_of_aleph_0_le] },
  { refine iff_of_false (ne_of_gt _) h,
    rcases lt_aleph_0.1 (not_le.1 h) with ⟨n, rfl⟩,
    norm_cast,
    dsimp [bit1, bit0],
    linarith }
end

@[simp] theorem bit1_lt_aleph_0 {c : cardinal} : bit1 c < ℵ₀ ↔ c < ℵ₀ :=
by simp [bit1, bit0, add_lt_aleph_0_iff, one_lt_aleph_0]

@[simp] theorem aleph_0_le_bit1 {c : cardinal} : ℵ₀ ≤ bit1 c ↔ ℵ₀ ≤ c :=
by { rw ←not_iff_not, simp }

@[simp] lemma bit0_le_bit0 {a b : cardinal} : bit0 a ≤ bit0 b ↔ a ≤ b :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { rw [bit0_eq_self ha, bit0_eq_self hb] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (hb.trans_le ha).not_le,
    have A : bit0 b < ℵ₀, by simpa using hb,
    exact lt_irrefl _ ((A.trans_le ha).trans_le h) },
  { rw bit0_eq_self hb,
    exact iff_of_true ((bit0_lt_aleph_0.2 ha).le.trans hb) (ha.le.trans hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit0_le_bit0 }
end

@[simp] lemma bit0_le_bit1 {a b : cardinal} : bit0 a ≤ bit1 b ↔ a ≤ b :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { rw [bit0_eq_self ha, bit1_eq_self_iff.2 hb] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (hb.trans_le ha).not_le,
    have A : bit1 b < ℵ₀, by simpa using hb,
    exact lt_irrefl _ ((A.trans_le ha).trans_le h) },
  { rw bit1_eq_self_iff.2 hb,
    exact iff_of_true ((bit0_lt_aleph_0.2 ha).le.trans hb) (ha.le.trans hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact nat.bit0_le_bit1_iff }
end

@[simp] lemma bit1_le_bit1 {a b : cardinal} : bit1 a ≤ bit1 b ↔ a ≤ b :=
⟨λ h, bit0_le_bit1.1 ((self_le_add_right (bit0 a) 1).trans h), λ h,
  (add_le_add_right (add_le_add_left h a) 1).trans (add_le_add_right (add_le_add_right h b) 1)⟩

@[simp] lemma bit1_le_bit0 {a b : cardinal} : bit1 a ≤ bit0 b ↔ (a < b ∨ (a ≤ b ∧ ℵ₀ ≤ a)) :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { simp only [bit1_eq_self_iff.mpr ha, bit0_eq_self hb, ha, and_true],
    refine ⟨λ h, or.inr h, λ h, _⟩,
    cases h,
    { exact le_of_lt h },
    { exact h } },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (λ h, _),
    { have A : bit0 b < ℵ₀, by simpa using hb,
      exact lt_irrefl _ ((A.trans_le ha).trans_le h) },
    { exact not_le_of_lt (hb.trans_le ha) (h.elim le_of_lt and.left) } },
  { rw bit0_eq_self hb,
    exact iff_of_true ((bit1_lt_aleph_0.2 ha).le.trans hb) (or.inl $ ha.trans_le hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    simp [not_le.mpr ha] }
end

@[simp] lemma bit0_lt_bit0 {a b : cardinal} : bit0 a < bit0 b ↔ a < b :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { rw [bit0_eq_self ha, bit0_eq_self hb] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (hb.le.trans ha).not_lt,
    have A : bit0 b < ℵ₀, by simpa using hb,
    exact lt_irrefl _ ((A.trans_le ha).trans h) },
  { rw bit0_eq_self hb,
    exact iff_of_true ((bit0_lt_aleph_0.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit0_lt_bit0 }
end

@[simp] lemma bit1_lt_bit0 {a b : cardinal} : bit1 a < bit0 b ↔ a < b :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { rw [bit1_eq_self_iff.2 ha, bit0_eq_self hb] },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (hb.le.trans ha).not_lt,
    have A : bit0 b < ℵ₀, by simpa using hb,
    exact lt_irrefl _ ((A.trans_le ha).trans h) },
  { rw bit0_eq_self hb,
    exact iff_of_true ((bit1_lt_aleph_0.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact nat.bit1_lt_bit0_iff }
end

@[simp] lemma bit1_lt_bit1 {a b : cardinal} : bit1 a < bit1 b ↔ a < b :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { rw [bit1_eq_self_iff.2 ha, bit1_eq_self_iff.2 hb] },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (hb.le.trans ha).not_lt,
    have A : bit1 b < ℵ₀, by simpa using hb,
    exact lt_irrefl _ ((A.trans_le ha).trans h) },
  { rw bit1_eq_self_iff.2 hb,
    exact iff_of_true ((bit1_lt_aleph_0.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit1_lt_bit1 }
end

@[simp] lemma bit0_lt_bit1 {a b : cardinal} : bit0 a < bit1 b ↔ (a < b ∨ (a ≤ b ∧ a < ℵ₀)) :=
begin
  cases le_or_lt ℵ₀ a with ha ha; cases le_or_lt ℵ₀ b with hb hb,
  { simp [bit0_eq_self ha, bit1_eq_self_iff.2 hb, not_lt.mpr ha] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (λ h, _),
    { have A : bit1 b < ℵ₀, by simpa using hb,
      exact lt_irrefl _ ((A.trans_le ha).trans h) },
    { exact (hb.trans_le ha).not_le (h.elim le_of_lt and.left) } },
  { rw [bit1_eq_self_iff.2 hb],
    exact iff_of_true ((bit0_lt_aleph_0.2 ha).trans_le hb) (or.inl $ ha.trans_le hb) },
  { rcases lt_aleph_0.1 ha with ⟨m, rfl⟩,
    rcases lt_aleph_0.1 hb with ⟨n, rfl⟩,
    norm_cast,
    simp only [ha, and_true, nat.bit0_lt_bit1_iff, or_iff_right_of_imp le_of_lt] }
end

lemma one_lt_two : (1 : cardinal) < 2 :=
-- This strategy works generally to prove inequalities between numerals in `cardinality`.
by { norm_cast, norm_num }

@[simp] lemma one_lt_bit0 {a : cardinal} : 1 < bit0 a ↔ 0 < a :=
by simp [←bit1_zero]

@[simp] lemma one_lt_bit1 (a : cardinal) : 1 < bit1 a ↔ 0 < a :=
by simp [←bit1_zero]

end bit

end cardinal


import probability_theory.independence
import singular

/- In this file we define the conditional expectation of a random variable
with respect to some sub-σ-algebra. -/

noncomputable theory
open_locale classical big_operators nnreal ennreal

variables {α β : Type*}

namespace measure_theory

local infix ` . `:max := measure.with_density

local notation π ` .[`:25 𝓕:25 `] `:0 f := @measure.with_density _ 𝓕 π f
local notation μ ` ≪[`:25 𝓕:25 `] `:0 ν := @measure.absolutely_continuous _ 𝓕 μ ν

local notation `∫⁻[`:25 𝓕:25 `]` binders `, ` r:(scoped:60 f, f) ` ∂` μ:70 :=
  @lintegral _ 𝓕 μ r
local notation `∫⁻[`:25 𝓕:25 `]` binders ` in ` s `, ` r:(scoped:60 f, f) ` ∂` μ:70 :=
  @lintegral _ 𝓕 (@measure.restrict _ 𝓕 μ s) r

section

variables [measurable_space α] {μ : measure α}

lemma lintegral_add_lt_top {f g : α → ℝ≥0∞} (hf₁ : measurable f) (hg₁ : measurable g)
  (hf₂ : ∫⁻ a, f a ∂μ < ∞) (hg₂ : ∫⁻ a, g a ∂μ < ∞) : ∫⁻ a, f a + g a ∂μ < ∞ :=
begin
  rw lintegral_add hf₁ hg₁,
  exact ennreal.add_lt_top.2 ⟨hf₂, hg₂⟩,
end

#check lintegral_eq_zero_iff

lemma ae_eq_of_abs_sub_ae_eq {f g : α → ℝ}
  (hf : measurable f) (hg : measurable g) :
  f - g =ᵐ[μ] 0 ↔ f =ᵐ[μ] g :=
begin
  sorry
end

lemma ae_eq_of_with_density_eq {f g : α → ℝ≥0∞}
  (hf : measurable f) (hg : measurable g) (h : μ . f = μ . g) :
  f =ᵐ[μ] g :=
begin
  sorry,
end

section

variables {𝓕 𝓖 : measurable_space α} (h : 𝓖 ≤ 𝓕) {π : @measure α 𝓕}

-- Note `f` is measurable w.r.t. to `𝓖` (which is stronger than measurable w.r.t. to `𝓕`).
lemma lintegral_in_trim {f : α → ℝ≥0∞} (hf : measurable f)
  {s : set α} (hs : measurable_set s) :
  ∫⁻ x in s, f x ∂(π.trim h) = ∫⁻[𝓕] x in s, f x ∂π :=
by rw [restrict_trim h _ hs, lintegral_trim h hf]

-- same here
lemma trim_with_density_eq {f : α → ℝ≥0∞} (hf : measurable f) :
  (π.trim h) . f = (π .[𝓕] f).trim h :=
begin
  refine measure.ext (λ s hs, _),
  rw [with_density_apply _ hs, trim_measurable_set_eq _ hs,
      lintegral_in_trim h hf hs, with_density_apply],
  exact h _ hs,
end

end

end

open measure_theory.measure probability_theory

lemma measure.trim_absolutely_continuous {𝓕 𝓖 : measurable_space α} {μ ν : @measure α 𝓕}
  (h : 𝓖 ≤ 𝓕) (hμν : μ ≪[𝓕] ν) : μ.trim h ≪[𝓖] ν.trim h :=
begin
  refine absolutely_continuous.mk (λ s hs₁ hs₂, _),
  rw [measure.trim, to_measure_apply _ _ hs₁, to_outer_measure_apply,
      hμν (nonpos_iff_eq_zero.1 (hs₂ ▸ le_trim h) : ν s = 0)],
end

lemma measure.with_density_trim_absolutely_continuous
  {𝓕 𝓖 : measurable_space α} (μ : @measure α 𝓕) (h : 𝓖 ≤ 𝓕) (f : α → ℝ≥0∞) :
  (μ .[𝓕] f).trim h ≪ μ.trim h :=
measure.trim_absolutely_continuous h $ @with_density_absolutely_continuous _ 𝓕 μ f

/-
Perhaps it would make more sense to define conditional probability using `fact`.

I am worried that when applying lemmas such as `condition_on_add`, Lean will find
two different proofs for the arguments, and so won't be able to apply.
-/

/-- Given a real-valued random `f` variable with finite expectation, its conditional
expectation with respect to some sub-σ-algebra `𝓖`, is a `𝓖`-random variable `g`
such that for all `𝓖`-measurable sets `s`, `∫⁻ x in s, f x ∂π = ∫⁻ x in s, g x ∂π`.

This definition of contional expectation allow us to define the usual notion of
contional probability. In particular, for all events `A ∈ 𝓕`, `𝔼(A | 𝓖)` is the
condition of `𝓖` on the indicator function on `A`; and for all random variables
`h`, the expectation of `f` with respect to `h` is the condition of `f` on `σ(h)`. -/
def condition_on {𝓕 𝓖 : measurable_space α} (h : 𝓖 ≤ 𝓕)
  (π : @measure α 𝓕) [@finite_measure α 𝓕 π] (f : α → ℝ≥0∞)
  (hf₁ : @measurable _ _ 𝓕 _ f) (hf₂ : ∫⁻[𝓕] x, f x ∂π < ∞) : α → ℝ≥0∞ :=
classical.some
  (@signed_measure.exists_with_density_of_absolute_continuous _ _
    (π.trim h) ((π .[𝓕] f).trim h) _
    (@measure_theory.finite_measure_trim _ _ _ _ h $
      @finite_measure_with_density α 𝓕 _ _ hf₂)
    (measure.trim_absolutely_continuous h $
      @with_density_absolutely_continuous α 𝓕 π f))

namespace condition_on

variables {𝓕 𝓖 : measurable_space α} (h : 𝓖 ≤ 𝓕)
  (π : @measure α 𝓕) [@finite_measure α 𝓕 π] (f g : α → ℝ≥0∞)
  (hf₁ : @measurable _ _ 𝓕 _ f) (hf₂ : ∫⁻[𝓕] x, f x ∂π < ∞)
  (hg₁ : @measurable _ _ 𝓕 _ g) (hg₂ : ∫⁻[𝓕] x, g x ∂π < ∞)

lemma condition_on_measurable : measurable (condition_on h π f hf₁ hf₂) :=
(exists_prop.1 (classical.some_spec
  (@signed_measure.exists_with_density_of_absolute_continuous _ _
    (π.trim h) ((π .[𝓕] f).trim h) _
    (@measure_theory.finite_measure_trim _ _ _ _ h $
      @finite_measure_with_density α 𝓕 _ _ hf₂)
    (measure.trim_absolutely_continuous h $
      @with_density_absolutely_continuous α 𝓕 π f)))).1

lemma condition_on_spec :
  π.trim h . (condition_on h π f hf₁ hf₂) = (π .[𝓕] f).trim h :=
(exists_prop.1 (classical.some_spec
  (@signed_measure.exists_with_density_of_absolute_continuous _ _
    (π.trim h) ((π .[𝓕] f).trim h) _
    (@measure_theory.finite_measure_trim _ _ _ _ h $
      @finite_measure_with_density α 𝓕 _ _ hf₂)
    (measure.trim_absolutely_continuous h $
      @with_density_absolutely_continuous α 𝓕 π f)))).2.symm

instance : partial_order (measurable_space α) := measurable_space.partial_order

/-- Alternate formualtion of `condition_on_spec` which is more useful most of the time.
This lemma works better with `erw` than `rw`. -/
lemma condition_on_spec' {s : set α} (hs : @measurable_set α 𝓖 s) :
  ∫⁻ x in s, condition_on h π f hf₁ hf₂ x ∂(π.trim h) = ∫⁻[𝓕] x in s, f x ∂π :=
begin
  rw [← with_density_apply _ hs, condition_on_spec, trim_measurable_set_eq h hs,
      with_density_apply],
  exact h _ hs
end

/-- The condition of a random variable `f` with respect to a sub-σ-algebra `𝓖` is
*essentially unique*.

Note that in this lemma, `g` is measurable with respect to `𝓖` instead of `𝓕`
(which is stronger than measurable w.r.t.`𝓕`). -/
lemma condition_on_essentially_unique (hg₁ : measurable g)
  (hg : ∀ ⦃s : set α⦄ (hs : @measurable_set α 𝓖 s),
    ∫⁻ x in s, condition_on h π f hf₁ hf₂ x ∂(π.trim h) = ∫⁻[𝓕] x in s, g x ∂π) :
  g =ᵐ[π.trim h] condition_on h π f hf₁ hf₂ :=
begin
  refine ae_eq_of_with_density_eq hg₁ (condition_on_measurable h π f hf₁ hf₂) _,
  refine measure.ext (λ s hs, _),
  rw [with_density_apply _ hs, with_density_apply _ hs, hg hs,
      lintegral_in_trim h hg₁ hs]
end

lemma condition_on_add :
  condition_on h π (f + g)
    (@measurable.add _ α _ _ 𝓕 _ _ _ hf₁ hg₁)
    (@lintegral_add_lt_top α 𝓕 _ _ _ hf₁ hg₁ hf₂ hg₂)
  =ᵐ[π.trim h] condition_on h π f hf₁ hf₂ + condition_on h π g hg₁ hg₂ :=
begin
  refine filter.eventually_eq.symm (condition_on_essentially_unique _ _ _ _ _ _ _ _),
  { exact (condition_on_measurable h π f hf₁ hf₂).add
      (condition_on_measurable h π g hg₁ hg₂) },
  { intros s hs,
    erw [condition_on_spec', @lintegral_add α 𝓕 _ _ _ hf₁ hg₁],
    rw [← condition_on_spec' h π f hf₁ hf₂, ← condition_on_spec' h π g hg₁ hg₂,
        ← lintegral_add, lintegral_in_trim],
    refl,
    all_goals { try { exact hs } },
    { exact (condition_on_measurable h π f hf₁ hf₂).add
        (condition_on_measurable h π g hg₁ hg₂) },
    { exact condition_on_measurable h π f hf₁ hf₂ },
    { exact condition_on_measurable h π g hg₁ hg₂ } }
end

example {f : α → ℝ≥0∞} (hf : @measurable _ _ 𝓖 _ f) (h : 𝓖 ≤ 𝓕) :
  @measurable _ _ 𝓕 _ f := λ s hs, h _ (hf hs)

lemma condition_on_smul (r : ℝ≥0∞) (hr : r < ∞) :
  condition_on h π (r • f)
    (@measurable.const_smul _ _ _ _ _ _ 𝓕 _ _ hf₁ r)
    (by { erw @lintegral_const_mul α 𝓕 _ _ _ hf₁, exact ennreal.mul_lt_top hr hf₂ })
  =ᵐ[π.trim h] r • condition_on h π f hf₁ hf₂ :=
begin
  refine filter.eventually_eq.symm (condition_on_essentially_unique _ _ _ _ _ _ _ _),
  { exact (condition_on_measurable h π f hf₁ hf₂).const_smul r },
  { intros s hs,
    erw [condition_on_spec', lintegral_const_mul, lintegral_const_mul],
    rw [← condition_on_spec' h π f hf₁ hf₂ hs, lintegral_in_trim],
    all_goals { try { exact hs } },
    { exact condition_on_measurable h π f hf₁ hf₂ },
    { exact λ s hs, h _ (condition_on_measurable h π f hf₁ hf₂ hs) },
    { exact hf₁ } }
end

/-- **Law of total expectation** -/
lemma lintegral_condition_on_eq :
  ∫⁻ x, condition_on h π f hf₁ hf₂ x ∂(π.trim h) =
  ∫⁻[𝓕] x, f x ∂π :=
begin
  rw [← lintegral_univ_eq, condition_on_spec', lintegral_univ_eq],
  exact measurable_set.univ,
end

#exit

lemma condition_on_indep
  (hf : indep (measurable_space.comap f ennreal.measurable_space) 𝓖 (π.trim h)) :
  condition_on h π f hf₁ hf₂ =ᵐ[π.trim h] f :=
begin
  sorry,
  -- refine ae_eq_of_with_density_eq (condition_on_measurable _ _ _ hf₁ _) hf₁ _,
  -- have := condition_on_measurable h π _ hf₁ hf₂,
  -- symmetry,
  -- apply condition_on_spec,
end

end condition_on

end measure_theory


import ring_theory.coprime
import data.real.basic
import data.nat.modeq

open nat
noncomputable theory

namespace fermat

def is_sum_of_relprime_squares (n:ℕ) :=
∃ a b, n = a^2 + b^2 ∧ gcd a b = 1.

theorem lem_1_4 (N q: ℕ) :
  is_sum_of_relprime_squares N →
  nat.prime q → is_sum_of_relprime_squares q → q ∣ N →
  is_sum_of_relprime_squares (N/q) :=
begin
intros Hrel Hqprime Hqsq Hqdiv,
unfold is_sum_of_relprime_squares at Hrel,
cases Hrel with a Hrel,
cases Hrel with b Hrel,
cases Hrel with H1 H2,
sorry
end

theorem strong_induction {p : ℕ → Prop} (n : ℕ) (h : ∀ (k : ℕ), (∀ (m : ℕ), m < k → p m) → p k) :
p n := sorry.

-- induction on N, then induction on p
--
theorem descent_wolog (p N : ℕ) :
  N > 0 → prime p → p ∣ N → is_sum_of_relprime_squares N → is_sum_of_relprime_squares p:=
begin
  revert N,
  apply strong_induction p,
  intros p Hih1 N,

  apply strong_induction N,
  clear N,
  intros Ntemp Hih2,

  intros HNpos Hprime Hdiv Hsum,
  have HN : ∃ N, 0 < N ∧ is_sum_of_relprime_squares N ∧ p ∣ N ∧ N < p^2/2,
  {
  sorry,
  },
  clear Hdiv Hsum,
  rcases HN with ⟨N, HNpos, Hsum, Hdiv, Hle⟩,

  have Hcases : (N = p ∨ p < N),
  {
    let  H : _ := nat.le_of_dvd HNpos Hdiv,
    -- This shouldn't be so complicated.
    by_contra,
    push_neg at h,
    have h2 : (p = N),
    { exact le_antisymm H h.2, },
    rw h2 at h,
    let H :=  h.1,
    contradiction,
  },
  cases Hcases with Heasy HpltN,
  { -- case N = p; easy!
  rw ←Heasy,
  assumption,
  },
  { -- case p < N
    -- show that there's a smaller prime factor q
    have Hq : ∃ q, prime q ∧ q ∣ N ∧ q < p,
    { -- split into cases using dite
    exact dite (N.factors = [p])
    (begin -- case p is the only prime factor; contradiction
    intros Hps2,
    let h2 := prod_factors HNpos,
    rewrite Hps2 at h2,
    simp at h2,
    exfalso,
    linarith
    end
    )
    (begin -- case that there are factors other than p
    let Hpfactor := (mem_factors HNpos).2 ⟨Hprime, Hdiv⟩,
    have Hp : [p] ⊆ N.factors,
    { sorry, },
    intros HNp,
    have Hothers : ∀ q ∈ (N/p).factors, q < p,
    { sorry, },
    have Hp : ¬((N/p).factors = list.nil),
    { sorry },
    set qs := (N/p).factors,
    destruct qs,
    { sorry }, -- contradiction
    { intros q _ Hqs,
      specialize Hothers q _,
      { rewrite Hqs, simp },
      existsi q,
      let Hy := (nat.mem_factors HNpos).1 _, swap,
      {
        exact q
      },
      swap,
      { -- q ∈ (N/p).factors → q ∈ N.factors
        sorry,
      },
      {
      have Hyy : prime q ∧ q ∣ N,
      { exact Hy },
      cases Hyy with Hqprime Hqdiv,
      split,
      { assumption },
      split,
      { assumption },
      { assumption },
      },
    }
    end
    )
    },
    -- use lemma 1.4 to show that p | (N/q) and N/q is also a sum of squares of rel prime integers
    cases Hq with q Hq,
    specialize Hih1 q Hq.2.2 N HNpos Hq.1 Hq.2.1 Hsum,
    -- by our strong induction on N (Hih2), we'll be done
    apply Hih2 (N/q),
    {
    sorry,
    },
    { -- Prove that N/q is non-negative
    sorry },
    { assumption },
    { sorry },
    { apply lem_1_4 N q Hsum Hq.1 Hih1 Hq.2.1, }
  }
end

theorem descent (p x y : ℕ) :
  p ∣ x^2 + y^2 → gcd x y = 1 → ∃ z w, p = z^2 + w^2 :=
begin
  intros Hp Hrel,
  sorry
end

-- Reciprocity step

theorem reciprocity (p : ℕ) :
  prime p → 1 ≡ p [MOD 4] → ∃ N, N > 0 ∧ p ∣ N ∧ is_sum_of_relprime_squares N :=
begin
  intros Hprime Hpmod,
  rw modeq.modeq_iff_dvd' at Hpmod,
  swap,
  {
  sorry,
  },
  rcases Hpmod with ⟨k, Hpmod⟩,
  have Hp : p = 4 * k + 1, {
  unfold prime at Hprime,
  rcases Hprime with ⟨Hpineq, _⟩,
  sorry,
  },
  have h₁ : ∀ x, x ≠ 0 → (x^(2 * k) - 1) * (x^(2*k) + 1) ≡ 0 [MOD p],
  { sorry },
  sorry
end

theorem fermat (p : ℕ) :
  prime p → 1 ≡ p [MOD 4] → ∃ a b, p = a^2 + b^2 :=
begin
  intros Hprime Hpmod,
  rcases (reciprocity p Hprime Hpmod) with ⟨N, HNpos, Hdiv, Hsum⟩,
  rcases (descent_wolog p N HNpos Hprime Hdiv Hsum) with ⟨a, b, Hsum, _⟩,
  existsi a,
  existsi b,
  assumption
end

end fermat


open Nat.Linear

example (x₁ x₂ x₃ : Nat) : (x₁ + x₂) + (x₂ + x₃) = x₃ + 2*x₂ + x₁ :=
  Expr.eq_of_toNormPoly [x₁, x₂, x₃]
   (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
   (Expr.add (Expr.add (Expr.var 2) (Expr.mulL 2 (Expr.var 1))) (Expr.var 0))
   rfl

example (x₁ x₂ x₃ : Nat) : ((x₁ + x₂) + (x₂ + x₃) = x₃ + x₂) = (x₁ + x₂ = 0) :=
  Expr.of_cancel_eq [x₁, x₂, x₃]
    (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
    (Expr.add (Expr.var 2) (Expr.var 1))
    (Expr.add (Expr.var 0) (Expr.var 1))
    (Expr.num 0)
    rfl

example (x₁ x₂ x₃ : Nat) : ((x₁ + x₂) + (x₂ + x₃) ≤ x₃ + x₂) = (x₁ + x₂ ≤ 0) :=
  Expr.of_cancel_le [x₁, x₂, x₃]
    (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
    (Expr.add (Expr.var 2) (Expr.var 1))
    (Expr.add (Expr.var 0) (Expr.var 1))
    (Expr.num 0)
    rfl

example (x₁ x₂ x₃ : Nat) : ((x₁ + x₂) + (x₂ + x₃) < x₃ + x₂) = (x₁ + x₂ < 0) :=
  Expr.of_cancel_lt [x₁, x₂, x₃]
    (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
    (Expr.add (Expr.var 2) (Expr.var 1))
    (Expr.add (Expr.var 0) (Expr.var 1))
    (Expr.num 0)
    rfl

example (x₁ x₂ : Nat) : x₁ + 2 ≤ 3*x₂ → 4*x₂ ≤ 3 + x₁ → 3 ≤ 2*x₂ → False :=
  Certificate.of_combine_isUnsat [x₁, x₂]
    [ (1, { eq := false, lhs := Expr.add (Expr.var 0) (Expr.num 2), rhs := Expr.mulL 3 (Expr.var 1) }),
      (1, { eq := false, lhs := Expr.mulL 4 (Expr.var 1), rhs := Expr.add (Expr.num 3) (Expr.var 0) }),
      (0, { eq := false, lhs := Expr.num 3, rhs := Expr.mulL 2 (Expr.var 1) }) ]
    rfl

example (x : Nat) (xs : List Nat) : (sizeOf x < 1 + (1 + sizeOf x + sizeOf xs)) = True :=
  ExprCnstr.eq_true_of_isValid [sizeOf x, sizeOf xs]
    { eq := false, lhs := Expr.inc (Expr.var 0), rhs := Expr.add (Expr.num 1) (Expr.add (Expr.add (Expr.num 1) (Expr.var 0)) (Expr.var 1)) }
    rfl

example (x : Nat) (xs : List Nat) : (1 + (1 + sizeOf x + sizeOf xs) < sizeOf x) = False :=
  ExprCnstr.eq_false_of_isUnsat [sizeOf x, sizeOf xs]
    { eq := false, lhs := Expr.inc <| Expr.add (Expr.num 1) (Expr.add (Expr.add (Expr.num 1) (Expr.var 0)) (Expr.var 1)), rhs := Expr.var 0 }
    rfl


import data.nat.basic

def triv : set nat := { 1 }

lemma mem_one (a : nat) : a ∈ triv ↔ a = 1 := iff.rfl

lemma mul (a b : nat) : a ∈ triv → b ∈ triv → nat.mul a b = 1 :=
  assume ha_in_triv : a ∈ triv,
  assume hb_in_triv : b ∈ triv,
  have ha_one : a = 1, from iff.mp (mem_one a) ha_in_triv,
  have hb_one : b = 1, from iff.mp (mem_one b) hb_in_triv,
  show nat.mul a b = 1, from
    calc
      nat.mul a b = nat.mul 1 b : by rw ha_one
              ... = nat.mul 1 1 : by rw hb_one
              ... = 1 : one_mul 1

def triv_mul (a b : triv) : triv := ⟨ nat.mul a b, mul a b a.property b.property ⟩
instance : has_mul triv := ⟨ triv_mul ⟩

def triv_one : triv := ⟨1, rfl⟩
instance : has_one triv := ⟨ triv_one ⟩

def one (a : triv) : a = 1 :=
  have h : ↑a = 1, from
    calc
      ↑a = 1 : iff.mp (mem_one a) a.property,
  subtype.ext h

def triv_mul_one (a : triv) : a * 1 = a :=
  have h : ↑(a * 1) = ↑a, from
    calc
      ↑(a * 1) = ↑(triv_mul a 1) : rfl
            ... = nat.mul ↑a 1 : rfl
            ... = (↑a) * 1 : rfl
            ... = ↑a : by rw nat.mul_one,
  subtype.ext h

def triv_one_mul (a : triv) : 1 * a = a :=
  have h : ↑(1 * a) = ↑a, from
    calc
      ↑(1 * a) = ↑(triv_mul 1 a) : rfl
            ... = nat.mul 1 ↑a : rfl
            ... = 1 * (↑a): rfl
            ... = ↑a : by rw nat.one_mul,
  subtype.ext h

def triv_mul_assoc (a b c : triv) : (a * b) * c = a * (b * c) :=
  have h : ↑((a * b) * c) = ↑(a * (b * c)), from
    calc
      ↑((a * b) * c) = ↑(triv_mul (triv_mul a b) c) : rfl
                  ... = nat.mul (nat.mul ↑a ↑b) ↑c : rfl
                  ... = (↑a * ↑b) * ↑c : rfl
                  ... = ↑a * (↑b * ↑c) : by rw nat.mul_assoc
                  ... = nat.mul ↑a (nat.mul ↑b ↑c) : rfl
                  ... = ↑(a * (b * c)) : rfl,
  subtype.ext h

def triv_inv (a : triv) : triv := a
instance : has_inv triv := ⟨ triv_inv ⟩

def triv_mul_left_inv (a : triv) : a⁻¹ * a = 1 :=
  calc
    a⁻¹ * a = a * a : rfl
        ... = a * 1 : by rw one a
        ... = a : by rw triv_mul_one a
        ... = 1 : by rw one a

instance : group triv :=
{
  mul := triv_mul,
  one := triv_one,
  mul_one := triv_mul_one,
  one_mul := triv_one_mul,
  mul_assoc := triv_mul_assoc,
  inv := triv_inv,
  mul_left_inv := triv_mul_left_inv
}


/- Author: E.W.Ayers © 2019 -/
import ..equate

open robot

constant k : Type
constant V : Type
constant k_f : field k
noncomputable instance : field k := k_f
constant V_ac : add_comm_group V
noncomputable instance : add_comm_group V := V_ac

constant p : k → V → V
infixr ` • `: 100 := p
constant is_linear : (V → V) → Prop
constant adj : (V → V) → (V → V)
variables {μ ν : k} {u v w x y z : V} {A : V → V}
postfix `†`:200 := adj
@[equate] axiom L1 {A : V → V} : is_linear A → A (x + y) = A x + A y
@[equate] axiom L2 {A : V → V}: is_linear A → μ • A x = A (μ • x)
@[equate] axiom SS : μ • ν • x = (μ * ν) • x
@[equate] axiom LL : μ • (x + y) = μ • x + μ • y
constant ip : V → V → k
notation `⟪` x `,` y `⟫`  := ip x y
@[equate] axiom ipL : ⟪x + y,z⟫ = ⟪x,z⟫ + ⟪y,z⟫
@[equate] axiom ipR : ⟪x, y + z⟫ = ⟪x,y⟫ + ⟪x,z⟫
@[equate] axiom ipSL : ⟪μ • x,z⟫ = μ * ⟪x,z⟫
@[equate] axiom ipSR : ⟪x,μ • z⟫ = μ * ⟪x,z⟫
@[equate] axiom ADJ : is_linear A → ⟪A† x, y ⟫ = ⟪x, A y⟫
@[equate] lemma COMP_DEF {α β γ} {f : β → γ} {g : α → β} {x : α} : (f ∘ g) x = f (g x) := rfl

open tactic

example (il : is_linear A) :
    μ • A (x) + ν • A (y) = A (μ • x + ν • y)  :=
by equate

example (il : is_linear A) :
    ⟪A† (x + y) + w, z⟫ = ⟪A† x + A† y + w ,z⟫ :=
by equate
example (il : is_linear A) :
    ⟪A† (x + y) + (w + v), z⟫ = ⟪A† x + v + A† y + w,z⟫ :=
by equate -- [FIXME]
example (il : is_linear A) :
    ⟪ A† ( u + v ) + w , x ⟫ = ⟪ A† u + w + A† v , x ⟫
:= by equate

example (il : is_linear A) :
    ⟪A† x + A† y ,z⟫ = ⟪A† (x + y), z⟫ :=
by equate
example (il : is_linear A) :
    ⟪(x + y),  A z⟫ = ⟪A† x + A† y ,z⟫ :=
by equate

@[equate] lemma adj_linear (il : is_linear A) :
    ⟪A† (x + y), z⟫ = ⟪A† x + A† y ,z⟫ :=
by equate
-- try the same two problems again but this time with the adjoint lemma.
example (il : is_linear A) :
    ⟪A† (x + y) + w, z⟫ = ⟪A† x + A† y + w ,z⟫ :=
by equate
example (il : is_linear A) :
    ⟪ A† ( u + v ) + w , x ⟫ = ⟪ A† u + w + A† v , x ⟫
:= by equate

/- Can it find a better solution to the problem if we add in the proof that A† is linear? -/
@[equate] lemma adj_linear_2 (il : is_linear A) : A†(x + y) = A†x + A†y := sorry

example (il : is_linear A) :
    ⟪ A† ( u + v ) + w , x ⟫ = ⟪ A† u + w + A† v , x ⟫
:= by equate