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

! This file was ported from Lean 3 source module data.fintype.fin
! leanprover-community/mathlib commit 759575657f189ccb424b990164c8b1fa9f55cdfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fin.Interval

/-!
# The structure of `fintype (fin n)`

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

This file contains some basic results about the `fintype` instance for `fin`,
especially properties of `finset.univ : finset (fin n)`.
-/

open Finset

open Fintype

namespace Fin

variable {α β : Type _} {n : ℕ}

#print Fin.map_valEmbedding_univ /-
-- TODO: replace `subtype` with `coe` in the name of this lemma and `fin.map_subtype_embedding_Iio`
theorem map_valEmbedding_univ : (Finset.univ : Finset (Fin n)).map Fin.valEmbedding = Iio n :=
  by
  ext
  simp [order_iso_subtype.symm.surjective.exists, OrderIso.symm]
#align fin.map_subtype_embedding_univ Fin.map_valEmbedding_univ
-/

/- warning: fin.Ioi_zero_eq_map -> Fin.Ioi_zero_eq_map is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, Eq.{1} (Finset.{0} (Fin (Nat.succ n))) (Finset.Ioi.{0} (Fin (Nat.succ n)) (PartialOrder.toPreorder.{0} (Fin (Nat.succ n)) (Fin.partialOrder (Nat.succ n))) (Fin.locallyFiniteOrderTop (Nat.succ n)) (OfNat.ofNat.{0} (Fin (Nat.succ n)) 0 (OfNat.mk.{0} (Fin (Nat.succ n)) 0 (Zero.zero.{0} (Fin (Nat.succ n)) (Fin.hasZeroOfNeZero (Nat.succ n) (NeZero.succ n)))))) (Finset.map.{0, 0} (Fin n) (Fin (Nat.succ n)) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Preorder.toLE.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (PartialOrder.toPreorder.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.partialOrder (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))))) (Fin.succEmbedding n)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))
but is expected to have type
  forall {n : Nat}, Eq.{1} (Finset.{0} (Fin (Nat.succ n))) (Finset.Ioi.{0} (Fin (Nat.succ n)) (PartialOrder.toPreorder.{0} (Fin (Nat.succ n)) (Fin.instPartialOrderFin (Nat.succ n))) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin (Nat.succ n)) (OfNat.ofNat.{0} (Fin (Nat.succ n)) 0 (Fin.instOfNatFin (Nat.succ n) 0 (NeZero.succ n)))) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Fin n) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Fin n) => LE.le.{0} (Fin n) (instLEFin n) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instLEFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Fin.succEmbedding n)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))
Case conversion may be inaccurate. Consider using '#align fin.Ioi_zero_eq_map Fin.Ioi_zero_eq_mapₓ'. -/
@[simp]
theorem Ioi_zero_eq_map : Ioi (0 : Fin n.succ) = univ.map (Fin.succEmbedding _).toEmbedding :=
  by
  ext i
  simp only [mem_Ioi, mem_map, mem_univ, Function.Embedding.coeFn_mk, exists_true_left]
  constructor
  · refine' cases _ _ i
    · rintro ⟨⟨⟩⟩
    · intro j _
      exact ⟨j, rfl⟩
  · rintro ⟨i, _, rfl⟩
    exact succ_pos _
#align fin.Ioi_zero_eq_map Fin.Ioi_zero_eq_map

/- warning: fin.Iio_last_eq_map -> Fin.Iio_last_eq_map is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, Eq.{1} (Finset.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Finset.Iio.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (PartialOrder.toPreorder.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.partialOrder (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Fin.locallyFiniteOrderBot (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.last n)) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.hasLe (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Fin.castSucc n)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))
but is expected to have type
  forall {n : Nat}, Eq.{1} (Finset.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (Finset.Iio.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (PartialOrder.toPreorder.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Fin.instPartialOrderFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Fin.last n)) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Fin n) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Fin n) => LE.le.{0} (Fin n) (instLEFin n) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instLEFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Fin.castSucc n)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))
Case conversion may be inaccurate. Consider using '#align fin.Iio_last_eq_map Fin.Iio_last_eq_mapₓ'. -/
@[simp]
theorem Iio_last_eq_map : Iio (Fin.last n) = Finset.univ.map Fin.castSucc.toEmbedding :=
  by
  apply Finset.map_injective Fin.valEmbedding
  rw [Finset.map_map, Fin.map_valEmbedding_Iio, Fin.val_last]
  exact map_subtype_embedding_univ.symm
#align fin.Iio_last_eq_map Fin.Iio_last_eq_map

/- warning: fin.Ioi_succ -> Fin.Ioi_succ is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (i : Fin n), Eq.{1} (Finset.{0} (Fin (Nat.succ n))) (Finset.Ioi.{0} (Fin (Nat.succ n)) (PartialOrder.toPreorder.{0} (Fin (Nat.succ n)) (Fin.partialOrder (Nat.succ n))) (Fin.locallyFiniteOrderTop (Nat.succ n)) (Fin.succ n i)) (Finset.map.{0, 0} (Fin n) (Fin (Nat.succ n)) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Preorder.toLE.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (PartialOrder.toPreorder.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.partialOrder (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))))) (Fin.succEmbedding n)) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) i))
but is expected to have type
  forall {n : Nat} (i : Fin n), Eq.{1} (Finset.{0} (Fin (Nat.succ n))) (Finset.Ioi.{0} (Fin (Nat.succ n)) (PartialOrder.toPreorder.{0} (Fin (Nat.succ n)) (Fin.instPartialOrderFin (Nat.succ n))) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin (Nat.succ n)) (Fin.succ n i)) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Fin n) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Fin n) => LE.le.{0} (Fin n) (instLEFin n) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instLEFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Fin.succEmbedding n)) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) i))
Case conversion may be inaccurate. Consider using '#align fin.Ioi_succ Fin.Ioi_succₓ'. -/
@[simp]
theorem Ioi_succ (i : Fin n) : Ioi i.succ = (Ioi i).map (Fin.succEmbedding _).toEmbedding :=
  by
  ext i
  simp only [mem_filter, mem_Ioi, mem_map, mem_univ, true_and_iff, Function.Embedding.coeFn_mk,
    exists_true_left]
  constructor
  · refine' cases _ _ i
    · rintro ⟨⟨⟩⟩
    · intro i hi
      refine' ⟨i, succ_lt_succ_iff.mp hi, rfl⟩
  · rintro ⟨i, hi, rfl⟩
    simpa
#align fin.Ioi_succ Fin.Ioi_succ

/- warning: fin.Iio_cast_succ -> Fin.Iio_castSucc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (i : Fin n), Eq.{1} (Finset.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Finset.Iio.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (PartialOrder.toPreorder.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.partialOrder (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Fin.locallyFiniteOrderBot (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (coeFn.{1, 1} (OrderEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.hasLe n) (Fin.hasLe (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (fun (_x : RelEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.hasLe (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))) => (Fin n) -> (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (RelEmbedding.hasCoeToFun.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.hasLe (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))) (Fin.castSucc n) i)) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (LE.le.{0} (Fin n) (Fin.hasLe n)) (LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.hasLe (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (Fin.castSucc n)) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) i))
but is expected to have type
  forall {n : Nat} (i : Fin n), Eq.{1} (Finset.{0} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Fin n) => Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) i)) (Finset.Iio.{0} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Fin n) => Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) i) (PartialOrder.toPreorder.{0} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Fin n) => Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) i) (Fin.instPartialOrderFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (FunLike.coe.{1, 1, 1} (Function.Embedding.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (Fin n) (fun (_x : Fin n) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Fin n) => Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) _x) (EmbeddingLike.toFunLike.{1, 1, 1} (Function.Embedding.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Function.instEmbeddingLikeEmbedding.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Fin n) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Fin n) => LE.le.{0} (Fin n) (instLEFin n) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instLEFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Fin.castSucc n)) i)) (Finset.map.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (RelEmbedding.toEmbedding.{0, 0} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Fin n) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Fin n) => LE.le.{0} (Fin n) (instLEFin n) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => LE.le.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (instLEFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Fin.castSucc n)) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) i))
Case conversion may be inaccurate. Consider using '#align fin.Iio_cast_succ Fin.Iio_castSuccₓ'. -/
@[simp]
theorem Iio_castSucc (i : Fin n) : Iio (castSucc i) = (Iio i).map Fin.castSucc.toEmbedding :=
  by
  apply Finset.map_injective Fin.valEmbedding
  rw [Finset.map_map, Fin.map_valEmbedding_Iio]
  exact (Fin.map_valEmbedding_Iio i).symm
#align fin.Iio_cast_succ Fin.Iio_castSucc

/- warning: fin.card_filter_univ_succ' -> Fin.card_filter_univ_succ' is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (p : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) -> Prop) [_inst_1 : DecidablePred.{1} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) p], Eq.{1} Nat (Finset.card.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Finset.filter.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) p (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) => _inst_1 a) (Finset.univ.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.fintype (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (ite.{1} Nat (p (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne)) (NeZero.succ n)))))) (_inst_1 (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne)) (NeZero.succ n)))))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) Prop p (Fin.succ n)) (fun (a : Fin n) => _inst_1 (Fin.succ n a)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))))
but is expected to have type
  forall {n : Nat} (p : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) -> Prop) [_inst_1 : DecidablePred.{1} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p], Eq.{1} Nat (Finset.card.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Finset.filter.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => _inst_1 a) (Finset.univ.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Fin.fintype (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (ite.{1} Nat (p (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ n)))) (_inst_1 (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ n)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) Prop p (Fin.succ n)) (fun (a : Fin n) => instDecidablePredCompProp.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p (Fin.succ n) (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => _inst_1 a) a) (Finset.univ.{0} (Fin n) (Fin.fintype n)))))
