/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Robert Y. Lewis, Gabriel Ebner

! This file was ported from Lean 3 source module tactic.lint.type_classes
! leanprover-community/mathlib commit 8f66240cab125b938b327d3850169d490cfbcdd8
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Bool.Basic
import Mathbin.Meta.RbMap
import Mathbin.Tactic.Lint.Basic

/-!
# Linters about type classes

This file defines several linters checking the correct usage of type classes
and the appropriate definition of instances:

 * `instance_priority` ensures that blanket instances have low priority.
 * `has_nonempty_instances` checks that every type has a `nonempty` instance, an `inhabited`
   instance, or a `unique` instance.
 * `impossible_instance` checks that there are no instances which can never apply.
 * `incorrect_type_class_argument` checks that only type classes are used in
   instance-implicit arguments.
 * `dangerous_instance` checks for instances that generate subproblems with metavariables.
 * `fails_quickly` checks that type class resolution finishes quickly.
 * `class_structure` checks that every `class` is a structure, i.e. `@[class] def` is forbidden.
 * `has_coe_variable` checks that there is no instance of type `has_coe α t`.
 * `inhabited_nonempty` checks whether `[inhabited α]` arguments could be generalized
   to `[nonempty α]`.
 * `decidable_classical` checks propositions for `[decidable_... p]` hypotheses that are not used
   in the statement, and could thus be removed by using `classical` in the proof.
 * `linter.has_coe_to_fun` checks whether necessary `has_coe_to_fun` instances are declared.
 * `linter.check_reducibility` checks whether non-instances with a class as type are reducible.
-/

open Tactic

/-- Pretty prints a list of arguments of a declaration. Assumes `l` is a list of argument positions
and binders (or any other element that can be pretty printed).
`l` can be obtained e.g. by applying `list.indexes_values` to a list obtained by
`get_pi_binders`. -/
unsafe def print_arguments {α} [has_to_tactic_format α] (l : List (ℕ × α)) : tactic String := do
  let fs ←
    l.mapM fun ⟨n, b⟩ => (fun s => to_fmt "argument " ++ to_fmt (n + 1) ++ ": " ++ s) <$> pp b
  return <| fs tt
#align print_arguments print_arguments

/-- checks whether an instance that always applies has priority ≥ 1000. -/
private unsafe def instance_priority (d : declaration) : tactic (Option String) := do
  let nm := d.to_name
  let b ← is_instance nm
  -- return `none` if `d` is not an instance
      if ¬b then return none
    else do
      let (is_persistent, prio) ← has_attribute `instance nm
      -- return `none` if `d` is has low priority
          if prio < 1000 then return none
        else do
          let (_, tp) ← open_pis d
          let tp ← whnf tp transparency.none
          let (fn, args) := tp
          let cls ← get_decl fn
          let (pi_args, _) := cls
          guard (args = pi_args)
          let/- List all the arguments of the class that block type-class inference from firing
              (if they are metavariables). These are all the arguments except instance-arguments and
              out-params. -/
          relevant_args :=
            (args pi_args).filterMap fun ⟨e, ⟨_, info, tp⟩⟩ =>
              if info = BinderInfo.inst_implicit ∨ tp `out_param then none else some e
          let always_applies := relevant_args expr.is_local_constant ∧ relevant_args
          if always_applies then return <| some "set priority below 1000" else return none
#align instance_priority instance_priority

library_note "implicit instance arguments"/--
There are places where typeclass arguments are specified with implicit `{}` brackets instead of
the usual `[]` brackets. This is done when the instances can be inferred because they are implicit
arguments to the type of one of the other arguments. When they can be inferred from these other
arguments,  it is faster to use this method than to use type class inference.

For example, when writing lemmas about `(f : α →+* β)`, it is faster to specify the fact that `α`
and `β` are `semiring`s as `{rα : semiring α} {rβ : semiring β}` rather than the usual
`[semiring α] [semiring β]`.
-/

library_note "lower instance priority"/--
Certain instances always apply during type-class resolution. For example, the instance
`add_comm_group.to_add_group {α} [add_comm_group α] : add_group α` applies to all type-class
resolution problems of the form `add_group _`, and type-class inference will then do an
exhaustive search to find a commutative group. These instances take a long time to fail.
Other instances will only apply if the goal has a certain shape. For example
`int.add_group : add_group ℤ` or
`add_group.prod {α β} [add_group α] [add_group β] : add_group (α × β)`. Usually these instances
will fail quickly, and when they apply, they are almost always the desired instance.
For this reason, we want the instances of the second type (that only apply in specific cases) to
always have higher priority than the instances of the first type (that always apply).
See also #1561.

Therefore, if we create an instance that always applies, we set the priority of these instances to
100 (or something similar, which is below the default value of 1000).
-/

/-- A linter object for checking instance priorities of instances that always apply.
This is in the default linter set. -/
@[linter]
unsafe def linter.instance_priority : linter
    where
  test := instance_priority
  no_errors_found := "All instance priorities are good."
  errors_found :=
    "DANGEROUS INSTANCE PRIORITIES.\nThe following instances always apply, and therefore should have a priority < 1000.\nIf you don't know what priority to choose, use priority 100.\nSee note [lower instance priority] for instructions to change the priority."
  auto_decls := true
#align linter.instance_priority linter.instance_priority

/-- Reports declarations of types that do not have an nonemptiness instance.
A `nonempty`, `inhabited` or `unique` instance suffices, and we prefer a computable `inhabited`
or `unique` instance if possible. -/
private unsafe def has_nonempty_instance (d : declaration) : tactic (Option String) := do
  let tt ← pure d.is_trusted |
    pure none
  let ff ← has_attribute' `reducible d.to_name |
    pure none
  let ff ← has_attribute' `class d.to_name |
    pure none
  let (_, ty) ← open_pis d.type
  let ty ← whnf ty
  if ty = q(Prop) then pure none
    else do
      let q(Sort _) ← whnf ty |
        pure none
      let insts ← attribute.get_instances `instance
      let insts_tys ← insts fun i => expr.pi_codomain <$> declaration.type <$> get_decl i
      let nonempty_insts := insts_tys fun i => i ∈ [`` Nonempty, `` Inhabited, `unique]
      let nonempty_tys := nonempty_insts fun i => i
      if d ∈ nonempty_tys then pure none else pure "nonempty/inhabited/unique instance missing"
#align has_nonempty_instance has_nonempty_instance

/-- A linter for missing `nonempty` instances. -/
@[linter]
unsafe def linter.has_nonempty_instance : linter
    where
  test := has_nonempty_instance
  auto_decls := false
  no_errors_found := "No types have missing nonempty instances."
  errors_found :=
    "TYPES ARE MISSING NONEMPTY INSTANCES.\nThe following types should have an associated instance of the class\n`nonempty`, or if computably possible `inhabited` or `unique`:"
  is_fast := false
#align linter.has_nonempty_instance linter.has_nonempty_instance

attribute [nolint has_nonempty_instance] PEmpty

/-- Checks whether an instance can never be applied. -/
private unsafe def impossible_instance (d : declaration) : tactic (Option String) := do
  let tt ← is_instance d.to_name |
    return none
  let (binders, _) ← get_pi_binders_nondep d.type
  let bad_arguments := binders.filterₓ fun nb => nb.2.info ≠ BinderInfo.inst_implicit
  let _ :: _ ← return bad_arguments |
    return none
  (fun s => some <| "Impossible to infer " ++ s) <$> print_arguments bad_arguments
#align impossible_instance impossible_instance

