/-
Copyright (c) 2020 Jean Lo. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jean Lo
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.algebra.group
import Mathlib.logic.function.iterate
import Mathlib.PostPort

universes u_1 u_2 l u_3

namespace Mathlib

/-!
# Flows and invariant sets

This file defines a flow on a topological space `α` by a topological
monoid `τ` as a continuous monoid-act of `τ` on `α`. Anticipating the
cases where `τ` is one of `ℕ`, `ℤ`, `ℝ⁺`, or `ℝ`, we use additive
notation for the monoids, though the definition does not require
commutativity.

A subset `s` of `α` is invariant under a family of maps `ϕₜ : α → α`
if `ϕₜ s ⊆ s` for all `t`. In many cases `ϕ` will be a flow on
`α`. For the cases where `ϕ` is a flow by an ordered (additive,
commutative) monoid, we additionally define forward invariance, where
`t` ranges over those elements which are nonnegative.

Additionally, we define such constructions as the restriction of a
flow onto an invariant subset, and the time-reveral of a flow by a
group.
-/

/-!
### Invariant sets
-/

/-- A set `s ⊆ α` is invariant under `ϕ : τ → α → α` if
    `ϕ t s ⊆ s` for all `t` in `τ`. -/
def is_invariant {τ : Type u_1} {α : Type u_2} (ϕ : τ → α → α) (s : set α) :=
  ∀ (t : τ), set.maps_to (ϕ t) s s

theorem is_invariant_iff_image {τ : Type u_1} {α : Type u_2} (ϕ : τ → α → α) (s : set α) :
    is_invariant ϕ s ↔ ∀ (t : τ), ϕ t '' s ⊆ s :=
  sorry

/-- A set `s ⊆ α` is forward-invariant under `ϕ : τ → α → α` if
    `ϕ t s ⊆ s` for all `t ≥ 0`. -/
def is_fw_invariant {τ : Type u_1} {α : Type u_2} [preorder τ] [HasZero τ] (ϕ : τ → α → α)
    (s : set α) :=
  ∀ {t : τ}, 0 ≤ t → set.maps_to (ϕ t) s s

theorem is_invariant.is_fw_invariant {τ : Type u_1} {α : Type u_2} [preorder τ] [HasZero τ]
    {ϕ : τ → α → α} {s : set α} (h : is_invariant ϕ s) : is_fw_invariant ϕ s :=
  fun (t : τ) (ht : 0 ≤ t) => h t

/-- If `τ` is a `canonically_ordered_add_monoid` (e.g., `ℕ` or `ℝ≥0`), then the notions
`is_fw_invariant` and `is_invariant` are equivalent. -/
theorem is_fw_invariant.is_invariant {τ : Type u_1} {α : Type u_2}
    [canonically_ordered_add_monoid τ] {ϕ : τ → α → α} {s : set α} (h : is_fw_invariant ϕ s) :
    is_invariant ϕ s :=
  fun (t : τ) => h (zero_le t)

/-- If `τ` is a `canonically_ordered_add_monoid` (e.g., `ℕ` or `ℝ≥0`), then the notions
`is_fw_invariant` and `is_invariant` are equivalent. -/
theorem is_fw_invariant_iff_is_invariant {τ : Type u_1} {α : Type u_2}
    [canonically_ordered_add_monoid τ] {ϕ : τ → α → α} {s : set α} :
    is_fw_invariant ϕ s ↔ is_invariant ϕ s :=
  { mp := is_fw_invariant.is_invariant, mpr := is_invariant.is_fw_invariant }

/-!
### Flows
-/

/-- A flow on a topological space `α` by an a additive topological
    monoid `τ` is a continuous monoid action of `τ` on `α`.-/
structure flow (τ : Type u_1) [topological_space τ] [add_monoid τ] [has_continuous_add τ]
    (α : Type u_2) [topological_space α]
    where
  to_fun : τ → α → α
  cont' : continuous (function.uncurry to_fun)
  map_add' : ∀ (t₁ t₂ : τ) (x : α), to_fun (t₁ + t₂) x = to_fun t₁ (to_fun t₂ x)
  map_zero' : ∀ (x : α), to_fun 0 x = x

namespace flow

protected instance inhabited {τ : Type u_1} [add_monoid τ] [topological_space τ]
    [has_continuous_add τ] {α : Type u_2} [topological_space α] : Inhabited (flow τ α) :=
  { default := mk (fun (_x : τ) (x : α) => x) continuous_snd sorry sorry }

protected instance has_coe_to_fun {τ : Type u_1} [add_monoid τ] [topological_space τ]
    [has_continuous_add τ] {α : Type u_2} [topological_space α] : has_coe_to_fun (flow τ α) :=
  has_coe_to_fun.mk (fun (x : flow τ α) => τ → α → α) to_fun

theorem ext {τ : Type u_1} [add_monoid τ] [topological_space τ] [has_continuous_add τ]
    {α : Type u_2} [topological_space α] {ϕ₁ : flow τ α} {ϕ₂ : flow τ α} :
    (∀ (t : τ) (x : α), coe_fn ϕ₁ t x = coe_fn ϕ₂ t x) → ϕ₁ = ϕ₂ :=
  sorry

