/-
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.int.interval
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.CharZero.Lemmas
import Mathbin.Order.LocallyFinite
import Mathbin.Data.Finset.LocallyFinite

/-!
# Finite intervals of integers

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

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

open Finset Int

instance : LocallyFiniteOrder ℤ
    where
  finsetIcc a b :=
    (Finset.range (b + 1 - a).toNat).map <| Nat.castEmbedding.trans <| addLeftEmbedding a
  finsetIco a b := (Finset.range (b - a).toNat).map <| Nat.castEmbedding.trans <| addLeftEmbedding a
  finsetIoc a b :=
    (Finset.range (b - a).toNat).map <| Nat.castEmbedding.trans <| addLeftEmbedding (a + 1)
  finsetIoo a b :=
    (Finset.range (b - a - 1).toNat).map <| Nat.castEmbedding.trans <| addLeftEmbedding (a + 1)
  finset_mem_Icc a b x :=
    by
    simp_rw [mem_map, exists_prop, mem_range, Int.lt_toNat, Function.Embedding.trans_apply,
      Nat.castEmbedding_apply, addLeftEmbedding_apply]
    constructor
    · rintro ⟨a, h, rfl⟩
      rw [lt_sub_iff_add_lt, Int.lt_add_one_iff, add_comm] at h
      exact ⟨Int.le.intro rfl, h⟩
    · rintro ⟨ha, hb⟩
      use (x - a).toNat
      rw [← lt_add_one_iff] at hb
      rw [to_nat_sub_of_le ha]
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩
  finset_mem_Ico a b x :=
    by
    simp_rw [mem_map, exists_prop, mem_range, Int.lt_toNat, Function.Embedding.trans_apply,
      Nat.castEmbedding_apply, addLeftEmbedding_apply]
    constructor
    · rintro ⟨a, h, rfl⟩
      exact ⟨Int.le.intro rfl, lt_sub_iff_add_lt'.mp h⟩
    · rintro ⟨ha, hb⟩
      use (x - a).toNat
      rw [to_nat_sub_of_le ha]
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩
  finset_mem_Ioc a b x :=
    by
    simp_rw [mem_map, exists_prop, mem_range, Int.lt_toNat, Function.Embedding.trans_apply,
      Nat.castEmbedding_apply, addLeftEmbedding_apply]
    constructor
    · rintro ⟨a, h, rfl⟩
      rw [← add_one_le_iff, le_sub_iff_add_le', add_comm _ (1 : ℤ), ← add_assoc] at h
      exact ⟨Int.le.intro rfl, h⟩
    · rintro ⟨ha, hb⟩
      use (x - (a + 1)).toNat
      rw [to_nat_sub_of_le ha, ← add_one_le_iff, sub_add, add_sub_cancel]
      exact ⟨sub_le_sub_right hb _, add_sub_cancel'_right _ _⟩
  finset_mem_Ioo a b x :=
    by
    simp_rw [mem_map, exists_prop, mem_range, Int.lt_toNat, Function.Embedding.trans_apply,
      Nat.castEmbedding_apply, addLeftEmbedding_apply]
    constructor
    · rintro ⟨a, h, rfl⟩
      rw [sub_sub, lt_sub_iff_add_lt'] at h
      exact ⟨Int.le.intro rfl, h⟩
    · rintro ⟨ha, hb⟩
      use (x - (a + 1)).toNat
      rw [to_nat_sub_of_le ha, sub_sub]
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩

namespace Int

variable (a b : ℤ)

/- warning: int.Icc_eq_finset_map -> Int.Icc_eq_finset_map is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (AddCommGroupWithOne.toAddGroupWithOne.{0} Int (Ring.toAddCommGroupWithOne.{0} Int Int.ring))) (StrictOrderedSemiring.to_charZero.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))))) a)) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) a))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (Ring.toAddGroupWithOne.{0} Int Int.instRingInt)) (StrictOrderedSemiring.to_charZero.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) a)) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) a))))
Case conversion may be inaccurate. Consider using '#align int.Icc_eq_finset_map Int.Icc_eq_finset_mapₓ'. -/
theorem Icc_eq_finset_map :
    Icc a b =
      (Finset.range (b + 1 - a).toNat).map (Nat.castEmbedding.trans <| addLeftEmbedding a) :=
  rfl
#align int.Icc_eq_finset_map Int.Icc_eq_finset_map

/- warning: int.Ico_eq_finset_map -> Int.Ico_eq_finset_map is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (AddCommGroupWithOne.toAddGroupWithOne.{0} Int (Ring.toAddCommGroupWithOne.{0} Int Int.ring))) (StrictOrderedSemiring.to_charZero.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))))) a)) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (Ring.toAddGroupWithOne.{0} Int Int.instRingInt)) (StrictOrderedSemiring.to_charZero.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) a)) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))))
Case conversion may be inaccurate. Consider using '#align int.Ico_eq_finset_map Int.Ico_eq_finset_mapₓ'. -/
theorem Ico_eq_finset_map :
    Ico a b = (Finset.range (b - a).toNat).map (Nat.castEmbedding.trans <| addLeftEmbedding a) :=
  rfl
#align int.Ico_eq_finset_map Int.Ico_eq_finset_map

/- warning: int.Ioc_eq_finset_map -> Int.Ioc_eq_finset_map is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (AddCommGroupWithOne.toAddGroupWithOne.{0} Int (Ring.toAddCommGroupWithOne.{0} Int Int.ring))) (StrictOrderedSemiring.to_charZero.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))))) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) a (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (Ring.toAddGroupWithOne.{0} Int Int.instRingInt)) (StrictOrderedSemiring.to_charZero.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) a (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))))
Case conversion may be inaccurate. Consider using '#align int.Ioc_eq_finset_map Int.Ioc_eq_finset_mapₓ'. -/
theorem Ioc_eq_finset_map :
    Ioc a b =
      (Finset.range (b - a).toNat).map (Nat.castEmbedding.trans <| addLeftEmbedding (a + 1)) :=
  rfl
#align int.Ioc_eq_finset_map Int.Ioc_eq_finset_map

/- warning: int.Ioo_eq_finset_map -> Int.Ioo_eq_finset_map is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (AddCommGroupWithOne.toAddGroupWithOne.{0} Int (Ring.toAddCommGroupWithOne.{0} Int Int.ring))) (StrictOrderedSemiring.to_charZero.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (StrictOrderedRing.toStrictOrderedSemiring.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))))) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) a (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} (Finset.{0} Int) (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b) (Finset.map.{0, 0} Nat Int (Function.Embedding.trans.{1, 1, 1} Nat Int Int (Nat.castEmbedding.{0} Int (AddGroupWithOne.toAddMonoidWithOne.{0} Int (Ring.toAddGroupWithOne.{0} Int Int.instRingInt)) (StrictOrderedSemiring.to_charZero.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))) (addLeftEmbedding.{0} Int (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Int (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Int (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Int (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Int (LinearOrderedSemiring.toStrictOrderedSemiring.{0} Int (LinearOrderedCommSemiring.toLinearOrderedSemiring.{0} Int (LinearOrderedCommRing.toLinearOrderedCommSemiring.{0} Int Int.linearOrderedCommRing))))))) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) a (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))) (Finset.range (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))))
Case conversion may be inaccurate. Consider using '#align int.Ioo_eq_finset_map Int.Ioo_eq_finset_mapₓ'. -/
theorem Ioo_eq_finset_map :
    Ioo a b =
      (Finset.range (b - a - 1).toNat).map (Nat.castEmbedding.trans <| addLeftEmbedding (a + 1)) :=
  rfl
#align int.Ioo_eq_finset_map Int.Ioo_eq_finset_map

/- warning: int.card_Icc -> Int.card_Icc is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) a))
Case conversion may be inaccurate. Consider using '#align int.card_Icc Int.card_Iccₓ'. -/
@[simp]
theorem card_Icc : (Icc a b).card = (b + 1 - a).toNat :=
  by
  change (Finset.map _ _).card = _
  rw [Finset.card_map, Finset.card_range]
#align int.card_Icc Int.card_Icc

/- warning: int.card_Ico -> Int.card_Ico is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_Ico Int.card_Icoₓ'. -/
@[simp]
theorem card_Ico : (Ico a b).card = (b - a).toNat :=
  by
  change (Finset.map _ _).card = _
  rw [Finset.card_map, Finset.card_range]
#align int.card_Ico Int.card_Ico

/- warning: int.card_Ioc -> Int.card_Ioc is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_Ioc Int.card_Iocₓ'. -/
@[simp]
theorem card_Ioc : (Ioc a b).card = (b - a).toNat :=
  by
  change (Finset.map _ _).card = _
  rw [Finset.card_map, Finset.card_range]