/-- A linter object for `impossible_instance`. -/
@[linter]
unsafe def linter.impossible_instance : linter
    where
  test := impossible_instance
  auto_decls := true
  no_errors_found := "All instances are applicable."
  errors_found :=
    "IMPOSSIBLE INSTANCES FOUND.\nThese instances have an argument that cannot be found during type-class resolution, and " ++
        "therefore can never succeed. Either mark the arguments with square brackets (if it is a " ++
      "class), or don't make it an instance."
#align linter.impossible_instance linter.impossible_instance

/-- Checks whether an instance can never be applied. -/
private unsafe def incorrect_type_class_argument (d : declaration) : tactic (Option String) := do
  let (binders, _) ← get_pi_binders d.type
  let instance_arguments :=
    binders.indexesValues fun b : binder => b.info = BinderInfo.inst_implicit
  let bad_arguments
    ←/- the head of the type should either unfold to a class, or be a local constant.
            A local constant is allowed, because that could be a class when applied to the
            proper arguments. -/
          instance_arguments.filterM
        fun ⟨_, b⟩ => do
        let (_, head) ← open_pis b.type
        if head then return ff
          else do
            not <$> is_class head
  let _ :: _ ← return bad_arguments |
    return none
  (fun s => some <| "These are not classes. " ++ s) <$> print_arguments bad_arguments
#align incorrect_type_class_argument incorrect_type_class_argument

/-- A linter object for `incorrect_type_class_argument`. -/
@[linter]
unsafe def linter.incorrect_type_class_argument : linter
    where
  test := incorrect_type_class_argument
  auto_decls := true
  no_errors_found := "All declarations have correct type-class arguments."
  errors_found :=
    "INCORRECT TYPE-CLASS ARGUMENTS.\nSome declarations have non-classes between [square brackets]:"
#align linter.incorrect_type_class_argument linter.incorrect_type_class_argument

/-- Checks whether an instance is dangerous: it creates a new type-class problem with metavariable
arguments. -/
private unsafe def dangerous_instance (d : declaration) : tactic (Option String) := do
  let tt ← is_instance d.to_name |
    return none
  let (local_constants, target) ← open_pis d.type
  let instance_arguments :=
    local_constants.indexesValues fun e : expr => e.local_binding_info = BinderInfo.inst_implicit
  let bad_arguments :=
    local_constants.indexesValues fun x =>
      !target.has_local_constant x && x.local_binding_info ≠ BinderInfo.inst_implicit &&
        instance_arguments.any fun nb => nb.2.local_type.has_local_constant x
  let bad_arguments : List (ℕ × binder) := bad_arguments.map fun ⟨n, e⟩ => ⟨n, e.to_binder⟩
  let _ :: _ ← return bad_arguments |
    return none
  (fun s => some <| "The following arguments become metavariables. " ++ s) <$>
      print_arguments bad_arguments
#align dangerous_instance dangerous_instance

/-- A linter object for `dangerous_instance`. -/
@[linter]
unsafe def linter.dangerous_instance : linter
    where
  test := dangerous_instance
  no_errors_found := "No dangerous instances."
  errors_found :=
    "DANGEROUS INSTANCES FOUND.\nThese instances are recursive, and create a new " ++
        "type-class problem which will have metavariables.\nPossible solution: remove the instance attribute or make it a local instance instead.\n\nCurrently this linter does not check whether the metavariables only occur in arguments marked " ++
      "with `out_param`, in which case this linter gives a false positive."
  auto_decls := true
#align linter.dangerous_instance linter.dangerous_instance

/-- Auxilliary definition for `find_nondep` -/
unsafe def find_nondep_aux : List expr → expr_set → tactic expr_set
  | [], r => return r
  | h :: hs, r => do
    let type ← infer_type h
    find_nondep_aux hs <| r type
#align find_nondep_aux find_nondep_aux

/-- Finds all hypotheses that don't occur in the target or other hypotheses. -/
unsafe def find_nondep : tactic (List expr) := do
  let ctx ← local_context
  let tgt ← target
  let lconsts ← find_nondep_aux ctx tgt.list_local_consts'
  return <| ctx fun e => !lconsts e
#align find_nondep find_nondep