protected theorem continuous {τ : Type u_1} [add_monoid τ] [topological_space τ]
    [has_continuous_add τ] {α : Type u_2} [topological_space α] (ϕ : flow τ α) {β : Type u_3}
    [topological_space β] {t : β → τ} (ht : continuous t) {f : β → α} (hf : continuous f) :
    continuous fun (x : β) => coe_fn ϕ (t x) (f x) :=
  continuous.comp (cont' ϕ) (continuous.prod_mk ht hf)

theorem Mathlib.continuous.flow {τ : Type u_1} [add_monoid τ] [topological_space τ]
    [has_continuous_add τ] {α : Type u_2} [topological_space α] (ϕ : flow τ α) {β : Type u_3}
    [topological_space β] {t : β → τ} (ht : continuous t) {f : β → α} (hf : continuous f) :
    continuous fun (x : β) => coe_fn ϕ (t x) (f x) :=
  flow.continuous

theorem map_add {τ : Type u_1} [add_monoid τ] [topological_space τ] [has_continuous_add τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) (t₁ : τ) (t₂ : τ) (x : α) :
    coe_fn ϕ (t₁ + t₂) x = coe_fn ϕ t₁ (coe_fn ϕ t₂ x) :=
  map_add' ϕ t₁ t₂ x

@[simp] theorem map_zero {τ : Type u_1} [add_monoid τ] [topological_space τ] [has_continuous_add τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) : coe_fn ϕ 0 = id :=
  funext (map_zero' ϕ)

theorem map_zero_apply {τ : Type u_1} [add_monoid τ] [topological_space τ] [has_continuous_add τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) (x : α) : coe_fn ϕ 0 x = x :=
  map_zero' ϕ x

/-- Iterations of a continuous function from a topological space `α`
    to itself defines a semiflow by `ℕ` on `α`. -/
def from_iter {α : Type u_2} [topological_space α] {g : α → α} (h : continuous g) : flow ℕ α :=
  mk (fun (n : ℕ) (x : α) => nat.iterate g n x) sorry (function.iterate_add_apply g) sorry

/-- Restriction of a flow onto an invariant set. -/
def restrict {τ : Type u_1} [add_monoid τ] [topological_space τ] [has_continuous_add τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) {s : set α} (h : is_invariant (⇑ϕ) s) :
    flow τ ↥s :=
  mk (fun (t : τ) => set.maps_to.restrict (coe_fn ϕ t) s s (h t)) sorry sorry sorry

end flow

namespace flow

theorem is_invariant_iff_image_eq {τ : Type u_1} [add_comm_group τ] [topological_space τ]
    [topological_add_group τ] {α : Type u_2} [topological_space α] (ϕ : flow τ α) (s : set α) :
    is_invariant (⇑ϕ) s ↔ ∀ (t : τ), coe_fn ϕ t '' s = s :=
  sorry

/-- The time-reversal of a flow `ϕ` by a (commutative, additive) group
    is defined `ϕ.reverse t x = ϕ (-t) x`. -/
def reverse {τ : Type u_1} [add_comm_group τ] [topological_space τ] [topological_add_group τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) : flow τ α :=
  mk (fun (t : τ) => coe_fn ϕ (-t)) sorry sorry sorry

/-- The map `ϕ t` as a homeomorphism. -/
def to_homeomorph {τ : Type u_1} [add_comm_group τ] [topological_space τ] [topological_add_group τ]
    {α : Type u_2} [topological_space α] (ϕ : flow τ α) (t : τ) : α ≃ₜ α :=
  homeomorph.mk (equiv.mk (coe_fn ϕ t) (coe_fn ϕ (-t)) sorry sorry)

theorem image_eq_preimage {τ : Type u_1} [add_comm_group τ] [topological_space τ]
    [topological_add_group τ] {α : Type u_2} [topological_space α] (ϕ : flow τ α) (t : τ)
    (s : set α) : coe_fn ϕ t '' s = coe_fn ϕ (-t) ⁻¹' s :=
  equiv.image_eq_preimage (homeomorph.to_equiv (to_homeomorph ϕ t)) s

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

! This file was ported from Lean 3 source module category_theory.limits.shapes.finite_products
! leanprover-community/mathlib commit ac3ae212f394f508df43e37aa093722fa9b65d31
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Limits.Shapes.FiniteLimits
import Mathlib.CategoryTheory.Limits.Shapes.Products

/-!
# Categories with finite (co)products

Typeclasses representing categories with (co)products over finite indexing types.
-/

universe w v u

open CategoryTheory

open Classical

namespace CategoryTheory.Limits

variable (C : Type u) [Category.{v} C]

/-- A category has finite products if there is a chosen limit for every diagram
with shape `Discrete J`, where we have `[Finite J]`.

We require this condition only for `J = Fin n` in the definition, then deduce a version for any
`J : Type _` as a corollary of this definition.
-/
class HasFiniteProducts : Prop where
  /-- `C` has finite products -/
  out (n : ℕ) : HasLimitsOfShape (Discrete (Fin n)) C
#align category_theory.limits.has_finite_products CategoryTheory.Limits.HasFiniteProducts

/-- If `C` has finite limits then it has finite products. -/
instance (priority := 10) hasFiniteProducts_of_hasFiniteLimits [HasFiniteLimits C] :
    HasFiniteProducts C :=
  ⟨fun _ => inferInstance⟩
#align category_theory.limits.has_finite_products_of_has_finite_limits CategoryTheory.Limits.hasFiniteProducts_of_hasFiniteLimits

instance hasLimitsOfShape_discrete [HasFiniteProducts C] (ι : Type w) [Finite ι] :
    HasLimitsOfShape (Discrete ι) C := by
  rcases Finite.exists_equiv_fin ι with ⟨n, ⟨e⟩⟩
  haveI : HasLimitsOfShape (Discrete (Fin n)) C := HasFiniteProducts.out n
  exact hasLimitsOfShape_of_equivalence (Discrete.equivalence e.symm)
#align category_theory.limits.has_limits_of_shape_discrete CategoryTheory.Limits.hasLimitsOfShape_discrete

/-- We can now write this for powers. -/
noncomputable example [HasFiniteProducts C] (X : C) : C :=
  ∏ fun _ : Fin 5 => X

/-- If a category has all products then in particular it has finite products.
-/
theorem hasFiniteProducts_of_hasProducts [HasProducts.{w} C] : HasFiniteProducts C :=
  ⟨fun _ => hasLimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift.{w})⟩
#align category_theory.limits.has_finite_products_of_has_products CategoryTheory.Limits.hasFiniteProducts_of_hasProducts

/-- A category has finite coproducts if there is a chosen colimit for every diagram
with shape `Discrete J`, where we have `[Fintype J]`.

We require this condition only for `J = Fin n` in the definition, then deduce a version for any
`J : Type _` as a corollary of this definition.
-/
class HasFiniteCoproducts : Prop where
  /-- `C` has all finite coproducts -/
  out (n : ℕ) : HasColimitsOfShape (Discrete (Fin n)) C
#align category_theory.limits.has_finite_coproducts CategoryTheory.Limits.HasFiniteCoproducts

-- attribute [class] HasFiniteCoproducts Porting note: this doesn't seem necessary in Lean 4

instance hasColimitsOfShape_discrete [HasFiniteCoproducts C] (ι : Type w) [Finite ι] :
    HasColimitsOfShape (Discrete ι) C := by
  rcases Finite.exists_equiv_fin ι with ⟨n, ⟨e⟩⟩
  haveI : HasColimitsOfShape (Discrete (Fin n)) C := HasFiniteCoproducts.out n
  exact hasColimitsOfShape_of_equivalence (Discrete.equivalence e.symm)
#align category_theory.limits.has_colimits_of_shape_discrete CategoryTheory.Limits.hasColimitsOfShape_discrete

/-- If `C` has finite colimits then it has finite coproducts. -/
instance (priority := 10) hasFiniteCoproducts_of_hasFiniteColimits [HasFiniteColimits C] :
    HasFiniteCoproducts C :=
  ⟨fun J => by infer_instance⟩
#align category_theory.limits.has_finite_coproducts_of_has_finite_colimits CategoryTheory.Limits.hasFiniteCoproducts_of_hasFiniteColimits

/-- If a category has all coproducts then in particular it has finite coproducts.
-/
theorem hasFiniteCoproducts_of_hasCoproducts [HasCoproducts.{w} C] : HasFiniteCoproducts C :=
  ⟨fun _ => hasColimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift.{w})⟩
#align category_theory.limits.has_finite_coproducts_of_has_coproducts CategoryTheory.Limits.hasFiniteCoproducts_of_hasCoproducts

end CategoryTheory.Limits


/-
Copyright (c) 2019 Abhimanyu Pallavi Sudhir. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Abhimanyu Pallavi Sudhir, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.filter.ultrafilter
import Mathlib.order.filter.germ
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Ultraproducts

If `φ` is an ultrafilter, then the space of germs of functions `f : α → β` at `φ` is called
the *ultraproduct*. In this file we prove properties of ultraproducts that rely on `φ` being an
ultrafilter. Definitions and properties that work for any filter should go to `order.filter.germ`.

## Tags

ultrafilter, ultraproduct
-/

namespace filter

namespace germ

/-- If `φ` is an ultrafilter then the ultraproduct is a division ring. -/
protected instance division_ring {α : Type u} {β : Type v} {φ : ultrafilter α} [division_ring β] : division_ring (germ (↑φ) β) :=
  division_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry ring.one sorry sorry
    sorry sorry div_inv_monoid.inv div_inv_monoid.div sorry sorry sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a field. -/
protected instance field {α : Type u} {β : Type v} {φ : ultrafilter α} [field β] : field (germ (↑φ) β) :=
  field.mk comm_ring.add sorry comm_ring.zero sorry sorry comm_ring.neg comm_ring.sub sorry sorry comm_ring.mul sorry
    comm_ring.one sorry sorry sorry sorry sorry division_ring.inv sorry sorry sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a linear order. -/
protected instance linear_order {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_order β] : linear_order (germ (↑φ) β) :=
  linear_order.mk partial_order.le partial_order.lt sorry sorry sorry sorry
    (fun (a b : germ (↑φ) β) => classical.prop_decidable (a ≤ b)) Mathlib.decidable_eq_of_decidable_le
    Mathlib.decidable_lt_of_decidable_le

@[simp] theorem const_div {α : Type u} {β : Type v} {φ : ultrafilter α} [division_ring β] (x : β) (y : β) : ↑(x / y) = ↑x / ↑y :=
  rfl

theorem coe_lt {α : Type u} {β : Type v} {φ : ultrafilter α} [preorder β] {f : α → β} {g : α → β} : ↑f < ↑g ↔ filter.eventually (fun (x : α) => f x < g x) ↑φ := sorry

theorem coe_pos {α : Type u} {β : Type v} {φ : ultrafilter α} [preorder β] [HasZero β] {f : α → β} : 0 < ↑f ↔ filter.eventually (fun (x : α) => 0 < f x) ↑φ :=
  coe_lt

theorem const_lt {α : Type u} {β : Type v} {φ : ultrafilter α} [preorder β] {x : β} {y : β} : ↑x < ↑y ↔ x < y :=
  iff.trans coe_lt lift_rel_const_iff

theorem lt_def {α : Type u} {β : Type v} {φ : ultrafilter α} [preorder β] : Less = lift_rel Less := sorry

/-- If `φ` is an ultrafilter then the ultraproduct is an ordered ring. -/
protected instance ordered_ring {α : Type u} {β : Type v} {φ : ultrafilter α} [ordered_ring β] : ordered_ring (germ (↑φ) β) :=
  ordered_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry ring.one sorry sorry
    sorry sorry ordered_add_comm_group.le ordered_add_comm_group.lt sorry sorry sorry sorry sorry sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a linear ordered ring. -/
protected instance linear_ordered_ring {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_ring β] : linear_ordered_ring (germ (↑φ) β) :=
  linear_ordered_ring.mk ordered_ring.add sorry ordered_ring.zero sorry sorry ordered_ring.neg ordered_ring.sub sorry
    sorry ordered_ring.mul sorry ordered_ring.one sorry sorry sorry sorry ordered_ring.le ordered_ring.lt sorry sorry
    sorry sorry sorry sorry sorry linear_order.decidable_le linear_order.decidable_eq linear_order.decidable_lt sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a linear ordered field. -/
