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

Additional equiv and encodable instances for lists, finsets, and fintypes.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.equiv.denumerable
import Mathlib.data.finset.sort
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

namespace encodable

def encode_list {α : Type u_1} [encodable α] : List α → ℕ :=
  sorry

def decode_list {α : Type u_1} [encodable α] : ℕ → Option (List α) :=
  sorry

protected instance list {α : Type u_1} [encodable α] : encodable (List α) :=
  mk encode_list decode_list sorry

@[simp] theorem encode_list_nil {α : Type u_1} [encodable α] : encode [] = 0 :=
  rfl

@[simp] theorem encode_list_cons {α : Type u_1} [encodable α] (a : α) (l : List α) : encode (a :: l) = Nat.succ (nat.mkpair (encode a) (encode l)) :=
  rfl

@[simp] theorem decode_list_zero {α : Type u_1} [encodable α] : decode (List α) 0 = some [] :=
  rfl

@[simp] theorem decode_list_succ {α : Type u_1} [encodable α] (v : ℕ) : decode (List α) (Nat.succ v) =
  (fun (_x : α) (_y : List α) => _x :: _y) <$> decode α (prod.fst (nat.unpair v)) <*>
    decode (List α) (prod.snd (nat.unpair v)) := sorry

theorem length_le_encode {α : Type u_1} [encodable α] (l : List α) : list.length l ≤ encode l := sorry

def encode_multiset {α : Type u_1} [encodable α] (s : multiset α) : ℕ :=
  encode (multiset.sort enle s)

def decode_multiset {α : Type u_1} [encodable α] (n : ℕ) : Option (multiset α) :=
  coe <$> decode (List α) n

protected instance multiset {α : Type u_1} [encodable α] : encodable (multiset α) :=
  mk encode_multiset decode_multiset sorry

def encodable_of_list {α : Type u_1} [DecidableEq α] (l : List α) (H : ∀ (x : α), x ∈ l) : encodable α :=
  mk (fun (a : α) => list.index_of a l) (list.nth l) sorry

def trunc_encodable_of_fintype (α : Type u_1) [DecidableEq α] [fintype α] : trunc (encodable α) :=
  quot.rec_on_subsingleton (finset.val finset.univ)
    (fun (l : List α) (H : ∀ (x : α), x ∈ Quot.mk setoid.r l) => trunc.mk (encodable_of_list l H)) finset.mem_univ

/-- A noncomputable way to arbitrarily choose an ordering on a finite type.
  It is not made into a global instance, since it involves an arbitrary choice.
  This can be locally made into an instance with `local attribute [instance] fintype.encodable`. -/
def fintype.encodable (α : Type u_1) [fintype α] : encodable α :=
  trunc.out (trunc_encodable_of_fintype α)

protected instance vector {α : Type u_1} [encodable α] {n : ℕ} : encodable (vector α n) :=
  encodable.subtype

protected instance fin_arrow {α : Type u_1} [encodable α] {n : ℕ} : encodable (fin n → α) :=
  of_equiv (vector α n) (equiv.symm (equiv.vector_equiv_fin α n))

protected instance fin_pi (n : ℕ) (π : fin n → Type u_1) [(i : fin n) → encodable (π i)] : encodable ((i : fin n) → π i) :=
  of_equiv (↥(set_of fun (f : fin n → sigma fun (i : fin n) => π i) => ∀ (i : fin n), sigma.fst (f i) = i))
    (equiv.pi_equiv_subtype_sigma (fin n) π)

protected instance array {α : Type u_1} [encodable α] {n : ℕ} : encodable (array n α) :=
  of_equiv (fin n → α) (equiv.array_equiv_fin n α)

protected instance finset {α : Type u_1} [encodable α] : encodable (finset α) :=
  of_equiv (Subtype fun (s : multiset α) => multiset.nodup s)
    (equiv.mk (fun (_x : finset α) => sorry) (fun (_x : Subtype fun (s : multiset α) => multiset.nodup s) => sorry) sorry
      sorry)

def fintype_arrow (α : Type u_1) (β : Type u_2) [DecidableEq α] [fintype α] [encodable β] : trunc (encodable (α → β)) :=
  trunc.map
    (fun (f : α ≃ fin (fintype.card α)) => of_equiv (fin (fintype.card α) → β) (equiv.arrow_congr f (equiv.refl β)))
    (fintype.equiv_fin α)