#align int.card_Ioc Int.card_Ioc

/- warning: int.card_Ioo -> Int.card_Ioo is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Finset.card.{0} Int (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))
Case conversion may be inaccurate. Consider using '#align int.card_Ioo Int.card_Iooₓ'. -/
@[simp]
theorem card_Ioo : (Ioo a b).card = (b - a - 1).toNat :=
  by
  change (Finset.map _ _).card = _
  rw [Finset.card_map, Finset.card_range]
#align int.card_Ioo Int.card_Ioo

/- warning: int.card_Icc_of_le -> Int.card_Icc_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne))))) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Finset.card.{0} Int (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1)))) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Finset.card.{0} Int (Finset.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) a))
Case conversion may be inaccurate. Consider using '#align int.card_Icc_of_le Int.card_Icc_of_leₓ'. -/
theorem card_Icc_of_le (h : a ≤ b + 1) : ((Icc a b).card : ℤ) = b + 1 - a := by
  rw [card_Icc, to_nat_sub_of_le h]
#align int.card_Icc_of_le Int.card_Icc_of_le

/- warning: int.card_Ico_of_le -> Int.card_Ico_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Finset.card.{0} Int (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Finset.card.{0} Int (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_Ico_of_le Int.card_Ico_of_leₓ'. -/
theorem card_Ico_of_le (h : a ≤ b) : ((Ico a b).card : ℤ) = b - a := by
  rw [card_Ico, to_nat_sub_of_le h]
#align int.card_Ico_of_le Int.card_Ico_of_le

/- warning: int.card_Ioc_of_le -> Int.card_Ioc_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Finset.card.{0} Int (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Finset.card.{0} Int (Finset.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_Ioc_of_le Int.card_Ioc_of_leₓ'. -/
theorem card_Ioc_of_le (h : a ≤ b) : ((Ioc a b).card : ℤ) = b - a := by
  rw [card_Ioc, to_nat_sub_of_le h]
#align int.card_Ioc_of_le Int.card_Ioc_of_le

/- warning: int.card_Ioo_of_lt -> Int.card_Ioo_of_lt is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LT.lt.{0} Int Int.hasLt a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Finset.card.{0} Int (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))
but is expected to have type
  forall (a : Int) (b : Int), (LT.lt.{0} Int Int.instLTInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Finset.card.{0} Int (Finset.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))
Case conversion may be inaccurate. Consider using '#align int.card_Ioo_of_lt Int.card_Ioo_of_ltₓ'. -/
theorem card_Ioo_of_lt (h : a < b) : ((Ioo a b).card : ℤ) = b - a - 1 := by
  rw [card_Ioo, sub_sub, to_nat_sub_of_le h]
#align int.card_Ioo_of_lt Int.card_Ioo_of_lt

/- warning: int.card_fintype_Icc -> Int.card_fintype_Icc is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIcc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} Int (Set.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIcc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Icc Int.card_fintype_Iccₓ'. -/
@[simp]
theorem card_fintype_Icc : Fintype.card (Set.Icc a b) = (b + 1 - a).toNat := by
  rw [← card_Icc, Fintype.card_ofFinset]
#align int.card_fintype_Icc Int.card_fintype_Icc

/- warning: int.card_fintype_Ico -> Int.card_fintype_Ico is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIco.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIco.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ico Int.card_fintype_Icoₓ'. -/
@[simp]
theorem card_fintype_Ico : Fintype.card (Set.Ico a b) = (b - a).toNat := by
  rw [← card_Ico, Fintype.card_ofFinset]
#align int.card_fintype_Ico Int.card_fintype_Ico

/- warning: int.card_fintype_Ioc -> Int.card_fintype_Ioc is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIoc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIoc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ioc Int.card_fintype_Iocₓ'. -/
@[simp]
theorem card_fintype_Ioc : Fintype.card (Set.Ioc a b) = (b - a).toNat := by
  rw [← card_Ioc, Fintype.card_ofFinset]
#align int.card_fintype_Ioc Int.card_fintype_Ioc

/- warning: int.card_fintype_Ioo -> Int.card_fintype_Ioo is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIoo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIoo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b)) (Int.toNat (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ioo Int.card_fintype_Iooₓ'. -/
@[simp]
theorem card_fintype_Ioo : Fintype.card (Set.Ioo a b) = (b - a - 1).toNat := by
  rw [← card_Ioo, Fintype.card_ofFinset]
#align int.card_fintype_Ioo Int.card_fintype_Ioo

/- warning: int.card_fintype_Icc_of_le -> Int.card_fintype_Icc_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne))))) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIcc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) b (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1)))) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Fintype.card.{0} (Set.Elem.{0} Int (Set.Icc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIcc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) b (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Icc_of_le Int.card_fintype_Icc_of_leₓ'. -/
theorem card_fintype_Icc_of_le (h : a ≤ b + 1) : (Fintype.card (Set.Icc a b) : ℤ) = b + 1 - a := by
  rw [card_fintype_Icc, to_nat_sub_of_le h]
#align int.card_fintype_Icc_of_le Int.card_fintype_Icc_of_le

/- warning: int.card_fintype_Ico_of_le -> Int.card_fintype_Ico_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIco.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIco.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ico_of_le Int.card_fintype_Ico_of_leₓ'. -/
theorem card_fintype_Ico_of_le (h : a ≤ b) : (Fintype.card (Set.Ico a b) : ℤ) = b - a := by
  rw [card_fintype_Ico, to_nat_sub_of_le h]
#align int.card_fintype_Ico_of_le Int.card_fintype_Ico_of_le

/- warning: int.card_fintype_Ioc_of_le -> Int.card_fintype_Ioc_of_le is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.hasLe a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIoc.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a))
but is expected to have type
  forall (a : Int) (b : Int), (LE.le.{0} Int Int.instLEInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ioc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIoc.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ioc_of_le Int.card_fintype_Ioc_of_leₓ'. -/
theorem card_fintype_Ioc_of_le (h : a ≤ b) : (Fintype.card (Set.Ioc a b) : ℤ) = b - a := by
  rw [card_fintype_Ioc, to_nat_sub_of_le h]
#align int.card_fintype_Ioc_of_le Int.card_fintype_Ioc_of_le

/- warning: int.card_fintype_Ioo_of_lt -> Int.card_fintype_Ioo_of_lt is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), (LT.lt.{0} Int Int.hasLt a b) -> (Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} Int) Type (Set.hasCoeToSort.{0} Int) (Set.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) a b)) (Set.fintypeIoo.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) b a) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))))
but is expected to have type
  forall (a : Int) (b : Int), (LT.lt.{0} Int Int.instLTInt a b) -> (Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Fintype.card.{0} (Set.Elem.{0} Int (Set.Ioo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) a b)) (Set.fintypeIoo.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing a b))) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) b a) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))))
Case conversion may be inaccurate. Consider using '#align int.card_fintype_Ioo_of_lt Int.card_fintype_Ioo_of_ltₓ'. -/
theorem card_fintype_Ioo_of_lt (h : a < b) : (Fintype.card (Set.Ioo a b) : ℤ) = b - a - 1 := by
  rw [card_fintype_Ioo, sub_sub, to_nat_sub_of_le h]
#align int.card_fintype_Ioo_of_lt Int.card_fintype_Ioo_of_lt

/- warning: int.image_Ico_mod -> Int.image_Ico_emod is a dubious translation:
lean 3 declaration is
  forall (n : Int) (a : Int), (LE.le.{0} Int Int.hasLe (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) a) -> (Eq.{1} (Finset.{0} Int) (Finset.image.{0, 0} Int Int (fun (a : Int) (b : Int) => Int.decidableEq a b) (fun (_x : Int) => HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.hasMod) _x a) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder n (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) n a))) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (OrderedAddCommGroup.toPartialOrder.{0} Int (StrictOrderedRing.toOrderedAddCommGroup.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing))))) Int.locallyFiniteOrder (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) a))
but is expected to have type
  forall (n : Int) (a : Int), (LE.le.{0} Int Int.instLEInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) a) -> (Eq.{1} (Finset.{0} Int) (Finset.image.{0, 0} Int Int (fun (a : Int) (b : Int) => Int.instDecidableEqInt a b) (fun (_x : Int) => HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.instModInt_1) _x a) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing n (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) n a))) (Finset.Ico.{0} Int (PartialOrder.toPreorder.{0} Int (StrictOrderedRing.toPartialOrder.{0} Int (LinearOrderedRing.toStrictOrderedRing.{0} Int (LinearOrderedCommRing.toLinearOrderedRing.{0} Int Int.linearOrderedCommRing)))) instLocallyFiniteOrderIntToPreorderToPartialOrderToStrictOrderedRingToLinearOrderedRingLinearOrderedCommRing (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) a))