protected instance linear_ordered_field {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_field β] : linear_ordered_field (germ (↑φ) β) :=
  linear_ordered_field.mk linear_ordered_ring.add sorry linear_ordered_ring.zero sorry sorry linear_ordered_ring.neg
    linear_ordered_ring.sub sorry sorry linear_ordered_ring.mul sorry linear_ordered_ring.one sorry sorry sorry sorry
    linear_ordered_ring.le linear_ordered_ring.lt sorry sorry sorry sorry sorry sorry sorry
    linear_ordered_ring.decidable_le linear_ordered_ring.decidable_eq linear_ordered_ring.decidable_lt sorry sorry
    field.inv sorry sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a linear ordered commutative ring. -/
protected instance linear_ordered_comm_ring {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_comm_ring β] : linear_ordered_comm_ring (germ (↑φ) β) :=
  linear_ordered_comm_ring.mk linear_ordered_ring.add sorry linear_ordered_ring.zero sorry sorry linear_ordered_ring.neg
    linear_ordered_ring.sub sorry sorry linear_ordered_ring.mul sorry linear_ordered_ring.one sorry sorry sorry sorry
    linear_ordered_ring.le linear_ordered_ring.lt sorry sorry sorry sorry sorry sorry sorry
    linear_ordered_ring.decidable_le linear_ordered_ring.decidable_eq linear_ordered_ring.decidable_lt sorry sorry

/-- If `φ` is an ultrafilter then the ultraproduct is a decidable linear ordered commutative
group. -/
protected instance linear_ordered_add_comm_group {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_add_comm_group β] : linear_ordered_add_comm_group (germ (↑φ) β) :=
  linear_ordered_add_comm_group.mk ordered_add_comm_group.add sorry ordered_add_comm_group.zero sorry sorry
    ordered_add_comm_group.neg ordered_add_comm_group.sub sorry sorry ordered_add_comm_group.le ordered_add_comm_group.lt
    sorry sorry sorry sorry linear_order.decidable_le linear_order.decidable_eq linear_order.decidable_lt sorry

theorem max_def {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_order β] (x : germ (↑φ) β) (y : germ (↑φ) β) : max x y = map₂ max x y := sorry

theorem min_def {α : Type u} {β : Type v} {φ : ultrafilter α} [K : linear_order β] (x : germ (↑φ) β) (y : germ (↑φ) β) : min x y = map₂ min x y := sorry

theorem abs_def {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_add_comm_group β] (x : germ (↑φ) β) : abs x = map abs x := sorry

@[simp] theorem const_max {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_order β] (x : β) (y : β) : ↑(max x y) = max ↑x ↑y :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(max x y) = max ↑x ↑y)) (max_def ↑x ↑y)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(max x y) = map₂ max ↑x ↑y)) (map₂_const (↑φ) x y max))) (Eq.refl ↑(max x y)))

@[simp] theorem const_min {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_order β] (x : β) (y : β) : ↑(min x y) = min ↑x ↑y :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(min x y) = min ↑x ↑y)) (min_def ↑x ↑y)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(min x y) = map₂ min ↑x ↑y)) (map₂_const (↑φ) x y min))) (Eq.refl ↑(min x y)))

@[simp] theorem const_abs {α : Type u} {β : Type v} {φ : ultrafilter α} [linear_ordered_add_comm_group β] (x : β) : ↑(abs x) = abs ↑x :=
  const_max x (-x)


import computability.partrec
import computability.encoding
import pair
import part

namespace num

@[simp] lemma bit1_ne_zero (n : num) : n.bit1 ≠ 0 := by cases n; trivial
@[simp] lemma bit0_zero_iff_zero (n : num) : n.bit0 = 0 ↔ n = 0 :=
⟨λ h, by { cases n, { refl, }, contradiction, }, by { rintro rfl, refl, }⟩
@[simp] lemma succ_ne_zero (n : num) : n + 1 ≠ 0 := by cases n; trivial

end num

open num (to_bits of_bits)

section
parameter (ι : Type)

/--
A `code` is a description of an algorithm. The semantics are as follows:
  - `fst` takes as input the encoding of a pair `(a, b)` and outputs `a`
  - `snd` takes as input the encoding of a pair `(a, b)` and outputs `b`
  - `bit` takes as input a natural number `n` and appends the bit `b` (in the least significant place)
  - `pair` given `c₁` and `c₂`, the code which outputs the pair `(c₁(x), c₂(x))` on input `x`
  - `comp` compose two codes
  - `case` given `f g h`, calls `f` if the least significant bit (lsb) is `0`, `g` if it is `1`, and `h` if the number is `0`
  - `fix` given `f`, repeatedly calls `f` on the input, interpreting the result as a tuple `(a, b)` until `a = 0`.
-/
inductive code
| fst : code
| snd : code
| bit : bool → code
| pair : code → code → code
| comp : code → code → code
| case : code → code → code → code
| fix : code → code