/-- Tests whether type-class inference search will end quickly on certain unsolvable
type-class problems. This is to detect loops or very slow searches, which are problematic
(recall that normal type-class search often creates unsolvable subproblems, which have to fail
quickly for type-class inference to perform well.
We create these type-class problems by taking an instance, and removing the last hypothesis that
doesn't appear in the goal (or a later hypothesis). Note: this argument is necessarily an
instance-implicit argument if it passes the `linter.incorrect_type_class_argument`.
This tactic succeeds if `mk_instance` succeeds quickly or fails quickly with the error
message that it cannot find an instance. It fails if the tactic takes too long, or if any other
error message is raised (usually a maximum depth in the search).
-/
unsafe def fails_quickly (max_steps : ℕ) (d : declaration) : tactic (Option String) :=
  retrieve do
    let tt ← is_instance d.to_name |
      return none
    let e := d.type
    let g ← mk_meta_var e
    set_goals [g]
    intros
    let l@(_ :: _) ← find_nondep |
      return none
    -- if all arguments occur in the goal, this instance is ok
        clear
        l
    reset_instance_cache
    let state ← read
    let state_msg := "\nState:\n" ++ toString StateM
    let tgt ← target >>= instantiate_mvars
    let Sum.inr msg ← retrieve_or_report_error <| tactic.try_for max_steps <| mk_instance tgt |
      return none
    /- it's ok if type-class inference can find an instance with fewer hypotheses.
            This happens a lot for `has_sizeof` and `has_well_founded`, but can also happen if there is a
            noncomputable instance with fewer assumptions. -/
        return <|
        if "tactic.mk_instance failed to generate instance for".isPrefixOfₓ msg then none
        else
          some <|
            (· ++ state_msg) <|
              if msg = "try_for tactic failed, timeout" then "type-class inference timed out"
              else msg
#align fails_quickly fails_quickly

/-- A linter object for `fails_quickly`.
We currently set the number of steps in the type-class search pretty high.
Some instances take quite some time to fail, and we seem to run against the caching issue in
https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/odd.20repeated.20type.20class.20search
-/
@[linter]
unsafe def linter.fails_quickly : linter
    where
  test := fails_quickly 30000
  auto_decls := true
  no_errors_found := "No type-class searches timed out."
  errors_found :=
    "TYPE CLASS SEARCHES TIMED OUT.\nThe following instances are part of a loop, or an excessively long search.\nIt is common that the loop occurs in a different class than the one flagged below,\nbut usually an instance that is part of the loop is also flagged.\nTo debug:\n(1) run `scripts/mk_all.sh` and create a file with `import all` and\n`set_option trace.class_instances true`\n(2) Recreate the state shown in the error message. You can do this easily by copying the type of\nthe instance (the output of `#check @my_instance`), turning this into an example and removing the\nlast argument in square brackets. Prove the example using `by apply_instance`.\nFor example, if `additive.topological_add_group` raises an error, run\n```\nexample {G : Type*} [topological_space G] [group G] : topological_add_group (additive G) :=\nby apply_instance\n```\n(3) What error do you get?\n(3a) If the error is \"tactic.mk_instance failed to generate instance\",\nthere might be nothing wrong. But it might take unreasonably long for the type-class inference to\nfail. Check the trace to see if type-class inference takes any unnecessary long unexpected turns.\nIf not, feel free to increase the value in the definition of the linter `fails_quickly`.\n(3b) If the error is \"maximum class-instance resolution depth has been reached\" there is almost\ncertainly a loop in the type-class inference. Find which instance causes the type-class inference to\ngo astray, and fix that instance."
  is_fast := false
#align linter.fails_quickly linter.fails_quickly

/-- Checks that all uses of the `@[class]` attribute apply to structures or inductive types.
  This is future-proofing for lean 4, which no longer supports `@[class] def`. -/
private unsafe def class_structure (n : Name) : tactic (Option String) := do
  let is_class ← has_attribute' `class n
  if is_class then do
      let env ← get_env
      pure <| if env n then none else "is a non-structure or inductive type marked @[class]"
    else pure none
#align class_structure class_structure

/-- A linter object for `class_structure`. -/
@[linter]
unsafe def linter.class_structure : linter
    where
  test d := class_structure d.to_name
  auto_decls := true
  no_errors_found := "All classes are structures."
  errors_found := "USE OF @[class] def IS DISALLOWED:"
#align linter.class_structure linter.class_structure

/-- Tests whether there is no instance of type `has_coe α t` where `α` is a variable,
or `has_coe t α` where `α` does not occur in `t`.
See note [use has_coe_t].
-/
private unsafe def has_coe_variable (d : declaration) : tactic (Option String) := do
  let tt ← is_instance d.to_name |
    return none
  let q(Coe $(a) $(b)) ← return d.type.pi_codomain |
    return none
  if a then return <| some <| "illegal instance, first argument is variable"
    else
      if b ∧ ¬b a then
        return <|
          some <| "illegal instance, second argument is variable not occurring in first argument"
      else return none
#align has_coe_variable has_coe_variable

/-- A linter object for `has_coe_variable`. -/
@[linter]
unsafe def linter.has_coe_variable : linter
    where
  test := has_coe_variable
  auto_decls := true
  no_errors_found := "No invalid `has_coe` instances."
  errors_found :=
    "INVALID `has_coe` INSTANCES.\nMake the following declarations instances of the class `has_coe_t` instead of `has_coe`."
#align linter.has_coe_variable linter.has_coe_variable

/-- Checks whether a declaration is prop-valued and takes an `inhabited _` argument that is unused
elsewhere in the type. In this case, that argument can be replaced with `nonempty _`. -/
private unsafe def inhabited_nonempty (d : declaration) : tactic (Option String) := do
  let tt ← is_prop d.type |
    return none
  let (binders, _) ← get_pi_binders_nondep d.type
  let inhd_binders := binders.filterₓ fun pr => pr.2.type.is_app_of `inhabited
  if inhd_binders = 0 then return none
    else
      (fun s => some <| "The following `inhabited` instances should be `nonempty`. " ++ s) <$>
        print_arguments inhd_binders
#align inhabited_nonempty inhabited_nonempty

/-- A linter object for `inhabited_nonempty`. -/
@[linter]
unsafe def linter.inhabited_nonempty : linter
    where
  test := inhabited_nonempty
  auto_decls := false
  no_errors_found := "No uses of `inhabited` arguments should be replaced with `nonempty`."
  errors_found := "USES OF `inhabited` SHOULD BE REPLACED WITH `nonempty`."
#align linter.inhabited_nonempty linter.inhabited_nonempty

/-- Checks whether a declaration is `Prop`-valued and takes a `decidable* _`
hypothesis that is unused elsewhere in the type.
In this case, that hypothesis can be replaced with `classical` in the proof.
Theorems in the `decidable` namespace are exempt from the check. -/
private unsafe def decidable_classical (d : declaration) : tactic (Option String) := do
  let tt ← is_prop d.type |
    return none
  let ff ← pure <| `decidable.isPrefixOfₓ d.to_name |
    return none
  let (binders, _) ← get_pi_binders_nondep d.type
  let deceq_binders :=
    binders.filterₓ fun pr =>
      pr.2.type.is_app_of `decidable_eq ∨
        pr.2.type.is_app_of `decidable_pred ∨
          pr.2.type.is_app_of `decidable_rel ∨ pr.2.type.is_app_of `decidable
  if deceq_binders = 0 then return none
    else
      (fun s =>
          some <|
            "The following `decidable` hypotheses should be replaced with\n                      `classical` in the proof. " ++
              s) <$>
        print_arguments deceq_binders
#align decidable_classical decidable_classical

/-- A linter object for `decidable_classical`. -/
@[linter]
unsafe def linter.decidable_classical : linter
    where
  test := decidable_classical
  auto_decls := false
  no_errors_found := "No uses of `decidable` arguments should be replaced with `classical`."
  errors_found := "USES OF `decidable` SHOULD BE REPLACED WITH `classical` IN THE PROOF."
#align linter.decidable_classical linter.decidable_classical

/- The file `logic/basic.lean` emphasizes the differences between what holds under classical
and non-classical logic. It makes little sense to make all these lemmas classical, so we add them
to the list of lemmas which are not checked by the linter `decidable_classical`. -/
attribute [nolint decidable_classical] dec_em dec_em' Not.decidable_imp_symm

/-- Checks whether a declaration is `Prop`-valued and takes a `fintype _`
hypothesis that is unused elsewhere in the type.
In this case, that hypothesis can be replaced with `casesI nonempty_fintype _` in the proof. -/
unsafe def linter.fintype_finite_fun (d : declaration) : tactic (Option String) := do
  let tt ← is_prop d.type |
    return none
  let (binders, _) ← get_pi_binders_nondep d.type
  let fintype_binders := binders.filterₓ fun pr => pr.2.type.is_app_of `fintype
  if fintype_binders = 0 then return none
    else
      (fun s =>
          some <|
            "The following `fintype` hypotheses should be replaced with\n                      `casesI nonempty_fintype _` in the proof. " ++
              s) <$>
        print_arguments fintype_binders
#align linter.fintype_finite_fun linter.fintype_finite_fun

/-- A linter object for `fintype` vs `finite`. -/
@[linter]
unsafe def linter.fintype_finite : linter
    where
  test := linter.fintype_finite_fun
  auto_decls := false
  no_errors_found :=
    "No uses of `fintype` arguments should be replaced with `casesI nonempty_fintype _`."
  errors_found :=
    "USES OF `fintype` SHOULD BE REPLACED WITH `casesI nonempty_fintype _` IN THE PROOF."
#align linter.fintype_finite linter.fintype_finite

private unsafe def has_coe_to_fun_linter (d : declaration) : tactic (Option String) :=
  retrieve do
    let tt ← return d.is_trusted |
      pure none
    mk_meta_var d >>= set_goals ∘ pure
    let args ← unfreezing intros
    let expr.sort _ ← target |
      pure none
    let ty : expr := (expr.const d.to_name d.univ_levels).mk_app args
    let some coe_fn_inst ← try_core <| to_expr ``(CoeFun $(ty) _) >>= mk_instance |
      pure none
    set_bool_option `pp.all True
    let some trans_inst@(expr.app (expr.app _ trans_inst_1) trans_inst_2) ←
      try_core <| to_expr ``(@coeFnTrans $(ty) _ _ _ _) |
      pure none
    let tt ← succeeds <| unify trans_inst coe_fn_inst Transparency.reducible |
      pure none
    set_bool_option `pp.all True
    let trans_inst_1 ← pp trans_inst_1
    let trans_inst_2 ← pp trans_inst_2
    pure <|
        format.to_string <|
          "`has_coe_to_fun` instance is definitionally equal to a transitive instance composed of: " ++
                  trans_inst_1 2 ++
                format.line ++
              "and" ++
            trans_inst_2 2
#align has_coe_to_fun_linter has_coe_to_fun_linter

/-- Linter that checks whether `has_coe_to_fun` instances comply with Note [function coercion]. -/
@[linter]
unsafe def linter.has_coe_to_fun : linter
    where
  test := has_coe_to_fun_linter
  auto_decls := true
  no_errors_found := "has_coe_to_fun is used correctly"
  errors_found :=
    "INVALID/MISSING `has_coe_to_fun` instances.\nYou should add a `has_coe_to_fun` instance for the following types.\nSee Note [function coercion]."
#align linter.has_coe_to_fun linter.has_coe_to_fun

/-- Checks whether an instance contains a semireducible non-instance with a class as
type in its value. We add some restrictions to get not too many false positives:
* We only consider classes with an `add` or `mul` field, since those classes are most likely to
  occur as a field to another class, and be an extension of another class.
* We only consider instances of type-valued classes and non-instances that are definitions.
* We currently ignore declarations `foo` that have a `foo._main` declaration. We could look inside,
or at the generated equation lemmas, but it's unlikely that there are many problematic instances
defined using the equation compiler.
-/
unsafe def check_reducible_non_instances (d : declaration) : tactic (Option String) := do
  let tt ← is_instance d.to_name |
    return none
  let ff ← is_prop d.type |
    return none
  let env ← get_env
  let-- We only check if the class of the instance contains an `add` or a `mul` field.
  cls := d.type.pi_codomain.get_app_fn.const_name
  let some constrs ← return <| env.structure_fields cls |
    return none
  let tt ← return <| constrs.Mem `add || constrs.Mem `mul |
    return none
  let l ←
    d.value.list_constant.filterM fun nm => do
        let d ← env.get nm
        let ff ← is_instance nm |
          return false
        let tt ← is_class d.type |
          return false
        let tt ← return d.is_definition |
          return false
        let-- We only check if the class of the non-instance contains an `add` or a `mul` field.
        cls := d.type.pi_codomain.get_app_fn.const_name
        let some constrs ← return <| env.structure_fields cls |
          return false
        let tt ← return <| constrs.Mem `add || constrs.Mem `mul |
          return false
        let ff ← has_attribute' `reducible nm |
          return false
        return tt
  if l then return none
    else-- we currently ignore declarations that have a `foo._main` declaration.
        if l = [d ++ `_main] then return none
      else
        return <|
          some <|
            "This instance contains the declarations " ++ toString l ++
              ", which are semireducible non-instances."
#align check_reducible_non_instances check_reducible_non_instances

/-- A linter that checks whether an instance contains a semireducible non-instance. -/
@[linter]
unsafe def linter.check_reducibility : linter
    where
  test := check_reducible_non_instances
  auto_decls := false
  no_errors_found := "All non-instances are reducible."
  errors_found :=
    "THE FOLLOWING INSTANCES MIGHT NOT REDUCE.\nThese instances contain one or more declarations that are not instances and are also not marked\n`@[reducible]`. This means that type-class inference cannot unfold these declarations, " ++
          "which might mean that type-class inference cannot infer that two instances are definitionally " ++
        "equal. This can cause unexpected errors when this class occurs " ++
      "as an *argument* to a type-class problem. See note [reducible non-instances]."
  is_fast := true
#align linter.check_reducibility linter.check_reducibility


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Stephen Morgan, Scott Morrison, Floris van Doorn

! This file was ported from Lean 3 source module category_theory.discrete_category
! leanprover-community/mathlib commit 23aa88e32dcc9d2a24cca7bc23268567ed4cd7d6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.EqToHom
import Mathbin.Data.Ulift

/-!
# Discrete categories

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

We define `discrete α` as a structure containing a term `a : α` for any type `α`,
and use this type alias to provide a `small_category` instance
whose only morphisms are the identities.

There is an annoying technical difficulty that it has turned out to be inconvenient
to allow categories with morphisms living in `Prop`,
so instead of defining `X ⟶ Y` in `discrete α` as `X = Y`,
one might define it as `plift (X = Y)`.
In fact, to allow `discrete α` to be a `small_category`
(i.e. with morphisms in the same universe as the objects),
we actually define the hom type `X ⟶ Y` as `ulift (plift (X = Y))`.

`discrete.functor` promotes a function `f : I → C` (for any category `C`) to a functor
`discrete.functor f : discrete I ⥤ C`.

Similarly, `discrete.nat_trans` and `discrete.nat_iso` promote `I`-indexed families of morphisms,
or `I`-indexed families of isomorphisms to natural transformations or natural isomorphism.

We show equivalences of types are the same as (categorical) equivalences of the corresponding
discrete categories.
-/

namespace CategoryTheory

-- morphism levels before object levels. See note [category_theory universes].
universe v₁ v₂ v₃ u₁ u₁' u₂ u₃

#print CategoryTheory.Discrete /-
-- This is intentionally a structure rather than a type synonym
-- to enforce using `discrete_equiv` (or `discrete.mk` and `discrete.as`) to move between
-- `discrete α` and `α`. Otherwise there is too much API leakage.
/-- A wrapper for promoting any type to a category,
with the only morphisms being equalities.
-/
@[ext]
structure Discrete (α : Type u₁) where
  as : α
#align category_theory.discrete CategoryTheory.Discrete
-/

#print CategoryTheory.Discrete.mk_as /-
@[simp]
theorem Discrete.mk_as {α : Type u₁} (X : Discrete α) : Discrete.mk X.as = X :=
  by
  ext
  rfl
#align category_theory.discrete.mk_as CategoryTheory.Discrete.mk_as
-/

#print CategoryTheory.discreteEquiv /-
/-- `discrete α` is equivalent to the original type `α`.-/
@[simps]
def discreteEquiv {α : Type u₁} : Discrete α ≃ α
    where
  toFun := Discrete.as
  invFun := Discrete.mk
  left_inv := by tidy
  right_inv := by tidy
#align category_theory.discrete_equiv CategoryTheory.discreteEquiv
-/

instance {α : Type u₁} [DecidableEq α] : DecidableEq (Discrete α) :=
  discreteEquiv.DecidableEq

#print CategoryTheory.discreteCategory /-
/-- The "discrete" category on a type, whose morphisms are equalities.

Because we do not allow morphisms in `Prop` (only in `Type`),
somewhat annoyingly we have to define `X ⟶ Y` as `ulift (plift (X = Y))`.

See <https://stacks.math.columbia.edu/tag/001A>
-/
instance discreteCategory (α : Type u₁) : SmallCategory (Discrete α)
    where
  Hom X Y := ULift (PLift (X.as = Y.as))
  id X := ULift.up (PLift.up rfl)
  comp X Y Z g f := by
    cases X
    cases Y
    cases Z
    rcases f with ⟨⟨⟨⟩⟩⟩
    exact g
#align category_theory.discrete_category CategoryTheory.discreteCategory
-/

namespace Discrete

variable {α : Type u₁}

instance [Inhabited α] : Inhabited (Discrete α) :=
  ⟨⟨default⟩⟩

instance [Subsingleton α] : Subsingleton (Discrete α) :=
  ⟨by
    intros
    ext
    apply Subsingleton.elim⟩

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/-- A simple tactic to run `cases` on any `discrete α` hypotheses. -/
unsafe def _root_.tactic.discrete_cases : tactic Unit :=
  sorry
#align tactic.discrete_cases tactic.discrete_cases

run_cmd
  add_interactive [`` tactic.discrete_cases]

attribute [local tidy] tactic.discrete_cases

instance [Unique α] : Unique (Discrete α) :=
  Unique.mk' (Discrete α)

#print CategoryTheory.Discrete.eq_of_hom /-
/-- Extract the equation from a morphism in a discrete category. -/
theorem eq_of_hom {X Y : Discrete α} (i : X ⟶ Y) : X.as = Y.as :=
  i.down.down
#align category_theory.discrete.eq_of_hom CategoryTheory.Discrete.eq_of_hom
-/

#print CategoryTheory.Discrete.eqToHom /-
/-- Promote an equation between the wrapped terms in `X Y : discrete α` to a morphism `X ⟶ Y`
in the discrete category. -/
abbrev eqToHom {X Y : Discrete α} (h : X.as = Y.as) : X ⟶ Y :=
  eqToHom
    (by
      ext
      exact h)
#align category_theory.discrete.eq_to_hom CategoryTheory.Discrete.eqToHom
-/

#print CategoryTheory.Discrete.eqToIso /-
/-- Promote an equation between the wrapped terms in `X Y : discrete α` to an isomorphism `X ≅ Y`
in the discrete category. -/
abbrev eqToIso {X Y : Discrete α} (h : X.as = Y.as) : X ≅ Y :=
  eqToIso
    (by
      ext
      exact h)
#align category_theory.discrete.eq_to_iso CategoryTheory.Discrete.eqToIso
-/

#print CategoryTheory.Discrete.eqToHom' /-
/-- A variant of `eq_to_hom` that lifts terms to the discrete category. -/
abbrev eqToHom' {a b : α} (h : a = b) : Discrete.mk a ⟶ Discrete.mk b :=
  eqToHom h
#align category_theory.discrete.eq_to_hom' CategoryTheory.Discrete.eqToHom'
-/

#print CategoryTheory.Discrete.eqToIso' /-
/-- A variant of `eq_to_iso` that lifts terms to the discrete category. -/
abbrev eqToIso' {a b : α} (h : a = b) : Discrete.mk a ≅ Discrete.mk b :=
  eqToIso h
#align category_theory.discrete.eq_to_iso' CategoryTheory.Discrete.eqToIso'
-/

#print CategoryTheory.Discrete.id_def /-
@[simp]
theorem id_def (X : Discrete α) : ULift.up (PLift.up (Eq.refl X.as)) = 𝟙 X :=
  rfl
#align category_theory.discrete.id_def CategoryTheory.Discrete.id_def
-/

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

instance {I : Type u₁} {i j : Discrete I} (f : i ⟶ j) : IsIso f :=
  ⟨⟨eqToHom (eq_of_hom f).symm, by tidy⟩⟩

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
#print CategoryTheory.Discrete.functor /-
/-- Any function `I → C` gives a functor `discrete I ⥤ C`.
-/
def functor {I : Type u₁} (F : I → C) : Discrete I ⥤ C
    where
  obj := F ∘ Discrete.as
  map X Y f :=
    by
    trace
      "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
    cases f
    exact 𝟙 (F X)
#align category_theory.discrete.functor CategoryTheory.Discrete.functor
-/

/- warning: category_theory.discrete.functor_obj -> CategoryTheory.Discrete.functor_obj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} (F : I -> C) (i : I), Eq.{succ u3} C (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F) (CategoryTheory.Discrete.mk.{u2} I i)) (F i)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} (F : I -> C) (i : I), Eq.{succ u3} C (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F)) (CategoryTheory.Discrete.mk.{u2} I i)) (F i)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.functor_obj CategoryTheory.Discrete.functor_objₓ'. -/
@[simp]
theorem functor_obj {I : Type u₁} (F : I → C) (i : I) :
    (Discrete.functor F).obj (Discrete.mk i) = F i :=
  rfl
#align category_theory.discrete.functor_obj CategoryTheory.Discrete.functor_obj

/- warning: category_theory.discrete.functor_map -> CategoryTheory.Discrete.functor_map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} (F : I -> C) {i : CategoryTheory.Discrete.{u2} I} (f : Quiver.Hom.{succ u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) i i), Eq.{succ u1} (Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F) i) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F) i)) (CategoryTheory.Functor.map.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F) i i f) (CategoryTheory.CategoryStruct.id.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) (F (CategoryTheory.Discrete.as.{u2} I i)))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} (F : I -> C) {i : CategoryTheory.Discrete.{u2} I} (f : Quiver.Hom.{succ u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) i i), Eq.{succ u1} (Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F)) i) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F)) i)) (Prefunctor.map.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I F)) i i f) (CategoryTheory.CategoryStruct.id.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) (F (CategoryTheory.Discrete.as.{u2} I i)))
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.functor_map CategoryTheory.Discrete.functor_mapₓ'. -/
theorem functor_map {I : Type u₁} (F : I → C) {i : Discrete I} (f : i ⟶ i) :
    (Discrete.functor F).map f = 𝟙 (F i.as) := by tidy
#align category_theory.discrete.functor_map CategoryTheory.Discrete.functor_map

#print CategoryTheory.Discrete.functorComp /-
/-- The discrete functor induced by a composition of maps can be written as a
composition of two discrete functors.
-/
@[simps]
def functorComp {I : Type u₁} {J : Type u₁'} (f : J → C) (g : I → J) :
    Discrete.functor (f ∘ g) ≅ Discrete.functor (Discrete.mk ∘ g) ⋙ Discrete.functor f :=
  NatIso.ofComponents (fun X => Iso.refl _) (by tidy)
#align category_theory.discrete.functor_comp CategoryTheory.Discrete.functorComp
-/

/- warning: category_theory.discrete.nat_trans -> CategoryTheory.Discrete.natTrans is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, (forall (i : CategoryTheory.Discrete.{u2} I), Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F i) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G i)) -> (Quiver.Hom.{succ (max u2 u1), max u2 u1 u2 u3} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u1, max u2 u1 u2 u3} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Category.toCategoryStruct.{max u2 u1, max u2 u1 u2 u3} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1))) F G)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, (forall (i : CategoryTheory.Discrete.{u2} I), Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F) i) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G) i)) -> (Quiver.Hom.{max (succ u2) (succ u1), max (max u2 u3) u1} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u1, max (max u2 u3) u1} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Category.toCategoryStruct.{max u2 u1, max (max u2 u3) u1} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1))) F G)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.nat_trans CategoryTheory.Discrete.natTransₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- For functors out of a discrete category,
a natural transformation is just a collection of maps,
as the naturality squares are trivial.
-/
@[simps]
def natTrans {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ⟶ G.obj i) : F ⟶ G
    where
  app := f
  naturality' X Y g :=
    by
    trace
      "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
    cases g
    simp
#align category_theory.discrete.nat_trans CategoryTheory.Discrete.natTrans

/- warning: category_theory.discrete.nat_iso -> CategoryTheory.Discrete.natIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, (forall (i : CategoryTheory.Discrete.{u2} I), CategoryTheory.Iso.{u1, u3} C _inst_1 (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F i) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G i)) -> (CategoryTheory.Iso.{max u2 u1, max u2 u1 u2 u3} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) F G)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, (forall (i : CategoryTheory.Discrete.{u2} I), CategoryTheory.Iso.{u1, u3} C _inst_1 (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F) i) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G) i)) -> (CategoryTheory.Iso.{max u2 u1, max (max u2 u3) u1} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) F G)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.nat_iso CategoryTheory.Discrete.natIsoₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- For functors out of a discrete category,
a natural isomorphism is just a collection of isomorphisms,
as the naturality squares are trivial.
-/
@[simps]
def natIso {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ≅ G.obj i) : F ≅ G :=
  NatIso.ofComponents f fun X Y g =>
    by
    trace
      "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
    cases g
    simp
#align category_theory.discrete.nat_iso CategoryTheory.Discrete.natIso

/- warning: category_theory.discrete.nat_iso_app -> CategoryTheory.Discrete.natIso_app is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} (f : forall (i : CategoryTheory.Discrete.{u2} I), CategoryTheory.Iso.{u1, u3} C _inst_1 (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F i) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G i)) (i : CategoryTheory.Discrete.{u2} I), Eq.{succ u1} (CategoryTheory.Iso.{u1, u3} C _inst_1 (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F i) (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G i)) (CategoryTheory.Iso.app.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F G (CategoryTheory.Discrete.natIso.{u1, u2, u3} C _inst_1 I F G f) i) (f i)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} {G : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1} (f : forall (i : CategoryTheory.Discrete.{u2} I), CategoryTheory.Iso.{u1, u3} C _inst_1 (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F) i) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G) i)) (i : CategoryTheory.Discrete.{u2} I), Eq.{succ u1} (CategoryTheory.Iso.{u1, u3} C _inst_1 (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F) i) (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 G) i)) (CategoryTheory.Iso.app.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F G (CategoryTheory.Discrete.natIso.{u1, u2, u3} C _inst_1 I F G f) i) (f i)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.nat_iso_app CategoryTheory.Discrete.natIso_appₓ'. -/
@[simp]
theorem natIso_app {I : Type u₁} {F G : Discrete I ⥤ C} (f : ∀ i : Discrete I, F.obj i ≅ G.obj i)
    (i : Discrete I) : (Discrete.natIso f).app i = f i := by tidy
#align category_theory.discrete.nat_iso_app CategoryTheory.Discrete.natIso_app

/- warning: category_theory.discrete.nat_iso_functor -> CategoryTheory.Discrete.natIsoFunctor is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, CategoryTheory.Iso.{max u2 u1, max u2 u1 u2 u3} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) F (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I (Function.comp.{succ u2, succ u2, succ u3} I (CategoryTheory.Discrete.{u2} I) C (CategoryTheory.Functor.obj.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F) (CategoryTheory.Discrete.mk.{u2} I)))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {I : Type.{u2}} {F : CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1}, CategoryTheory.Iso.{max u2 u1, max (max u2 u3) u1} (CategoryTheory.Functor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) (CategoryTheory.Functor.category.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1) F (CategoryTheory.Discrete.functor.{u1, u2, u3} C _inst_1 I (Function.comp.{succ u2, succ u2, succ u3} I (CategoryTheory.Discrete.{u2} I) C (Prefunctor.obj.{succ u2, succ u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.CategoryStruct.toQuiver.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.Category.toCategoryStruct.{u2, u2} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u2, u1, u2, u3} (CategoryTheory.Discrete.{u2} I) (CategoryTheory.discreteCategory.{u2} I) C _inst_1 F)) (CategoryTheory.Discrete.mk.{u2} I)))
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.nat_iso_functor CategoryTheory.Discrete.natIsoFunctorₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- Every functor `F` from a discrete category is naturally isomorphic (actually, equal) to
  `discrete.functor (F.obj)`. -/
@[simp]
def natIsoFunctor {I : Type u₁} {F : Discrete I ⥤ C} : F ≅ Discrete.functor (F.obj ∘ Discrete.mk) :=
  natIso fun i =>
    by
    trace
      "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
    rfl
#align category_theory.discrete.nat_iso_functor CategoryTheory.Discrete.natIsoFunctor

/- warning: category_theory.discrete.comp_nat_iso_discrete -> CategoryTheory.Discrete.compNatIsoDiscrete is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] {I : Type.{u3}} {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u2, u5} D] (F : I -> C) (G : CategoryTheory.Functor.{u1, u2, u4, u5} C _inst_1 D _inst_2), CategoryTheory.Iso.{max u3 u2, max u3 u2 u3 u5} (CategoryTheory.Functor.{u3, u2, u3, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) D _inst_2) (CategoryTheory.Functor.category.{u3, u2, u3, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) D _inst_2) (CategoryTheory.Functor.comp.{u3, u1, u2, u3, u4, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u1, u3, u4} C _inst_1 I F) G) (CategoryTheory.Discrete.functor.{u2, u3, u5} D _inst_2 I (Function.comp.{succ u3, succ u4, succ u5} I C D (CategoryTheory.Functor.obj.{u1, u2, u4, u5} C _inst_1 D _inst_2 G) F))
but is expected to have type
  forall {C : Type.{u4}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] {I : Type.{u3}} {D : Type.{u5}} [_inst_2 : CategoryTheory.Category.{u2, u5} D] (F : I -> C) (G : CategoryTheory.Functor.{u1, u2, u4, u5} C _inst_1 D _inst_2), CategoryTheory.Iso.{max u3 u2, max (max (max u5 u3) u2) u3} (CategoryTheory.Functor.{u3, u2, u3, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) D _inst_2) (CategoryTheory.Functor.category.{u3, u2, u3, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) D _inst_2) (CategoryTheory.Functor.comp.{u3, u1, u2, u3, u4, u5} (CategoryTheory.Discrete.{u3} I) (CategoryTheory.discreteCategory.{u3} I) C _inst_1 D _inst_2 (CategoryTheory.Discrete.functor.{u1, u3, u4} C _inst_1 I F) G) (CategoryTheory.Discrete.functor.{u2, u3, u5} D _inst_2 I (Function.comp.{succ u3, succ u4, succ u5} I C D (Prefunctor.obj.{succ u1, succ u2, u4, u5} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u4, u5} C _inst_1 D _inst_2 G)) F))
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.comp_nat_iso_discrete CategoryTheory.Discrete.compNatIsoDiscreteₓ'. -/
/-- Composing `discrete.functor F` with another functor `G` amounts to composing `F` with `G.obj` -/
@[simp]
def compNatIsoDiscrete {I : Type u₁} {D : Type u₃} [Category.{v₃} D] (F : I → C) (G : C ⥤ D) :
    Discrete.functor F ⋙ G ≅ Discrete.functor (G.obj ∘ F) :=
  natIso fun i => Iso.refl _
#align category_theory.discrete.comp_nat_iso_discrete CategoryTheory.Discrete.compNatIsoDiscrete

/- warning: category_theory.discrete.equivalence -> CategoryTheory.Discrete.equivalence is a dubious translation:
lean 3 declaration is
  forall {I : Type.{u1}} {J : Type.{u2}}, (Equiv.{succ u1, succ u2} I J) -> (CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} I) (CategoryTheory.discreteCategory.{u1} I) (CategoryTheory.Discrete.{u2} J) (CategoryTheory.discreteCategory.{u2} J))
but is expected to have type
  forall {I : Type.{u1}} {J : Type.{u2}}, (Equiv.{succ u1, succ u2} I J) -> (CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} I) (CategoryTheory.Discrete.{u2} J) (CategoryTheory.discreteCategory.{u1} I) (CategoryTheory.discreteCategory.{u2} J))
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.equivalence CategoryTheory.Discrete.equivalenceₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- We can promote a type-level `equiv` to
an equivalence between the corresponding `discrete` categories.
-/
@[simps]
def equivalence {I : Type u₁} {J : Type u₂} (e : I ≃ J) : Discrete I ≌ Discrete J
    where
  Functor := Discrete.functor (Discrete.mk ∘ (e : I → J))
  inverse := Discrete.functor (Discrete.mk ∘ (e.symm : J → I))
  unitIso :=
    Discrete.natIso fun i =>
      eqToIso
        (by
          trace
            "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
          simp)
  counitIso :=
    Discrete.natIso fun j =>
      eqToIso
        (by
          trace
            "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
          simp)
#align category_theory.discrete.equivalence CategoryTheory.Discrete.equivalence

/- warning: category_theory.discrete.equiv_of_equivalence -> CategoryTheory.Discrete.equivOfEquivalence is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}}, (CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} α) (CategoryTheory.discreteCategory.{u1} α) (CategoryTheory.Discrete.{u2} β) (CategoryTheory.discreteCategory.{u2} β)) -> (Equiv.{succ u1, succ u2} α β)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}}, (CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} α) (CategoryTheory.Discrete.{u2} β) (CategoryTheory.discreteCategory.{u1} α) (CategoryTheory.discreteCategory.{u2} β)) -> (Equiv.{succ u1, succ u2} α β)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.equiv_of_equivalence CategoryTheory.Discrete.equivOfEquivalenceₓ'. -/
/-- We can convert an equivalence of `discrete` categories to a type-level `equiv`. -/
@[simps]
def equivOfEquivalence {α : Type u₁} {β : Type u₂} (h : Discrete α ≌ Discrete β) : α ≃ β
    where
  toFun := Discrete.as ∘ h.Functor.obj ∘ Discrete.mk
  invFun := Discrete.as ∘ h.inverse.obj ∘ Discrete.mk
  left_inv a := by simpa using eq_of_hom (h.unit_iso.app (discrete.mk a)).2
  right_inv a := by simpa using eq_of_hom (h.counit_iso.app (discrete.mk a)).1
#align category_theory.discrete.equiv_of_equivalence CategoryTheory.Discrete.equivOfEquivalence

end Discrete

namespace Discrete

variable {J : Type v₁}

open Opposite

/- warning: category_theory.discrete.opposite -> CategoryTheory.Discrete.opposite is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}), CategoryTheory.Equivalence.{u1, u1, u1, u1} (Opposite.{succ u1} (CategoryTheory.Discrete.{u1} α)) (CategoryTheory.Category.opposite.{u1, u1} (CategoryTheory.Discrete.{u1} α) (CategoryTheory.discreteCategory.{u1} α)) (CategoryTheory.Discrete.{u1} α) (CategoryTheory.discreteCategory.{u1} α)
but is expected to have type
  forall (α : Type.{u1}), CategoryTheory.Equivalence.{u1, u1, u1, u1} (Opposite.{succ u1} (CategoryTheory.Discrete.{u1} α)) (CategoryTheory.Discrete.{u1} α) (CategoryTheory.Category.opposite.{u1, u1} (CategoryTheory.Discrete.{u1} α) (CategoryTheory.discreteCategory.{u1} α)) (CategoryTheory.discreteCategory.{u1} α)
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.opposite CategoryTheory.Discrete.oppositeₓ'. -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic tactic.op_induction' -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[] -/
/-- A discrete category is equivalent to its opposite category. -/
@[simps functor_obj_as inverse_obj]
protected def opposite (α : Type u₁) : (Discrete α)ᵒᵖ ≌ Discrete α :=
  by
  let F : Discrete α ⥤ (Discrete α)ᵒᵖ := Discrete.functor fun x => op (Discrete.mk x)
  refine'
    equivalence.mk (functor.left_op F) F _
      (discrete.nat_iso fun X =>
        by
        trace
          "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
        simp [F])
  refine'
    nat_iso.of_components
      (fun X =>
        by
        run_tac
          tactic.op_induction'
        trace
          "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `discrete_cases #[]"
        simp [F])
      _
  tidy
#align category_theory.discrete.opposite CategoryTheory.Discrete.opposite

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

/- warning: category_theory.discrete.functor_map_id -> CategoryTheory.Discrete.functor_map_id is a dubious translation:
lean 3 declaration is
  forall {J : Type.{u1}} {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u2, u3} C] (F : CategoryTheory.Functor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1) {j : CategoryTheory.Discrete.{u1} J} (f : Quiver.Hom.{succ u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) j j), Eq.{succ u2} (Quiver.Hom.{succ u2, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (CategoryTheory.Functor.obj.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F j) (CategoryTheory.Functor.obj.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F j)) (CategoryTheory.Functor.map.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F j j f) (CategoryTheory.CategoryStruct.id.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1) (CategoryTheory.Functor.obj.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F j))