Case conversion may be inaccurate. Consider using '#align fin.card_filter_univ_succ' Fin.card_filter_univ_succ'ₓ'. -/
theorem card_filter_univ_succ' (p : Fin (n + 1) → Prop) [DecidablePred p] :
    (univ.filterₓ p).card = ite (p 0) 1 0 + (univ.filterₓ (p ∘ Fin.succ)).card :=
  by
  rw [Fin.univ_succ, filter_cons, card_disj_union, filter_map, card_map]
  split_ifs <;> simp
#align fin.card_filter_univ_succ' Fin.card_filter_univ_succ'

/- warning: fin.card_filter_univ_succ -> Fin.card_filter_univ_succ is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (p : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) -> Prop) [_inst_1 : DecidablePred.{1} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) p], Eq.{1} Nat (Finset.card.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Finset.filter.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) p (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) => _inst_1 a) (Finset.univ.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Fin.fintype (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))))) (ite.{1} Nat (p (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne)) (NeZero.succ n)))))) (_inst_1 (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (One.one.{0} Nat Nat.hasOne)) (NeZero.succ n)))))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) Prop p (Fin.succ n)) (fun (a : Fin n) => _inst_1 (Fin.succ n a)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) Prop p (Fin.succ n)) (fun (a : Fin n) => _inst_1 (Fin.succ n a)) (Finset.univ.{0} (Fin n) (Fin.fintype n)))))
but is expected to have type
  forall {n : Nat} (p : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) -> Prop) [_inst_1 : DecidablePred.{1} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p], Eq.{1} Nat (Finset.card.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Finset.filter.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => _inst_1 a) (Finset.univ.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Fin.fintype (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))))) (ite.{1} Nat (p (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ n)))) (_inst_1 (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ n)))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) Prop p (Fin.succ n)) (fun (a : Fin n) => instDecidablePredCompProp.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p (Fin.succ n) (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => _inst_1 a) a) (Finset.univ.{0} (Fin n) (Fin.fintype n)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Finset.card.{0} (Fin n) (Finset.filter.{0} (Fin n) (Function.comp.{1, 1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) Prop p (Fin.succ n)) (fun (a : Fin n) => instDecidablePredCompProp.{1, 1} (Fin n) (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) p (Fin.succ n) (fun (a : Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) => _inst_1 a) a) (Finset.univ.{0} (Fin n) (Fin.fintype n)))))