parameter {ι}
@[simp] def code.eval : code → ℕ →. ℕ
| code.fst := λ n, part.some (nat.unpair' n).1
| code.snd := λ n, part.some (nat.unpair' n).2
| (code.bit b) := nat.bit b
| (code.pair f g) := λ v, do x ← f.eval v, y ← g.eval v, return (nat.mkpair' x y)
| (code.comp f g) := λ v, g.eval v >>= f.eval
| (code.case f g h) := λ v, match to_bits v with
  | [] := h.eval 0
  | (ff :: xs) := f.eval (of_bits xs)
  | (tt :: xs) := g.eval (of_bits xs)
end
| (code.fix f) := pfun.fix $ λ v, (f.eval v).map $ λ v',
  if (nat.unpair' v').1 = 0 then sum.inl (nat.unpair' v').2 else sum.inr (nat.unpair' v').2

end

section
/- In this section we define many useful starting codes, such as `code.id` and `code.const`
   These functions rely on the particular encoding of `nat.mkpair'`; the reason for this is to minimize
   the number of primitive functions we need to put in `code`. Specifically, they take advantage of the fact
   that we can split a sequence of bits into a tuple where the first entry has constant size by adding
   some constant number of "header" bits. -/

open code
local infixr `∘`:90 := comp

def zero : code := fst ∘ (bit tt)
@[simp] lemma zero_apply (n : ℕ) : zero.eval n = part.some 0 :=
by simp [zero, nat.bit, ← nat.mkpair'_zero]

def one : code := bit tt ∘ zero
@[simp] lemma one_apply (n : ℕ) : one.eval n = part.some 1 :=
by simp [one, nat.bit]

protected def code.id : code := snd ∘ (bit tt)
@[simp] lemma id_apply (n : ℕ) : code.id.eval n = part.some n :=
by simp [code.id, nat.bit, ← nat.mkpair'_zero]

def append_const : list bool → code
| [] := code.id
| (b :: bs) := bit b ∘ (append_const bs)
lemma append_const_apply' (bs : list bool) (n : ℕ) (hn : n ≠ 0 ∨ bs ∈ set.range to_bits) :
  (append_const bs).eval n = part.some (of_bits (bs ++ to_bits n)) :=
begin
  induction bs with hd tl ih, { simp [append_const], },
  specialize ih _, { cases hn, { tauto, }, exact or.inr (num.mem_to_bits_range_of_cons hn), },
  by_cases tl = [],
  { subst h, simp [num.mem_to_bits_range_iff] at hn,
    cases hn, swap, { subst hn, simp [append_const], refl, },
    cases hd; simp [append_const, ih, (show (to_bits n) ≠ [], by simpa)]; refl, },
  cases hd; simp [append_const, ih, h]; refl,
end

protected def code.const (c : ℕ) : code := (append_const (to_bits c)) ∘ zero
@[simp] lemma const_apply (c n : ℕ) : (code.const c).eval n = part.some c :=
begin
  simp only [code.const, code.eval, zero_apply, part.bind_eq_bind, part.bind_some],
  rw append_const_apply', { simp, },
  right, simp,
end

/-- Clamps a natural number to {0, 1}. Returns `0` if the input is `0` and `1` otherwise -/
def to_bit : code := case one one zero
@[simp] lemma to_bit_apply (n : ℕ) : to_bit.eval n = part.some (if n = 0 then 0 else 1) :=
begin
  have : to_bits n = [] ↔ n = 0 := by simp,
  simp only [to_bit, code.eval],
  rcases (to_bits n) with _|_|_,
  all_goals { intro, simp * at *, },
end

/-- Returns the right shift of a number `n` -/
def tail : code := snd ∘ (bit ff) ∘ (bit tt) ∘ (bit tt)
@[simp] lemma tail_apply_bit1 (n : ℕ) : tail.eval (bit1 n) = part.some n :=
by simp [tail, nat.bit, ← nat.mkpair'_one]
@[simp] lemma tail_apply_bit0 (n : ℕ) : tail.eval (bit0 n) = part.some n :=
begin
  cases n, { simp [tail], dec_trivial, },
  simp [tail, nat.unpair', nat.bit, num.bit0_of_bit0, num.bit1_of_bit1, nat.unpair',
    list.unpair, list.unpair', list.take_while, list.after],
end

/-- Evaluates `f` and `g` on the input;, then appends the bit `to_bit f` to `g` -/
def cons_bit (f g : code) : code := (case (tail ∘ tail) (bit ff) zero) ∘ (pair (to_bit ∘ f) g)
@[simp] lemma cons_bit_apply (f g : code) (n : ℕ) :
  (cons_bit f g).eval n = f.eval n >>= (λ r, (g.eval n).map (if r = 0 then bit0 else bit1)) :=
begin
  simp only [cons_bit, code.eval],
  cases (f.eval n) with d₁ v₁, by_cases h₁ : d₁, swap, { simp [h₁], }, simp only [h₁, part.mk_some],
  cases (g.eval n) with d₂ v₂, by_cases h₂ : d₂, swap, { simp [h₂], }, simp only [h₂, part.mk_some],
  cases v₁ h₁,
  { simp [nat.mkpair'_zero, nat.bit], },
  { simp [nat.mkpair'_one, nat.bit, num.bit0_of_bit0, num.bit1_of_bit1], }
end

protected def code.ite (c f g : code) : code :=
(case f g f) ∘ (cons_bit c code.id)
@[simp] lemma code_ite_apply (c f g : code) (n : ℕ) :
  (code.ite c f g).eval n = (c.eval n) >>= λ r, if r = 0 then f.eval n else g.eval n :=
begin
  simp only [code.ite, code.eval, cons_bit_apply, id_apply],
  cases (c.eval n) with d₁ v₁, by_cases h₁ : d₁, swap, { simp [h₁], }, simp only [h₁, part.mk_some],
  cases v₁ h₁,
  { cases n; simp, }, simp,
end

end


import data.nat.choose
import data.finset

import config
import etv

import main.defs
import main.lemmas
import main.induction_step

open_locale classical
noncomputable theory

variables {α : Type*} [linear_order α] (C : config α)

open order_dual

theorem config.mirror_main_goal (n : ℕ) :
  C.main_goal n → C.mirror.main_goal n :=
begin
  intros h Sm hSm cap4_free cup_free,
  have eq_S : Sm.of_mirror.mirror = Sm := finset.of_mirror_mirror,
  rw ←eq_S at ⊢ hSm cap4_free cup_free,
  set S := Sm.of_mirror,
  rw finset.mirror_card at hSm,
  rw mirror.has_ncap at cap4_free,
  rw mirror.has_ncup at cup_free,
  have goal := h S hSm cap4_free cup_free,
  rcases goal with ⟨p, q, r, s, interweave⟩,
  existsi [to_dual s, to_dual r, to_dual q, to_dual p],
  rw mirror.has_interweaved_laced,
  exact interweave,
end

theorem config.main_lemma (n : ℕ) : C.main_goal n :=
begin
  induction n with n ih,
  { intros S hS cap4_free cup_free, simp at hS,
    set Sl := S.sort (≤) with def_Sl,
    have Sl_card : 3 ≤ Sl.length := by rw def_Sl; simp; exact hS,
    have mem_Sl : ∀ {a : α}, a ∈ Sl ↔ a ∈ S := begin
      intros a, rw def_Sl, exact finset.mem_sort (≤) end,
    -- Take three elements of S
    rcases list.take_head3 Sl_card with ⟨a, b, c, Sl', eq_Sl⟩,
    have abc_mem : a ∈ S ∧ b ∈ S ∧ c ∈ S := by
      rw [←mem_Sl, ←mem_Sl, ←mem_Sl]; rw eq_Sl; simp,
    have abc_lt : a < b ∧ b < c := begin
      have sorted := S.sort_sorted_lt,
      rw [←def_Sl, eq_Sl] at sorted,
      simp at sorted, tauto, end,
    refine ⟨a, b, b, c, _, ⟨_, _⟩⟩,
    exact ⟨abc_lt.left, le_refl b, abc_lt.right⟩,
    use [1, 1, [a], [a, b], [b]], simp, tauto,
    use [1, 1, [b], [b, c], [c]], simp, tauto, },
  { intros S hS cap4_free cup_free,
    by_cases join_n3_n2 : C.has_join (n+3) (n+2) S, swap,
    { apply C.main_induction_wlog; assumption, },
    by_cases join_n2_n3 : C.has_join (n+2) (n+3) S, swap,
    { rw ←finset.mirror_card at hS,
      rw ←mirror.has_join at join_n2_n3,
      rw ←mirror.has_ncap at cap4_free,
      rw ←mirror.has_ncup at cup_free,
      have mirrored_goal := C.mirror.main_induction_wlog n
        (C.mirror_main_goal n ih)
        S.mirror join_n2_n3 hS cap4_free cup_free,
      rcases mirrored_goal with ⟨sm, rm, qm, pm, mgoal⟩,
      have eq_p := pm.to_dual_of_dual, set p := pm.of_dual,
      have eq_q := qm.to_dual_of_dual, set q := qm.of_dual,
      have eq_r := rm.to_dual_of_dual, set r := rm.of_dual,
      have eq_s := sm.to_dual_of_dual, set s := sm.of_dual,
      existsi [p, q, r, s],
      rw [←eq_p, ←eq_q, ←eq_r, ←eq_s, mirror.has_interweaved_laced] at mgoal,
      assumption, },
    apply C.join_n2_n3_join_n3_n2; assumption, },
end

theorem main (n : ℕ) (C : config α)
  (S : finset α) (hS : nat.choose (n+2) 2 + 2 ≤ S.card) :
  C.has_ncap 4 S ∨ C.has_ngon (n+3) S :=
begin
  by_cases has_cap4 : C.has_ncap 4 S, left, exact has_cap4,
  by_cases has_cup : C.has_ncup (n+3) S,
  { right, apply ncup_is_ngon _ has_cup, dec_trivial, },
  rcases C.main_lemma n S hS has_cap4 has_cup with ⟨p, q, r, s, laced⟩,
  right, exact C.has_interweaved_laced_has_ngon
    has_cap4 laced,
end


/-
Copyright (c) 2020 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.lie.basic
import Mathlib.algebra.ring_quot
import Mathlib.linear_algebra.tensor_algebra
import Mathlib.PostPort

universes u₁ u₂ u₃

namespace Mathlib

/-!
# Universal enveloping algebra

Given a commutative ring `R` and a Lie algebra `L` over `R`, we construct the universal
enveloping algebra of `L`, together with its universal property.

## Main definitions

  * `universal_enveloping_algebra`: the universal enveloping algebra, endowed with an
    `R`-algebra structure.
  * `universal_enveloping_algebra.ι`: the Lie algebra morphism from `L` to its universal
    enveloping algebra.
  * `universal_enveloping_algebra.lift`: given an associative algebra `A`, together with a Lie
    algebra morphism `f : L →ₗ⁅R⁆ A`, `lift R L f : universal_enveloping_algebra R L →ₐ[R] A` is the
    unique morphism of algebras through which `f` factors.
  * `universal_enveloping_algebra.ι_comp_lift`: states that the lift of a morphism is indeed part
    of a factorisation.
  * `universal_enveloping_algebra.lift_unique`: states that lifts of morphisms are indeed unique.
  * `universal_enveloping_algebra.hom_ext`: a restatement of `lift_unique` as an extensionality
    lemma.

## Tags

lie algebra, universal enveloping algebra, tensor algebra
-/

namespace universal_enveloping_algebra

/-- The quotient by the ideal generated by this relation is the universal enveloping algebra.

Note that we have avoided using the more natural expression:
| lie_compat (x y : L) : rel (ιₜ ⁅x, y⁆) ⁅ιₜ x, ιₜ y⁆
so that our construction needs only the semiring structure of the tensor algebra. -/
inductive rel (R : Type u₁) (L : Type u₂) [comm_ring R] [lie_ring L] [lie_algebra R L] : tensor_algebra R L → tensor_algebra R L → Prop
where
| lie_compat : ∀ (x y : L),
  rel R L
    (coe_fn (tensor_algebra.ι R) (has_bracket.bracket x y) +
      coe_fn (tensor_algebra.ι R) y * coe_fn (tensor_algebra.ι R) x)
    (coe_fn (tensor_algebra.ι R) x * coe_fn (tensor_algebra.ι R) y)

end universal_enveloping_algebra

/-- The universal enveloping algebra of a Lie algebra. -/
def universal_enveloping_algebra (R : Type u₁) (L : Type u₂) [comm_ring R] [lie_ring L] [lie_algebra R L] :=
  ring_quot sorry

namespace universal_enveloping_algebra

/-- The quotient map from the tensor algebra to the universal enveloping algebra as a morphism of
associative algebras. -/
def mk_alg_hom (R : Type u₁) (L : Type u₂) [comm_ring R] [lie_ring L] [lie_algebra R L] : alg_hom R (tensor_algebra R L) (universal_enveloping_algebra R L) :=
  ring_quot.mk_alg_hom R (rel R L)

/-- The natural Lie algebra morphism from a Lie algebra to its universal enveloping algebra. -/
def ι (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] : lie_algebra.morphism R L (universal_enveloping_algebra R L) :=
  lie_algebra.morphism.mk
    (linear_map.to_fun (linear_map.comp (alg_hom.to_linear_map (mk_alg_hom R L)) (tensor_algebra.ι R))) sorry sorry sorry

/-- The universal property of the universal enveloping algebra: Lie algebra morphisms into
associative algebras lift to associative algebra morphisms from the universal enveloping algebra. -/
def lift (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] : lie_algebra.morphism R L A ≃ alg_hom R (universal_enveloping_algebra R L) A :=
  equiv.mk
    (fun (f : lie_algebra.morphism R L A) =>
      coe_fn (ring_quot.lift_alg_hom R) { val := coe_fn (tensor_algebra.lift R) ↑f, property := sorry })
    (fun (F : alg_hom R (universal_enveloping_algebra R L) A) =>
      lie_algebra.morphism.comp (lie_algebra.of_associative_algebra_hom F) (ι R))
    sorry sorry

@[simp] theorem lift_symm_apply (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] (F : alg_hom R (universal_enveloping_algebra R L) A) : coe_fn (equiv.symm (lift R)) F = lie_algebra.morphism.comp (lie_algebra.of_associative_algebra_hom F) (ι R) :=
  rfl

@[simp] theorem ι_comp_lift (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] (f : lie_algebra.morphism R L A) : ⇑(coe_fn (lift R) f) ∘ ⇑(ι R) = ⇑f :=
  funext (iff.mp lie_algebra.morphism.ext_iff (equiv.symm_apply_apply (lift R) f))

@[simp] theorem lift_ι_apply (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] (f : lie_algebra.morphism R L A) (x : L) : coe_fn (coe_fn (lift R) f) (coe_fn (ι R) x) = coe_fn f x := sorry

theorem lift_unique (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] (f : lie_algebra.morphism R L A) (g : alg_hom R (universal_enveloping_algebra R L) A) : ⇑g ∘ ⇑(ι R) = ⇑f ↔ g = coe_fn (lift R) f := sorry

/-- See note [partially-applied ext lemmas]. -/
theorem hom_ext (R : Type u₁) {L : Type u₂} [comm_ring R] [lie_ring L] [lie_algebra R L] {A : Type u₃} [ring A] [algebra R A] {g₁ : alg_hom R (universal_enveloping_algebra R L) A} {g₂ : alg_hom R (universal_enveloping_algebra R L) A} (h : ⇑g₁ ∘ ⇑(ι R) = ⇑g₂ ∘ ⇑(ι R)) : g₁ = g₂ := sorry


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin

! This file was ported from Lean 3 source module field_theory.chevalley_warning
! leanprover-community/mathlib commit e001509c11c4d0f549d91d89da95b4a0b43c714f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.FieldTheory.Finite.Basic

/-!
# The Chevalley–Warning theorem

This file contains a proof of the Chevalley–Warning theorem.
Throughout most of this file, `K` denotes a finite field
and `q` is notation for the cardinality of `K`.

## Main results

1. Let `f` be a multivariate polynomial in finitely many variables (`X s`, `s : σ`)
   such that the total degree of `f` is less than `(q-1)` times the cardinality of `σ`.
   Then the evaluation of `f` on all points of `σ → K` (aka `K^σ`) sums to `0`.
   (`sum_eval_eq_zero`)
2. The Chevalley–Warning theorem (`char_dvd_card_solutions_of_sum_lt`).
   Let `f i` be a finite family of multivariate polynomials
   in finitely many variables (`X s`, `s : σ`) such that
   the sum of the total degrees of the `f i` is less than the cardinality of `σ`.
   Then the number of common solutions of the `f i`
   is divisible by the characteristic of `K`.

## Notation

- `K` is a finite field
- `q` is notation for the cardinality of `K`
- `σ` is the indexing type for the variables of a multivariate polynomial ring over `K`

-/

universe u v

open BigOperators

section FiniteField

open MvPolynomial

open Function hiding eval

open Finset FiniteField

variable {K σ ι : Type _} [Fintype K] [Field K] [Fintype σ] [DecidableEq σ]

-- mathport name: exprq
local notation "q" => Fintype.card K

theorem MvPolynomial.sum_eval_eq_zero (f : MvPolynomial σ K)
    (h : f.totalDegree < (q - 1) * Fintype.card σ) : (∑ x, eval x f) = 0 :=
  by
  haveI : DecidableEq K := Classical.decEq K
  calc
    (∑ x, eval x f) = ∑ x : σ → K, ∑ d in f.support, f.coeff d * ∏ i, x i ^ d i := by
      simp only [eval_eq']
    _ = ∑ d in f.support, ∑ x : σ → K, f.coeff d * ∏ i, x i ^ d i := sum_comm
    _ = 0 := sum_eq_zero _

  intro d hd
  obtain ⟨i, hi⟩ : ∃ i, d i < q - 1; exact f.exists_degree_lt (q - 1) h hd
  calc
    (∑ x : σ → K, f.coeff d * ∏ i, x i ^ d i) = f.coeff d * ∑ x : σ → K, ∏ i, x i ^ d i :=
      mul_sum.symm
    _ = 0 := (mul_eq_zero.mpr ∘ Or.inr) _

  calc
    (∑ x : σ → K, ∏ i, x i ^ d i) =
        ∑ (x₀ : { j // j ≠ i } → K) (x : { x : σ → K // x ∘ coe = x₀ }), ∏ j, (x : σ → K) j ^ d j :=
      (Fintype.sum_fiberwise _ _).symm
    _ = 0 := Fintype.sum_eq_zero _ _

  intro x₀
  let e : K ≃ { x // x ∘ coe = x₀ } := (Equiv.subtypeEquivCodomain _).symm
  calc
    (∑ x : { x : σ → K // x ∘ coe = x₀ }, ∏ j, (x : σ → K) j ^ d j) =
        ∑ a : K, ∏ j : σ, (e a : σ → K) j ^ d j :=
      (e.sum_comp _).symm
    _ = ∑ a : K, (∏ j, x₀ j ^ d j) * a ^ d i := (Fintype.sum_congr _ _ _)
    _ = (∏ j, x₀ j ^ d j) * ∑ a : K, a ^ d i := by rw [mul_sum]
    _ = 0 := by rw [sum_pow_lt_card_sub_one _ hi, MulZeroClass.mul_zero]

  intro a
  let e' : Sum { j // j = i } { j // j ≠ i } ≃ σ := Equiv.sumCompl _
  letI : Unique { j // j = i } :=
    { default := ⟨i, rfl⟩
      uniq := fun ⟨j, h⟩ => Subtype.val_injective h }
  calc
    (∏ j : σ, (e a : σ → K) j ^ d j) =
        (e a : σ → K) i ^ d i * ∏ j : { j // j ≠ i }, (e a : σ → K) j ^ d j :=
      by
      rw [← e'.prod_comp, Fintype.prod_sum_type, univ_unique, prod_singleton]
      rfl
    _ = a ^ d i * ∏ j : { j // j ≠ i }, (e a : σ → K) j ^ d j := by
      rw [Equiv.subtypeEquivCodomain_symm_apply_eq]
    _ = a ^ d i * ∏ j, x₀ j ^ d j := (congr_arg _ (Fintype.prod_congr _ _ _))
    -- see below
        _ =
        (∏ j, x₀ j ^ d j) * a ^ d i :=
      mul_comm _ _

  · -- the remaining step of the calculation above
    rintro ⟨j, hj⟩
    show (e a : σ → K) j ^ d j = x₀ ⟨j, hj⟩ ^ d j
    rw [Equiv.subtypeEquivCodomain_symm_apply_ne]
#align mv_polynomial.sum_eval_eq_zero MvPolynomial.sum_eval_eq_zero

variable [DecidableEq K] (p : ℕ) [CharP K p]

/-- The **Chevalley–Warning theorem**, finitary version.
Let `(f i)` be a finite family of multivariate polynomials
in finitely many variables (`X s`, `s : σ`) over a finite field of characteristic `p`.
Assume that the sum of the total degrees of the `f i` is less than the cardinality of `σ`.
Then the number of common solutions of the `f i` is divisible by `p`. -/
theorem char_dvd_card_solutions_of_sum_lt {s : Finset ι} {f : ι → MvPolynomial σ K}
    (h : (∑ i in s, (f i).totalDegree) < Fintype.card σ) :
    p ∣ Fintype.card { x : σ → K // ∀ i ∈ s, eval x (f i) = 0 } :=
  by
  have hq : 0 < q - 1 := by
    rw [← Fintype.card_units, Fintype.card_pos_iff]
    exact ⟨1⟩
  let S : Finset (σ → K) := { x ∈ univ | ∀ i ∈ s, eval x (f i) = 0 }
  have hS : ∀ x : σ → K, x ∈ S ↔ ∀ i : ι, i ∈ s → eval x (f i) = 0 :=
    by
    intro x
    simp only [S, true_and_iff, sep_def, mem_filter, mem_univ]
  /- The polynomial `F = ∏ i in s, (1 - (f i)^(q - 1))` has the nice property
    that it takes the value `1` on elements of `{x : σ → K // ∀ i ∈ s, (f i).eval x = 0}`
    while it is `0` outside that locus.
    Hence the sum of its values is equal to the cardinality of
    `{x : σ → K // ∀ i ∈ s, (f i).eval x = 0}` modulo `p`. -/
  let F : MvPolynomial σ K := ∏ i in s, 1 - f i ^ (q - 1)
  have hF : ∀ x, eval x F = if x ∈ S then 1 else 0 :=
    by
    intro x
    calc
      eval x F = ∏ i in s, eval x (1 - f i ^ (q - 1)) := eval_prod s _ x
      _ = if x ∈ S then 1 else 0 := _

    simp only [(eval x).map_sub, (eval x).map_pow, (eval x).map_one]
    split_ifs with hx hx
    · apply Finset.prod_eq_one
      intro i hi
      rw [hS] at hx
      rw [hx i hi, zero_pow hq, sub_zero]
    · obtain ⟨i, hi, hx⟩ : ∃ i : ι, i ∈ s ∧ eval x (f i) ≠ 0 := by
        simpa only [hS, not_forall, not_imp] using hx
      apply Finset.prod_eq_zero hi
      rw [pow_card_sub_one_eq_one (eval x (f i)) hx, sub_self]
  -- In particular, we can now show:
  have key : (∑ x, eval x F) = Fintype.card { x : σ → K // ∀ i ∈ s, eval x (f i) = 0 }
  rw [Fintype.card_of_subtype S hS, card_eq_sum_ones, Nat.cast_sum, Nat.cast_one, ←
    Fintype.sum_extend_by_zero S, sum_congr rfl fun x hx => hF x]
  -- With these preparations under our belt, we will approach the main goal.
  show p ∣ Fintype.card { x // ∀ i : ι, i ∈ s → eval x (f i) = 0 }
  rw [← CharP.cast_eq_zero_iff K, ← key]
  show (∑ x, eval x F) = 0
  -- We are now ready to apply the main machine, proven before.
  apply F.sum_eval_eq_zero
  -- It remains to verify the crucial assumption of this machine
  show F.total_degree < (q - 1) * Fintype.card σ
  calc
    F.total_degree ≤ ∑ i in s, (1 - f i ^ (q - 1)).totalDegree := total_degree_finset_prod s _
    _ ≤ ∑ i in s, (q - 1) * (f i).totalDegree := (sum_le_sum fun i hi => _)
    -- see ↓
        _ =
        (q - 1) * ∑ i in s, (f i).totalDegree :=
      mul_sum.symm
    _ < (q - 1) * Fintype.card σ := by rwa [mul_lt_mul_left hq]

  -- Now we prove the remaining step from the preceding calculation
  show (1 - f i ^ (q - 1)).totalDegree ≤ (q - 1) * (f i).totalDegree
  calc
    (1 - f i ^ (q - 1)).totalDegree ≤
        max (1 : MvPolynomial σ K).totalDegree (f i ^ (q - 1)).totalDegree :=
      total_degree_sub _ _
    _ ≤ (f i ^ (q - 1)).totalDegree := by simp only [max_eq_right, Nat.zero_le, total_degree_one]
    _ ≤ (q - 1) * (f i).totalDegree := total_degree_pow _ _

#align char_dvd_card_solutions_of_sum_lt char_dvd_card_solutions_of_sum_lt

/-- The **Chevalley–Warning theorem**, fintype version.
Let `(f i)` be a finite family of multivariate polynomials
in finitely many variables (`X s`, `s : σ`) over a finite field of characteristic `p`.
Assume that the sum of the total degrees of the `f i` is less than the cardinality of `σ`.
Then the number of common solutions of the `f i` is divisible by `p`. -/
theorem char_dvd_card_solutions_of_fintype_sum_lt [Fintype ι] {f : ι → MvPolynomial σ K}
    (h : (∑ i, (f i).totalDegree) < Fintype.card σ) :
    p ∣ Fintype.card { x : σ → K // ∀ i, eval x (f i) = 0 } := by
  simpa using char_dvd_card_solutions_of_sum_lt p h
#align char_dvd_card_solutions_of_fintype_sum_lt char_dvd_card_solutions_of_fintype_sum_lt

/-- The **Chevalley–Warning theorem**, unary version.
Let `f` be a multivariate polynomial in finitely many variables (`X s`, `s : σ`)
over a finite field of characteristic `p`.
Assume that the total degree of `f` is less than the cardinality of `σ`.
Then the number of solutions of `f` is divisible by `p`.
See `char_dvd_card_solutions_of_sum_lt` for a version that takes a family of polynomials `f i`. -/
theorem char_dvd_card_solutions {f : MvPolynomial σ K} (h : f.totalDegree < Fintype.card σ) :
    p ∣ Fintype.card { x : σ → K // eval x f = 0 } :=
  by
  let F : Unit → MvPolynomial σ K := fun _ => f
  have : (∑ i : Unit, (F i).totalDegree) < Fintype.card σ := h
  simpa only [F, Fintype.univ_punit, forall_eq, mem_singleton] using
    char_dvd_card_solutions_of_sum_lt p this
#align char_dvd_card_solutions char_dvd_card_solutions

/-- The **Chevalley–Warning theorem**, binary version.
Let `f₁`, `f₂` be two multivariate polynomials in finitely many variables (`X s`, `s : σ`) over a
finite field of characteristic `p`.
Assume that the sum of the total degrees of `f₁` and `f₂` is less than the cardinality of `σ`.
Then the number of common solutions of the `f₁` and `f₂` is divisible by `p`. -/
theorem char_dvd_card_solutions_of_add_lt {f₁ f₂ : MvPolynomial σ K}
    (h : f₁.totalDegree + f₂.totalDegree < Fintype.card σ) :
    p ∣ Fintype.card { x : σ → K // eval x f₁ = 0 ∧ eval x f₂ = 0 } :=
  by
  let F : Bool → MvPolynomial σ K := fun b => cond b f₂ f₁
  have : (∑ b : Bool, (F b).totalDegree) < Fintype.card σ := (add_comm _ _).trans_lt h
  simpa only [F, Bool.forall_bool] using char_dvd_card_solutions_of_fintype_sum_lt p this
#align char_dvd_card_solutions_of_add_lt char_dvd_card_solutions_of_add_lt

end FiniteField


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

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

/-!
# Finite intervals in `fin n`

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

This file proves that `fin n` is a `locally_finite_order` and calculates the cardinality of its
intervals as finsets and fintypes.
-/

open Finset Fin Function

open BigOperators

variable (n : ℕ)

instance : LocallyFiniteOrder (Fin n) :=
  OrderIso.locallyFiniteOrder Fin.orderIsoSubtype

instance : LocallyFiniteOrderBot (Fin n) :=
  OrderIso.locallyFiniteOrderBot Fin.orderIsoSubtype

instance : ∀ n, LocallyFiniteOrderTop (Fin n)
  | 0 => IsEmpty.toLocallyFiniteOrderTop
  | n + 1 => inferInstance

namespace Fin

variable {n} (a b : Fin n)

/- warning: fin.Icc_eq_finset_subtype -> Fin.Icc_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b) (Finset.fin n (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b) (Finset.fin n (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Icc_eq_finset_subtype Fin.Icc_eq_finset_subtypeₓ'. -/
theorem Icc_eq_finset_subtype : Icc a b = (Icc (a : ℕ) b).Fin n :=
  rfl
#align fin.Icc_eq_finset_subtype Fin.Icc_eq_finset_subtype

/- warning: fin.Ico_eq_finset_subtype -> Fin.Ico_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b) (Finset.fin n (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b) (Finset.fin n (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Ico_eq_finset_subtype Fin.Ico_eq_finset_subtypeₓ'. -/
theorem Ico_eq_finset_subtype : Ico a b = (Ico (a : ℕ) b).Fin n :=
  rfl
#align fin.Ico_eq_finset_subtype Fin.Ico_eq_finset_subtype

/- warning: fin.Ioc_eq_finset_subtype -> Fin.Ioc_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b) (Finset.fin n (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b) (Finset.fin n (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Ioc_eq_finset_subtype Fin.Ioc_eq_finset_subtypeₓ'. -/
theorem Ioc_eq_finset_subtype : Ioc a b = (Ioc (a : ℕ) b).Fin n :=
  rfl
#align fin.Ioc_eq_finset_subtype Fin.Ioc_eq_finset_subtype

/- warning: fin.Ioo_eq_finset_subtype -> Fin.Ioo_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b) (Finset.fin n (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b) (Finset.fin n (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Ioo_eq_finset_subtype Fin.Ioo_eq_finset_subtypeₓ'. -/
theorem Ioo_eq_finset_subtype : Ioo a b = (Ioo (a : ℕ) b).Fin n :=
  rfl
#align fin.Ioo_eq_finset_subtype Fin.Ioo_eq_finset_subtype

/- warning: fin.map_subtype_embedding_Icc -> Fin.map_valEmbedding_Icc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Icc Fin.map_valEmbedding_Iccₓ'. -/
@[simp]
theorem map_valEmbedding_Icc : (Icc a b).map Fin.valEmbedding = Icc a b := by
  simp [Icc_eq_finset_subtype, Finset.fin, Finset.map_map, Icc_filter_lt_of_lt_right]
#align fin.map_subtype_embedding_Icc Fin.map_valEmbedding_Icc

/- warning: fin.map_subtype_embedding_Ico -> Fin.map_valEmbedding_Ico is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Ico Fin.map_valEmbedding_Icoₓ'. -/
@[simp]
theorem map_valEmbedding_Ico : (Ico a b).map Fin.valEmbedding = Ico a b := by
  simp [Ico_eq_finset_subtype, Finset.fin, Finset.map_map]
#align fin.map_subtype_embedding_Ico Fin.map_valEmbedding_Ico

/- warning: fin.map_subtype_embedding_Ioc -> Fin.map_valEmbedding_Ioc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Ioc Fin.map_valEmbedding_Iocₓ'. -/
@[simp]
theorem map_valEmbedding_Ioc : (Ioc a b).map Fin.valEmbedding = Ioc a b := by
  simp [Ioc_eq_finset_subtype, Finset.fin, Finset.map_map, Ioc_filter_lt_of_lt_right]
#align fin.map_subtype_embedding_Ioc Fin.map_valEmbedding_Ioc

/- warning: fin.map_subtype_embedding_Ioo -> Fin.map_valEmbedding_Ioo is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Ioo Fin.map_valEmbedding_Iooₓ'. -/
@[simp]
theorem map_valEmbedding_Ioo : (Ioo a b).map Fin.valEmbedding = Ioo a b := by
  simp [Ioo_eq_finset_subtype, Finset.fin, Finset.map_map]
#align fin.map_subtype_embedding_Ioo Fin.map_valEmbedding_Ioo

/- warning: fin.card_Icc -> Fin.card_Icc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Fin.val n b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_Icc Fin.card_Iccₓ'. -/
@[simp]
theorem card_Icc : (Icc a b).card = b + 1 - a := by
  rw [← Nat.card_Icc, ← map_subtype_embedding_Icc, card_map]
#align fin.card_Icc Fin.card_Icc

/- warning: fin.card_Ico -> Fin.card_Ico is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_Ico Fin.card_Icoₓ'. -/
@[simp]
theorem card_Ico : (Ico a b).card = b - a := by
  rw [← Nat.card_Ico, ← map_subtype_embedding_Ico, card_map]
#align fin.card_Ico Fin.card_Ico

/- warning: fin.card_Ioc -> Fin.card_Ioc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_Ioc Fin.card_Iocₓ'. -/
@[simp]
theorem card_Ioc : (Ioc a b).card = b - a := by
  rw [← Nat.card_Ioc, ← map_subtype_embedding_Ioc, card_map]
#align fin.card_Ioc Fin.card_Ioc

/- warning: fin.card_Ioo -> Fin.card_Ioo is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a)) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align fin.card_Ioo Fin.card_Iooₓ'. -/
@[simp]
theorem card_Ioo : (Ioo a b).card = b - a - 1 := by
  rw [← Nat.card_Ioo, ← map_subtype_embedding_Ioo, card_map]
#align fin.card_Ioo Fin.card_Ioo

/- warning: fin.card_fintype_Icc -> Fin.card_fintypeIcc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a b)) (Set.fintypeIcc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Icc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a b)) (Set.fintypeIcc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Fin.val n b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Icc Fin.card_fintypeIccₓ'. -/
@[simp]
theorem card_fintypeIcc : Fintype.card (Set.Icc a b) = b + 1 - a := by
  rw [← card_Icc, Fintype.card_ofFinset]
#align fin.card_fintype_Icc Fin.card_fintypeIcc

/- warning: fin.card_fintype_Ico -> Fin.card_fintypeIco is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a b)) (Set.fintypeIco.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Ico.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a b)) (Set.fintypeIco.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Ico Fin.card_fintypeIcoₓ'. -/
@[simp]
theorem card_fintypeIco : Fintype.card (Set.Ico a b) = b - a := by
  rw [← card_Ico, Fintype.card_ofFinset]
#align fin.card_fintype_Ico Fin.card_fintypeIco

/- warning: fin.card_fintype_Ioc -> Fin.card_fintypeIoc is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a b)) (Set.fintypeIoc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Ioc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a b)) (Set.fintypeIoc.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Ioc Fin.card_fintypeIocₓ'. -/
@[simp]
theorem card_fintypeIoc : Fintype.card (Set.Ioc a b) = b - a := by
  rw [← card_Ioc, Fintype.card_ofFinset]
#align fin.card_fintype_Ioc Fin.card_fintypeIoc

/- warning: fin.card_fintype_Ioo -> Fin.card_fintypeIoo is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a b)) (Set.fintypeIoo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrder n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a)) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {n : Nat} (a : Fin n) (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Ioo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a b)) (Set.fintypeIoo.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderFinToPreorderInstPartialOrderFin n) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val n b) (Fin.val n a)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Ioo Fin.card_fintypeIooₓ'. -/
@[simp]
theorem card_fintypeIoo : Fintype.card (Set.Ioo a b) = b - a - 1 := by
  rw [← card_Ioo, Fintype.card_ofFinset]
#align fin.card_fintype_Ioo Fin.card_fintypeIoo

/- warning: fin.Ici_eq_finset_subtype -> Fin.Ici_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a) (Finset.fin n (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) n))
but is expected to have type
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a) (Finset.fin n (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) n))
Case conversion may be inaccurate. Consider using '#align fin.Ici_eq_finset_subtype Fin.Ici_eq_finset_subtypeₓ'. -/
theorem Ici_eq_finset_subtype : Ici a = (Icc (a : ℕ) n).Fin n :=
  by
  ext
  simp
#align fin.Ici_eq_finset_subtype Fin.Ici_eq_finset_subtype

/- warning: fin.Ioi_eq_finset_subtype -> Fin.Ioi_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a) (Finset.fin n (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) n))
but is expected to have type
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a) (Finset.fin n (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) n))
Case conversion may be inaccurate. Consider using '#align fin.Ioi_eq_finset_subtype Fin.Ioi_eq_finset_subtypeₓ'. -/
theorem Ioi_eq_finset_subtype : Ioi a = (Ioc (a : ℕ) n).Fin n :=
  by
  ext
  simp
#align fin.Ioi_eq_finset_subtype Fin.Ioi_eq_finset_subtype

/- warning: fin.Iic_eq_finset_subtype -> Fin.Iic_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b) (Finset.fin n (Finset.Iic.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.orderBot Nat.locallyFiniteOrder) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b) (Finset.fin n (Finset.Iic.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) Nat.orderBot instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Iic_eq_finset_subtype Fin.Iic_eq_finset_subtypeₓ'. -/
theorem Iic_eq_finset_subtype : Iic b = (Iic (b : ℕ)).Fin n :=
  rfl
#align fin.Iic_eq_finset_subtype Fin.Iic_eq_finset_subtype

/- warning: fin.Iio_eq_finset_subtype -> Fin.Iio_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b) (Finset.fin n (Finset.Iio.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.orderBot Nat.locallyFiniteOrder) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} (Fin n)) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b) (Finset.fin n (Finset.Iio.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) Nat.orderBot instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring) (Fin.val n b)))
Case conversion may be inaccurate. Consider using '#align fin.Iio_eq_finset_subtype Fin.Iio_eq_finset_subtypeₓ'. -/
theorem Iio_eq_finset_subtype : Iio b = (Iio (b : ℕ)).Fin n :=
  rfl
#align fin.Iio_eq_finset_subtype Fin.Iio_eq_finset_subtype

/- warning: fin.map_subtype_embedding_Ici -> Fin.map_valEmbedding_Ici is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Ici Fin.map_valEmbedding_Iciₓ'. -/
@[simp]
theorem map_valEmbedding_Ici : (Ici a).map Fin.valEmbedding = Icc a (n - 1) :=
  by
  ext x
  simp only [exists_prop, embedding.coe_subtype, mem_Ici, mem_map, mem_Icc]
  constructor
  · rintro ⟨x, hx, rfl⟩
    exact ⟨hx, le_tsub_of_add_le_right <| x.2⟩
  cases n
  · exact Fin.elim0 a
  · exact fun hx => ⟨⟨x, Nat.lt_succ_iff.2 hx.2⟩, hx.1, rfl⟩
#align fin.map_subtype_embedding_Ici Fin.map_valEmbedding_Ici

/- warning: fin.map_subtype_embedding_Ioi -> Fin.map_valEmbedding_Ioi is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (Fin.val n a) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Ioi Fin.map_valEmbedding_Ioiₓ'. -/
@[simp]
theorem map_valEmbedding_Ioi : (Ioi a).map Fin.valEmbedding = Ioc a (n - 1) :=
  by
  ext x
  simp only [exists_prop, embedding.coe_subtype, mem_Ioi, mem_map, mem_Ioc]
  constructor
  · rintro ⟨x, hx, rfl⟩
    exact ⟨hx, le_tsub_of_add_le_right <| x.2⟩
  cases n
  · exact Fin.elim0 a
  · exact fun hx => ⟨⟨x, Nat.lt_succ_iff.2 hx.2⟩, hx.1, rfl⟩
#align fin.map_subtype_embedding_Ioi Fin.map_valEmbedding_Ioi

/- warning: fin.map_subtype_embedding_Iic -> Fin.map_valEmbedding_Iic is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) (Finset.Iic.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.orderBot Nat.locallyFiniteOrder) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (Finset.Iic.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) Nat.orderBot instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Iic Fin.map_valEmbedding_Iicₓ'. -/
@[simp]
theorem map_valEmbedding_Iic : (Iic b).map Fin.valEmbedding = Iic b := by
  simp [Iic_eq_finset_subtype, Finset.fin, Finset.map_map, Iic_filter_lt_of_lt_right]
#align fin.map_subtype_embedding_Iic Fin.map_valEmbedding_Iic

/- warning: fin.map_subtype_embedding_Iio -> Fin.map_valEmbedding_Iio is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) (Finset.Iio.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.orderBot Nat.locallyFiniteOrder) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Fin n) Nat (Fin.valEmbedding n) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (Finset.Iio.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) Nat.orderBot instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring) (Fin.val n b))
Case conversion may be inaccurate. Consider using '#align fin.map_subtype_embedding_Iio Fin.map_valEmbedding_Iioₓ'. -/
@[simp]
theorem map_valEmbedding_Iio : (Iio b).map Fin.valEmbedding = Iio b := by
  simp [Iio_eq_finset_subtype, Finset.fin, Finset.map_map]
#align fin.map_subtype_embedding_Iio Fin.map_valEmbedding_Iio

/- warning: fin.card_Ici -> Fin.card_Ici is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (Fin.val n a)))
Case conversion may be inaccurate. Consider using '#align fin.card_Ici Fin.card_Iciₓ'. -/
@[simp]
theorem card_Ici : (Ici a).card = n - a := by
  cases n
  · exact Fin.elim0 a
  rw [← card_map, map_subtype_embedding_Ici, Nat.card_Icc]
  rfl
#align fin.card_Ici Fin.card_Ici

/- warning: fin.card_Ioi -> Fin.card_Ioi is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Fin.val n a)))
Case conversion may be inaccurate. Consider using '#align fin.card_Ioi Fin.card_Ioiₓ'. -/
@[simp]
theorem card_Ioi : (Ioi a).card = n - 1 - a := by
  rw [← card_map, map_subtype_embedding_Ioi, Nat.card_Ioc]