def fintype_pi (α : Type u_1) (π : α → Type u_2) [DecidableEq α] [fintype α] [(a : α) → encodable (π a)] : trunc (encodable ((a : α) → π a)) :=
  trunc.bind (trunc_encodable_of_fintype α)
    fun (a : encodable α) =>
      trunc.bind (fintype_arrow α (sigma fun (a : α) => π a))
        fun (f : encodable (α → sigma fun (a : α) => π a)) =>
          trunc.mk
            (of_equiv
              (Subtype
                fun (a : α → sigma fun (a : α) => π a) =>
                  a ∈ set_of fun (f : α → sigma fun (a : α) => π a) => ∀ (i : α), sigma.fst (f i) = i)
              (equiv.pi_equiv_subtype_sigma α π))

/-- The elements of a `fintype` as a sorted list. -/
def sorted_univ (α : Type u_1) [fintype α] [encodable α] : List α :=
  finset.sort (⇑(encode' α) ⁻¹'o LessEq) finset.univ

theorem mem_sorted_univ {α : Type u_1} [fintype α] [encodable α] (x : α) : x ∈ sorted_univ α :=
  iff.mpr (finset.mem_sort (⇑(encode' α) ⁻¹'o LessEq)) (finset.mem_univ x)

theorem length_sorted_univ {α : Type u_1} [fintype α] [encodable α] : list.length (sorted_univ α) = fintype.card α :=
  finset.length_sort (⇑(encode' α) ⁻¹'o LessEq)

theorem sorted_univ_nodup {α : Type u_1} [fintype α] [encodable α] : list.nodup (sorted_univ α) :=
  finset.sort_nodup (⇑(encode' α) ⁻¹'o LessEq) finset.univ

/-- An encodable `fintype` is equivalent a `fin`.-/
def fintype_equiv_fin {α : Type u_1} [fintype α] [encodable α] : α ≃ fin (fintype.card α) :=
  equiv.trans (fintype.equiv_fin_of_forall_mem_list mem_sorted_univ sorted_univ_nodup) (equiv.cast sorry)

protected instance fintype_arrow_of_encodable {α : Type u_1} {β : Type u_2} [encodable α] [fintype α] [encodable β] : encodable (α → β) :=
  of_equiv (fin (fintype.card α) → β) (equiv.arrow_congr fintype_equiv_fin (equiv.refl β))

end encodable

namespace denumerable

theorem denumerable_list_aux {α : Type u_1} [denumerable α] (n : ℕ) : ∃ (a : List α), ∃ (H : a ∈ encodable.decode_list n), encodable.encode_list a = n := sorry

protected instance denumerable_list {α : Type u_1} [denumerable α] : denumerable (List α) :=
  mk denumerable_list_aux

@[simp] theorem list_of_nat_zero {α : Type u_1} [denumerable α] : of_nat (List α) 0 = [] :=
  rfl

@[simp] theorem list_of_nat_succ {α : Type u_1} [denumerable α] (v : ℕ) : of_nat (List α) (Nat.succ v) = of_nat α (prod.fst (nat.unpair v)) :: of_nat (List α) (prod.snd (nat.unpair v)) := sorry

def lower : List ℕ → ℕ → List ℕ :=
  sorry

def raise : List ℕ → ℕ → List ℕ :=
  sorry

theorem lower_raise (l : List ℕ) (n : ℕ) : lower (raise l n) n = l := sorry

theorem raise_lower {l : List ℕ} {n : ℕ} : list.sorted LessEq (n :: l) → raise (lower l n) n = l := sorry

theorem raise_chain (l : List ℕ) (n : ℕ) : list.chain LessEq n (raise l n) := sorry

theorem raise_sorted (l : List ℕ) (n : ℕ) : list.sorted LessEq (raise l n) := sorry

/- Warning: this is not the same encoding as used in `encodable` -/

protected instance multiset {α : Type u_1} [denumerable α] : denumerable (multiset α) :=
  mk'
    (equiv.mk
      (fun (s : multiset α) => encodable.encode (lower (multiset.sort LessEq (multiset.map encodable.encode s)) 0))
      (fun (n : ℕ) => multiset.map (of_nat α) ↑(raise (of_nat (List ℕ) n) 0)) sorry sorry)

def lower' : List ℕ → ℕ → List ℕ :=
  sorry

def raise' : List ℕ → ℕ → List ℕ :=
  sorry

theorem lower_raise' (l : List ℕ) (n : ℕ) : lower' (raise' l n) n = l := sorry

theorem raise_lower' {l : List ℕ} {n : ℕ} : (∀ (m : ℕ), m ∈ l → n ≤ m) → list.sorted Less l → raise' (lower' l n) n = l := sorry

theorem raise'_chain (l : List ℕ) {m : ℕ} {n : ℕ} : m < n → list.chain Less m (raise' l n) := sorry

theorem raise'_sorted (l : List ℕ) (n : ℕ) : list.sorted Less (raise' l n) := sorry

def raise'_finset (l : List ℕ) (n : ℕ) : finset ℕ :=
  finset.mk ↑(raise' l n) sorry

/- Warning: this is not the same encoding as used in `encodable` -/

protected instance finset {α : Type u_1} [denumerable α] : denumerable (finset α) :=
  mk'
    (equiv.mk
      (fun (s : finset α) => encodable.encode (lower' (finset.sort LessEq (finset.map (equiv.to_embedding (eqv α)) s)) 0))
      (fun (n : ℕ) => finset.map (equiv.to_embedding (equiv.symm (eqv α))) (raise'_finset (of_nat (List ℕ) n) 0)) sorry
      sorry)

end denumerable

namespace equiv

/-- The type lists on unit is canonically equivalent to the natural numbers. -/
def list_unit_equiv : List Unit ≃ ℕ :=
  mk list.length (list.repeat Unit.unit) sorry sorry

def list_nat_equiv_nat : List ℕ ≃ ℕ :=
  denumerable.eqv (List ℕ)

def list_equiv_self_of_equiv_nat {α : Type} (e : α ≃ ℕ) : List α ≃ α :=
  equiv.trans (equiv.trans (list_equiv_of_equiv e) list_nat_equiv_nat) (equiv.symm e)


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.category.Cat
import category_theory.limits.types
import category_theory.limits.preserves.basic

/-!
# The category of small categories has all small limits.

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

An object in the limit consists of a family of objects,
which are carried to one another by the functors in the diagram.
A morphism between two such objects is a family of morphisms between the corresponding objects,
which are carried to one another by the action on morphisms of the functors in the diagram.

## Future work
Can the indexing category live in a lower universe?
-/

noncomputable theory

universes v u

open category_theory.limits

namespace category_theory

variables {J : Type v} [small_category J]

namespace Cat

namespace has_limits

instance category_objects {F : J ⥤ Cat.{u u}} {j} :
  small_category ((F ⋙ Cat.objects.{u u}).obj j) :=
(F.obj j).str

/-- Auxiliary definition:
the diagram whose limit gives the morphism space between two objects of the limit category. -/
@[simps]
def hom_diagram {F : J ⥤ Cat.{v v}} (X Y : limit (F ⋙ Cat.objects.{v v})) : J ⥤ Type v :=
{ obj := λ j, limit.π (F ⋙ Cat.objects) j X ⟶ limit.π (F ⋙ Cat.objects) j Y,
  map := λ j j' f g,
  begin
    refine eq_to_hom _ ≫ (F.map f).map g ≫ eq_to_hom _,
    exact (congr_fun (limit.w (F ⋙ Cat.objects) f) X).symm,
    exact (congr_fun (limit.w (F ⋙ Cat.objects) f) Y),
  end,
  map_id' := λ X, begin
    ext f, dsimp,
    simp [functor.congr_hom (F.map_id X) f],
  end,
  map_comp' := λ X Y Z f g, begin
    ext h, dsimp,
    simp [functor.congr_hom (F.map_comp f g) h, eq_to_hom_map],
    refl,
  end, }

@[simps]
instance (F : J ⥤ Cat.{v v}) : category (limit (F ⋙ Cat.objects)) :=
{ hom := λ X Y, limit (hom_diagram X Y),
  id := λ X, types.limit.mk.{v v} (hom_diagram X X) (λ j, 𝟙 _) (λ j j' f, by simp),
  comp := λ X Y Z f g, types.limit.mk.{v v} (hom_diagram X Z)
    (λ j, limit.π (hom_diagram X Y) j f ≫ limit.π (hom_diagram Y Z) j g)
    (λ j j' h, begin
      rw [←congr_fun (limit.w (hom_diagram X Y) h) f, ←congr_fun (limit.w (hom_diagram Y Z) h) g],
      dsimp,
      simp,
    end),
  id_comp' := λ _ _ _, by { ext, simp only [category.id_comp, types.limit.π_mk'] },
  comp_id' := λ _ _ _, by { ext, simp only [types.limit.π_mk', category.comp_id] } }

/-- Auxiliary definition: the limit category. -/
@[simps]
def limit_cone_X (F : J ⥤ Cat.{v v}) : Cat.{v v} :=
{ α := limit (F ⋙ Cat.objects), }.

/-- Auxiliary definition: the cone over the limit category. -/
@[simps]
def limit_cone (F : J ⥤ Cat.{v v}) : cone F :=
{ X := limit_cone_X F,
  π :=
  { app := λ j,
    { obj := limit.π (F ⋙ Cat.objects) j,
      map := λ X Y, limit.π (hom_diagram X Y) j, },
    naturality' := λ j j' f, category_theory.functor.ext
      (λ X, (congr_fun (limit.w (F ⋙ Cat.objects) f) X).symm)
      (λ X Y h, (congr_fun (limit.w (hom_diagram X Y) f) h).symm), } }

/-- Auxiliary definition: the universal morphism to the proposed limit cone. -/
@[simps]
def limit_cone_lift (F : J ⥤ Cat.{v v}) (s : cone F) : s.X ⟶ limit_cone_X F :=
{ obj := limit.lift (F ⋙ Cat.objects)
  { X := s.X,
    π :=
    { app := λ j, (s.π.app j).obj,
      naturality' := λ j j' f, (congr_arg functor.obj (s.π.naturality f) : _), } },
  map := λ X Y f,
  begin
    fapply types.limit.mk.{v v},
    { intro j,
      refine eq_to_hom _ ≫ (s.π.app j).map f ≫ eq_to_hom _;
      simp, },
    { intros j j' h,
      dsimp,
      simp only [category.assoc, functor.map_comp,
        eq_to_hom_map, eq_to_hom_trans, eq_to_hom_trans_assoc],
      rw [←functor.comp_map],
      have := (s.π.naturality h).symm,
      conv at this { congr, skip, dsimp, simp, },
      erw [functor.congr_hom this f],
      dsimp, simp, },
  end,
  map_id' := λ X, by simp,
  map_comp' := λ X Y Z f g, by simp }

@[simp]
lemma limit_π_hom_diagram_eq_to_hom {F : J ⥤ Cat.{v v}}
  (X Y : limit (F ⋙ Cat.objects.{v v})) (j : J) (h : X = Y) :
  limit.π (hom_diagram X Y) j (eq_to_hom h) =
    eq_to_hom (congr_arg (limit.π (F ⋙ Cat.objects.{v v}) j) h) :=
by { subst h, simp, }

/-- Auxiliary definition: the proposed cone is a limit cone. -/
def limit_cone_is_limit (F : J ⥤ Cat.{v v}) : is_limit (limit_cone F) :=
{ lift := limit_cone_lift F,
  fac' := λ s j, category_theory.functor.ext (by tidy) (λ X Y f, types.limit.π_mk _ _ _ _),
  uniq' := λ s m w,
  begin
    symmetry,
    fapply category_theory.functor.ext,
    { intro X,
      ext,
      dsimp, simp only [types.limit.lift_π_apply', ←w j],
      refl, },
    { intros X Y f,
      dsimp, simp [(λ j, functor.congr_hom (w j).symm f)],
      congr, },
  end, }

end has_limits

/-- The category of small categories has all small limits. -/
instance : has_limits (Cat.{v v}) :=
{ has_limits_of_shape := λ J _, by exactI
  { has_limit := λ F, ⟨⟨⟨has_limits.limit_cone F, has_limits.limit_cone_is_limit F⟩⟩⟩, } }

instance : preserves_limits Cat.objects.{v v} :=
{ preserves_limits_of_shape := λ J _, by exactI
  { preserves_limit := λ F,
    preserves_limit_of_preserves_limit_cone (has_limits.limit_cone_is_limit F)
      (limits.is_limit.of_iso_limit (limit.is_limit (F ⋙ Cat.objects))
        (cones.ext (by refl) (by tidy))), }}

end Cat

end category_theory


example : 3 = 3 :=
begin
  generalize : 3 = x,
  revert x,
  intro y,
  reflexivity
end


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.data.option.basic
import Mathlib.Lean3Lib.init.meta.tactic
import Mathlib.Lean3Lib.init.control.lawful

universes u_1 u

namespace Mathlib

protected instance option.is_lawful_monad : is_lawful_monad Option :=
  is_lawful_monad.mk (fun (α β : Type u_1) (x : α) (f : α → Option β) => rfl)
    fun (α β γ : Type u_1) (x : Option α) (f : α → Option β) (g : β → Option γ) => Option.rec rfl (fun (x : α) => rfl) x

theorem option.eq_of_eq_some {α : Type u} {x : Option α} {y : Option α} : (∀ (z : α), x = some z ↔ y = some z) → x = y := sorry

theorem option.eq_some_of_is_some {α : Type u} {o : Option α} (h : ↥(option.is_some o)) : o = some (option.get h) := sorry

theorem option.eq_none_of_is_none {α : Type u} {o : Option α} : ↥(option.is_none o) → o = none := sorry


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

open interactive
open interactive.types

namespace tactic
namespace interactive
open expr lean.parser

local postfix `?`:9001 := optional

/--
This is a "finishing" tactic modification of `simp`. It has two forms.

* `simpa [rules, ...] using e` will simplify the goal and the type of
  `e` using `rules`, then try to close the goal using `e`.

  Simplifying the type of `e` makes it more likely to match the goal
  (which has also been simplified). This construction also tends to be
  more robust under changes to the simp lemma set.

* `simpa [rules, ...]` will simplify the goal and the type of a
  hypothesis `this` if present in the context, then try to close the goal using
  the `assumption` tactic. -/
meta def simpa (use_iota_eqn : parse $ (tk "!")?) (trace_lemmas : parse $ (tk "?")?)
  (no_dflt : parse only_flag) (hs : parse simp_arg_list) (attr_names : parse with_ident_list)
  (tgt : parse (tk "using" *> texpr)?) (cfg : simp_config_ext := {}) : tactic unit :=
let simp_at lc (close_tac : tactic unit) := focus1 $
  simp use_iota_eqn trace_lemmas no_dflt hs attr_names (loc.ns lc)
    {fail_if_unchanged := ff, ..cfg} >>
  (((close_tac <|> trivial) >> done) <|> fail "simpa failed") in
match tgt with
| none := get_local `this >> simp_at [some `this, none] assumption <|> simp_at [none] assumption
| some e := focus1 $ do
  e ← i_to_expr e <|> do
  { ty ← target,
    -- for positional error messages, we don't care about the result
    e ← i_to_expr_strict ``(%%e : %%ty),
    pty ← pp ty, ptgt ← pp e,
    -- Fail deliberately, to advise regarding `simp; exact` usage
    fail ("simpa failed, 'using' expression type not directly " ++
      "inferrable. Try:\n\nsimpa ... using\nshow " ++
      to_fmt pty ++ ",\nfrom " ++ ptgt : format) },
  match e with
  | local_const _ lc _ _ := simp_at [some lc, none] (get_local lc >>= tactic.exact)
  | e := do
    t ← infer_type e,
    assertv `this t e,
    simp_at [some `this, none] (get_local `this >>= tactic.exact),
    all_goals (try apply_instance)
  end
end

add_tactic_doc
{ name       := "simpa",
  category   := doc_category.tactic,
  decl_names := [`tactic.interactive.simpa],
  tags       := ["simplification"] }

end interactive
end tactic


import Lean.CoreM

#eval Lean.addDecl <| .mutualDefnDecl [{
  name := `FalseIntro
  levelParams := []
  type := .const ``False []
  value := .const `FalseIntro []
  hints := .opaque
  safety := .partial
}]

theorem False.intro : False := FalseIntro