Case conversion may be inaccurate. Consider using '#align fin.card_filter_univ_succ Fin.card_filter_univ_succₓ'. -/
theorem card_filter_univ_succ (p : Fin (n + 1) → Prop) [DecidablePred p] :
    (univ.filterₓ p).card =
      if p 0 then (univ.filterₓ (p ∘ Fin.succ)).card + 1 else (univ.filterₓ (p ∘ Fin.succ)).card :=
  (card_filter_univ_succ' p).trans (by split_ifs <;> simp [add_comm 1])
#align fin.card_filter_univ_succ Fin.card_filter_univ_succ

#print Fin.card_filter_univ_eq_vector_get_eq_count /-
theorem card_filter_univ_eq_vector_get_eq_count [DecidableEq α] (a : α) (v : Vector α n) :
    (univ.filterₓ fun i => a = v.get? i).card = v.toList.count a :=
  by
  induction' v using Vector.inductionOn with n x xs hxs
  · simp
  ·
    simp_rw [card_filter_univ_succ', Vector.get_cons_zero, Vector.toList_cons, Function.comp,
      Vector.get_cons_succ, hxs, List.count_cons', add_comm (ite (a = x) 1 0)]
#align fin.card_filter_univ_eq_vector_nth_eq_count Fin.card_filter_univ_eq_vector_get_eq_count
-/

end Fin


import defs.var
import util.option
import util.set

theorem lookup_uniq {t: Type} [decidable_eq t] {Γ: cx t} {x: var} {v v': t}:
  cx.lookup Γ x v ->
  cx.lookup Γ x v' ->
  v = v' :=
begin
  simp [cx.lookup],
  intros la lb,
  by_cases v = v',
  exact h,
  let a := cx_elem.mk x v,
  let b := cx_elem.mk x v',
  let a_ne_b: cx_elem.mk x v ≠ cx_elem.mk x v' := begin
    intro h1,
    simp at h1,
    exact h h1,
  end,
  let hm := Γ.nodupkeys a b la lb a_ne_b,
  simp [ne_var] at hm,
  exfalso,
  exact hm,
end

theorem lookup_insert {t: Type} (Γ: cx t) (x: var) (v: t):
  cx.lookup (cx.insert Γ x v) x v :=
begin
  simp [cx.insert],
  simp [cx.lookup],
  left,
  exact iff.elim_right mem_singleton_iff rfl,
end

theorem useless_insert_ne {t: Type} {Γ: cx t} {x y: var} {vy v: t}:
  x ≠ y ->
  (cx.lookup (cx.insert Γ y vy) x v ↔ cx.lookup Γ x v) :=
begin
  intro h_ne,
  simp [cx.insert],
  simp [cx.lookup],
  split,
  intro h,
  cases h,
  cases iff.elim_left mem_singleton_iff h,
  exfalso,
  exact h_ne rfl,
  exact mem_pred h,
  intro h,
  right,
  split,
  exact h,
  simp,
  exact h_ne,
end

theorem entries_same {t: Type} {Γ Γ': cx t}:
  Γ.entries = Γ'.entries -> Γ = Γ' :=
begin
  cases Γ,
  cases Γ',
  simp,
  intro h,
  exact h,
end

theorem useless_insert_twice {t: Type}
  (Γ: cx t) (x: var) (v v': t):
  cx.insert (cx.insert Γ x v') x v = cx.insert Γ x v :=
begin
  simp [cx.insert],
  rw pred_distr_union,
  let hm: (cx_elem.mk x v').x = x := by simp,
  let hm': ¬ ¬ (cx_elem.mk x v').x = x := fun f, f hm,
  rw @singleton_not_pred (cx_elem t) (cx_elem.mk x v') (fun e, e.x ≠ x) hm',
  rw union_emp,
  rw double_pred,
end

theorem insert_comm' {t: Type}
  {Γ: cx t} {x y: var} {vx vy: t} {a: cx_elem t}:
  x ≠ y ->
  a ∈ (cx.insert (cx.insert Γ y vy) x vx).entries ->
  a ∈ (cx.insert (cx.insert Γ x vx) y vy).entries :=
begin
  intro h_ne,
  simp [cx.insert],
  intro h,
  cases h,
  right,
  split,
  left,
  exact h,
  rw iff.elim_left mem_singleton_iff h,
  simp,
  exact h_ne,
  cases h,
  cases h_left,
  left,
  exact h_left,
  right,
  split,
  right,
  split,
  exact mem_pred h_left,
  simp,
  exact h_right,
  simp,
  cases h_left,
  exact h_left_right,
end

theorem insert_comm {t: Type}
  (Γ: cx t) (x y: var) (vx vy: t) (h: x ≠ y):
  cx.insert (cx.insert Γ y vy) x vx =
  cx.insert (cx.insert Γ x vx) y vy :=
entries_same (set_ext begin
  intro a,
  split,
  exact insert_comm' h,
  let h' := fun x, h (symm x),
  exact insert_comm' h',
end)


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne
-/

import measure_theory.measure_space

/-!
# Sequence of measurable functions associated to a sequence of a.e.-measurable functions

We define here tools to prove statements about limits (infi, supr...) of sequences of
`ae_measurable` functions.
Given a sequence of a.e.-measurable functions `f : ι → α → β` with hypothesis
`hf : ∀ i, ae_measurable (f i) μ`, and a pointwise property `p : α → (ι → β) → Prop` such that we
have `hp : ∀ᵐ x ∂μ, p x (λ n, f n x)`, we define a sequence of measurable functions `ae_seq hf p`
and a measurable set `ae_seq_set hf p`, such that
* `μ (ae_seq_set hf p)ᶜ = 0`
* `x ∈ ae_seq_set hf p → ∀ i : ι, ae_seq hf hp i x = f i x`
* `x ∈ ae_seq_set hf p → p x (λ n, f n x)`
-/

open measure_theory
open_locale classical

variables {α β γ ι : Type*} [measurable_space α] [measurable_space β]
  {f : ι → α → β} {μ : measure α} {p : α → (ι → β) → Prop}

/-- If we have the additional hypothesis `∀ᵐ x ∂μ, p x (λ n, f n x)`, this is a measurable set
whose complement has measure 0 such that for all `x ∈ ae_seq_set`, `f i x` is equal to
`(hf i).mk (f i) x` for all `i` and we have the pointwise property `p x (λ n, f n x)`. -/
def ae_seq_set (hf : ∀ i, ae_measurable (f i) μ) (p : α → (ι → β) → Prop) : set α :=
(to_measurable μ {x | (∀ i, f i x = (hf i).mk (f i) x) ∧ p x (λ n, f n x)}ᶜ)ᶜ

/-- A sequence of measurable functions that are equal to `f` and verify property `p` on the
measurable set `ae_seq_set hf p`. -/
noncomputable
def ae_seq (hf : ∀ i, ae_measurable (f i) μ) (p : α → (ι → β) → Prop) : ι → α → β :=
λ i x, ite (x ∈ ae_seq_set hf p) ((hf i).mk (f i) x) (⟨f i x⟩ : nonempty β).some

namespace ae_seq

section mem_ae_seq_set

lemma mk_eq_fun_of_mem_ae_seq_set (hf : ∀ i, ae_measurable (f i) μ) {x : α}
  (hx : x ∈ ae_seq_set hf p) (i : ι) :
  (hf i).mk (f i) x = f i x :=
begin
  have h_ss : ae_seq_set hf p ⊆ {x | ∀ i, f i x = (hf i).mk (f i) x},
  { rw [ae_seq_set, ←compl_compl {x | ∀ i, f i x = (hf i).mk (f i) x}, set.compl_subset_compl],
    refine set.subset.trans (set.compl_subset_compl.mpr (λ x h, _)) (subset_to_measurable _ _),
    exact h.1, },
  exact (h_ss hx i).symm,
end

lemma ae_seq_eq_mk_of_mem_ae_seq_set (hf : ∀ i, ae_measurable (f i) μ) {x : α}
  (hx : x ∈ ae_seq_set hf p) (i : ι) :
  ae_seq hf p i x = (hf i).mk (f i) x :=
by simp only [ae_seq, hx, if_true]

lemma ae_seq_eq_fun_of_mem_ae_seq_set (hf : ∀ i, ae_measurable (f i) μ) {x : α}
  (hx : x ∈ ae_seq_set hf p) (i : ι) :
  ae_seq hf p i x = f i x :=
by simp only [ae_seq_eq_mk_of_mem_ae_seq_set hf hx i, mk_eq_fun_of_mem_ae_seq_set hf hx i]

lemma prop_of_mem_ae_seq_set (hf : ∀ i, ae_measurable (f i) μ)
  {x : α} (hx : x ∈ ae_seq_set hf p) :
  p x (λ n, ae_seq hf p n x) :=
begin
  simp only [ae_seq, hx, if_true],
  rw funext (λ n, mk_eq_fun_of_mem_ae_seq_set hf hx n),
  have h_ss : ae_seq_set hf p ⊆ {x | p x (λ n, f n x)},
  { rw [←compl_compl {x | p x (λ n, f n x)}, ae_seq_set, set.compl_subset_compl],
    refine set.subset.trans (set.compl_subset_compl.mpr _) (subset_to_measurable _ _),
    exact λ x hx, hx.2, },
  have hx' := set.mem_of_subset_of_mem h_ss hx,
  exact hx',
end

lemma fun_prop_of_mem_ae_seq_set (hf : ∀ i, ae_measurable (f i) μ)
  {x : α} (hx : x ∈ ae_seq_set hf p) :
  p x (λ n, f n x) :=
begin
  have h_eq : (λ n, f n x) = λ n, ae_seq hf p n x,
    from funext (λ n, (ae_seq_eq_fun_of_mem_ae_seq_set hf hx n).symm),
  rw h_eq,
  exact prop_of_mem_ae_seq_set hf hx,
end

end mem_ae_seq_set

lemma ae_seq_set_measurable_set {hf : ∀ i, ae_measurable (f i) μ} :
  measurable_set (ae_seq_set hf p) :=
(measurable_set_to_measurable _ _).compl

lemma measurable (hf : ∀ i, ae_measurable (f i) μ) (p : α → (ι → β) → Prop)
  (i : ι) :
  measurable (ae_seq hf p i) :=
begin
  refine measurable.ite ae_seq_set_measurable_set (hf i).measurable_mk _,
  by_cases hα : nonempty α,
  { exact @measurable_const _ _ _ _ (⟨f i hα.some⟩ : nonempty β).some },
  { exact measurable_of_not_nonempty hα _ }
end

lemma measure_compl_ae_seq_set_eq_zero [encodable ι] (hf : ∀ i, ae_measurable (f i) μ)
  (hp : ∀ᵐ x ∂μ, p x (λ n, f n x)) :
  μ (ae_seq_set hf p)ᶜ = 0 :=
begin
  rw [ae_seq_set, compl_compl, measure_to_measurable],
  have hf_eq := λ i, (hf i).ae_eq_mk,
  simp_rw [filter.eventually_eq, ←ae_all_iff] at hf_eq,
  exact filter.eventually.and hf_eq hp,
end

lemma ae_seq_eq_mk_ae [encodable ι] (hf : ∀ i, ae_measurable (f i) μ)
  (hp : ∀ᵐ x ∂μ, p x (λ n, f n x)) :
  ∀ᵐ (a : α) ∂μ, ∀ (i : ι), ae_seq hf p i a = (hf i).mk (f i) a :=
begin
  have h_ss : ae_seq_set hf p ⊆ {a : α | ∀ i, ae_seq hf p i a = (hf i).mk (f i) a},
    from λ x hx i, by simp only [ae_seq, hx, if_true],
  exact le_antisymm (le_trans (measure_mono (set.compl_subset_compl.mpr h_ss))
    (le_of_eq (measure_compl_ae_seq_set_eq_zero hf hp))) (zero_le _),
end

lemma ae_seq_eq_fun_ae [encodable ι] (hf : ∀ i, ae_measurable (f i) μ)
  (hp : ∀ᵐ x ∂μ, p x (λ n, f n x)) :
  ∀ᵐ (a : α) ∂μ, ∀ (i : ι), ae_seq hf p i a = f i a :=
begin
  have h_ss : {a : α | ¬∀ (i : ι), ae_seq hf p i a = f i a} ⊆ (ae_seq_set hf p)ᶜ,
    from λ x, mt (λ hx i, (ae_seq_eq_fun_of_mem_ae_seq_set hf hx i)),
  exact measure_mono_null h_ss (measure_compl_ae_seq_set_eq_zero hf hp),
end

lemma ae_seq_n_eq_fun_n_ae [encodable ι] (hf : ∀ i, ae_measurable (f i) μ)
  (hp : ∀ᵐ x ∂μ, p x (λ n, f n x)) (n : ι) :
  ae_seq hf p n =ᵐ[μ] f n:=
ae_all_iff.mp (ae_seq_eq_fun_ae hf hp) n

lemma supr [complete_lattice β] [encodable ι]
  (hf : ∀ i, ae_measurable (f i) μ) (hp : ∀ᵐ x ∂μ, p x (λ n, f n x)) :
  (⨆ n, ae_seq hf p n) =ᵐ[μ] ⨆ n, f n :=
begin
  simp_rw [filter.eventually_eq, ae_iff, supr_apply],
  have h_ss : ae_seq_set hf p ⊆ {a : α | (⨆ (i : ι), ae_seq hf p i a) = ⨆ (i : ι), f i a},
  { intros x hx,
    congr,
    exact funext (λ i, ae_seq_eq_fun_of_mem_ae_seq_set hf hx i), },
  exact measure_mono_null (set.compl_subset_compl.mpr h_ss)
    (measure_compl_ae_seq_set_eq_zero hf hp),
end

end ae_seq


--

set_option tactic.hygienic false in
theorem ex1 {a p q r : Prop} : p → (p → q) → (q → r) → r := by
intro _ h1 h2;
apply h2;
apply h1;
exact a_1 -- Bad practice, using name generated by `intro`.

theorem ex2 {a p q r : Prop} : p → (p → q) → (q → r) → r := by
intro _ h1 h2;
apply h2;
apply h1;
exact a_1 -- error "unknown identifier"

theorem ex3 {a p q r : Prop} : p → (p → q) → (q → r) → r := by
intro _ h1 h2;
apply h2;
apply h1;
assumption

example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by
  cases h₂;
  { apply h₁; exact h }; -- error "unknown identifier"
  exact h

set_option tactic.hygienic false in
example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by
cases h₂;
{ apply h₁; exact h }; -- hygiene is disabled
exact h

-- Hygienic versions
example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by
cases h₂ with
| inl h => apply h₁; exact h
| inr h => exact h

example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by
cases h₂;
{ apply h₁; assumption };
assumption

example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by
match h₂ with
| Or.inl _ => apply h₁; assumption
| Or.inr h => exact h

example {p q : Prop} (h₁ : p → q) (h₂ : p ∨ q) : q := by unhygienic
  cases h₂
  { apply h₁; exact h } -- hygiene is disabled
  exact h


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov, Patrick Massot
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.intervals.basic
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Projection of a line onto a closed interval

Given a linearly ordered type `α`, in this file we define

* `set.proj_Icc (a b : α) (h : a ≤ b)` to be the map `α → [a, b]` sending `(-∞, a]` to `a`, `[b, ∞)`
  to `b`, and each point `x ∈ [a, b]` to itself;
* `set.Icc_extend {a b : α} (h : a ≤ b) (f : Icc a b → β)` to be the extension of `f` to `α` defined
  as `f ∘ proj_Icc a b h`.

We also prove some trivial properties of these maps.
-/

namespace set

/-- Projection of `α` to the closed interval `[a, b]`. -/
def proj_Icc {α : Type u_1} [linear_order α] (a : α) (b : α) (h : a ≤ b) (x : α) : ↥(Icc a b) :=
  { val := max a (min b x), property := sorry }

theorem proj_Icc_of_le_left {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) {x : α}
    (hx : x ≤ a) : proj_Icc a b h x = { val := a, property := iff.mpr left_mem_Icc h } :=
  sorry

@[simp] theorem proj_Icc_left {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    proj_Icc a b h a = { val := a, property := iff.mpr left_mem_Icc h } :=
  proj_Icc_of_le_left h le_rfl

theorem proj_Icc_of_right_le {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) {x : α}
    (hx : b ≤ x) : proj_Icc a b h x = { val := b, property := iff.mpr right_mem_Icc h } :=
  sorry

@[simp] theorem proj_Icc_right {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    proj_Icc a b h b = { val := b, property := iff.mpr right_mem_Icc h } :=
  proj_Icc_of_right_le h le_rfl

theorem proj_Icc_of_mem {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) {x : α}
    (hx : x ∈ Icc a b) : proj_Icc a b h x = { val := x, property := hx } :=
  sorry

@[simp] theorem proj_Icc_coe {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b)
    (x : ↥(Icc a b)) : proj_Icc a b h ↑x = x :=
  subtype.cases_on x fun (x_val : α) (x_property : x_val ∈ Icc a b) => proj_Icc_of_mem h x_property

theorem proj_Icc_surj_on {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    surj_on (proj_Icc a b h) (Icc a b) univ :=
  fun (x : ↥(Icc a b)) (_x : x ∈ univ) =>
    Exists.intro ↑x { left := subtype.property x, right := proj_Icc_coe h x }

theorem proj_Icc_surjective {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    function.surjective (proj_Icc a b h) :=
  fun (x : ↥(Icc a b)) => Exists.intro (↑x) (proj_Icc_coe h x)

@[simp] theorem range_proj_Icc {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    range (proj_Icc a b h) = univ :=
  function.surjective.range_eq (proj_Icc_surjective h)

theorem monotone_proj_Icc {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    monotone (proj_Icc a b h) :=
  fun (x y : α) (hxy : x ≤ y) => max_le_max le_rfl (min_le_min le_rfl hxy)

theorem strict_mono_incr_on_proj_Icc {α : Type u_1} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    strict_mono_incr_on (proj_Icc a b h) (Icc a b) :=
  sorry

/-- Extend a function `[a, b] → β` to a map `α → β`. -/
def Icc_extend {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α} (h : a ≤ b)
    (f : ↥(Icc a b) → β) : α → β :=
  f ∘ proj_Icc a b h

@[simp] theorem Icc_extend_range {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) (f : ↥(Icc a b) → β) : range (Icc_extend h f) = range f :=
  sorry

theorem Icc_extend_of_le_left {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) {x : α} (f : ↥(Icc a b) → β) (hx : x ≤ a) :
    Icc_extend h f x = f { val := a, property := iff.mpr left_mem_Icc h } :=
  congr_arg f (proj_Icc_of_le_left h hx)

@[simp] theorem Icc_extend_left {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) (f : ↥(Icc a b) → β) :
    Icc_extend h f a = f { val := a, property := iff.mpr left_mem_Icc h } :=
  Icc_extend_of_le_left h f le_rfl

theorem Icc_extend_of_right_le {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) {x : α} (f : ↥(Icc a b) → β) (hx : b ≤ x) :
    Icc_extend h f x = f { val := b, property := iff.mpr right_mem_Icc h } :=
  congr_arg f (proj_Icc_of_right_le h hx)

@[simp] theorem Icc_extend_right {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) (f : ↥(Icc a b) → β) :
    Icc_extend h f b = f { val := b, property := iff.mpr right_mem_Icc h } :=
  Icc_extend_of_right_le h f le_rfl

theorem Icc_extend_of_mem {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α} (h : a ≤ b)
    {x : α} (f : ↥(Icc a b) → β) (hx : x ∈ Icc a b) :
    Icc_extend h f x = f { val := x, property := hx } :=
  congr_arg f (proj_Icc_of_mem h hx)

@[simp] theorem Icc_extend_coe {α : Type u_1} {β : Type u_2} [linear_order α] {a : α} {b : α}
    (h : a ≤ b) (f : ↥(Icc a b) → β) (x : ↥(Icc a b)) : Icc_extend h f ↑x = f x :=
  congr_arg f (proj_Icc_coe h x)

end set

theorem monotone.Icc_extend {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] {a : α}
    {b : α} (h : a ≤ b) {f : ↥(set.Icc a b) → β} (hf : monotone f) :
    monotone (set.Icc_extend h f) :=
  monotone.comp hf (set.monotone_proj_Icc h)

theorem strict_mono.strict_mono_incr_on_Icc_extend {α : Type u_1} {β : Type u_2} [linear_order α]
    [preorder β] {a : α} {b : α} (h : a ≤ b) {f : ↥(set.Icc a b) → β} (hf : strict_mono f) :
    strict_mono_incr_on (set.Icc_extend h f) (set.Icc a b) :=
  strict_mono.comp_strict_mono_incr_on hf (set.strict_mono_incr_on_proj_Icc h)

end Mathlib


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.eq_to_hom
import Mathlib.PostPort

universes u₁ v₁

namespace Mathlib

/-#
Disjoint unions of categories, functors, and natural transformations.
-/

namespace category_theory

/--
`sum C D` gives the direct sum of two categories.
-/
protected instance sum (C : Type u₁) [category C] (D : Type u₁) [category D] : category (C ⊕ D) :=
  category.mk

@[simp] theorem sum_comp_inl (C : Type u₁) [category C] (D : Type u₁) [category D] {P : C} {Q : C}
    {R : C} (f : sum.inl P ⟶ sum.inl Q) (g : sum.inl Q ⟶ sum.inl R) : f ≫ g = f ≫ g :=
  rfl

@[simp] theorem sum_comp_inr (C : Type u₁) [category C] (D : Type u₁) [category D] {P : D} {Q : D}
    {R : D} (f : sum.inr P ⟶ sum.inr Q) (g : sum.inr Q ⟶ sum.inr R) : f ≫ g = f ≫ g :=
  rfl

namespace sum

/-- `inl_` is the functor `X ↦ inl X`. -/
-- Unfortunate naming here, suggestions welcome.

def inl_ (C : Type u₁) [category C] (D : Type u₁) [category D] : C ⥤ C ⊕ D :=
  functor.mk (fun (X : C) => sum.inl X) fun (X Y : C) (f : X ⟶ Y) => f

/-- `inr_` is the functor `X ↦ inr X`. -/
def inr_ (C : Type u₁) [category C] (D : Type u₁) [category D] : D ⥤ C ⊕ D :=
  functor.mk (fun (X : D) => sum.inr X) fun (X Y : D) (f : X ⟶ Y) => f

/-- The functor exchanging two direct summand categories. -/
def swap (C : Type u₁) [category C] (D : Type u₁) [category D] : C ⊕ D ⥤ D ⊕ C :=
  functor.mk (fun (X : C ⊕ D) => sorry) fun (X Y : C ⊕ D) (f : X ⟶ Y) => sorry

@[simp] theorem swap_obj_inl (C : Type u₁) [category C] (D : Type u₁) [category D] (X : C) :
    functor.obj (swap C D) (sum.inl X) = sum.inr X :=
  rfl

@[simp] theorem swap_obj_inr (C : Type u₁) [category C] (D : Type u₁) [category D] (X : D) :
    functor.obj (swap C D) (sum.inr X) = sum.inl X :=
  rfl

@[simp] theorem swap_map_inl (C : Type u₁) [category C] (D : Type u₁) [category D] {X : C} {Y : C}
    {f : sum.inl X ⟶ sum.inl Y} : functor.map (swap C D) f = f :=
  rfl

@[simp] theorem swap_map_inr (C : Type u₁) [category C] (D : Type u₁) [category D] {X : D} {Y : D}
    {f : sum.inr X ⟶ sum.inr Y} : functor.map (swap C D) f = f :=
  rfl

namespace swap

/-- `swap` gives an equivalence between `C ⊕ D` and `D ⊕ C`. -/
def equivalence (C : Type u₁) [category C] (D : Type u₁) [category D] : C ⊕ D ≌ D ⊕ C :=
  equivalence.mk (swap C D) (swap D C)
    (nat_iso.of_components (fun (X : C ⊕ D) => eq_to_iso sorry) sorry)
    (nat_iso.of_components (fun (X : D ⊕ C) => eq_to_iso sorry) sorry)

protected instance is_equivalence (C : Type u₁) [category C] (D : Type u₁) [category D] :
    is_equivalence (swap C D) :=
  is_equivalence.of_equivalence (equivalence C D)

/-- The double swap on `C ⊕ D` is naturally isomorphic to the identity functor. -/
def symmetry (C : Type u₁) [category C] (D : Type u₁) [category D] : swap C D ⋙ swap D C ≅ 𝟭 :=
  iso.symm (equivalence.unit_iso (equivalence C D))

end swap

end sum

namespace functor

/-- The sum of two functors. -/
def sum {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁} [category C]
    {D : Type u₁} [category D] (F : A ⥤ B) (G : C ⥤ D) : A ⊕ C ⥤ B ⊕ D :=
  mk (fun (X : A ⊕ C) => sorry) fun (X Y : A ⊕ C) (f : X ⟶ Y) => sorry

@[simp] theorem sum_obj_inl {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] (F : A ⥤ B) (G : C ⥤ D) (a : A) :
    obj (sum F G) (sum.inl a) = sum.inl (obj F a) :=
  rfl

@[simp] theorem sum_obj_inr {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] (F : A ⥤ B) (G : C ⥤ D) (c : C) :
    obj (sum F G) (sum.inr c) = sum.inr (obj G c) :=
  rfl

@[simp] theorem sum_map_inl {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] (F : A ⥤ B) (G : C ⥤ D) {a : A} {a' : A}
    (f : sum.inl a ⟶ sum.inl a') : map (sum F G) f = map F f :=
  rfl

@[simp] theorem sum_map_inr {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] (F : A ⥤ B) (G : C ⥤ D) {c : C} {c' : C}
    (f : sum.inr c ⟶ sum.inr c') : map (sum F G) f = map G f :=
  rfl

end functor

namespace nat_trans

/-- The sum of two natural transformations. -/
def sum {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁} [category C]
    {D : Type u₁} [category D] {F : A ⥤ B} {G : A ⥤ B} {H : C ⥤ D} {I : C ⥤ D} (α : F ⟶ G)
    (β : H ⟶ I) : functor.sum F H ⟶ functor.sum G I :=
  mk fun (X : A ⊕ C) => sorry

@[simp] theorem sum_app_inl {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] {F : A ⥤ B} {G : A ⥤ B} {H : C ⥤ D} {I : C ⥤ D}
    (α : F ⟶ G) (β : H ⟶ I) (a : A) : app (sum α β) (sum.inl a) = app α a :=
  rfl

@[simp] theorem sum_app_inr {A : Type u₁} [category A] {B : Type u₁} [category B] {C : Type u₁}
    [category C] {D : Type u₁} [category D] {F : A ⥤ B} {G : A ⥤ B} {H : C ⥤ D} {I : C ⥤ D}
    (α : F ⟶ G) (β : H ⟶ I) (c : C) : app (sum α β) (sum.inr c) = app β c :=
  rfl

end Mathlib


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

/-!
# Bundled hom instances for module and multiplicative actions

This file defines instances for module, mul_action and related structures on bundled `_hom` types.

These are analogous to the instances in `algebra.module.pi`, but for bundled instead of unbundled
functions.
-/

variables {R S A B : Type*}

namespace add_monoid_hom

section
variables [monoid R] [monoid S] [add_monoid A] [add_comm_monoid B]
variables [distrib_mul_action R B] [distrib_mul_action S B]

instance : distrib_mul_action R (A →+ B) :=
{ smul := λ r f,
  { to_fun := r • f,
    map_zero' := by simp,
    map_add' := λ x y, by simp [smul_add] },
  one_smul := λ f, by simp,
  mul_smul := λ r s f, by simp [mul_smul],
  smul_add := λ r f g, ext $ λ x, by simp [smul_add],
  smul_zero := λ r, ext $ λ x, by simp [smul_zero] }

@[simp] lemma coe_smul (r : R) (f : A →+ B) : ⇑(r • f) = r • f := rfl
lemma smul_apply (r : R) (f : A →+ B) (x : A) : (r • f) x = r • f x := rfl

instance [smul_comm_class R S B] : smul_comm_class R S (A →+ B) :=
⟨λ a b f, ext $ λ x, smul_comm _ _ _⟩

instance [has_scalar R S] [is_scalar_tower R S B] : is_scalar_tower R S (A →+ B) :=
⟨λ a b f, ext $ λ x, smul_assoc _ _ _⟩

instance [distrib_mul_action Rᵐᵒᵖ B] [is_central_scalar R B] : is_central_scalar R (A →+ B) :=
⟨λ a b, ext $ λ x, op_smul_eq_smul _ _⟩

end

instance [semiring R] [add_monoid A] [add_comm_monoid B] [module R B] :
  module R (A →+ B) :=
{ add_smul := λ r s x, ext $ λ y, by simp [add_smul],
  zero_smul := λ x, ext $ λ y, by simp [zero_smul],
  ..add_monoid_hom.distrib_mul_action }

end add_monoid_hom


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import data.fintype.card
import data.finset.prod

/-!
# fintype instance for the product of two fintypes.

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

-/

open function
open_locale nat

universes u v

variables {α β γ : Type*}

open finset function

namespace set
variables {s t : set α}

lemma to_finset_prod (s : set α) (t : set β) [fintype s] [fintype t] [fintype (s ×ˢ t)] :
  (s ×ˢ t).to_finset = s.to_finset ×ˢ t.to_finset :=
by { ext, simp }

lemma to_finset_off_diag {s : set α} [decidable_eq α] [fintype s] [fintype s.off_diag] :
  s.off_diag.to_finset = s.to_finset.off_diag :=
finset.ext $ by simp

end set

instance (α β : Type*) [fintype α] [fintype β] : fintype (α × β) :=
⟨univ ×ˢ univ, λ ⟨a, b⟩, by simp⟩

@[simp] lemma finset.univ_product_univ {α β : Type*} [fintype α] [fintype β] :
  (univ : finset α) ×ˢ (univ : finset β) = univ :=
rfl

@[simp] theorem fintype.card_prod (α β : Type*) [fintype α] [fintype β] :
  fintype.card (α × β) = fintype.card α * fintype.card β :=
card_product _ _

section
open_locale classical

@[simp] lemma infinite_prod :
  infinite (α × β) ↔ infinite α ∧ nonempty β ∨ nonempty α ∧ infinite β :=
begin
  refine ⟨λ H, _, λ H, H.elim (and_imp.2 $ @prod.infinite_of_left α β)
    (and_imp.2 $ @prod.infinite_of_right α β)⟩,
  rw and.comm, contrapose! H, introI H',
  rcases infinite.nonempty (α × β) with ⟨a, b⟩,
  haveI := fintype_of_not_infinite (H.1 ⟨b⟩), haveI := fintype_of_not_infinite (H.2 ⟨a⟩),
  exact H'.false
end

instance pi.infinite_of_left {ι : Sort*} {π : ι → Sort*} [∀ i, nontrivial $ π i]
  [infinite ι] : infinite (Π i : ι, π i) :=
begin
  choose m n hm using λ i, exists_pair_ne (π i),
  refine infinite.of_injective (λ i, m.update i (n i)) (λ x y h, not_not.1 $ λ hne, _),
  simp_rw [update_eq_iff, update_noteq hne] at h,
  exact (hm x h.1.symm).elim,
end

/-- If at least one `π i` is infinite and the rest nonempty, the pi type of all `π` is infinite. -/
lemma pi.infinite_of_exists_right {ι : Type*} {π : ι → Type*} (i : ι)
  [infinite $ π i] [∀ i, nonempty $ π i] :
  infinite (Π i : ι, π i) :=
let ⟨m⟩ := @pi.nonempty ι π _ in infinite.of_injective _ (update_injective m i)

/-- See `pi.infinite_of_exists_right` for the case that only one `π i` is infinite. -/
instance pi.infinite_of_right {ι : Sort*} {π : ι → Sort*} [∀ i, infinite $ π i] [nonempty ι] :
  infinite (Π i : ι, π i) :=
pi.infinite_of_exists_right (classical.arbitrary ι)

/-- Non-dependent version of `pi.infinite_of_left`. -/
instance function.infinite_of_left {ι π : Sort*} [nontrivial π]
  [infinite ι] : infinite (ι → π) :=
pi.infinite_of_left

/-- Non-dependent version of `pi.infinite_of_exists_right` and `pi.infinite_of_right`. -/
instance function.infinite_of_right {ι π : Sort*} [infinite π] [nonempty ι] :
  infinite (ι → π) :=
pi.infinite_of_right

end


/-
Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis, Chris Hughes
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.associated
import Mathlib.algebra.big_operators.basic
import Mathlib.data.nat.enat
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-- `multiplicity a b` returns the largest natural number `n` such that
  `a ^ n ∣ b`, as an `enat` or natural with infinity. If `∀ n, a ^ n ∣ b`,
  then it returns `⊤`-/
def multiplicity {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] (a : α) (b : α) : enat :=
  enat.find fun (n : ℕ) => ¬a ^ (n + 1) ∣ b

namespace multiplicity

/-- `multiplicity.finite a b` indicates that the multiplicity of `a` in `b` is finite. -/
def finite {α : Type u_1} [comm_monoid α] (a : α) (b : α) :=
  ∃ (n : ℕ), ¬a ^ (n + 1) ∣ b

theorem finite_iff_dom {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} : finite a b ↔ roption.dom (multiplicity a b) :=
  iff.rfl

theorem finite_def {α : Type u_1} [comm_monoid α] {a : α} {b : α} : finite a b ↔ ∃ (n : ℕ), ¬a ^ (n + 1) ∣ b :=
  iff.rfl

theorem int.coe_nat_multiplicity (a : ℕ) (b : ℕ) : multiplicity ↑a ↑b = multiplicity a b := sorry

theorem not_finite_iff_forall {α : Type u_1} [comm_monoid α] {a : α} {b : α} : ¬finite a b ↔ ∀ (n : ℕ), a ^ n ∣ b := sorry

theorem not_unit_of_finite {α : Type u_1} [comm_monoid α] {a : α} {b : α} (h : finite a b) : ¬is_unit a := sorry

theorem finite_of_finite_mul_left {α : Type u_1} [comm_monoid α] {a : α} {b : α} {c : α} : finite a (b * c) → finite a c := sorry

theorem finite_of_finite_mul_right {α : Type u_1} [comm_monoid α] {a : α} {b : α} {c : α} : finite a (b * c) → finite a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finite a (b * c) → finite a b)) (mul_comm b c))) finite_of_finite_mul_left

theorem pow_dvd_of_le_multiplicity {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} : ↑k ≤ multiplicity a b → a ^ k ∣ b := sorry

theorem pow_multiplicity_dvd {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} (h : finite a b) : a ^ roption.get (multiplicity a b) h ∣ b :=
  pow_dvd_of_le_multiplicity
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(roption.get (multiplicity a b) h) ≤ multiplicity a b)) (enat.coe_get h)))
      (le_refl (multiplicity a b)))

theorem is_greatest {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {m : ℕ} (hm : multiplicity a b < ↑m) : ¬a ^ m ∣ b := sorry

theorem is_greatest' {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {m : ℕ} (h : finite a b) (hm : roption.get (multiplicity a b) h < m) : ¬a ^ m ∣ b :=
  is_greatest
    (eq.mp (Eq._oldrec (Eq.refl (↑(roption.get (multiplicity a b) h) < ↑m)) (enat.coe_get h))
      (eq.mp (Eq._oldrec (Eq.refl (roption.get (multiplicity a b) h < m)) (Eq.symm (propext enat.coe_lt_coe))) hm))

theorem unique {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} (hk : a ^ k ∣ b) (hsucc : ¬a ^ (k + 1) ∣ b) : ↑k = multiplicity a b := sorry

theorem unique' {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} (hk : a ^ k ∣ b) (hsucc : ¬a ^ (k + 1) ∣ b) : k = roption.get (multiplicity a b) (Exists.intro k hsucc) := sorry

theorem le_multiplicity_of_pow_dvd {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} (hk : a ^ k ∣ b) : ↑k ≤ multiplicity a b :=
  le_of_not_gt fun (hk' : ↑k > multiplicity a b) => is_greatest hk' hk

theorem pow_dvd_iff_le_multiplicity {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} : a ^ k ∣ b ↔ ↑k ≤ multiplicity a b :=
  { mp := le_multiplicity_of_pow_dvd, mpr := pow_dvd_of_le_multiplicity }

theorem multiplicity_lt_iff_neg_dvd {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {k : ℕ} : multiplicity a b < ↑k ↔ ¬a ^ k ∣ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (multiplicity a b < ↑k ↔ ¬a ^ k ∣ b)) (propext pow_dvd_iff_le_multiplicity)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (multiplicity a b < ↑k ↔ ¬↑k ≤ multiplicity a b)) (propext not_le)))
      (iff.refl (multiplicity a b < ↑k)))

theorem eq_some_iff {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {n : ℕ} : multiplicity a b = ↑n ↔ a ^ n ∣ b ∧ ¬a ^ (n + 1) ∣ b := sorry

theorem eq_top_iff {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} : multiplicity a b = ⊤ ↔ ∀ (n : ℕ), a ^ n ∣ b := sorry

theorem one_right {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} (ha : ¬is_unit a) : multiplicity a 1 = 0 := sorry

@[simp] theorem get_one_right {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} (ha : finite a 1) : roption.get (multiplicity a 1) ha = 0 := sorry

@[simp] theorem multiplicity_unit {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} (b : α) (ha : is_unit a) : multiplicity a b = ⊤ :=
  iff.mpr eq_top_iff fun (_x : ℕ) => iff.mp is_unit_iff_forall_dvd (is_unit.pow _x ha) b

@[simp] theorem one_left {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] (b : α) : multiplicity 1 b = ⊤ := sorry

theorem multiplicity_eq_zero_of_not_dvd {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} (ha : ¬a ∣ b) : multiplicity a b = 0 := sorry

theorem eq_top_iff_not_finite {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} : multiplicity a b = ⊤ ↔ ¬finite a b :=
  roption.eq_none_iff'

theorem multiplicity_le_multiplicity_iff {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {c : α} {d : α} : multiplicity a b ≤ multiplicity c d ↔ ∀ (n : ℕ), a ^ n ∣ b → c ^ n ∣ d := sorry

theorem multiplicity_le_multiplicity_of_dvd {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} {c : α} (hdvd : a ∣ b) : multiplicity b c ≤ multiplicity a c :=
  iff.mpr multiplicity_le_multiplicity_iff fun (n : ℕ) (h : b ^ n ∣ c) => dvd_trans (pow_dvd_pow_of_dvd hdvd n) h

theorem dvd_of_multiplicity_pos {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} (h : 0 < multiplicity a b) : a ∣ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∣ b)) (Eq.symm (pow_one a))))
    (pow_dvd_of_le_multiplicity (iff.mp enat.pos_iff_one_le h))

theorem dvd_iff_multiplicity_pos {α : Type u_1} [comm_monoid α] [DecidableRel has_dvd.dvd] {a : α} {b : α} : 0 < multiplicity a b ↔ a ∣ b := sorry

theorem finite_nat_iff {a : ℕ} {b : ℕ} : finite a b ↔ a ≠ 1 ∧ 0 < b := sorry

theorem ne_zero_of_finite {α : Type u_1} [comm_monoid_with_zero α] {a : α} {b : α} (h : finite a b) : b ≠ 0 := sorry

@[simp] protected theorem zero {α : Type u_1} [comm_monoid_with_zero α] [DecidableRel has_dvd.dvd] (a : α) : multiplicity a 0 = ⊤ := sorry

@[simp] theorem multiplicity_zero_eq_zero_of_ne_zero {α : Type u_1} [comm_monoid_with_zero α] [DecidableRel has_dvd.dvd] (a : α) (ha : a ≠ 0) : multiplicity 0 a = 0 :=
  multiplicity_eq_zero_of_not_dvd (eq.mpr (id (Eq._oldrec (Eq.refl (¬0 ∣ a)) (propext zero_dvd_iff))) ha)

theorem min_le_multiplicity_add {α : Type u_1} [comm_semiring α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} : min (multiplicity p a) (multiplicity p b) ≤ multiplicity p (a + b) := sorry

@[simp] protected theorem neg {α : Type u_1} [comm_ring α] [DecidableRel has_dvd.dvd] (a : α) (b : α) : multiplicity a (-b) = multiplicity a b := sorry

theorem multiplicity_add_of_gt {α : Type u_1} [comm_ring α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} (h : multiplicity p b < multiplicity p a) : multiplicity p (a + b) = multiplicity p b := sorry

theorem multiplicity_sub_of_gt {α : Type u_1} [comm_ring α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} (h : multiplicity p b < multiplicity p a) : multiplicity p (a - b) = multiplicity p b := sorry

theorem multiplicity_add_eq_min {α : Type u_1} [comm_ring α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} (h : multiplicity p a ≠ multiplicity p b) : multiplicity p (a + b) = min (multiplicity p a) (multiplicity p b) := sorry

theorem finite_mul_aux {α : Type u_1} [comm_cancel_monoid_with_zero α] {p : α} (hp : prime p) {n : ℕ} {m : ℕ} {a : α} {b : α} : ¬p ^ (n + 1) ∣ a → ¬p ^ (m + 1) ∣ b → ¬p ^ (n + m + 1) ∣ a * b := sorry

theorem finite_mul {α : Type u_1} [comm_cancel_monoid_with_zero α] {p : α} {a : α} {b : α} (hp : prime p) : finite p a → finite p b → finite p (a * b) := sorry

theorem finite_mul_iff {α : Type u_1} [comm_cancel_monoid_with_zero α] {p : α} {a : α} {b : α} (hp : prime p) : finite p (a * b) ↔ finite p a ∧ finite p b :=
  { mp := fun (h : finite p (a * b)) => { left := finite_of_finite_mul_right h, right := finite_of_finite_mul_left h },
    mpr := fun (h : finite p a ∧ finite p b) => finite_mul hp (and.left h) (and.right h) }

theorem finite_pow {α : Type u_1} [comm_cancel_monoid_with_zero α] {p : α} {a : α} (hp : prime p) {k : ℕ} (ha : finite p a) : finite p (a ^ k) := sorry

@[simp] theorem multiplicity_self {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {a : α} (ha : ¬is_unit a) (ha0 : a ≠ 0) : multiplicity a a = 1 := sorry

@[simp] theorem get_multiplicity_self {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {a : α} (ha : finite a a) : roption.get (multiplicity a a) ha = 1 := sorry

protected theorem mul' {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} (hp : prime p) (h : roption.dom (multiplicity p (a * b))) : roption.get (multiplicity p (a * b)) h =
  roption.get (multiplicity p a) (and.left (iff.mp (finite_mul_iff hp) h)) +
    roption.get (multiplicity p b) (and.right (iff.mp (finite_mul_iff hp) h)) := sorry

protected theorem mul {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} {a : α} {b : α} (hp : prime p) : multiplicity p (a * b) = multiplicity p a + multiplicity p b := sorry

theorem finset.prod {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {β : Type u_2} {p : α} (hp : prime p) (s : finset β) (f : β → α) : multiplicity p (finset.prod s fun (x : β) => f x) = finset.sum s fun (x : β) => multiplicity p (f x) := sorry

protected theorem pow' {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} {a : α} (hp : prime p) (ha : finite p a) {k : ℕ} : roption.get (multiplicity p (a ^ k)) (finite_pow hp ha) = k * roption.get (multiplicity p a) ha := sorry

theorem pow {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} {a : α} (hp : prime p) {k : ℕ} : multiplicity p (a ^ k) = k •ℕ multiplicity p a := sorry

theorem multiplicity_pow_self {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} (h0 : p ≠ 0) (hu : ¬is_unit p) (n : ℕ) : multiplicity p (p ^ n) = ↑n := sorry

theorem multiplicity_pow_self_of_prime {α : Type u_1} [comm_cancel_monoid_with_zero α] [DecidableRel has_dvd.dvd] {p : α} (hp : prime p) (n : ℕ) : multiplicity p (p ^ n) = ↑n :=
  multiplicity_pow_self (prime.ne_zero hp) (prime.not_unit hp) n

end multiplicity

theorem multiplicity_eq_zero_of_coprime {p : ℕ} {a : ℕ} {b : ℕ} (hp : p ≠ 1) (hle : multiplicity p a ≤ multiplicity p b) (hab : nat.coprime a b) : multiplicity p a = 0 := sorry


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison, Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.natural_transformation
import Mathlib.PostPort

universes u₁ u₂ v₁ v₂ v₃ u₃

namespace Mathlib

namespace category_theory

/--
`functor.category C D` gives the category structure on functors and natural transformations
between categories `C` and `D`.

Notice that if `C` and `D` are both small categories at the same universe level,
this is another small category at that level.
However if `C` and `D` are both large categories at the same universe level,
this is a small category at the next higher level.
-/
protected instance functor.category (C : Type u₁) [category C] (D : Type u₂) [category D] :
    category (C ⥤ D) :=
  category.mk

namespace nat_trans

@[simp] theorem vcomp_eq_comp {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D}
    {G : C ⥤ D} {H : C ⥤ D} (α : F ⟶ G) (β : G ⟶ H) : vcomp α β = α ≫ β :=
  rfl

theorem vcomp_app' {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D} {G : C ⥤ D}
    {H : C ⥤ D} (α : F ⟶ G) (β : G ⟶ H) (X : C) : app (α ≫ β) X = app α X ≫ app β X :=
  rfl

theorem congr_app {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D} {G : C ⥤ D}
    {α : F ⟶ G} {β : F ⟶ G} (h : α = β) (X : C) : app α X = app β X :=
  eq.mpr (id (Eq._oldrec (Eq.refl (app α X = app β X)) h)) (Eq.refl (app β X))

@[simp] theorem id_app {C : Type u₁} [category C] {D : Type u₂} [category D] (F : C ⥤ D) (X : C) :
    app 𝟙 X = 𝟙 :=
  rfl

@[simp] theorem comp_app {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D}
    {G : C ⥤ D} {H : C ⥤ D} (α : F ⟶ G) (β : G ⟶ H) (X : C) : app (α ≫ β) X = app α X ≫ app β X :=
  rfl

theorem app_naturality {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] {F : C ⥤ D ⥤ E} {G : C ⥤ D ⥤ E} (T : F ⟶ G) (X : C) {Y : D} {Z : D} (f : Y ⟶ Z) :
    functor.map (functor.obj F X) f ≫ app (app T X) Z =
        app (app T X) Y ≫ functor.map (functor.obj G X) f :=
  naturality (app T X) f

theorem naturality_app {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] {F : C ⥤ D ⥤ E} {G : C ⥤ D ⥤ E} (T : F ⟶ G) (Z : D) {X : C} {Y : C} (f : X ⟶ Y) :
    app (functor.map F f) Z ≫ app (app T Y) Z = app (app T X) Z ≫ app (functor.map G f) Z :=
  congr_fun (congr_arg app (naturality T f)) Z

/-- A natural transformation is a monomorphism if each component is. -/
theorem mono_app_of_mono {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D}
    {G : C ⥤ D} (α : F ⟶ G) [∀ (X : C), mono (app α X)] : mono α :=
  sorry

/-- A natural transformation is an epimorphism if each component is. -/
theorem epi_app_of_epi {C : Type u₁} [category C] {D : Type u₂} [category D] {F : C ⥤ D} {G : C ⥤ D}
    (α : F ⟶ G) [∀ (X : C), epi (app α X)] : epi α :=
  sorry

/-- `hcomp α β` is the horizontal composition of natural transformations. -/
def hcomp {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃} [category E]
    {F : C ⥤ D} {G : C ⥤ D} {H : D ⥤ E} {I : D ⥤ E} (α : F ⟶ G) (β : H ⟶ I) : F ⋙ H ⟶ G ⋙ I :=
  mk fun (X : C) => app β (functor.obj F X) ≫ functor.map I (app α X)

infixl:80 " ◫ " => Mathlib.category_theory.nat_trans.hcomp

@[simp] theorem hcomp_app {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] {F : C ⥤ D} {G : C ⥤ D} {H : D ⥤ E} {I : D ⥤ E} (α : F ⟶ G) (β : H ⟶ I) (X : C) :
    app (α ◫ β) X = app β (functor.obj F X) ≫ functor.map I (app α X) :=
  rfl

@[simp] theorem hcomp_id_app {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] {F : C ⥤ D} {G : C ⥤ D} {H : D ⥤ E} (α : F ⟶ G) (X : C) :
    app (α ◫ 𝟙) X = functor.map H (app α X) :=
  sorry

theorem id_hcomp_app {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] {F : C ⥤ D} {G : C ⥤ D} {H : E ⥤ C} (α : F ⟶ G) (X : E) :
    app (𝟙 ◫ α) X = app α (functor.obj H X) :=
  sorry

-- Note that we don't yet prove a `hcomp_assoc` lemma here: even stating it is painful, because we

-- need to use associativity of functor composition. (It's true without the explicit associator,

-- because functor composition is definitionally associative, but relying on the definitional equality

-- causes bad problems with elaboration later.)

theorem exchange {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃} [category E]
    {F : C ⥤ D} {G : C ⥤ D} {H : C ⥤ D} {I : D ⥤ E} {J : D ⥤ E} {K : D ⥤ E} (α : F ⟶ G) (β : G ⟶ H)
    (γ : I ⟶ J) (δ : J ⟶ K) : (α ≫ β) ◫ (γ ≫ δ) = α ◫ γ ≫ β ◫ δ :=
  sorry

end nat_trans

namespace functor

/-- Flip the arguments of a bifunctor. See also `currying.lean`. -/
protected def flip {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃} [category E]
    (F : C ⥤ D ⥤ E) : D ⥤ C ⥤ E :=
  mk
    (fun (k : D) =>
      mk (fun (j : C) => obj (obj F j) k) fun (j j' : C) (f : j ⟶ j') => nat_trans.app (map F f) k)
    fun (c c' : D) (f : c ⟶ c') => nat_trans.mk fun (j : C) => map (obj F j) f

@[simp] theorem flip_obj_obj {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] (F : C ⥤ D ⥤ E) (c : C) (d : D) :
    obj (obj (functor.flip F) d) c = obj (obj F c) d :=
  rfl

@[simp] theorem flip_obj_map {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] (F : C ⥤ D ⥤ E) {c : C} {c' : C} (f : c ⟶ c') (d : D) :
    map (obj (functor.flip F) d) f = nat_trans.app (map F f) d :=
  rfl

@[simp] theorem flip_map_app {C : Type u₁} [category C] {D : Type u₂} [category D] {E : Type u₃}
    [category E] (F : C ⥤ D ⥤ E) {d : D} {d' : D} (f : d ⟶ d') (c : C) :
    nat_trans.app (map (functor.flip F) f) c = map (obj F c) f :=
  rfl

end Mathlib


theorem byCases_Bool_bind [Monad m] (x : m Bool) (f g : Bool → m β) (isTrue : f true = g true) (isFalse : f false = g false) : (x >>= f) = (x >>= g) := by
  have : f = g := by
    funext b; cases b <;> assumption
  rw [this]

theorem eq_findM [Monad m] [LawfulMonad m] (p : α → m Bool) (xs : List α) :
    (do for x in xs do
          let b ← p x
          if b then
            return some x
        return none)
    =
    xs.findM? p := by
  induction xs with simp [List.findM?]
  | cons x xs ih =>
    rw [← ih]; simp
    apply byCases_Bool_bind <;> simp

theorem eq_findSomeM_findM [Monad m] [LawfulMonad m] (p : α → m Bool) (xss : List (List α)) :
    (do for xs in xss do
           for x in xs do
             let b ← p x
             if b then
               return some x
        return none)
    =
    xss.findSomeM? (fun xs => xs.findM? p) := by
  induction xss with simp [List.findSomeM?]
  | cons xs xss ih =>
    rw [← ih, ← eq_findM]
    induction xs with simp
    | cons x xs ih =>
      apply byCases_Bool_bind <;> simp [ih]

theorem eq_findSomeM_findM' [Monad m] [LawfulMonad m] (p : α → m Bool) (xss : List (List α)) :
    (do for xs in xss do
           for x in xs do
             let b ← p x
             if b then
               return some x
        return none)
    =
    xss.findSomeM? (fun xs => xs.findM? p) := by
  induction xss <;> simp [List.findSomeM?]
  rename List α => xs
  rename _ = _  => ih
  rw [← ih, ← eq_findM]
  induction xs <;> simp
  rename _ = _ => ih
  apply byCases_Bool_bind <;> simp [ih]

theorem z_add (x : Nat) : 0 + x = x := by
  induction x
  rfl
  rename _ = _ => ih
  show Nat.succ (0 + _) = _
  rw [ih]


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.algebra.tower
import Mathlib.algebra.invertible
import Mathlib.linear_algebra.basis
import Mathlib.ring_theory.adjoin
import Mathlib.PostPort

universes u v w u₁ u_1 u_2 u_3 u_4 v₁ w₁

namespace Mathlib

/-!
# Towers of algebras

We set up the basic theory of algebra towers.
An algebra tower A/S/R is expressed by having instances of `algebra A S`,
`algebra R S`, `algebra R A` and `is_scalar_tower R S A`, the later asserting the
compatibility condition `(r • s) • a = r • (s • a)`.

In `field_theory/tower.lean` we use this to prove the tower law for finite extensions,
that if `R` and `S` are both fields, then `[A:R] = [A:S] [S:A]`.

In this file we prepare the main lemma:
if `{bi | i ∈ I}` is an `R`-basis of `S` and `{cj | j ∈ J}` is a `S`-basis
of `A`, then `{bi cj | i ∈ I, j ∈ J}` is an `R`-basis of `A`. This statement does not require the
base rings to be a field, so we also generalize the lemma to rings in this file.
-/

namespace is_scalar_tower

protected instance polynomial (R : Type u) {S : Type v} {A : Type w} [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] [algebra R A] [is_scalar_tower R S A] : is_scalar_tower R S (polynomial A) :=
  of_algebra_map_eq fun (x : R) => congr_arg (⇑polynomial.C) (algebra_map_apply R S A x)

theorem aeval_apply (R : Type u) (S : Type v) (A : Type w) [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] [algebra R A] [is_scalar_tower R S A] (x : A) (p : polynomial R) : coe_fn (polynomial.aeval x) p = coe_fn (polynomial.aeval x) (polynomial.map (algebra_map R S) p) := sorry

/-- Suppose that `R -> S -> A` is a tower of algebras.
If an element `r : R` is invertible in `S`, then it is invertible in `A`. -/
def invertible.algebra_tower (R : Type u) (S : Type v) (A : Type w) [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] [algebra R A] [is_scalar_tower R S A] (r : R) [invertible (coe_fn (algebra_map R S) r)] : invertible (coe_fn (algebra_map R A) r) :=
  invertible.copy (invertible.map (↑(algebra_map S A)) (coe_fn (algebra_map R S) r)) (coe_fn (algebra_map R A) r) sorry

/-- A natural number that is invertible when coerced to `R` is also invertible
when coerced to any `R`-algebra. -/
def invertible_algebra_coe_nat (R : Type u) (A : Type w) [comm_semiring R] [semiring A] [algebra R A] (n : ℕ) [inv : invertible ↑n] : invertible ↑n :=
  invertible.algebra_tower ℕ R A n

theorem algebra_map_aeval (R : Type u) (A : Type w) (B : Type u₁) [comm_semiring R] [comm_semiring A] [comm_semiring B] [algebra R A] [algebra A B] [algebra R B] [is_scalar_tower R A B] (x : A) (p : polynomial R) : coe_fn (algebra_map A B) (coe_fn (polynomial.aeval x) p) = coe_fn (polynomial.aeval (coe_fn (algebra_map A B) x)) p := sorry

theorem aeval_eq_zero_of_aeval_algebra_map_eq_zero (R : Type u) (A : Type w) (B : Type u₁) [comm_semiring R] [comm_semiring A] [comm_semiring B] [algebra R A] [algebra A B] [algebra R B] [is_scalar_tower R A B] {x : A} {p : polynomial R} (h : function.injective ⇑(algebra_map A B)) (hp : coe_fn (polynomial.aeval (coe_fn (algebra_map A B) x)) p = 0) : coe_fn (polynomial.aeval x) p = 0 := sorry

theorem aeval_eq_zero_of_aeval_algebra_map_eq_zero_field {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_semiring R] [field A] [comm_semiring B] [nontrivial B] [algebra R A] [algebra R B] [algebra A B] [is_scalar_tower R A B] {x : A} {p : polynomial R} (h : coe_fn (polynomial.aeval (coe_fn (algebra_map A B) x)) p = 0) : coe_fn (polynomial.aeval x) p = 0 :=
  aeval_eq_zero_of_aeval_algebra_map_eq_zero R A B (ring_hom.injective (algebra_map A B)) h

end is_scalar_tower

namespace algebra

theorem adjoin_algebra_map' {R : Type u} {S : Type v} {A : Type w} [comm_ring R] [comm_ring S] [comm_ring A] [algebra R S] [algebra S A] (s : set S) : adjoin R (⇑(algebra_map S (comap R S A)) '' s) = subalgebra.map (adjoin R s) (to_comap R S A) := sorry

theorem adjoin_algebra_map (R : Type u) (S : Type v) (A : Type w) [comm_ring R] [comm_ring S] [comm_ring A] [algebra R S] [algebra S A] [algebra R A] [is_scalar_tower R S A] (s : set S) : adjoin R (⇑(algebra_map S A) '' s) = subalgebra.map (adjoin R s) (is_scalar_tower.to_alg_hom R S A) := sorry

theorem adjoin_res (C : Type u_1) (D : Type u_2) (E : Type u_3) [comm_semiring C] [comm_semiring D] [comm_semiring E] [algebra C D] [algebra C E] [algebra D E] [is_scalar_tower C D E] (S : set E) : subalgebra.res C (adjoin D S) =
  subalgebra.under (subalgebra.map ⊤ (is_scalar_tower.to_alg_hom C D E))
    (adjoin (↥(subalgebra.map ⊤ (is_scalar_tower.to_alg_hom C D E))) S) := sorry

theorem adjoin_res_eq_adjoin_res (C : Type u_1) (D : Type u_2) (E : Type u_3) (F : Type u_4) [comm_semiring C] [comm_semiring D] [comm_semiring E] [comm_semiring F] [algebra C D] [algebra C E] [algebra C F] [algebra D F] [algebra E F] [is_scalar_tower C D F] [is_scalar_tower C E F] {S : set D} {T : set E} (hS : adjoin C S = ⊤) (hT : adjoin C T = ⊤) : subalgebra.res C (adjoin E (⇑(algebra_map D F) '' S)) = subalgebra.res C (adjoin D (⇑(algebra_map E F) '' T)) := sorry

end algebra

namespace subalgebra

@[simp] theorem aeval_coe (R : Type u) {A : Type w} [comm_semiring R] [comm_semiring A] [algebra R A] {S : subalgebra R A} {x : ↥S} {p : polynomial R} : coe_fn (polynomial.aeval ↑x) p = ↑(coe_fn (polynomial.aeval x) p) :=
  Eq.symm (is_scalar_tower.algebra_map_aeval R (↥S) A x p)

end subalgebra

theorem algebra.fg_trans' {R : Type u_1} {S : Type u_2} {A : Type u_3} [comm_ring R] [comm_ring S] [comm_ring A] [algebra R S] [algebra S A] [algebra R A] [is_scalar_tower R S A] (hRS : subalgebra.fg ⊤) (hSA : subalgebra.fg ⊤) : subalgebra.fg ⊤ := sorry

theorem linear_independent_smul {R : Type u} {S : Type v} {A : Type w} [comm_ring R] [ring S] [add_comm_group A] [algebra R S] [module S A] [module R A] [is_scalar_tower R S A] {ι : Type v₁} {b : ι → S} {ι' : Type w₁} {c : ι' → A} (hb : linear_independent R b) (hc : linear_independent S c) : linear_independent R fun (p : ι × ι') => b (prod.fst p) • c (prod.snd p) := sorry

theorem is_basis.smul {R : Type u} {S : Type v} {A : Type w} [comm_ring R] [ring S] [add_comm_group A] [algebra R S] [module S A] [module R A] [is_scalar_tower R S A] {ι : Type v₁} {b : ι → S} {ι' : Type w₁} {c : ι' → A} (hb : is_basis R b) (hc : is_basis S c) : is_basis R fun (p : ι × ι') => b (prod.fst p) • c (prod.snd p) := sorry

theorem is_basis.smul_repr {R : Type u} {S : Type v} {A : Type w} [comm_ring R] [ring S] [add_comm_group A] [algebra R S] [module S A] [module R A] [is_scalar_tower R S A] {ι : Type u_1} {ι' : Type u_2} {b : ι → S} {c : ι' → A} (hb : is_basis R b) (hc : is_basis S c) (x : A) (ij : ι × ι') : coe_fn (coe_fn (is_basis.repr (is_basis.smul hb hc)) x) ij =
  coe_fn (coe_fn (is_basis.repr hb) (coe_fn (coe_fn (is_basis.repr hc) x) (prod.snd ij))) (prod.fst ij) := sorry

theorem is_basis.smul_repr_mk {R : Type u} {S : Type v} {A : Type w} [comm_ring R] [ring S] [add_comm_group A] [algebra R S] [module S A] [module R A] [is_scalar_tower R S A] {ι : Type u_1} {ι' : Type u_2} {b : ι → S} {c : ι' → A} (hb : is_basis R b) (hc : is_basis S c) (x : A) (i : ι) (j : ι') : coe_fn (coe_fn (is_basis.repr (is_basis.smul hb hc)) x) (i, j) =
  coe_fn (coe_fn (is_basis.repr hb) (coe_fn (coe_fn (is_basis.repr hc) x) j)) i := sorry

theorem exists_subalgebra_of_fg (A : Type w) (B : Type u₁) (C : Type u_1) [comm_ring A] [comm_ring B] [comm_ring C] [algebra A B] [algebra B C] [algebra A C] [is_scalar_tower A B C] (hAC : subalgebra.fg ⊤) (hBC : submodule.fg ⊤) : ∃ (B₀ : subalgebra A B), subalgebra.fg B₀ ∧ submodule.fg ⊤ := sorry

/-- Artin--Tate lemma: if A ⊆ B ⊆ C is a chain of subrings of commutative rings, and
A is noetherian, and C is algebra-finite over A, and C is module-finite over B,
then B is algebra-finite over A.

References: Atiyah--Macdonald Proposition 7.8; Stacks 00IS; Altman--Kleiman 16.17. -/
theorem fg_of_fg_of_fg (A : Type w) (B : Type u₁) (C : Type u_1) [comm_ring A] [comm_ring B] [comm_ring C] [algebra A B] [algebra B C] [algebra A C] [is_scalar_tower A B C] [is_noetherian_ring A] (hAC : subalgebra.fg ⊤) (hBC : submodule.fg ⊤) (hBCi : function.injective ⇑(algebra_map B C)) : subalgebra.fg ⊤ := sorry

/-- Restrict the domain of an `alg_hom`. -/
def alg_hom.restrict_domain {A : Type w} (B : Type u₁) {C : Type u_1} {D : Type u_2} [comm_semiring A] [comm_semiring C] [comm_semiring D] [algebra A C] [algebra A D] (f : alg_hom A C D) [comm_semiring B] [algebra A B] [algebra B C] [is_scalar_tower A B C] : alg_hom A B D :=
  alg_hom.comp f (is_scalar_tower.to_alg_hom A B C)

/-- Extend the scalars of an `alg_hom`. -/
def alg_hom.extend_scalars {A : Type w} (B : Type u₁) {C : Type u_1} {D : Type u_2} [comm_semiring A] [comm_semiring C] [comm_semiring D] [algebra A C] [algebra A D] (f : alg_hom A C D) [comm_semiring B] [algebra A B] [algebra B C] [is_scalar_tower A B C] : alg_hom B C D :=
  alg_hom.mk (alg_hom.to_fun f) sorry sorry sorry sorry sorry

/-- `alg_hom`s from the top of a tower are equivalent to a pair of `alg_hom`s. -/
def alg_hom_equiv_sigma {A : Type w} {B : Type u₁} {C : Type u_1} {D : Type u_2} [comm_semiring A] [comm_semiring C] [comm_semiring D] [algebra A C] [algebra A D] [comm_semiring B] [algebra A B] [algebra B C] [is_scalar_tower A B C] : alg_hom A C D ≃ sigma fun (f : alg_hom A B D) => alg_hom B C D :=
  equiv.mk (fun (f : alg_hom A C D) => sigma.mk (alg_hom.restrict_domain B f) (alg_hom.extend_scalars B f))
    (fun (fg : sigma fun (f : alg_hom A B D) => alg_hom B C D) => is_scalar_tower.restrict_base A (sigma.snd fg)) sorry
    sorry