but is expected to have type
  forall {J : Type.{u1}} {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u2, u3} C] (F : CategoryTheory.Functor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1) {j : CategoryTheory.Discrete.{u1} J} (f : Quiver.Hom.{succ u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) j j), Eq.{succ u2} (Quiver.Hom.{succ u2, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (Prefunctor.obj.{succ u1, succ u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F) j) (Prefunctor.obj.{succ u1, succ u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F) j)) (Prefunctor.map.{succ u1, succ u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F) j j f) (CategoryTheory.CategoryStruct.id.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1) (Prefunctor.obj.{succ u1, succ u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.CategoryStruct.toQuiver.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.Category.toCategoryStruct.{u1, u1} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J))) C (CategoryTheory.CategoryStruct.toQuiver.{u2, u3} C (CategoryTheory.Category.toCategoryStruct.{u2, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u1, u3} (CategoryTheory.Discrete.{u1} J) (CategoryTheory.discreteCategory.{u1} J) C _inst_1 F) j))
Case conversion may be inaccurate. Consider using '#align category_theory.discrete.functor_map_id CategoryTheory.Discrete.functor_map_idₓ'. -/
@[simp]
theorem functor_map_id (F : Discrete J ⥤ C) {j : Discrete J} (f : j ⟶ j) : F.map f = 𝟙 (F.obj j) :=
  by
  have h : f = 𝟙 j := by
    cases f
    cases f
    ext
  rw [h]
  simp