Case conversion may be inaccurate. Consider using '#align int.image_Ico_mod Int.image_Ico_emodₓ'. -/
theorem image_Ico_emod (n a : ℤ) (h : 0 ≤ a) : (Ico n (n + a)).image (· % a) = Ico 0 a :=
  by
  obtain rfl | ha := eq_or_lt_of_le h
  · simp
  ext i
  simp only [mem_image, exists_prop, mem_range, mem_Ico]
  constructor
  · rintro ⟨i, h, rfl⟩
    exact ⟨mod_nonneg i (ne_of_gt ha), mod_lt_of_pos i ha⟩
  intro hia
  have hn := Int.emod_add_ediv n a
  obtain hi | hi := lt_or_le i (n % a)
  · refine' ⟨i + a * (n / a + 1), ⟨_, _⟩, _⟩
    · rw [add_comm (n / a), mul_add, mul_one, ← add_assoc]
      refine' hn.symm.le.trans (add_le_add_right _ _)
      simpa only [zero_add] using add_le_add hia.left (Int.emod_lt_of_pos n ha).le
    · refine' lt_of_lt_of_le (add_lt_add_right hi (a * (n / a + 1))) _
      rw [mul_add, mul_one, ← add_assoc, hn]
    · rw [Int.add_mul_emod_self_left, Int.emod_eq_of_lt hia.left hia.right]
  · refine' ⟨i + a * (n / a), ⟨_, _⟩, _⟩
    · exact hn.symm.le.trans (add_le_add_right hi _)
    · rw [add_comm n a]
      refine' add_lt_add_of_lt_of_le hia.right (le_trans _ hn.le)
      simp only [zero_le, le_add_iff_nonneg_left]
      exact Int.emod_nonneg n (ne_of_gt ha)
    · rw [Int.add_mul_emod_self_left, Int.emod_eq_of_lt hia.left hia.right]
#align int.image_Ico_mod Int.image_Ico_emod

end Int


import vector.additional

/-
This file defines currying and uncurrying for multivariate functions
-/

namespace multi
  section

    parameter {α : Type*}
    parameter {β : Type*}
    parameter {γ : Type*}
    parameter {n : ℕ}

    definition curry_by_head:
    ((vector α n.succ) → β) → α → (vector α n) → β :=
    λ f x yyy, f (x ::ᵥ yyy)

    @[simp] lemma curry_by_head_simp
    {f : (vector α n.succ) → β} {a : α} {v : vector α n}:
    curry_by_head f a v = f (a ::ᵥ v) := rfl

    definition curry_by_tail:
    ((vector α n.succ) → β) → (vector α n) → α → β :=
    λ f yyy x, f (x ::ᵥ yyy)

    @[simp] lemma curry_by_tail_simp
    {f : (vector α n.succ) → β} {v : vector α n} {a : α}:
    curry_by_tail f v a = f (a ::ᵥ v) := rfl

    definition uncurry_by_head:
    (α → (vector α n) → β) → (vector α n.succ) → β :=
    λ f xxx, f xxx.head xxx.tail

    definition uncurry_by_tail:
    ((vector α n) → α → β) → (vector α n.succ) → β :=
    λ f xxx, f xxx.tail xxx.head

    parameter {m : ℕ+}

    definition curry_by_head_alt:
    ((vector α m) → β) → α → (vector α m.nat_pred) → β :=
    λ f x yyy, f (x ::ᵥ yyy)

    definition curry_by_tail_alt:
    ((vector α m) → β) → (vector α m.nat_pred) → α → β :=
    λ f yyy x, f (x ::ᵥ yyy)

    definition uncurry_by_head_alt:
    (α → (vector α m.nat_pred) → β) → (vector α m) → β :=
    λ f xxx, f (xxx : vector α m.nat_pred.succ).head xxx.tail

    definition uncurry_by_tail_alt:
    ((vector α m.nat_pred) → α → β) → (vector α m) → β :=
    λ f xxx, f xxx.tail (xxx : vector α m.nat_pred.succ).head

  end
end multi


import tactic
import tactic.induction

import .util .defs .dist

noncomputable theory
open_locale classical

def bounded (n : ℕ) : set Point :=
{p : Point | dist p center ≤ n}

-----

lemma finite_bounded {n : ℕ} : (bounded n).finite :=
dist_le_set_finite

instance {n : ℕ} : fintype (bounded n) :=
fintype_subtype_of_set_finite finite_bounded

def bounded_area (n : ℕ) : ℕ :=
(bounded n).to_finset.card

def squares_in_bounded (b : Board) (n : ℕ) : ℕ :=
((bounded n).to_finset.filter (λ p, p ∈ b.squares)).card

def squares_in_bounded_exc_A (b : Board) (n : ℕ) : ℕ :=
((bounded n).to_finset.filter (λ p, p ∈ b.squares ∧ p ≠ b.A)).card

-----

lemma mem_bounded_ge {n k : ℕ} {p : Point}
  (h₁ : p ∈ bounded k)
  (h₂ : k ≤ n) :
  p ∈ bounded n :=
by { simp [bounded] at h₂ ⊢, exact le_trans h₁ h₂ }

lemma bounded_area_eq {n : ℕ} :
  bounded_area n = (n * 2 + 1) ^ 2 :=
begin
  rw bounded_area,
  let s := {p : ℤ × ℤ | |p.1| ≤ n ∧ |p.2| ≤ n}, haveI : fintype s,
  { apply fintype_subtype_of_set_finite, let s : set ℤ := {k : ℤ | |k| ≤ n},
    have h : s.finite,
    { rw (_ : s = finset.Icc (-(n : ℤ)) n), swap,
      { ext, simp only [finset.coe_Icc, set.mem_set_of_eq,
        set.mem_Icc, abs_le] },
      exact (finset.Icc (-↑n) ↑n).finite_to_set },
    exact set.finite.prod h h },
  rw (_ : (bounded n).to_finset.card = s.to_finset.card), swap,
  { simp_rw set.to_finset_card, apply fintype.card_congr,
    fapply equiv.of_bijective,
    { rintro p, use ⟨p.1.1, p.1.2⟩, cases p with p h, dsimp,
      rw bounded at h, dsimp at h, simp [dist_le_iff, center] at h, exact h },
    { fsplit,
      { rintro p₁ p₂ h₁, dsimp at h₁, generalize_proofs h₂ h₃ at h₁,
        simp at h₁, cases h₁, ext; assumption },
      { rintro ⟨p, h⟩, fsplit,
        { use ⟨p.1, p.2⟩, simp [bounded], change s with {p : _ | _} at h,
          simp at h, simpa [dist_le_iff, center] },
        { simp }}}},
  let fs' := finset.Icc (-(n : ℤ)) n,
  rw (_ : s.to_finset = fs'.product fs'), swap,
  { ext p, rw set.mem_to_finset, change s with {p : _ | _}, dsimp,
    simp_rw [finset.mem_product, finset.mem_Icc, abs_le] },
  simp only [finset.card_product, int.card_Icc, sub_neg_eq_add],
  rw (_ : (n + 1 + n : ℤ).to_nat = n * 2 + 1), swap,
  { norm_cast, rw (by linarith : n + 1 + n = n * 2 + 1),
    apply int.to_nat_coe_nat },
  rw sq,
end

lemma squares_in_bounded_le_area {n : ℕ} {b : Board} :
  squares_in_bounded b n ≤ bounded_area n :=
begin
  apply finset.card_le_card_of_inj_on id; rintro p hp,
  { rw finset.mem_filter at hp, tauto },
  { tauto },
end

lemma squares_in_bounded_exc_A_le_area {n : ℕ} {b : Board} :
  squares_in_bounded_exc_A b n ≤ bounded_area n :=
begin
  refine le_trans _ squares_in_bounded_le_area, { exact b },
  rw [squares_in_bounded, squares_in_bounded_exc_A],
  apply finset.card_le_card_of_inj_on id; rintro p hp,
  { rw finset.mem_filter at hp ⊢, tauto },
  { tauto },
end


/-
Copyright (c) 2022 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.noetherian
! leanprover-community/mathlib commit 7c4c90f422a4a4477e4d8bc4dc9f1e634e6b2349
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Subobject.Lattice
import Mathbin.CategoryTheory.EssentiallySmall
import Mathbin.CategoryTheory.Simple

/-!
# Artinian and noetherian categories

An artinian category is a category in which objects do not
have infinite decreasing sequences of subobjects.

A noetherian category is a category in which objects do not
have infinite increasing sequences of subobjects.

We show that any nonzero artinian object has a simple subobject.