#align fin.card_Ioi Fin.card_Ioi

/- warning: fin.card_Iic -> Fin.card_Iic is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Fin.val n b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align fin.card_Iic Fin.card_Iicₓ'. -/
@[simp]
theorem card_Iic : (Iic b).card = b + 1 := by
  rw [← Nat.card_Iic b, ← map_subtype_embedding_Iic, card_map]
#align fin.card_Iic Fin.card_Iic

/- warning: fin.card_Iio -> Fin.card_Iio is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Finset.card.{0} (Fin n) (Finset.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (Fin.val n b)
Case conversion may be inaccurate. Consider using '#align fin.card_Iio Fin.card_Iioₓ'. -/
@[simp]
theorem card_Iio : (Iio b).card = b := by
  rw [← Nat.card_Iio b, ← map_subtype_embedding_Iio, card_map]
#align fin.card_Iio Fin.card_Iio

/- warning: fin.card_fintype_Ici -> Fin.card_fintypeIci is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a)) (Set.fintypeIci.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Ici.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a)) (Set.fintypeIci.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (Fin.val n a)))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Ici Fin.card_fintypeIciₓ'. -/
@[simp]
theorem card_fintypeIci : Fintype.card (Set.Ici a) = n - a := by
  rw [Fintype.card_ofFinset, card_Ici]