#align category_theory.discrete.functor_map_id CategoryTheory.Discrete.functor_map_id

end Discrete

end CategoryTheory


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.ring.basic
import Mathlib.data.equiv.basic
import Mathlib.PostPort

universes u_1 u v

namespace Mathlib

/-- Add an extra element `1` to a type -/
def with_one (α : Type u_1) :=
  Option α

namespace with_one

protected instance Mathlib.with_zero.monad : Monad with_zero :=
  option.monad

protected instance has_one {α : Type u} : HasOne (with_one α) :=
  { one := none }

protected instance Mathlib.with_zero.inhabited {α : Type u} : Inhabited (with_zero α) :=
  { default := 0 }

protected instance Mathlib.with_zero.nontrivial {α : Type u} [Nonempty α] : nontrivial (with_zero α) :=
  option.nontrivial

protected instance Mathlib.with_zero.has_coe_t {α : Type u} : has_coe_t α (with_zero α) :=
  has_coe_t.mk some

theorem Mathlib.with_zero.some_eq_coe {α : Type u} {a : α} : some a = ↑a :=
  rfl

@[simp] theorem coe_ne_one {α : Type u} {a : α} : ↑a ≠ 1 :=
  option.some_ne_none a

@[simp] theorem one_ne_coe {α : Type u} {a : α} : 1 ≠ ↑a :=
  ne.symm coe_ne_one