## Future work
The Jordan-Hölder theorem, following https://stacks.math.columbia.edu/tag/0FCK.
-/

namespace CategoryTheory

open CategoryTheory.Limits

variable {C : Type _} [Category C]

/-- A noetherian object is an object
which does not have infinite increasing sequences of subobjects.

See https://stacks.math.columbia.edu/tag/0FCG
-/
class NoetherianObject (X : C) : Prop where
  subobject_gt_wellFounded : WellFounded ((· > ·) : Subobject X → Subobject X → Prop)
#align category_theory.noetherian_object CategoryTheory.NoetherianObject

/- ./././Mathport/Syntax/Translate/Command.lean:388:30: infer kinds are unsupported in Lean 4: #[`subobject_lt_wellFounded] [] -/
/-- An artinian object is an object
which does not have infinite decreasing sequences of subobjects.

See https://stacks.math.columbia.edu/tag/0FCF
-/
class ArtinianObject (X : C) : Prop where
  subobject_lt_wellFounded : WellFounded ((· < ·) : Subobject X → Subobject X → Prop)
#align category_theory.artinian_object CategoryTheory.ArtinianObject

variable (C)

/-- A category is noetherian if it is essentially small and all objects are noetherian. -/
class Noetherian extends EssentiallySmall C where
  NoetherianObject : ∀ X : C, NoetherianObject X
#align category_theory.noetherian CategoryTheory.Noetherian

attribute [instance] noetherian.noetherian_object

/-- A category is artinian if it is essentially small and all objects are artinian. -/
class Artinian extends EssentiallySmall C where
  ArtinianObject : ∀ X : C, ArtinianObject X
#align category_theory.artinian CategoryTheory.Artinian

attribute [instance] artinian.artinian_object

variable {C}

open Subobject

variable [HasZeroMorphisms C] [HasZeroObject C]

theorem exists_simple_subobject {X : C} [ArtinianObject X] (h : ¬IsZero X) :
    ∃ Y : Subobject X, Simple (Y : C) :=
  by
  haveI : Nontrivial (subobject X) := nontrivial_of_not_is_zero h
  haveI := isAtomic_of_orderBot_wellFounded_lt (artinian_object.subobject_lt_well_founded X)
  have := IsAtomic.eq_bot_or_exists_atom_le (⊤ : subobject X)
  obtain ⟨Y, s⟩ := (IsAtomic.eq_bot_or_exists_atom_le (⊤ : subobject X)).resolve_left top_ne_bot
  exact ⟨Y, (subobject_simple_iff_is_atom _).mpr s.1⟩
#align category_theory.exists_simple_subobject CategoryTheory.exists_simple_subobject

/-- Choose an arbitrary simple subobject of a non-zero artinian object. -/
noncomputable def simpleSubobject {X : C} [ArtinianObject X] (h : ¬IsZero X) : C :=
  (exists_simple_subobject h).some
#align category_theory.simple_subobject CategoryTheory.simpleSubobject

/-- The monomorphism from the arbitrary simple subobject of a non-zero artinian object. -/
noncomputable def simpleSubobjectArrow {X : C} [ArtinianObject X] (h : ¬IsZero X) :
    simpleSubobject h ⟶ X :=
  (exists_simple_subobject h).some.arrow deriving Mono
#align category_theory.simple_subobject_arrow CategoryTheory.simpleSubobjectArrow

instance {X : C} [ArtinianObject X] (h : ¬IsZero X) : Simple (simpleSubobject h) :=
  (exists_simple_subobject h).choose_spec

end CategoryTheory


/-
Copyright © 2022 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri, Sébastien Gouëzel, Heather Macbeth, Floris van Doorn

! This file was ported from Lean 3 source module topology.vector_bundle.constructions
! leanprover-community/mathlib commit be2c24f56783935652cefffb4bfca7e4b25d167e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.FiberBundle.Constructions
import Mathbin.Topology.VectorBundle.Basic

/-!
# Standard constructions on vector bundles

This file contains several standard constructions on vector bundles:

* `bundle.trivial.vector_bundle 𝕜 B F`: the trivial vector bundle with scalar field `𝕜` and model
  fiber `F` over the base `B`

* `vector_bundle.prod`: for vector bundles `E₁` and `E₂` with scalar field `𝕜` over a common base,
  a vector bundle structure on their direct sum `E₁ ×ᵇ E₂` (the notation stands for
  `λ x, E₁ x × E₂ x`).

* `vector_bundle.pullback`: for a vector bundle `E` over `B`, a vector bundle structure on its
  pullback `f *ᵖ E` by a map `f : B' → B` (the notation is a type synonym for `E ∘ f`).

## Tags
Vector bundle, direct sum, pullback
-/

noncomputable section

open Bundle Set FiberBundle

open Classical Bundle

/-! ### The trivial vector bundle -/

namespace Bundle.Trivial

variable (𝕜 : Type _) (B : Type _) (F : Type _) [NontriviallyNormedField 𝕜] [NormedAddCommGroup F]
  [NormedSpace 𝕜 F] [TopologicalSpace B]

instance trivialization.isLinear : (trivialization B F).isLinear 𝕜
    where linear x hx := ⟨fun y z => rfl, fun c y => rfl⟩
#align bundle.trivial.trivialization.is_linear Bundle.Trivial.trivialization.isLinear

variable {𝕜}