#align fin.card_fintype_Ici Fin.card_fintypeIci

/- warning: fin.card_fintype_Ioi -> Fin.card_fintypeIoi is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (a : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) a)) (Set.fintypeIoi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderTop n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) a))
but is expected to have type
  forall {n : Nat} (a : Fin n), (Fin n) -> (Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Ioi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) a)) (Set.fintypeIoi.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instForAllNatLocallyFiniteOrderTopFinToPreorderInstPartialOrderFin n) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Fin.val n a)))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Ioi Fin.card_fintypeIoiₓ'. -/
@[simp]
theorem card_fintypeIoi : Fintype.card (Set.Ioi a) = n - 1 - a := by
  rw [Fintype.card_ofFinset, card_Ioi]
#align fin.card_fintype_Ioi Fin.card_fintypeIoi

/- warning: fin.card_fintype_Iic -> Fin.card_fintypeIic is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) b)) (Set.fintypeIic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Iic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) b)) (Set.fintypeIic.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Fin.val n b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Iic Fin.card_fintypeIicₓ'. -/
@[simp]
theorem card_fintypeIic : Fintype.card (Set.Iic b) = b + 1 := by
  rw [Fintype.card_ofFinset, card_Iic]
#align fin.card_fintype_Iic Fin.card_fintypeIic

/- warning: fin.card_fintype_Iio -> Fin.card_fintypeIio is a dubious translation:
lean 3 declaration is
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} (Fin n)) Type (Set.hasCoeToSort.{0} (Fin n)) (Set.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) b)) (Set.fintypeIio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) (Fin.locallyFiniteOrderBot n) b)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin n) Nat (HasLiftT.mk.{1, 1} (Fin n) Nat (CoeTCₓ.coe.{1, 1} (Fin n) Nat (coeBase.{1, 1} (Fin n) Nat (Fin.coeToNat n)))) b)
but is expected to have type
  forall {n : Nat} (b : Fin n), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} (Fin n) (Set.Iio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) b)) (Set.fintypeIio.{0} (Fin n) (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) (instLocallyFiniteOrderBotFinToPreorderInstPartialOrderFin n) b)) (Fin.val n b)
Case conversion may be inaccurate. Consider using '#align fin.card_fintype_Iio Fin.card_fintypeIioₓ'. -/
@[simp]
theorem card_fintypeIio : Fintype.card (Set.Iio b) = b := by rw [Fintype.card_ofFinset, card_Iio]
#align fin.card_fintype_Iio Fin.card_fintypeIio

end Fin