theorem Mathlib.with_zero.ne_zero_iff_exists {α : Type u} {x : with_zero α} : x ≠ 0 ↔ ∃ (a : α), ↑a = x :=
  option.ne_none_iff_exists

-- `to_additive` fails to generate some meta info around eqn lemmas, so `lift` doesn't work

-- unless we explicitly define this instance

protected instance can_lift {α : Type u} : can_lift (with_one α) α :=
  can_lift.mk coe (fun (a : with_one α) => a ≠ 1) sorry

@[simp] theorem Mathlib.with_zero.coe_inj {α : Type u} {a : α} {b : α} : ↑a = ↑b ↔ a = b :=
  option.some_inj

protected theorem Mathlib.with_zero.cases_on {α : Type u} {P : with_zero α → Prop} (x : with_zero α) : P 0 → (∀ (a : α), P ↑a) → P x :=
  option.cases_on

protected instance Mathlib.with_zero.has_add {α : Type u} [Add α] : Add (with_zero α) :=
  { add := option.lift_or_get Add.add }

protected instance monoid {α : Type u} [semigroup α] : monoid (with_one α) :=
  monoid.mk Mul.mul sorry 1 sorry sorry

protected instance Mathlib.with_zero.add_comm_monoid {α : Type u} [add_comm_semigroup α] : add_comm_monoid (with_zero α) :=
  add_comm_monoid.mk add_monoid.add sorry add_monoid.zero sorry sorry sorry