theorem trivialization.coordChangeL (b : B) :
    (trivialization B F).coordChangeL 𝕜 (trivialization B F) b = ContinuousLinearEquiv.refl 𝕜 F :=
  by
  ext v
  rw [Trivialization.coordChangeL_apply']
  exacts[rfl, ⟨mem_univ _, mem_univ _⟩]
#align bundle.trivial.trivialization.coord_changeL Bundle.Trivial.trivialization.coordChangeL

variable (𝕜)

instance vectorBundle : VectorBundle 𝕜 F (Bundle.Trivial B F)
    where
  trivialization_linear' := by
    intro e he
    rw [eq_trivialization B F e]
    infer_instance
  continuousOn_coord_change' := by
    intro e e' he he'
    obtain rfl := eq_trivialization B F e
    obtain rfl := eq_trivialization B F e'
    simp_rw [Trivialization.coordChangeL]
    exact continuous_const.continuous_on
#align bundle.trivial.vector_bundle Bundle.Trivial.vectorBundle

end Bundle.Trivial

/-! ### Direct sum of two vector bundles -/

section

variable (𝕜 : Type _) {B : Type _} [NontriviallyNormedField 𝕜] [TopologicalSpace B] (F₁ : Type _)
  [NormedAddCommGroup F₁] [NormedSpace 𝕜 F₁] (E₁ : B → Type _) [TopologicalSpace (TotalSpace E₁)]
  (F₂ : Type _) [NormedAddCommGroup F₂] [NormedSpace 𝕜 F₂] (E₂ : B → Type _)
  [TopologicalSpace (TotalSpace E₂)]

namespace Trivialization

variable {F₁ E₁ F₂ E₂} [∀ x, AddCommMonoid (E₁ x)] [∀ x, Module 𝕜 (E₁ x)]
  [∀ x, AddCommMonoid (E₂ x)] [∀ x, Module 𝕜 (E₂ x)] (e₁ e₁' : Trivialization F₁ (π E₁))
  (e₂ e₂' : Trivialization F₂ (π E₂))

instance prod.isLinear [e₁.isLinear 𝕜] [e₂.isLinear 𝕜] : (e₁.Prod e₂).isLinear 𝕜
    where linear := fun x ⟨h₁, h₂⟩ =>
    (((e₁.linear 𝕜 h₁).mk' _).Prod_map ((e₂.linear 𝕜 h₂).mk' _)).isLinear
#align trivialization.prod.is_linear Trivialization.prod.isLinear

@[simp]
theorem coordChangeL_prod [e₁.isLinear 𝕜] [e₁'.isLinear 𝕜] [e₂.isLinear 𝕜] [e₂'.isLinear 𝕜] ⦃b⦄
    (hb : b ∈ (e₁.Prod e₂).baseSet ∩ (e₁'.Prod e₂').baseSet) :
    ((e₁.Prod e₂).coordChangeL 𝕜 (e₁'.Prod e₂') b : F₁ × F₂ →L[𝕜] F₁ × F₂) =
      (e₁.coordChangeL 𝕜 e₁' b : F₁ →L[𝕜] F₁).Prod_map (e₂.coordChangeL 𝕜 e₂' b) :=
  by
  rw [ContinuousLinearMap.ext_iff, ContinuousLinearMap.coe_prod_map']
  rintro ⟨v₁, v₂⟩
  show
    (e₁.prod e₂).coordChangeL 𝕜 (e₁'.prod e₂') b (v₁, v₂) =
      (e₁.coord_changeL 𝕜 e₁' b v₁, e₂.coord_changeL 𝕜 e₂' b v₂)
  rw [e₁.coord_changeL_apply e₁', e₂.coord_changeL_apply e₂', (e₁.prod e₂).coordChangeL_apply']
  exacts[rfl, hb, ⟨hb.1.2, hb.2.2⟩, ⟨hb.1.1, hb.2.1⟩]
#align trivialization.coord_changeL_prod Trivialization.coordChangeL_prod

variable {e₁ e₂} [∀ x : B, TopologicalSpace (E₁ x)] [∀ x : B, TopologicalSpace (E₂ x)]
  [FiberBundle F₁ E₁] [FiberBundle F₂ E₂]

theorem prod_apply [e₁.isLinear 𝕜] [e₂.isLinear 𝕜] {x : B} (hx₁ : x ∈ e₁.baseSet)
    (hx₂ : x ∈ e₂.baseSet) (v₁ : E₁ x) (v₂ : E₂ x) :
    prod e₁ e₂ ⟨x, (v₁, v₂)⟩ =
      ⟨x, e₁.continuousLinearEquivAt 𝕜 x hx₁ v₁, e₂.continuousLinearEquivAt 𝕜 x hx₂ v₂⟩ :=
  rfl
#align trivialization.prod_apply Trivialization.prod_apply

end Trivialization

open Trivialization

variable [∀ x, AddCommMonoid (E₁ x)] [∀ x, Module 𝕜 (E₁ x)] [∀ x, AddCommMonoid (E₂ x)]
  [∀ x, Module 𝕜 (E₂ x)] [∀ x : B, TopologicalSpace (E₁ x)] [∀ x : B, TopologicalSpace (E₂ x)]
  [FiberBundle F₁ E₁] [FiberBundle F₂ E₂]

/-- The product of two vector bundles is a vector bundle. -/
instance VectorBundle.prod [VectorBundle 𝕜 F₁ E₁] [VectorBundle 𝕜 F₂ E₂] :
    VectorBundle 𝕜 (F₁ × F₂) (E₁ ×ᵇ E₂)
    where
  trivialization_linear' := by
    rintro _ ⟨e₁, e₂, he₁, he₂, rfl⟩; skip
    infer_instance
  continuousOn_coord_change' :=
    by
    rintro _ _ ⟨e₁, e₂, he₁, he₂, rfl⟩ ⟨e₁', e₂', he₁', he₂', rfl⟩; skip
    refine'
        (((continuousOn_coord_change 𝕜 e₁ e₁').mono _).prodMapL 𝕜
              ((continuousOn_coord_change 𝕜 e₂ e₂').mono _)).congr
          _ <;>
      dsimp only [base_set_prod, mfld_simps]
    · mfld_set_tac
    · mfld_set_tac
    · rintro b hb
      rw [ContinuousLinearMap.ext_iff]
      rintro ⟨v₁, v₂⟩
      show
        (e₁.prod e₂).coordChangeL 𝕜 (e₁'.prod e₂') b (v₁, v₂) =
          (e₁.coord_changeL 𝕜 e₁' b v₁, e₂.coord_changeL 𝕜 e₂' b v₂)
      rw [e₁.coord_changeL_apply e₁', e₂.coord_changeL_apply e₂', (e₁.prod e₂).coordChangeL_apply']
      exacts[rfl, hb, ⟨hb.1.2, hb.2.2⟩, ⟨hb.1.1, hb.2.1⟩]
#align vector_bundle.prod VectorBundle.prod

variable {𝕜 F₁ E₁ F₂ E₂}

@[simp]
theorem Trivialization.continuousLinearEquivAt_prod {e₁ : Trivialization F₁ (π E₁)}
    {e₂ : Trivialization F₂ (π E₂)} [e₁.isLinear 𝕜] [e₂.isLinear 𝕜] {x : B} (hx₁ : x ∈ e₁.baseSet)
    (hx₂ : x ∈ e₂.baseSet) :
    (e₁.Prod e₂).continuousLinearEquivAt 𝕜 x ⟨hx₁, hx₂⟩ =
      (e₁.continuousLinearEquivAt 𝕜 x hx₁).Prod (e₂.continuousLinearEquivAt 𝕜 x hx₂) :=
  by
  ext1
  funext v
  obtain ⟨v₁, v₂⟩ := v
  rw [(e₁.prod e₂).continuousLinearEquivAt_apply 𝕜, Trivialization.prod]
  exact (congr_arg Prod.snd (prod_apply 𝕜 hx₁ hx₂ v₁ v₂) : _)
#align trivialization.continuous_linear_equiv_at_prod Trivialization.continuousLinearEquivAt_prod

end

/-! ### Pullbacks of vector bundles -/

section

variable (R 𝕜 : Type _) {B : Type _} (F : Type _) (E : B → Type _) {B' : Type _} (f : B' → B)

instance [∀ x : B, AddCommMonoid (E x)] : ∀ x : B', AddCommMonoid ((f *ᵖ E) x) := by
  delta_instance bundle.pullback

instance [Semiring R] [∀ x : B, AddCommMonoid (E x)] [∀ x, Module R (E x)] :
    ∀ x : B', Module R ((f *ᵖ E) x) := by delta_instance bundle.pullback

variable {E F} [TopologicalSpace B'] [TopologicalSpace (TotalSpace E)] [NontriviallyNormedField 𝕜]
  [NormedAddCommGroup F] [NormedSpace 𝕜 F] [TopologicalSpace B] [∀ x, AddCommMonoid (E x)]
  [∀ x, Module 𝕜 (E x)] {K : Type _} [ContinuousMapClass K B' B]

instance Trivialization.pullback_linear (e : Trivialization F (π E)) [e.isLinear 𝕜] (f : K) :
    (@Trivialization.pullback _ _ _ B' _ _ _ _ _ _ _ e f).isLinear 𝕜
    where linear x h := e.linear 𝕜 h
#align trivialization.pullback_linear Trivialization.pullback_linear

instance VectorBundle.pullback [∀ x, TopologicalSpace (E x)] [FiberBundle F E] [VectorBundle 𝕜 F E]
    (f : K) : VectorBundle 𝕜 F ((f : B' → B) *ᵖ E)
    where
  trivialization_linear' := by
    rintro _ ⟨e, he, rfl⟩; skip
    infer_instance
  continuousOn_coord_change' :=
    by
    rintro _ _ ⟨e, he, rfl⟩ ⟨e', he', rfl⟩; skip
    refine'
      ((continuousOn_coord_change 𝕜 e e').comp (map_continuous f).ContinuousOn fun b hb => hb).congr
        _
    rintro b (hb : f b ∈ e.base_set ∩ e'.base_set); ext v
    show ((e.pullback f).coordChangeL 𝕜 (e'.pullback f) b) v = (e.coord_changeL 𝕜 e' (f b)) v
    rw [e.coord_changeL_apply e' hb, (e.pullback f).coordChangeL_apply' _]
    exacts[rfl, hb]
#align vector_bundle.pullback VectorBundle.pullback

end


/-
Copyright (c) 2018 Michael Jendrusch. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Michael Jendrusch, Scott Morrison, Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.monoidal.functor
! leanprover-community/mathlib commit 3d7987cda72abc473c7cdbbb075170e9ac620042
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Monoidal.Category
import Mathlib.CategoryTheory.Adjunction.Basic
import Mathlib.CategoryTheory.Products.Basic

/-!
# (Lax) monoidal functors

A lax monoidal functor `F` between monoidal categories `C` and `D`
is a functor between the underlying categories equipped with morphisms
* `ε : 𝟙_ D ⟶ F.obj (𝟙_ C)` (called the unit morphism)
* `μ X Y : (F.obj X) ⊗ (F.obj Y) ⟶ F.obj (X ⊗ Y)` (called the tensorator, or strength).
satisfying various axioms.

A monoidal functor is a lax monoidal functor for which `ε` and `μ` are isomorphisms.

We show that the composition of (lax) monoidal functors gives a (lax) monoidal functor.

See also `CategoryTheory.Monoidal.Functorial` for a typeclass decorating an object-level
function with the additional data of a monoidal functor.
This is useful when stating that a pre-existing functor is monoidal.

See `CategoryTheory.Monoidal.NaturalTransformation` for monoidal natural transformations.

We show in `CategoryTheory.Monoidal.Mon_` that lax monoidal functors take monoid objects
to monoid objects.

## Future work
* Oplax monoidal functors.

## References

See <https://stacks.math.columbia.edu/tag/0FFL>.
-/

open CategoryTheory

universe v₁ v₂ v₃ u₁ u₂ u₃

open CategoryTheory.Category

open CategoryTheory.Functor

namespace CategoryTheory

section

open MonoidalCategory

variable (C : Type u₁) [Category.{v₁} C] [MonoidalCategory.{v₁} C] (D : Type u₂) [Category.{v₂} D]
  [MonoidalCategory.{v₂} D]

-- The direction of `left_unitality` and `right_unitality` as simp lemmas may look strange:
-- remember the rule of thumb that component indices of natural transformations
-- "weigh more" than structural maps.
-- (However by this argument `associativity` is currently stated backwards!)
/-- A lax monoidal functor is a functor `F : C ⥤ D` between monoidal categories,
equipped with morphisms `ε : 𝟙 _D ⟶ F.obj (𝟙_ C)` and `μ X Y : F.obj X ⊗ F.obj Y ⟶ F.obj (X ⊗ Y)`,
satisfying the appropriate coherences. -/
structure LaxMonoidalFunctor extends C ⥤ D where
  /-- unit morphism -/
  ε : 𝟙_ D ⟶ obj (𝟙_ C)
  /-- tensorator -/
  μ : ∀ X Y : C, obj X ⊗ obj Y ⟶ obj (X ⊗ Y)
  μ_natural :
    ∀ {X Y X' Y' : C} (f : X ⟶ Y) (g : X' ⟶ Y'),
      (map f ⊗ map g) ≫ μ Y Y' = μ X X' ≫ map (f ⊗ g) := by
    --Porting note: was `obviously`
    aesop_cat
  /-- associativity of the tensorator -/
  associativity :
    ∀ X Y Z : C,
      (μ X Y ⊗ 𝟙 (obj Z)) ≫ μ (X ⊗ Y) Z ≫ map (α_ X Y Z).hom =
        (α_ (obj X) (obj Y) (obj Z)).hom ≫ (𝟙 (obj X) ⊗ μ Y Z) ≫ μ X (Y ⊗ Z) := by
    --Porting note: was `obviously`
    aesop_cat
  -- unitality
  left_unitality : ∀ X : C, (λ_ (obj X)).hom = (ε ⊗ 𝟙 (obj X)) ≫ μ (𝟙_ C) X ≫ map (λ_ X).hom :=
    --Porting note: was `obviously`
    by aesop_cat
  right_unitality : ∀ X : C, (ρ_ (obj X)).hom = (𝟙 (obj X) ⊗ ε) ≫ μ X (𝟙_ C) ≫ map (ρ_ X).hom :=
    --Porting note: was `obviously`
    by aesop_cat
#align category_theory.lax_monoidal_functor CategoryTheory.LaxMonoidalFunctor

-- Porting note: todo: remove this configuration and use the default configuration.
-- We keep this to be consistent with Lean 3.
initialize_simps_projections LaxMonoidalFunctor (+toFunctor, -obj, -map)

--Porting note: was `[simp, reassoc.1]`
attribute [reassoc (attr := simp)] LaxMonoidalFunctor.μ_natural

attribute [simp] LaxMonoidalFunctor.left_unitality

attribute [simp] LaxMonoidalFunctor.right_unitality

--Porting note: was `[simp, reassoc.1]`
attribute [reassoc (attr := simp)] LaxMonoidalFunctor.associativity

-- When `rewrite_search` lands, add @[search] attributes to
-- LaxMonoidalFunctor.μ_natural LaxMonoidalFunctor.left_unitality
-- LaxMonoidalFunctor.right_unitality LaxMonoidalFunctor.associativity
section

variable {C D}

--Porting note: was `[simp, reassoc.1]`
@[reassoc (attr := simp)]
theorem LaxMonoidalFunctor.left_unitality_inv (F : LaxMonoidalFunctor C D) (X : C) :
    (λ_ (F.obj X)).inv ≫ (F.ε ⊗ 𝟙 (F.obj X)) ≫ F.μ (𝟙_ C) X = F.map (λ_ X).inv := by
  rw [Iso.inv_comp_eq, F.left_unitality, Category.assoc, Category.assoc, ← F.toFunctor.map_comp,
    Iso.hom_inv_id, F.toFunctor.map_id, comp_id]
#align category_theory.lax_monoidal_functor.left_unitality_inv CategoryTheory.LaxMonoidalFunctor.left_unitality_inv

--Porting note: was `[simp, reassoc.1]`
@[reassoc (attr := simp)]
theorem LaxMonoidalFunctor.right_unitality_inv (F : LaxMonoidalFunctor C D) (X : C) :
    (ρ_ (F.obj X)).inv ≫ (𝟙 (F.obj X) ⊗ F.ε) ≫ F.μ X (𝟙_ C) = F.map (ρ_ X).inv := by
  rw [Iso.inv_comp_eq, F.right_unitality, Category.assoc, Category.assoc, ← F.toFunctor.map_comp,
    Iso.hom_inv_id, F.toFunctor.map_id, comp_id]
#align category_theory.lax_monoidal_functor.right_unitality_inv CategoryTheory.LaxMonoidalFunctor.right_unitality_inv

--Porting note: was `[simp, reassoc.1]`
@[reassoc (attr := simp)]
theorem LaxMonoidalFunctor.associativity_inv (F : LaxMonoidalFunctor C D) (X Y Z : C) :
    (𝟙 (F.obj X) ⊗ F.μ Y Z) ≫ F.μ X (Y ⊗ Z) ≫ F.map (α_ X Y Z).inv =
      (α_ (F.obj X) (F.obj Y) (F.obj Z)).inv ≫ (F.μ X Y ⊗ 𝟙 (F.obj Z)) ≫ F.μ (X ⊗ Y) Z := by
  rw [Iso.eq_inv_comp, ← F.associativity_assoc, ← F.toFunctor.map_comp, Iso.hom_inv_id,
    F.toFunctor.map_id, comp_id]
#align category_theory.lax_monoidal_functor.associativity_inv CategoryTheory.LaxMonoidalFunctor.associativity_inv

end

/--
A monoidal functor is a lax monoidal functor for which the tensorator and unitor as isomorphisms.

See <https://stacks.math.columbia.edu/tag/0FFL>.
-/
structure MonoidalFunctor extends LaxMonoidalFunctor.{v₁, v₂} C D where
  ε_isIso : IsIso ε := by infer_instance
  μ_isIso : ∀ X Y : C, IsIso (μ X Y) := by infer_instance
#align category_theory.monoidal_functor CategoryTheory.MonoidalFunctor

initialize_simps_projections MonoidalFunctor (+toLaxMonoidalFunctor, -obj, -map, -ε, -μ)

attribute [instance] MonoidalFunctor.ε_isIso MonoidalFunctor.μ_isIso

variable {C D}

/-- The unit morphism of a (strong) monoidal functor as an isomorphism.
-/
noncomputable def MonoidalFunctor.εIso (F : MonoidalFunctor.{v₁, v₂} C D) :
    tensorUnit D ≅ F.obj (tensorUnit C) :=
  asIso F.ε
#align category_theory.monoidal_functor.ε_iso CategoryTheory.MonoidalFunctor.εIso

/-- The tensorator of a (strong) monoidal functor as an isomorphism.
-/
noncomputable def MonoidalFunctor.μIso (F : MonoidalFunctor.{v₁, v₂} C D) (X Y : C) :
    F.obj X ⊗ F.obj Y ≅ F.obj (X ⊗ Y) :=
  asIso (F.μ X Y)
#align category_theory.monoidal_functor.μ_iso CategoryTheory.MonoidalFunctor.μIso

end

open MonoidalCategory

namespace LaxMonoidalFunctor

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

/-- The identity lax monoidal functor. -/
@[simps]
def id : LaxMonoidalFunctor.{v₁, v₁} C C :=
  { 𝟭 C with
    ε := 𝟙 _
    μ := fun X Y => 𝟙 _ }
#align category_theory.lax_monoidal_functor.id CategoryTheory.LaxMonoidalFunctor.id

instance : Inhabited (LaxMonoidalFunctor C C) :=
  ⟨id C⟩

end LaxMonoidalFunctor

namespace MonoidalFunctor

section

variable {C : Type u₁} [Category.{v₁} C] [MonoidalCategory.{v₁} C]

variable {D : Type u₂} [Category.{v₂} D] [MonoidalCategory.{v₂} D]

variable (F : MonoidalFunctor.{v₁, v₂} C D)

theorem map_tensor {X Y X' Y' : C} (f : X ⟶ Y) (g : X' ⟶ Y') :
    F.map (f ⊗ g) = inv (F.μ X X') ≫ (F.map f ⊗ F.map g) ≫ F.μ Y Y' := by simp
#align category_theory.monoidal_functor.map_tensor CategoryTheory.MonoidalFunctor.map_tensor

theorem map_leftUnitor (X : C) :
    F.map (λ_ X).hom = inv (F.μ (𝟙_ C) X) ≫ (inv F.ε ⊗ 𝟙 (F.obj X)) ≫ (λ_ (F.obj X)).hom := by
  simp only [LaxMonoidalFunctor.left_unitality]
  slice_rhs 2 3 =>
    rw [← comp_tensor_id]
    simp
  simp
#align category_theory.monoidal_functor.map_left_unitor CategoryTheory.MonoidalFunctor.map_leftUnitor

theorem map_rightUnitor (X : C) :
    F.map (ρ_ X).hom = inv (F.μ X (𝟙_ C)) ≫ (𝟙 (F.obj X) ⊗ inv F.ε) ≫ (ρ_ (F.obj X)).hom := by
  simp only [LaxMonoidalFunctor.right_unitality]
  slice_rhs 2 3 =>
    rw [← id_tensor_comp]
    simp
  simp
#align category_theory.monoidal_functor.map_right_unitor CategoryTheory.MonoidalFunctor.map_rightUnitor

/-- The tensorator as a natural isomorphism. -/
noncomputable def μNatIso :
    Functor.prod F.toFunctor F.toFunctor ⋙ tensor D ≅ tensor C ⋙ F.toFunctor :=
  NatIso.ofComponents
    (by
      intros
      apply F.μIso)
    (by
      intros
      apply F.toLaxMonoidalFunctor.μ_natural)
#align category_theory.monoidal_functor.μ_nat_iso CategoryTheory.MonoidalFunctor.μNatIso

@[simp]
theorem μIso_hom (X Y : C) : (F.μIso X Y).hom = F.μ X Y :=
  rfl
#align category_theory.monoidal_functor.μ_iso_hom CategoryTheory.MonoidalFunctor.μIso_hom

--Porting note: was `[simp, reassoc.1]`
@[reassoc (attr := simp)]
theorem μ_inv_hom_id (X Y : C) : (F.μIso X Y).inv ≫ F.μ X Y = 𝟙 _ :=
  (F.μIso X Y).inv_hom_id
#align category_theory.monoidal_functor.μ_inv_hom_id CategoryTheory.MonoidalFunctor.μ_inv_hom_id

@[simp]
theorem μ_hom_inv_id (X Y : C) : F.μ X Y ≫ (F.μIso X Y).inv = 𝟙 _ :=
  (F.μIso X Y).hom_inv_id
#align category_theory.monoidal_functor.μ_hom_inv_id CategoryTheory.MonoidalFunctor.μ_hom_inv_id

@[simp]
theorem εIso_hom : F.εIso.hom = F.ε :=
  rfl
#align category_theory.monoidal_functor.ε_iso_hom CategoryTheory.MonoidalFunctor.εIso_hom

--Porting note: was `[simp, reassoc.1]`
@[reassoc (attr := simp)]
theorem ε_inv_hom_id : F.εIso.inv ≫ F.ε = 𝟙 _ :=
  F.εIso.inv_hom_id
#align category_theory.monoidal_functor.ε_inv_hom_id CategoryTheory.MonoidalFunctor.ε_inv_hom_id

@[simp]
theorem ε_hom_inv_id : F.ε ≫ F.εIso.inv = 𝟙 _ :=
  F.εIso.hom_inv_id
#align category_theory.monoidal_functor.ε_hom_inv_id CategoryTheory.MonoidalFunctor.ε_hom_inv_id

/-- Monoidal functors commute with left tensoring up to isomorphism -/
@[simps!]
noncomputable def commTensorLeft (X : C) :
    F.toFunctor ⋙ tensorLeft (F.toFunctor.obj X) ≅ tensorLeft X ⋙ F.toFunctor :=
  NatIso.ofComponents (fun Y => F.μIso X Y) @fun Y Z f => by
    convert F.μ_natural (𝟙 X) f using 2
    simp
#align category_theory.monoidal_functor.comm_tensor_left CategoryTheory.MonoidalFunctor.commTensorLeft

/-- Monoidal functors commute with right tensoring up to isomorphism -/
@[simps!]
noncomputable def commTensorRight (X : C) :
    F.toFunctor ⋙ tensorRight (F.toFunctor.obj X) ≅ tensorRight X ⋙ F.toFunctor :=
  NatIso.ofComponents (fun Y => F.μIso Y X) @fun Y Z f => by
    convert F.μ_natural f (𝟙 X) using 2
    simp
#align category_theory.monoidal_functor.comm_tensor_right CategoryTheory.MonoidalFunctor.commTensorRight

end

section

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

/-- The identity monoidal functor. -/
@[simps]
def id : MonoidalFunctor.{v₁, v₁} C C :=
  { 𝟭 C with
    ε := 𝟙 _
    μ := fun X Y => 𝟙 _ }
#align category_theory.monoidal_functor.id CategoryTheory.MonoidalFunctor.id

instance : Inhabited (MonoidalFunctor C C) :=
  ⟨id C⟩

end

end MonoidalFunctor

variable {C : Type u₁} [Category.{v₁} C] [MonoidalCategory.{v₁} C]

variable {D : Type u₂} [Category.{v₂} D] [MonoidalCategory.{v₂} D]

variable {E : Type u₃} [Category.{v₃} E] [MonoidalCategory.{v₃} E]

namespace LaxMonoidalFunctor

variable (F : LaxMonoidalFunctor.{v₁, v₂} C D) (G : LaxMonoidalFunctor.{v₂, v₃} D E)

-- The proofs here are horrendous; rewrite_search helps a lot.
/-- The composition of two lax monoidal functors is again lax monoidal. -/
@[simps]
def comp : LaxMonoidalFunctor.{v₁, v₃} C E :=
  { F.toFunctor ⋙ G.toFunctor with
    ε := G.ε ≫ G.map F.ε
    μ := fun X Y => G.μ (F.obj X) (F.obj Y) ≫ G.map (F.μ X Y)
    μ_natural := @fun _ _ _ _ f g =>
      by
      simp only [Functor.comp_map, assoc]
      rw [← Category.assoc, LaxMonoidalFunctor.μ_natural, Category.assoc, ← map_comp, ← map_comp,
        ← LaxMonoidalFunctor.μ_natural]
    associativity := fun X Y Z => by
      dsimp
      rw [id_tensor_comp]
      slice_rhs 3 4 => rw [← G.toFunctor.map_id, G.μ_natural]
      slice_rhs 1 3 => rw [← G.associativity]
      rw [comp_tensor_id]
      slice_lhs 2 3 => rw [← G.toFunctor.map_id, G.μ_natural]
      rw [Category.assoc, Category.assoc, Category.assoc, Category.assoc, Category.assoc, ←
        G.toFunctor.map_comp, ← G.toFunctor.map_comp, ← G.toFunctor.map_comp, ←
        G.toFunctor.map_comp, F.associativity]
    left_unitality := fun X => by
      dsimp
      rw [G.left_unitality, comp_tensor_id, Category.assoc, Category.assoc]
      apply congr_arg
      rw [F.left_unitality, map_comp, ← NatTrans.id_app, ← Category.assoc, ←
        LaxMonoidalFunctor.μ_natural, NatTrans.id_app, map_id, ← Category.assoc, map_comp]
    right_unitality := fun X => by
      dsimp
      rw [G.right_unitality, id_tensor_comp, Category.assoc, Category.assoc]
      apply congr_arg
      rw [F.right_unitality, map_comp, ← NatTrans.id_app, ← Category.assoc, ←
        LaxMonoidalFunctor.μ_natural, NatTrans.id_app, map_id, ← Category.assoc, map_comp] }
#align category_theory.lax_monoidal_functor.comp CategoryTheory.LaxMonoidalFunctor.comp

@[inherit_doc]
infixr:80 " ⊗⋙ " => comp

end LaxMonoidalFunctor

namespace LaxMonoidalFunctor

universe v₀ u₀

variable {B : Type u₀} [Category.{v₀} B] [MonoidalCategory.{v₀} B]

variable (F : LaxMonoidalFunctor.{v₀, v₁} B C) (G : LaxMonoidalFunctor.{v₂, v₃} D E)

attribute [local simp] μ_natural associativity left_unitality right_unitality

/-- The cartesian product of two lax monoidal functors is lax monoidal. -/
@[simps]
def prod : LaxMonoidalFunctor (B × D) (C × E) :=
  { F.toFunctor.prod G.toFunctor with
    ε := (ε F, ε G)
    μ := fun X Y => (μ F X.1 Y.1, μ G X.2 Y.2) }
#align category_theory.lax_monoidal_functor.prod CategoryTheory.LaxMonoidalFunctor.prod

end LaxMonoidalFunctor

namespace MonoidalFunctor

variable (C)

/-- The diagonal functor as a monoidal functor. -/
@[simps]
def diag : MonoidalFunctor C (C × C) :=
  { Functor.diag C with
    ε := 𝟙 _
    μ := fun X Y => 𝟙 _ }
#align category_theory.monoidal_functor.diag CategoryTheory.MonoidalFunctor.diag

end MonoidalFunctor

namespace LaxMonoidalFunctor

variable (F : LaxMonoidalFunctor.{v₁, v₂} C D) (G : LaxMonoidalFunctor.{v₁, v₃} C E)

/-- The cartesian product of two lax monoidal functors starting from the same monoidal category `C`
    is lax monoidal. -/
def prod' : LaxMonoidalFunctor C (D × E) :=
  (MonoidalFunctor.diag C).toLaxMonoidalFunctor ⊗⋙ F.prod G
#align category_theory.lax_monoidal_functor.prod' CategoryTheory.LaxMonoidalFunctor.prod'

@[simp]
theorem prod'_toFunctor : (F.prod' G).toFunctor = F.toFunctor.prod' G.toFunctor :=
  rfl
#align category_theory.lax_monoidal_functor.prod'_to_functor CategoryTheory.LaxMonoidalFunctor.prod'_toFunctor

@[simp]
theorem prod'_ε : (F.prod' G).ε = (F.ε, G.ε) := by
  dsimp [prod']
  simp
#align category_theory.lax_monoidal_functor.prod'_ε CategoryTheory.LaxMonoidalFunctor.prod'_ε

@[simp]

end LaxMonoidalFunctor

namespace MonoidalFunctor

variable (F : MonoidalFunctor.{v₁, v₂} C D) (G : MonoidalFunctor.{v₂, v₃} D E)

/-- The composition of two monoidal functors is again monoidal. -/
@[simps]
def comp : MonoidalFunctor.{v₁, v₃} C E :=
  {
    F.toLaxMonoidalFunctor.comp
      G.toLaxMonoidalFunctor with
    ε_isIso := by
      dsimp
      infer_instance
    μ_isIso := by
      dsimp
      infer_instance }
#align category_theory.monoidal_functor.comp CategoryTheory.MonoidalFunctor.comp

@[inherit_doc]
infixr:80
  " ⊗⋙ " =>-- We overload notation; potentially dangerous, but it seems to work.
  comp

end MonoidalFunctor

namespace MonoidalFunctor

universe v₀ u₀

variable {B : Type u₀} [Category.{v₀} B] [MonoidalCategory.{v₀} B]

variable (F : MonoidalFunctor.{v₀, v₁} B C) (G : MonoidalFunctor.{v₂, v₃} D E)

/-- The cartesian product of two monoidal functors is monoidal. -/
@[simps]
def prod : MonoidalFunctor (B × D) (C × E) :=
  {
    F.toLaxMonoidalFunctor.prod
      G.toLaxMonoidalFunctor with
    ε_isIso := (isIso_prod_iff C E).mpr ⟨ε_isIso F, ε_isIso G⟩
    μ_isIso := fun X Y => (isIso_prod_iff C E).mpr ⟨μ_isIso F X.1 Y.1, μ_isIso G X.2 Y.2⟩ }
#align category_theory.monoidal_functor.prod CategoryTheory.MonoidalFunctor.prod

end MonoidalFunctor

namespace MonoidalFunctor

variable (F : MonoidalFunctor.{v₁, v₂} C D) (G : MonoidalFunctor.{v₁, v₃} C E)

/-- The cartesian product of two monoidal functors starting from the same monoidal category `C`
    is monoidal. -/
def prod' : MonoidalFunctor C (D × E) :=
  diag C ⊗⋙ F.prod G
#align category_theory.monoidal_functor.prod' CategoryTheory.MonoidalFunctor.prod'

@[simp]
theorem prod'_toLaxMonoidalFunctor :
    (F.prod' G).toLaxMonoidalFunctor = F.toLaxMonoidalFunctor.prod' G.toLaxMonoidalFunctor :=
  rfl
#align category_theory.monoidal_functor.prod'_to_lax_monoidal_functor CategoryTheory.MonoidalFunctor.prod'_toLaxMonoidalFunctor

end MonoidalFunctor

/-- If we have a right adjoint functor `G` to a monoidal functor `F`, then `G` has a lax monoidal
structure as well.
-/
@[simps]
noncomputable def monoidalAdjoint (F : MonoidalFunctor C D) {G : D ⥤ C} (h : F.toFunctor ⊣ G) :
    LaxMonoidalFunctor D C where
  toFunctor := G
  ε := h.homEquiv _ _ (inv F.ε)
  μ X Y := h.homEquiv _ (X ⊗ Y) (inv (F.μ (G.obj X) (G.obj Y)) ≫ (h.counit.app X ⊗ h.counit.app Y))
  μ_natural := @fun X Y X' Y' f g => by
    rw [← h.homEquiv_naturality_left, ← h.homEquiv_naturality_right, Equiv.apply_eq_iff_eq, assoc,
      IsIso.eq_inv_comp, ← F.toLaxMonoidalFunctor.μ_natural_assoc, IsIso.hom_inv_id_assoc, ←
      tensor_comp, Adjunction.counit_naturality, Adjunction.counit_naturality, tensor_comp]
  associativity X Y Z := by
    dsimp only
    rw [← h.homEquiv_naturality_right, ← h.homEquiv_naturality_left, ←
      h.homEquiv_naturality_left, ← h.homEquiv_naturality_left, Equiv.apply_eq_iff_eq, ←
      cancel_epi (F.toLaxMonoidalFunctor.μ (G.obj X ⊗ G.obj Y) (G.obj Z)), ←
      cancel_epi (F.toLaxMonoidalFunctor.μ (G.obj X) (G.obj Y) ⊗ 𝟙 (F.obj (G.obj Z))),
      F.toLaxMonoidalFunctor.associativity_assoc (G.obj X) (G.obj Y) (G.obj Z), ←
      F.toLaxMonoidalFunctor.μ_natural_assoc, assoc, IsIso.hom_inv_id_assoc, ←
      F.toLaxMonoidalFunctor.μ_natural_assoc, IsIso.hom_inv_id_assoc, ← tensor_comp, ←
      tensor_comp, id_comp, Functor.map_id, Functor.map_id, id_comp, ← tensor_comp_assoc, ←
      tensor_comp_assoc, id_comp, id_comp, h.homEquiv_unit, h.homEquiv_unit, Functor.map_comp,
      assoc, assoc, h.counit_naturality, h.left_triangle_components_assoc, Functor.map_comp,
      assoc, h.counit_naturality, h.left_triangle_components_assoc]
    simp
  left_unitality X := by
    rw [← h.homEquiv_naturality_right, ← h.homEquiv_naturality_left, ← Equiv.symm_apply_eq,
      h.homEquiv_counit, F.map_leftUnitor, h.homEquiv_unit, assoc, assoc, assoc, F.map_tensor,
      assoc, assoc, IsIso.hom_inv_id_assoc, ← tensor_comp_assoc, Functor.map_id, id_comp,
      Functor.map_comp, assoc, h.counit_naturality, h.left_triangle_components_assoc, ←
      leftUnitor_naturality, ← tensor_comp_assoc, id_comp, comp_id]
    simp
  right_unitality X := by
    rw [← h.homEquiv_naturality_right, ← h.homEquiv_naturality_left, ← Equiv.symm_apply_eq,
      h.homEquiv_counit, F.map_rightUnitor, assoc, assoc, ← rightUnitor_naturality, ←
      tensor_comp_assoc, comp_id, id_comp, h.homEquiv_unit, F.map_tensor, assoc, assoc, assoc,
      IsIso.hom_inv_id_assoc, Functor.map_comp, Functor.map_id, ← tensor_comp_assoc, assoc,
      h.counit_naturality, h.left_triangle_components_assoc, id_comp]
    simp
#align category_theory.monoidal_adjoint CategoryTheory.monoidalAdjoint

/-- If a monoidal functor `F` is an equivalence of categories then its inverse is also monoidal. -/
@[simps]
noncomputable def monoidalInverse (F : MonoidalFunctor C D) [IsEquivalence F.toFunctor] :
    MonoidalFunctor D C
    where
  toLaxMonoidalFunctor := monoidalAdjoint F (asEquivalence _).toAdjunction
  ε_isIso := by
    dsimp [Equivalence.toAdjunction]
    infer_instance
  μ_isIso X Y := by
    dsimp [Equivalence.toAdjunction]
    infer_instance
#align category_theory.monoidal_inverse CategoryTheory.monoidalInverse

end CategoryTheory