/-- `coe` as a bundled morphism -/
def coe_mul_hom {α : Type u} [Mul α] : mul_hom α (with_one α) :=
  mul_hom.mk coe sorry

/-- Lift a semigroup homomorphism `f` to a bundled monoid homorphism. -/
def Mathlib.with_zero.lift {α : Type u} [add_semigroup α] {β : Type v} [add_monoid β] : add_hom α β ≃ (with_zero α →+ β) :=
  equiv.mk (fun (f : add_hom α β) => add_monoid_hom.mk (fun (x : with_zero α) => option.cases_on x 0 ⇑f) sorry sorry)
    (fun (F : with_zero α →+ β) => add_hom.comp (add_monoid_hom.to_add_hom F) with_zero.coe_add_hom) sorry sorry

@[simp] theorem Mathlib.with_zero.lift_coe {α : Type u} [add_semigroup α] {β : Type v} [add_monoid β] (f : add_hom α β) (x : α) : coe_fn (coe_fn with_zero.lift f) ↑x = coe_fn f x :=
  rfl

@[simp] theorem lift_one {α : Type u} [semigroup α] {β : Type v} [monoid β] (f : mul_hom α β) : coe_fn (coe_fn lift f) 1 = 1 :=
  rfl

theorem Mathlib.with_zero.lift_unique {α : Type u} [add_semigroup α] {β : Type v} [add_monoid β] (f : with_zero α →+ β) : f = coe_fn with_zero.lift (add_hom.comp (add_monoid_hom.to_add_hom f) with_zero.coe_add_hom) :=
  Eq.symm (equiv.apply_symm_apply with_zero.lift f)

/-- Given a multiplicative map from `α → β` returns a monoid homomorphism
  from `with_one α` to `with_one β` -/
def Mathlib.with_zero.map {α : Type u} {β : Type v} [add_semigroup α] [add_semigroup β] (f : add_hom α β) : with_zero α →+ with_zero β :=
  coe_fn with_zero.lift (add_hom.comp with_zero.coe_add_hom f)

@[simp] theorem Mathlib.with_zero.coe_add {α : Type u} [Add α] (a : α) (b : α) : ↑(a + b) = ↑a + ↑b :=
  rfl

end with_one

namespace with_zero

-- `to_additive` fails to generate some meta info around eqn lemmas, so `lift` doesn't work

-- unless we explicitly define this instance

protected instance can_lift {α : Type u} : can_lift (with_zero α) α :=
  can_lift.mk coe (fun (a : with_zero α) => a ≠ 0) sorry

protected instance has_one {α : Type u} [one : HasOne α] : HasOne (with_zero α) :=
  { one := ↑1 }

@[simp] theorem coe_one {α : Type u} [HasOne α] : ↑1 = 1 :=
  rfl

protected instance mul_zero_class {α : Type u} [Mul α] : mul_zero_class (with_zero α) :=
  mul_zero_class.mk (fun (o₁ o₂ : with_zero α) => option.bind o₁ fun (a : α) => option.map (fun (b : α) => a * b) o₂) 0
    sorry sorry

@[simp] theorem coe_mul {α : Type u} [Mul α] {a : α} {b : α} : ↑(a * b) = ↑a * ↑b :=
  rfl

@[simp] theorem zero_mul {α : Type u} [Mul α] (a : with_zero α) : 0 * a = 0 :=
  rfl

@[simp] theorem mul_zero {α : Type u} [Mul α] (a : with_zero α) : a * 0 = 0 :=
  option.cases_on a (Eq.refl (none * 0)) fun (a : α) => Eq.refl (some a * 0)

protected instance semigroup {α : Type u} [semigroup α] : semigroup (with_zero α) :=
  semigroup.mk mul_zero_class.mul sorry

protected instance comm_semigroup {α : Type u} [comm_semigroup α] : comm_semigroup (with_zero α) :=
  comm_semigroup.mk semigroup.mul sorry sorry

protected instance monoid_with_zero {α : Type u} [monoid α] : monoid_with_zero (with_zero α) :=
  monoid_with_zero.mk mul_zero_class.mul sorry 1 sorry sorry mul_zero_class.zero sorry sorry

protected instance comm_monoid_with_zero {α : Type u} [comm_monoid α] : comm_monoid_with_zero (with_zero α) :=
  comm_monoid_with_zero.mk monoid_with_zero.mul sorry monoid_with_zero.one sorry sorry sorry monoid_with_zero.zero sorry
    sorry

/-- Given an inverse operation on `α` there is an inverse operation
  on `with_zero α` sending `0` to `0`-/
def inv {α : Type u} [has_inv α] (x : with_zero α) : with_zero α :=
  do
    let a ← x
    return (a⁻¹)

protected instance has_inv {α : Type u} [has_inv α] : has_inv (with_zero α) :=
  has_inv.mk inv

@[simp] theorem coe_inv {α : Type u} [has_inv α] (a : α) : ↑(a⁻¹) = (↑a⁻¹) :=
  rfl

@[simp] theorem inv_zero {α : Type u} [has_inv α] : 0⁻¹ = 0 :=
  rfl

@[simp] theorem inv_one {α : Type u} [group α] : 1⁻¹ = 1 := sorry

/-- if `G` is a group then `with_zero G` is a group with zero. -/
protected instance group_with_zero {α : Type u} [group α] : group_with_zero (with_zero α) :=
  group_with_zero.mk monoid_with_zero.mul sorry monoid_with_zero.one sorry sorry monoid_with_zero.zero sorry sorry
    has_inv.inv (div_inv_monoid.div._default monoid_with_zero.mul sorry monoid_with_zero.one sorry sorry has_inv.inv)
    sorry sorry sorry

theorem div_coe {α : Type u} [group α] (a : α) (b : α) : ↑a / ↑b = ↑(a * (b⁻¹)) :=
  rfl

protected instance comm_group_with_zero {α : Type u} [comm_group α] : comm_group_with_zero (with_zero α) :=
  comm_group_with_zero.mk group_with_zero.mul sorry group_with_zero.one sorry sorry sorry group_with_zero.zero sorry sorry
    group_with_zero.inv group_with_zero.div sorry sorry sorry

protected instance semiring {α : Type u} [semiring α] : semiring (with_zero α) :=
  semiring.mk add_comm_monoid.add sorry add_comm_monoid.zero sorry sorry sorry mul_zero_class.mul sorry
    monoid_with_zero.one sorry sorry sorry sorry sorry sorry