/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.doc_commands
import Mathlib.tactic.reserved_notation
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4 u l w v

namespace Mathlib

/-!
# Basic logic properties

This file is one of the earliest imports in mathlib.

## Implementation notes

Theorems that require decidability hypotheses are in the namespace "decidable".
Classical versions are in the namespace "classical".

In the presence of automation, this whole file may be unnecessary. On the other hand,
maybe it is useful for writing automation.
-/

/- We add the `inline` attribute to optimize VM computation using these declarations. For example,
  `if p ∧ q then ... else ...` will not evaluate the decidability of `q` if `p` is false. -/

/-- An identity function with its main argument implicit. This will be printed as `hidden` even
if it is applied to a large term, so it can be used for elision,
as done in the `elide` and `unelide` tactics. -/
def hidden {α : Sort u_1} {a : α} : α := a

/-- Ex falso, the nondependent eliminator for the `empty` type. -/
def empty.elim {C : Sort u_1} : empty → C := sorry

protected instance empty.subsingleton : subsingleton empty :=
  subsingleton.intro fun (a : empty) => empty.elim a

protected instance subsingleton.prod {α : Type u_1} {β : Type u_2} [subsingleton α]
    [subsingleton β] : subsingleton (α × β) :=
  subsingleton.intro
    fun (a b : α × β) =>
      prod.cases_on a
        fun (a_fst : α) (a_snd : β) =>
          prod.cases_on b
            fun (b_fst : α) (b_snd : β) =>
              (fun (fst fst_1 : α) (snd snd_1 : β) =>
                  Eq.trans ((fun (fst : α) (snd : β) => Eq.refl (fst, snd)) fst snd)
                    (congr (congr (Eq.refl Prod.mk) (subsingleton.elim fst fst_1))
                      (subsingleton.elim snd snd_1)))
                a_fst b_fst a_snd b_snd

protected instance empty.decidable_eq : DecidableEq empty := fun (a : empty) => empty.elim a

protected instance sort.inhabited : Inhabited (Sort u_1) := { default := PUnit }

protected instance sort.inhabited' : Inhabited Inhabited.default := { default := PUnit.unit }

protected instance psum.inhabited_left {α : Sort u_1} {β : Sort u_2} [Inhabited α] :
    Inhabited (psum α β) :=
  { default := psum.inl Inhabited.default }

protected instance psum.inhabited_right {α : Sort u_1} {β : Sort u_2} [Inhabited β] :
    Inhabited (psum α β) :=
  { default := psum.inr Inhabited.default }

protected instance decidable_eq_of_subsingleton {α : Sort u_1} [subsingleton α] : DecidableEq α :=
  sorry

@[simp] theorem eq_iff_true_of_subsingleton {α : Type u_1} [subsingleton α] (x : α) (y : α) :
    x = y ↔ True :=
  of_eq_true
    (Eq.trans (iff_eq_of_eq_true_right (Eq.refl True))
      (eq_true_intro (Eq.symm (subsingleton.elim y x))))

/-- Add an instance to "undo" coercion transitivity into a chain of coercions, because
   most simp lemmas are stated with respect to simple coercions and will not match when
   part of a chain. -/
@[simp] theorem coe_coe {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} [has_coe α β] [has_coe_t β γ]
    (a : α) : ↑a = ↑↑a :=
  rfl

theorem coe_fn_coe_trans {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} [has_coe α β]
    [has_coe_t_aux β γ] [has_coe_to_fun γ] (x : α) : ⇑x = ⇑↑x :=
  rfl

@[simp] theorem coe_fn_coe_base {α : Sort u_1} {β : Sort u_2} [has_coe α β] [has_coe_to_fun β]
    (x : α) : ⇑x = ⇑↑x :=
  rfl

theorem coe_sort_coe_trans {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} [has_coe α β]
    [has_coe_t_aux β γ] [has_coe_to_sort γ] (x : α) : ↥x = ↥↑x :=
  rfl

/--
Many structures such as bundled morphisms coerce to functions so that you can
transparently apply them to arguments. For example, if `e : α ≃ β` and `a : α`
then you can write `e a` and this is elaborated as `⇑e a`. This type of
coercion is implemented using the `has_coe_to_fun` type class. There is one
important consideration:

If a type coerces to another type which in turn coerces to a function,
then it **must** implement `has_coe_to_fun` directly:
```lean
structure sparkling_equiv (α β) extends α ≃ β

-- if we add a `has_coe` instance,

-- if we add a `has_coe` instance,
instance {α β} : has_coe (sparkling_equiv α β) (α ≃ β) :=
⟨sparkling_equiv.to_equiv⟩

-- then a `has_coe_to_fun` instance **must** be added as well:

-- then a `has_coe_to_fun` instance **must** be added as well:
instance {α β} : has_coe_to_fun (sparkling_equiv α β) :=
⟨λ _, α → β, λ f, f.to_equiv.to_fun⟩
```

(Rationale: if we do not declare the direct coercion, then `⇑e a` is not in
simp-normal form. The lemma `coe_fn_coe_base` will unfold it to `⇑↑e a`. This
often causes loops in the simplifier.)
-/
@[simp] theorem coe_sort_coe_base {α : Sort u_1} {β : Sort u_2} [has_coe α β] [has_coe_to_sort β]
    (x : α) : ↥x = ↥↑x :=
  rfl

/-- `pempty` is the universe-polymorphic analogue of `empty`. -/
inductive pempty where

/-- Ex falso, the nondependent eliminator for the `pempty` type. -/
def pempty.elim {C : Sort u_1} : pempty → C := sorry

protected instance subsingleton_pempty : subsingleton pempty :=
  subsingleton.intro fun (a : pempty) => pempty.elim a

@[simp] theorem not_nonempty_pempty : ¬Nonempty pempty :=
  fun (_x : Nonempty pempty) =>
    (fun (_a : Nonempty pempty) =>
        nonempty.dcases_on _a fun (val : pempty) => idRhs False (pempty.elim val))
      _x

@[simp] theorem forall_pempty {P : pempty → Prop} : (∀ (x : pempty), P x) ↔ True :=
  { mp := fun (h : ∀ (x : pempty), P x) => trivial,
    mpr := fun (h : True) (x : pempty) => pempty.cases_on (fun (x : pempty) => P x) x }

@[simp] theorem exists_pempty {P : pempty → Prop} : (∃ (x : pempty), P x) ↔ False := sorry

theorem congr_arg_heq {α : Sort u_1} {β : α → Sort u_2} (f : (a : α) → β a) {a₁ : α} {a₂ : α} :
    a₁ = a₂ → f a₁ == f a₂ :=
  sorry

theorem plift.down_inj {α : Sort u_1} (a : plift α) (b : plift α) :
    plift.down a = plift.down b → a = b :=
  sorry

-- missing [symm] attribute for ne in core.

theorem ne_comm {α : Sort u_1} {a : α} {b : α} : a ≠ b ↔ b ≠ a := { mp := ne.symm, mpr := ne.symm }

@[simp] theorem eq_iff_eq_cancel_left {α : Type u_1} {b : α} {c : α} :
    (∀ {a : α}, a = b ↔ a = c) ↔ b = c :=
  { mp :=
      fun (h : ∀ {a : α}, a = b ↔ a = c) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (b = c)) (Eq.symm (propext h)))) (Eq.refl b),
    mpr :=
      fun (h : b = c) (a : α) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (a = b ↔ a = c)) h)) (iff.refl (a = c)) }

@[simp] theorem eq_iff_eq_cancel_right {α : Type u_1} {a : α} {b : α} :
    (∀ {c : α}, a = c ↔ b = c) ↔ a = b :=
  { mp :=
      fun (h : ∀ {c : α}, a = c ↔ b = c) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (a = b)) (propext h))) (Eq.refl b),
    mpr :=
      fun (h : a = b) (a_1 : α) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (a = a_1 ↔ b = a_1)) h)) (iff.refl (b = a_1)) }

/-- Wrapper for adding elementary propositions to the type class systems.
Warning: this can easily be abused. See the rest of this docstring for details.

Certain propositions should not be treated as a class globally,
but sometimes it is very convenient to be able to use the type class system
in specific circumstances.

For example, `zmod p` is a field if and only if `p` is a prime number.
In order to be able to find this field instance automatically by type class search,
we have to turn `p.prime` into an instance implicit assumption.

On the other hand, making `nat.prime` a class would require a major refactoring of the library,
and it is questionable whether making `nat.prime` a class is desirable at all.
The compromise is to add the assumption `[fact p.prime]` to `zmod.field`.

In particular, this class is not intended for turning the type class system
into an automated theorem prover for first order logic. -/
def fact (p : Prop) := p

theorem fact.elim {p : Prop} (h : fact p) : p := h

/-!
### Declarations about propositional connectives
-/

theorem false_ne_true : False ≠ True :=
  fun (ᾰ : False = True) => idRhs ((fun (_x : Prop) => _x) False) (Eq.symm ᾰ ▸ trivial)

/-! ### Declarations about `implies` -/

theorem iff_of_eq {a : Prop} {b : Prop} (e : a = b) : a ↔ b := e ▸ iff.rfl

theorem iff_iff_eq {a : Prop} {b : Prop} : a ↔ b ↔ a = b := { mp := propext, mpr := iff_of_eq }

@[simp] theorem eq_iff_iff {p : Prop} {q : Prop} : p = q ↔ (p ↔ q) := iff.symm iff_iff_eq

@[simp] theorem imp_self {a : Prop} : a → a ↔ True := iff_true_intro id

theorem imp_intro {α : Prop} {β : Prop} (h : α) : β → α := fun (_x : β) => h

theorem imp_false {a : Prop} : a → False ↔ ¬a := iff.rfl

theorem imp_and_distrib {b : Prop} {c : Prop} {α : Sort u_1} : α → b ∧ c ↔ (α → b) ∧ (α → c) :=
  { mp :=
      fun (h : α → b ∧ c) =>
        { left := fun (ha : α) => and.left (h ha), right := fun (ha : α) => and.right (h ha) },
    mpr :=
      fun (h : (α → b) ∧ (α → c)) (ha : α) => { left := and.left h ha, right := and.right h ha } }

@[simp] theorem and_imp {a : Prop} {b : Prop} {c : Prop} : a ∧ b → c ↔ a → b → c := sorry

theorem iff_def {a : Prop} {b : Prop} : a ↔ b ↔ (a → b) ∧ (b → a) := iff_iff_implies_and_implies a b

theorem iff_def' {a : Prop} {b : Prop} : a ↔ b ↔ (b → a) ∧ (a → b) := iff.trans iff_def and.comm

theorem imp_true_iff {α : Sort u_1} : α → True ↔ True := iff_true_intro fun (_x : α) => trivial

@[simp] theorem imp_iff_right {a : Prop} {b : Prop} (ha : a) : a → b ↔ b :=
  { mp := fun (f : a → b) => f ha, mpr := imp_intro }

/-! ### Declarations about `not` -/

/-- Ex falso for negation. From `¬ a` and `a` anything follows. This is the same as `absurd` with
the arguments flipped, but it is in the `not` namespace so that projection notation can be used. -/
def not.elim {a : Prop} {α : Sort u_1} (H1 : ¬a) (H2 : a) : α := absurd H2 H1

theorem not.imp {a : Prop} {b : Prop} (H2 : ¬b) (H1 : a → b) : ¬a := mt H1 H2

theorem not_not_of_not_imp {a : Prop} {b : Prop} : ¬(a → b) → ¬¬a := mt not.elim

theorem not_of_not_imp {b : Prop} {a : Prop} : ¬(a → b) → ¬b := mt imp_intro

theorem dec_em (p : Prop) [Decidable p] : p ∨ ¬p := decidable.em p

theorem em (p : Prop) : p ∨ ¬p := classical.em p

theorem or_not {p : Prop} : p ∨ ¬p := em p

theorem by_contradiction {p : Prop} : (¬p → False) → p := decidable.by_contradiction

-- alias by_contradiction ← by_contra

theorem by_contra {p : Prop} : (¬p → False) → p := decidable.by_contradiction

/--
In most of mathlib, we use the law of excluded middle (LEM) and the axiom of choice (AC) freely.
The `decidable` namespace contains versions of lemmas from the root namespace that explicitly
attempt to avoid the axiom of choice, usually by adding decidability assumptions on the inputs.

You can check if a lemma uses the axiom of choice by using `#print axioms foo` and seeing if
`classical.choice` appears in the list.
-/
-- See Note [decidable namespace]

protected theorem decidable.not_not {a : Prop} [Decidable a] : ¬¬a ↔ a :=
  { mp := decidable.by_contradiction, mpr := not_not_intro }

/-- The Double Negation Theorem: `¬ ¬ P` is equivalent to `P`.
The left-to-right direction, double negation elimination (DNE),
is classically true but not constructively. -/
@[simp] theorem not_not {a : Prop} : ¬¬a ↔ a := decidable.not_not

theorem of_not_not {a : Prop} : ¬¬a → a := by_contra

-- See Note [decidable namespace]

protected theorem decidable.of_not_imp {a : Prop} {b : Prop} [Decidable a] (h : ¬(a → b)) : a :=
  decidable.by_contradiction (not_not_of_not_imp h)

theorem of_not_imp {a : Prop} {b : Prop} : ¬(a → b) → a := decidable.of_not_imp

-- See Note [decidable namespace]

protected theorem decidable.not_imp_symm {a : Prop} {b : Prop} [Decidable a] (h : ¬a → b)
    (hb : ¬b) : a :=
  decidable.by_contradiction (hb ∘ h)

theorem not.decidable_imp_symm {a : Prop} {b : Prop} [Decidable a] : (¬a → b) → ¬b → a :=
  decidable.not_imp_symm

theorem not.imp_symm {a : Prop} {b : Prop} : (¬a → b) → ¬b → a := not.decidable_imp_symm

-- See Note [decidable namespace]

protected theorem decidable.not_imp_comm {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    ¬a → b ↔ ¬b → a :=
  { mp := not.decidable_imp_symm, mpr := not.decidable_imp_symm }

theorem not_imp_comm {a : Prop} {b : Prop} : ¬a → b ↔ ¬b → a := decidable.not_imp_comm

@[simp] theorem imp_not_self {a : Prop} : a → ¬a ↔ ¬a :=
  { mp := fun (h : a → ¬a) (ha : a) => h ha ha, mpr := fun (h : ¬a) (_x : a) => h }

theorem decidable.not_imp_self {a : Prop} [Decidable a] : ¬a → a ↔ a :=
  eq.mp (Eq._oldrec (Eq.refl (¬a → ¬¬a ↔ ¬¬a)) (propext decidable.not_not)) imp_not_self

@[simp] theorem not_imp_self {a : Prop} : ¬a → a ↔ a := decidable.not_imp_self

theorem imp.swap {a : Prop} {b : Prop} {c : Prop} : a → b → c ↔ b → a → c :=
  { mp := function.swap, mpr := function.swap }

theorem imp_not_comm {a : Prop} {b : Prop} : a → ¬b ↔ b → ¬a := imp.swap

/-! ### Declarations about `and` -/

theorem and_congr_left {a : Prop} {b : Prop} {c : Prop} (h : c → (a ↔ b)) : a ∧ c ↔ b ∧ c :=
  iff.trans and.comm (iff.trans (and_congr_right h) and.comm)

theorem and_congr_left' {a : Prop} {b : Prop} {c : Prop} (h : a ↔ b) : a ∧ c ↔ b ∧ c :=
  and_congr h iff.rfl

theorem and_congr_right' {a : Prop} {b : Prop} {c : Prop} (h : b ↔ c) : a ∧ b ↔ a ∧ c :=
  and_congr iff.rfl h

theorem not_and_of_not_left {a : Prop} (b : Prop) : ¬a → ¬(a ∧ b) := mt and.left

theorem not_and_of_not_right (a : Prop) {b : Prop} : ¬b → ¬(a ∧ b) := mt and.right

theorem and.imp_left {a : Prop} {b : Prop} {c : Prop} (h : a → b) : a ∧ c → b ∧ c := and.imp h id

theorem and.imp_right {a : Prop} {b : Prop} {c : Prop} (h : a → b) : c ∧ a → c ∧ b := and.imp id h

theorem and.right_comm {a : Prop} {b : Prop} {c : Prop} : (a ∧ b) ∧ c ↔ (a ∧ c) ∧ b := sorry

theorem and.rotate {a : Prop} {b : Prop} {c : Prop} : a ∧ b ∧ c ↔ b ∧ c ∧ a := sorry

theorem and_not_self_iff (a : Prop) : a ∧ ¬a ↔ False :=
  { mp := fun (h : a ∧ ¬a) => and.right h (and.left h), mpr := fun (h : False) => false.elim h }

theorem not_and_self_iff (a : Prop) : ¬a ∧ a ↔ False := sorry

theorem and_iff_left_of_imp {a : Prop} {b : Prop} (h : a → b) : a ∧ b ↔ a :=
  { mp := and.left, mpr := fun (ha : a) => { left := ha, right := h ha } }

theorem and_iff_right_of_imp {a : Prop} {b : Prop} (h : b → a) : a ∧ b ↔ b :=
  { mp := and.right, mpr := fun (hb : b) => { left := h hb, right := hb } }

@[simp] theorem and_iff_left_iff_imp {a : Prop} {b : Prop} : a ∧ b ↔ a ↔ a → b :=
  { mp := fun (h : a ∧ b ↔ a) (ha : a) => and.right (iff.mpr h ha), mpr := and_iff_left_of_imp }

@[simp] theorem and_iff_right_iff_imp {a : Prop} {b : Prop} : a ∧ b ↔ b ↔ b → a :=
  { mp := fun (h : a ∧ b ↔ b) (ha : b) => and.left (iff.mpr h ha), mpr := and_iff_right_of_imp }

@[simp] theorem and.congr_right_iff {a : Prop} {b : Prop} {c : Prop} :
    a ∧ b ↔ a ∧ c ↔ a → (b ↔ c) :=
  sorry

@[simp] theorem and.congr_left_iff {a : Prop} {b : Prop} {c : Prop} : a ∧ c ↔ b ∧ c ↔ c → (a ↔ b) :=
  sorry

@[simp] theorem and_self_left {a : Prop} {b : Prop} : a ∧ a ∧ b ↔ a ∧ b :=
  { mp := fun (h : a ∧ a ∧ b) => { left := and.left h, right := and.right (and.right h) },
    mpr :=
      fun (h : a ∧ b) =>
        { left := and.left h, right := { left := and.left h, right := and.right h } } }

@[simp] theorem and_self_right {a : Prop} {b : Prop} : (a ∧ b) ∧ b ↔ a ∧ b :=
  { mp := fun (h : (a ∧ b) ∧ b) => { left := and.left (and.left h), right := and.right h },
    mpr :=
      fun (h : a ∧ b) =>
        { left := { left := and.left h, right := and.right h }, right := and.right h } }

/-! ### Declarations about `or` -/

theorem or_congr_left {a : Prop} {b : Prop} {c : Prop} (h : a ↔ b) : a ∨ c ↔ b ∨ c :=
  or_congr h iff.rfl

theorem or_congr_right {a : Prop} {b : Prop} {c : Prop} (h : b ↔ c) : a ∨ b ↔ a ∨ c :=
  or_congr iff.rfl h

theorem or.right_comm {a : Prop} {b : Prop} {c : Prop} : (a ∨ b) ∨ c ↔ (a ∨ c) ∨ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a ∨ b) ∨ c ↔ (a ∨ c) ∨ b)) (propext (or_assoc a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∨ b ∨ c ↔ (a ∨ c) ∨ b)) (propext (or_assoc a c))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a ∨ b ∨ c ↔ a ∨ c ∨ b)) (propext (or_comm b c))))
        (iff.refl (a ∨ c ∨ b))))

theorem or_of_or_of_imp_of_imp {a : Prop} {b : Prop} {c : Prop} {d : Prop} (h₁ : a ∨ b) (h₂ : a → c)
    (h₃ : b → d) : c ∨ d :=
  or.imp h₂ h₃ h₁

theorem or_of_or_of_imp_left {a : Prop} {b : Prop} {c : Prop} (h₁ : a ∨ c) (h : a → b) : b ∨ c :=
  or.imp_left h h₁

theorem or_of_or_of_imp_right {a : Prop} {b : Prop} {c : Prop} (h₁ : c ∨ a) (h : a → b) : c ∨ b :=
  or.imp_right h h₁

theorem or.elim3 {a : Prop} {b : Prop} {c : Prop} {d : Prop} (h : a ∨ b ∨ c) (ha : a → d)
    (hb : b → d) (hc : c → d) : d :=
  or.elim h ha fun (h₂ : b ∨ c) => or.elim h₂ hb hc

theorem or_imp_distrib {a : Prop} {b : Prop} {c : Prop} : a ∨ b → c ↔ (a → c) ∧ (b → c) := sorry

-- See Note [decidable namespace]

protected theorem decidable.or_iff_not_imp_left {a : Prop} {b : Prop} [Decidable a] :
    a ∨ b ↔ ¬a → b :=
  { mp := or.resolve_left, mpr := fun (h : ¬a → b) => dite a Or.inl (Or.inr ∘ h) }

theorem or_iff_not_imp_left {a : Prop} {b : Prop} : a ∨ b ↔ ¬a → b := decidable.or_iff_not_imp_left

-- See Note [decidable namespace]

protected theorem decidable.or_iff_not_imp_right {a : Prop} {b : Prop} [Decidable b] :
    a ∨ b ↔ ¬b → a :=
  iff.trans or.comm decidable.or_iff_not_imp_left

theorem or_iff_not_imp_right {a : Prop} {b : Prop} : a ∨ b ↔ ¬b → a :=
  decidable.or_iff_not_imp_right

-- See Note [decidable namespace]

protected theorem decidable.not_imp_not {a : Prop} {b : Prop} [Decidable a] : ¬a → ¬b ↔ b → a :=
  { mp := fun (h : ¬a → ¬b) (hb : b) => decidable.by_contradiction fun (na : ¬a) => h na hb,
    mpr := mt }

theorem not_imp_not {a : Prop} {b : Prop} : ¬a → ¬b ↔ b → a := decidable.not_imp_not

@[simp] theorem or_iff_left_iff_imp {a : Prop} {b : Prop} : a ∨ b ↔ a ↔ b → a :=
  { mp := fun (h : a ∨ b ↔ a) (hb : b) => iff.mp h (Or.inr hb), mpr := or_iff_left_of_imp }

@[simp] theorem or_iff_right_iff_imp {a : Prop} {b : Prop} : a ∨ b ↔ b ↔ a → b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∨ b ↔ b ↔ a → b)) (propext (or_comm a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b ∨ a ↔ b ↔ a → b)) (propext or_iff_left_iff_imp)))
      (iff.refl (a → b)))

/-! ### Declarations about distributivity -/

/-- `∧` distributes over `∨` (on the left). -/
theorem and_or_distrib_left {a : Prop} {b : Prop} {c : Prop} : a ∧ (b ∨ c) ↔ a ∧ b ∨ a ∧ c := sorry

/-- `∧` distributes over `∨` (on the right). -/
theorem or_and_distrib_right {a : Prop} {b : Prop} {c : Prop} : (a ∨ b) ∧ c ↔ a ∧ c ∨ b ∧ c :=
  iff.trans (iff.trans and.comm and_or_distrib_left) (or_congr and.comm and.comm)

/-- `∨` distributes over `∧` (on the left). -/
theorem or_and_distrib_left {a : Prop} {b : Prop} {c : Prop} : a ∨ b ∧ c ↔ (a ∨ b) ∧ (a ∨ c) :=
  { mp :=
      Or._oldrec (fun (ha : a) => { left := Or.inl ha, right := Or.inl ha })
        (and.imp Or.inr Or.inr),
    mpr := And._oldrec (Or._oldrec (imp_intro ∘ Or.inl) (or.imp_right ∘ And.intro)) }

/-- `∨` distributes over `∧` (on the right). -/
theorem and_or_distrib_right {a : Prop} {b : Prop} {c : Prop} : a ∧ b ∨ c ↔ (a ∨ c) ∧ (b ∨ c) :=
  iff.trans (iff.trans or.comm or_and_distrib_left) (and_congr or.comm or.comm)

@[simp] theorem or_self_left {a : Prop} {b : Prop} : a ∨ a ∨ b ↔ a ∨ b :=
  { mp := fun (h : a ∨ a ∨ b) => or.elim h Or.inl id,
    mpr := fun (h : a ∨ b) => or.elim h Or.inl (Or.inr ∘ Or.inr) }

@[simp] theorem or_self_right {a : Prop} {b : Prop} : (a ∨ b) ∨ b ↔ a ∨ b :=
  { mp := fun (h : (a ∨ b) ∨ b) => or.elim h id Or.inr,
    mpr := fun (h : a ∨ b) => or.elim h (Or.inl ∘ Or.inl) Or.inr }

/-! Declarations about `iff` -/

theorem iff_of_true {a : Prop} {b : Prop} (ha : a) (hb : b) : a ↔ b :=
  { mp := fun (_x : a) => hb, mpr := fun (_x : b) => ha }

theorem iff_of_false {a : Prop} {b : Prop} (ha : ¬a) (hb : ¬b) : a ↔ b :=
  { mp := not.elim ha, mpr := not.elim hb }

theorem iff_true_left {a : Prop} {b : Prop} (ha : a) : a ↔ b ↔ b :=
  { mp := fun (h : a ↔ b) => iff.mp h ha, mpr := iff_of_true ha }

theorem iff_true_right {a : Prop} {b : Prop} (ha : a) : b ↔ a ↔ b :=
  iff.trans iff.comm (iff_true_left ha)

theorem iff_false_left {a : Prop} {b : Prop} (ha : ¬a) : a ↔ b ↔ ¬b :=
  { mp := fun (h : a ↔ b) => mt (iff.mpr h) ha, mpr := iff_of_false ha }

theorem iff_false_right {a : Prop} {b : Prop} (ha : ¬a) : b ↔ a ↔ ¬b :=
  iff.trans iff.comm (iff_false_left ha)

-- See Note [decidable namespace]

protected theorem decidable.not_or_of_imp {a : Prop} {b : Prop} [Decidable a] (h : a → b) :
    ¬a ∨ b :=
  dite a (fun (ha : a) => Or.inr (h ha)) fun (ha : ¬a) => Or.inl ha

theorem not_or_of_imp {a : Prop} {b : Prop} : (a → b) → ¬a ∨ b := decidable.not_or_of_imp

-- See Note [decidable namespace]

protected theorem decidable.imp_iff_not_or {a : Prop} {b : Prop} [Decidable a] : a → b ↔ ¬a ∨ b :=
  { mp := decidable.not_or_of_imp, mpr := or.neg_resolve_left }

theorem imp_iff_not_or {a : Prop} {b : Prop} : a → b ↔ ¬a ∨ b := decidable.imp_iff_not_or

-- See Note [decidable namespace]

protected theorem decidable.imp_or_distrib {a : Prop} {b : Prop} {c : Prop} [Decidable a] :
    a → b ∨ c ↔ (a → b) ∨ (a → c) :=
  sorry

theorem imp_or_distrib {a : Prop} {b : Prop} {c : Prop} : a → b ∨ c ↔ (a → b) ∨ (a → c) :=
  decidable.imp_or_distrib

-- See Note [decidable namespace]

protected theorem decidable.imp_or_distrib' {a : Prop} {b : Prop} {c : Prop} [Decidable b] :
    a → b ∨ c ↔ (a → b) ∨ (a → c) :=
  sorry

theorem imp_or_distrib' {a : Prop} {b : Prop} {c : Prop} : a → b ∨ c ↔ (a → b) ∨ (a → c) :=
  decidable.imp_or_distrib'

theorem not_imp_of_and_not {a : Prop} {b : Prop} : a ∧ ¬b → ¬(a → b) :=
  fun (ᾰ : a ∧ ¬b) (ᾰ_1 : a → b) =>
    and.dcases_on ᾰ fun (ᾰ_left : a) (ᾰ_right : ¬b) => idRhs False (ᾰ_right (ᾰ_1 ᾰ_left))

-- See Note [decidable namespace]

protected theorem decidable.not_imp {a : Prop} {b : Prop} [Decidable a] : ¬(a → b) ↔ a ∧ ¬b :=
  { mp := fun (h : ¬(a → b)) => { left := decidable.of_not_imp h, right := not_of_not_imp h },
    mpr := not_imp_of_and_not }

theorem not_imp {a : Prop} {b : Prop} : ¬(a → b) ↔ a ∧ ¬b := decidable.not_imp

-- for monotonicity

theorem imp_imp_imp {a : Prop} {b : Prop} {c : Prop} {d : Prop} (h₀ : c → a) (h₁ : b → d) :
    (a → b) → c → d :=
  fun (h₂ : a → b) => h₁ ∘ h₂ ∘ h₀

-- See Note [decidable namespace]

protected theorem decidable.peirce (a : Prop) (b : Prop) [Decidable a] : ((a → b) → a) → a :=
  dite a (fun (ha : a) (h : (a → b) → a) => ha) fun (ha : ¬a) (h : (a → b) → a) => h (not.elim ha)

theorem peirce (a : Prop) (b : Prop) : ((a → b) → a) → a := decidable.peirce a b

theorem peirce' {a : Prop} (H : ∀ (b : Prop), (a → b) → a) : a := H a id

-- See Note [decidable namespace]

protected theorem decidable.not_iff_not {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    ¬a ↔ ¬b ↔ (a ↔ b) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (¬a ↔ ¬b ↔ (a ↔ b))) (propext iff_def)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((¬a → ¬b) ∧ (¬b → ¬a) ↔ (a ↔ b))) (propext iff_def')))
      (and_congr decidable.not_imp_not decidable.not_imp_not))

theorem not_iff_not {a : Prop} {b : Prop} : ¬a ↔ ¬b ↔ (a ↔ b) := decidable.not_iff_not

-- See Note [decidable namespace]

protected theorem decidable.not_iff_comm {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    ¬a ↔ b ↔ (¬b ↔ a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (¬a ↔ b ↔ (¬b ↔ a))) (propext iff_def)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((¬a → b) ∧ (b → ¬a) ↔ (¬b ↔ a))) (propext iff_def)))
      (and_congr decidable.not_imp_comm imp_not_comm))

theorem not_iff_comm {a : Prop} {b : Prop} : ¬a ↔ b ↔ (¬b ↔ a) := decidable.not_iff_comm

-- See Note [decidable namespace]

protected theorem decidable.not_iff {a : Prop} {b : Prop} [Decidable b] : ¬(a ↔ b) ↔ (¬a ↔ b) :=
  sorry

theorem not_iff {a : Prop} {b : Prop} : ¬(a ↔ b) ↔ (¬a ↔ b) := decidable.not_iff

-- See Note [decidable namespace]

protected theorem decidable.iff_not_comm {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    a ↔ ¬b ↔ (b ↔ ¬a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ↔ ¬b ↔ (b ↔ ¬a))) (propext iff_def)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a → ¬b) ∧ (¬b → a) ↔ (b ↔ ¬a))) (propext iff_def)))
      (and_congr imp_not_comm decidable.not_imp_comm))

theorem iff_not_comm {a : Prop} {b : Prop} : a ↔ ¬b ↔ (b ↔ ¬a) := decidable.iff_not_comm

-- See Note [decidable namespace]

protected theorem decidable.iff_iff_and_or_not_and_not {a : Prop} {b : Prop} [Decidable b] :
    a ↔ b ↔ a ∧ b ∨ ¬a ∧ ¬b :=
  sorry

theorem iff_iff_and_or_not_and_not {a : Prop} {b : Prop} : a ↔ b ↔ a ∧ b ∨ ¬a ∧ ¬b :=
  decidable.iff_iff_and_or_not_and_not

theorem decidable.iff_iff_not_or_and_or_not {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    a ↔ b ↔ (¬a ∨ b) ∧ (a ∨ ¬b) :=
  sorry

theorem iff_iff_not_or_and_or_not {a : Prop} {b : Prop} : a ↔ b ↔ (¬a ∨ b) ∧ (a ∨ ¬b) :=
  decidable.iff_iff_not_or_and_or_not

-- See Note [decidable namespace]

protected theorem decidable.not_and_not_right {a : Prop} {b : Prop} [Decidable b] :
    ¬(a ∧ ¬b) ↔ a → b :=
  sorry

theorem not_and_not_right {a : Prop} {b : Prop} : ¬(a ∧ ¬b) ↔ a → b := decidable.not_and_not_right

/-- Transfer decidability of `a` to decidability of `b`, if the propositions are equivalent.
**Important**: this function should be used instead of `rw` on `decidable b`, because the
kernel will get stuck reducing the usage of `propext` otherwise,
and `dec_trivial` will not work. -/
def decidable_of_iff {b : Prop} (a : Prop) (h : a ↔ b) [D : Decidable a] : Decidable b :=
  decidable_of_decidable_of_iff D h

/-- Transfer decidability of `b` to decidability of `a`, if the propositions are equivalent.
This is the same as `decidable_of_iff` but the iff is flipped. -/
def decidable_of_iff' {a : Prop} (b : Prop) (h : a ↔ b) [D : Decidable b] : Decidable a :=
  decidable_of_decidable_of_iff D (iff.symm h)

/-- Prove that `a` is decidable by constructing a boolean `b` and a proof that `b ↔ a`.
(This is sometimes taken as an alternate definition of decidability.) -/
def decidable_of_bool {a : Prop} (b : Bool) (h : ↥b ↔ a) : Decidable a := sorry

/-! ### De Morgan's laws -/

theorem not_and_of_not_or_not {a : Prop} {b : Prop} (h : ¬a ∨ ¬b) : ¬(a ∧ b) :=
  fun (ᾰ : a ∧ b) =>
    and.dcases_on ᾰ
      fun (ᾰ_left : a) (ᾰ_right : b) => idRhs False (or.elim h (absurd ᾰ_left) (absurd ᾰ_right))

-- See Note [decidable namespace]

protected theorem decidable.not_and_distrib {a : Prop} {b : Prop} [Decidable a] :
    ¬(a ∧ b) ↔ ¬a ∨ ¬b :=
  sorry

-- See Note [decidable namespace]

protected theorem decidable.not_and_distrib' {a : Prop} {b : Prop} [Decidable b] :
    ¬(a ∧ b) ↔ ¬a ∨ ¬b :=
  sorry

/-- One of de Morgan's laws: the negation of a conjunction is logically equivalent to the
disjunction of the negations. -/
theorem not_and_distrib {a : Prop} {b : Prop} : ¬(a ∧ b) ↔ ¬a ∨ ¬b := decidable.not_and_distrib

@[simp] theorem not_and {a : Prop} {b : Prop} : ¬(a ∧ b) ↔ a → ¬b := and_imp

theorem not_and' {a : Prop} {b : Prop} : ¬(a ∧ b) ↔ b → ¬a := iff.trans not_and imp_not_comm

/-- One of de Morgan's laws: the negation of a disjunction is logically equivalent to the
conjunction of the negations. -/
theorem not_or_distrib {a : Prop} {b : Prop} : ¬(a ∨ b) ↔ ¬a ∧ ¬b := sorry

-- See Note [decidable namespace]

protected theorem decidable.or_iff_not_and_not {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    a ∨ b ↔ ¬(¬a ∧ ¬b) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∨ b ↔ ¬(¬a ∧ ¬b))) (Eq.symm (propext not_or_distrib))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∨ b ↔ ¬¬(a ∨ b))) (propext decidable.not_not)))
      (iff.refl (a ∨ b)))

theorem or_iff_not_and_not {a : Prop} {b : Prop} : a ∨ b ↔ ¬(¬a ∧ ¬b) :=
  decidable.or_iff_not_and_not

-- See Note [decidable namespace]

protected theorem decidable.and_iff_not_or_not {a : Prop} {b : Prop} [Decidable a] [Decidable b] :
    a ∧ b ↔ ¬(¬a ∨ ¬b) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (a ∧ b ↔ ¬(¬a ∨ ¬b))) (Eq.symm (propext decidable.not_and_distrib))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∧ b ↔ ¬¬(a ∧ b))) (propext decidable.not_not)))
      (iff.refl (a ∧ b)))

theorem and_iff_not_or_not {a : Prop} {b : Prop} : a ∧ b ↔ ¬(¬a ∨ ¬b) :=
  decidable.and_iff_not_or_not

/-! ### Declarations about equality -/

@[simp] theorem heq_iff_eq {α : Sort u_1} {a : α} {b : α} : a == b ↔ a = b :=
  { mp := eq_of_heq, mpr := heq_of_eq }

theorem proof_irrel_heq {p : Prop} {q : Prop} (hp : p) (hq : q) : hp == hq :=
  (fun (this : p = q) => Eq._oldrec (fun (hq : p) => HEq.refl hp) this hq)
    (propext { mp := fun (_x : p) => hq, mpr := fun (_x : q) => hp })

theorem ne_of_mem_of_not_mem {α : outParam (Type u_1)} {β : Type u_2} [has_mem α β] {s : β} {a : α}
    {b : α} (h : a ∈ s) : ¬b ∈ s → a ≠ b :=
  mt fun (e : a = b) => e ▸ h

theorem eq_equivalence {α : Sort u_1} : equivalence Eq :=
  { left := Eq.refl, right := { left := Eq.symm, right := Eq.trans } }

/-- Transport through trivial families is the identity. -/
@[simp] theorem eq_rec_constant {α : Sort u_1} {a : α} {a' : α} {β : Sort u_2} (y : β)
    (h : a = a') : Eq._oldrec y h = y :=
  sorry

@[simp] theorem eq_mp_rfl {α : Sort u_1} {a : α} : eq.mp (Eq.refl α) a = a := rfl

@[simp] theorem eq_mpr_rfl {α : Sort u_1} {a : α} : eq.mpr (Eq.refl α) a = a := rfl

theorem heq_of_eq_mp {α : Sort u_1} {β : Sort u_1} {a : α} {a' : β} (e : α = β)
    (h₂ : eq.mp e a = a') : a == a' :=
  sorry

theorem rec_heq_of_heq {α : Sort u_1} {a : α} {b : α} {β : Sort u_2} {C : α → Sort u_2} {x : C a}
    {y : β} (eq : a = b) (h : x == y) : Eq._oldrec x eq == y :=
  eq.drec h eq

@[simp] theorem eq_mpr_heq {α : Sort u} {β : Sort u} (h : β = α) (x : α) : eq.mpr h x == x :=
  eq.drec (fun (x : β) => HEq.refl (eq.mpr (Eq.refl β) x)) h x

protected theorem eq.congr {α : Sort u_1} {x₁ : α} {x₂ : α} {y₁ : α} {y₂ : α} (h₁ : x₁ = y₁)
    (h₂ : x₂ = y₂) : x₁ = x₂ ↔ y₁ = y₂ :=
  Eq._oldrec (Eq._oldrec (iff.refl (x₁ = x₂)) h₂) h₁

theorem eq.congr_left {α : Sort u_1} {x : α} {y : α} {z : α} (h : x = y) : x = z ↔ y = z :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x = z ↔ y = z)) h)) (iff.refl (y = z))

theorem eq.congr_right {α : Sort u_1} {x : α} {y : α} {z : α} (h : x = y) : z = x ↔ z = y :=
  eq.mpr (id (Eq._oldrec (Eq.refl (z = x ↔ z = y)) h)) (iff.refl (z = y))

theorem congr_arg2 {α : Type u_1} {β : Type u_2} {γ : Type u_3} (f : α → β → γ) {x : α} {x' : α}
    {y : β} {y' : β} (hx : x = x') (hy : y = y') : f x y = f x' y' :=
  Eq._oldrec (Eq._oldrec (Eq.refl (f x y)) hy) hx

/-! ### Declarations about quantifiers -/

theorem forall_imp {α : Sort u_1} {p : α → Prop} {q : α → Prop} (h : ∀ (a : α), p a → q a) :
    (∀ (a : α), p a) → ∀ (a : α), q a :=
  fun (h' : ∀ (a : α), p a) (a : α) => h a (h' a)

theorem forall₂_congr {α : Sort u_1} {β : Sort u_2} {p : α → β → Prop} {q : α → β → Prop}
    (h : ∀ (a : α) (b : β), p a b ↔ q a b) :
    (∀ (a : α) (b : β), p a b) ↔ ∀ (a : α) (b : β), q a b :=
  forall_congr fun (a : α) => forall_congr (h a)

theorem forall₃_congr {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} {p : α → β → γ → Prop}
    {q : α → β → γ → Prop} (h : ∀ (a : α) (b : β) (c : γ), p a b c ↔ q a b c) :
    (∀ (a : α) (b : β) (c : γ), p a b c) ↔ ∀ (a : α) (b : β) (c : γ), q a b c :=
  forall_congr fun (a : α) => forall₂_congr (h a)

theorem forall₄_congr {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} {δ : Sort u_4}
    {p : α → β → γ → δ → Prop} {q : α → β → γ → δ → Prop}
    (h : ∀ (a : α) (b : β) (c : γ) (d : δ), p a b c d ↔ q a b c d) :
    (∀ (a : α) (b : β) (c : γ) (d : δ), p a b c d) ↔ ∀ (a : α) (b : β) (c : γ) (d : δ), q a b c d :=
  forall_congr fun (a : α) => forall₃_congr (h a)

theorem Exists.imp {α : Sort u_1} {q : α → Prop} {p : α → Prop} (h : ∀ (a : α), p a → q a) :
    (∃ (a : α), p a) → ∃ (a : α), q a :=
  fun (p_1 : ∃ (a : α), p a) => exists_imp_exists h p_1

theorem exists_imp_exists' {α : Sort u_1} {β : Sort u_2} {p : α → Prop} {q : β → Prop} (f : α → β)
    (hpq : ∀ (a : α), p a → q (f a)) (hp : ∃ (a : α), p a) : ∃ (b : β), q b :=
  exists.elim hp fun (a : α) (hp' : p a) => Exists.intro (f a) (hpq a hp')

theorem exists₂_congr {α : Sort u_1} {β : Sort u_2} {p : α → β → Prop} {q : α → β → Prop}
    (h : ∀ (a : α) (b : β), p a b ↔ q a b) :
    (∃ (a : α), ∃ (b : β), p a b) ↔ ∃ (a : α), ∃ (b : β), q a b :=
  exists_congr fun (a : α) => exists_congr (h a)

theorem exists₃_congr {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} {p : α → β → γ → Prop}
    {q : α → β → γ → Prop} (h : ∀ (a : α) (b : β) (c : γ), p a b c ↔ q a b c) :
    (∃ (a : α), ∃ (b : β), ∃ (c : γ), p a b c) ↔ ∃ (a : α), ∃ (b : β), ∃ (c : γ), q a b c :=
  exists_congr fun (a : α) => exists₂_congr (h a)

theorem exists₄_congr {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} {δ : Sort u_4}
    {p : α → β → γ → δ → Prop} {q : α → β → γ → δ → Prop}
    (h : ∀ (a : α) (b : β) (c : γ) (d : δ), p a b c d ↔ q a b c d) :
    (∃ (a : α), ∃ (b : β), ∃ (c : γ), ∃ (d : δ), p a b c d) ↔
        ∃ (a : α), ∃ (b : β), ∃ (c : γ), ∃ (d : δ), q a b c d :=
  exists_congr fun (a : α) => exists₃_congr (h a)

theorem forall_swap {α : Sort u_1} {β : Sort u_2} {p : α → β → Prop} :
    (∀ (x : α) (y : β), p x y) ↔ ∀ (y : β) (x : α), p x y :=
  { mp := function.swap, mpr := function.swap }

theorem exists_swap {α : Sort u_1} {β : Sort u_2} {p : α → β → Prop} :
    (∃ (x : α), ∃ (y : β), p x y) ↔ ∃ (y : β), ∃ (x : α), p x y :=
  sorry

@[simp] theorem exists_imp_distrib {α : Sort u_1} {p : α → Prop} {b : Prop} :
    (∃ (x : α), p x) → b ↔ ∀ (x : α), p x → b :=
  sorry

/--
Extract an element from a existential statement, using `classical.some`.
-/
-- This enables projection notation.

def Exists.some {α : Sort u_1} {p : α → Prop} (P : ∃ (a : α), p a) : α := classical.some P

/--
Show that an element extracted from `P : ∃ a, p a` using `P.some` satisfies `p`.
-/
theorem Exists.some_spec {α : Sort u_1} {p : α → Prop} (P : ∃ (a : α), p a) : p (Exists.some P) :=
  classical.some_spec P

--theorem forall_not_of_not_exists (h : ¬ ∃ x, p x) : ∀ x, ¬ p x :=

--forall_imp_of_exists_imp h

theorem not_exists_of_forall_not {α : Sort u_1} {p : α → Prop} (h : ∀ (x : α), ¬p x) :
    ¬∃ (x : α), p x :=
  iff.mpr exists_imp_distrib h

@[simp] theorem not_exists {α : Sort u_1} {p : α → Prop} : (¬∃ (x : α), p x) ↔ ∀ (x : α), ¬p x :=
  exists_imp_distrib

theorem not_forall_of_exists_not {α : Sort u_1} {p : α → Prop} :
    (∃ (x : α), ¬p x) → ¬∀ (x : α), p x :=
  fun (ᾰ : ∃ (x : α), ¬p x) (ᾰ_1 : ∀ (x : α), p x) =>
    Exists.dcases_on ᾰ fun (ᾰ_w : α) (ᾰ_h : ¬p ᾰ_w) => idRhs False (ᾰ_h (ᾰ_1 ᾰ_w))

-- See Note [decidable namespace]

protected theorem decidable.not_forall {α : Sort u_1} {p : α → Prop} [Decidable (∃ (x : α), ¬p x)]
    [(x : α) → Decidable (p x)] : (¬∀ (x : α), p x) ↔ ∃ (x : α), ¬p x :=
  sorry

@[simp] theorem not_forall {α : Sort u_1} {p : α → Prop} : (¬∀ (x : α), p x) ↔ ∃ (x : α), ¬p x :=
  decidable.not_forall

-- See Note [decidable namespace]

protected theorem decidable.not_forall_not {α : Sort u_1} {p : α → Prop}
    [Decidable (∃ (x : α), p x)] : (¬∀ (x : α), ¬p x) ↔ ∃ (x : α), p x :=
  iff.mp decidable.not_iff_comm not_exists

theorem not_forall_not {α : Sort u_1} {p : α → Prop} : (¬∀ (x : α), ¬p x) ↔ ∃ (x : α), p x :=
  decidable.not_forall_not

-- See Note [decidable namespace]

protected theorem decidable.not_exists_not {α : Sort u_1} {p : α → Prop}
    [(x : α) → Decidable (p x)] : (¬∃ (x : α), ¬p x) ↔ ∀ (x : α), p x :=
  sorry

@[simp] theorem not_exists_not {α : Sort u_1} {p : α → Prop} :
    (¬∃ (x : α), ¬p x) ↔ ∀ (x : α), p x :=
  decidable.not_exists_not

@[simp] theorem forall_true_iff {α : Sort u_1} : α → True ↔ True :=
  iff_true_intro fun (_x : α) => trivial

-- Unfortunately this causes simp to loop sometimes, so we

-- add the 2 and 3 cases as simp lemmas instead

theorem forall_true_iff' {α : Sort u_1} {p : α → Prop} (h : ∀ (a : α), p a ↔ True) :
    (∀ (a : α), p a) ↔ True :=
  iff_true_intro fun (_x : α) => of_iff_true (h _x)

@[simp] theorem forall_2_true_iff {α : Sort u_1} {β : α → Sort u_2} :
    (∀ (a : α), β a → True) ↔ True :=
  forall_true_iff' fun (_x : α) => forall_true_iff

@[simp] theorem forall_3_true_iff {α : Sort u_1} {β : α → Sort u_2} {γ : (a : α) → β a → Sort u_3} :
    (∀ (a : α) (b : β a), γ a b → True) ↔ True :=
  forall_true_iff' fun (_x : α) => forall_2_true_iff

@[simp] theorem forall_const {b : Prop} (α : Sort u_1) [i : Nonempty α] : α → b ↔ b :=
  { mp := nonempty.elim i, mpr := fun (hb : b) (x : α) => hb }

@[simp] theorem exists_const {b : Prop} (α : Sort u_1) [i : Nonempty α] : (∃ (x : α), b) ↔ b :=
  { mp :=
      fun (_x : ∃ (x : α), b) =>
        (fun (_a : ∃ (x : α), b) => Exists.dcases_on _a fun (w : α) (h : b) => idRhs b h) _x,
    mpr := nonempty.elim i exists.intro }

theorem forall_and_distrib {α : Sort u_1} {p : α → Prop} {q : α → Prop} :
    (∀ (x : α), p x ∧ q x) ↔ (∀ (x : α), p x) ∧ ∀ (x : α), q x :=
  sorry

theorem exists_or_distrib {α : Sort u_1} {p : α → Prop} {q : α → Prop} :
    (∃ (x : α), p x ∨ q x) ↔ (∃ (x : α), p x) ∨ ∃ (x : α), q x :=
  sorry

@[simp] theorem exists_and_distrib_left {α : Sort u_1} {q : Prop} {p : α → Prop} :
    (∃ (x : α), q ∧ p x) ↔ q ∧ ∃ (x : α), p x :=
  sorry

@[simp] theorem exists_and_distrib_right {α : Sort u_1} {q : Prop} {p : α → Prop} :
    (∃ (x : α), p x ∧ q) ↔ (∃ (x : α), p x) ∧ q :=
  sorry

@[simp] theorem forall_eq {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∀ (a : α), a = a' → p a) ↔ p a' :=
  { mp := fun (h : ∀ (a : α), a = a' → p a) => h a' rfl,
    mpr := fun (h : p a') (a : α) (e : a = a') => Eq.symm e ▸ h }

@[simp] theorem forall_eq' {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∀ (a : α), a' = a → p a) ↔ p a' :=
  sorry

-- this lemma is needed to simplify the output of `list.mem_cons_iff`

@[simp] theorem forall_eq_or_imp {α : Sort u_1} {p : α → Prop} {q : α → Prop} {a' : α} :
    (∀ (a : α), a = a' ∨ q a → p a) ↔ p a' ∧ ∀ (a : α), q a → p a :=
  sorry

@[simp] theorem exists_eq {α : Sort u_1} {a' : α} : ∃ (a : α), a = a' := Exists.intro a' rfl

@[simp] theorem exists_eq' {α : Sort u_1} {a' : α} : ∃ (a : α), a' = a := Exists.intro a' rfl

@[simp] theorem exists_eq_left {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∃ (a : α), a = a' ∧ p a) ↔ p a' :=
  sorry

@[simp] theorem exists_eq_right {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∃ (a : α), p a ∧ a = a') ↔ p a' :=
  iff.trans (exists_congr fun (a : α) => and.comm) exists_eq_left

@[simp] theorem exists_eq_right_right {α : Sort u_1} {p : α → Prop} {b : Prop} {a' : α} :
    (∃ (a : α), p a ∧ b ∧ a = a') ↔ p a' ∧ b :=
  sorry

@[simp] theorem exists_eq_right_right' {α : Sort u_1} {p : α → Prop} {b : Prop} {a' : α} :
    (∃ (a : α), p a ∧ b ∧ a' = a) ↔ p a' ∧ b :=
  sorry

@[simp] theorem exists_apply_eq_apply {α : Type u_1} {β : Type u_2} (f : α → β) (a' : α) :
    ∃ (a : α), f a = f a' :=
  Exists.intro a' rfl

@[simp] theorem exists_apply_eq_apply' {α : Type u_1} {β : Type u_2} (f : α → β) (a' : α) :
    ∃ (a : α), f a' = f a :=
  Exists.intro a' rfl

@[simp] theorem exists_exists_and_eq_and {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : α → Prop}
    {q : β → Prop} : (∃ (b : β), (∃ (a : α), p a ∧ f a = b) ∧ q b) ↔ ∃ (a : α), p a ∧ q (f a) :=
  sorry

@[simp] theorem exists_exists_eq_and {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : β → Prop} :
    (∃ (b : β), (∃ (a : α), f a = b) ∧ p b) ↔ ∃ (a : α), p (f a) :=
  sorry

@[simp] theorem forall_apply_eq_imp_iff {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : β → Prop} :
    (∀ (a : α) (b : β), f a = b → p b) ↔ ∀ (a : α), p (f a) :=
  { mp := fun (h : ∀ (a : α) (b : β), f a = b → p b) (a : α) => h a (f a) rfl,
    mpr := fun (h : ∀ (a : α), p (f a)) (a : α) (b : β) (hab : f a = b) => hab ▸ h a }

@[simp] theorem forall_apply_eq_imp_iff' {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : β → Prop} :
    (∀ (b : β) (a : α), f a = b → p b) ↔ ∀ (a : α), p (f a) :=
  sorry

@[simp] theorem forall_eq_apply_imp_iff {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : β → Prop} :
    (∀ (a : α) (b : β), b = f a → p b) ↔ ∀ (a : α), p (f a) :=
  sorry

@[simp] theorem forall_eq_apply_imp_iff' {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : β → Prop} :
    (∀ (b : β) (a : α), b = f a → p b) ↔ ∀ (a : α), p (f a) :=
  sorry

@[simp] theorem forall_apply_eq_imp_iff₂ {α : Sort u_1} {β : Sort u_2} {f : α → β} {p : α → Prop}
    {q : β → Prop} : (∀ (b : β) (a : α), p a → f a = b → q b) ↔ ∀ (a : α), p a → q (f a) :=
  { mp := fun (h : ∀ (b : β) (a : α), p a → f a = b → q b) (a : α) (ha : p a) => h (f a) a ha rfl,
    mpr :=
      fun (h : ∀ (a : α), p a → q (f a)) (b : β) (a : α) (ha : p a) (hb : f a = b) => hb ▸ h a ha }

@[simp] theorem exists_eq_left' {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∃ (a : α), a' = a ∧ p a) ↔ p a' :=
  sorry

@[simp] theorem exists_eq_right' {α : Sort u_1} {p : α → Prop} {a' : α} :
    (∃ (a : α), p a ∧ a' = a) ↔ p a' :=
  sorry

theorem exists_comm {α : Sort u_1} {β : Sort u_2} {p : α → β → Prop} :
    (∃ (a : α), ∃ (b : β), p a b) ↔ ∃ (b : β), ∃ (a : α), p a b :=
  sorry

theorem forall_or_of_or_forall {α : Sort u_1} {p : α → Prop} {b : Prop} (h : b ∨ ∀ (x : α), p x)
    (x : α) : b ∨ p x :=
  or.imp_right (fun (h₂ : ∀ (x : α), p x) => h₂ x) h

-- See Note [decidable namespace]

protected theorem decidable.forall_or_distrib_left {α : Sort u_1} {q : Prop} {p : α → Prop}
    [Decidable q] : (∀ (x : α), q ∨ p x) ↔ q ∨ ∀ (x : α), p x :=
  sorry

theorem forall_or_distrib_left {α : Sort u_1} {q : Prop} {p : α → Prop} :
    (∀ (x : α), q ∨ p x) ↔ q ∨ ∀ (x : α), p x :=
  decidable.forall_or_distrib_left

-- See Note [decidable namespace]

protected theorem decidable.forall_or_distrib_right {α : Sort u_1} {q : Prop} {p : α → Prop}
    [Decidable q] : (∀ (x : α), p x ∨ q) ↔ (∀ (x : α), p x) ∨ q :=
  sorry

theorem forall_or_distrib_right {α : Sort u_1} {q : Prop} {p : α → Prop} :
    (∀ (x : α), p x ∨ q) ↔ (∀ (x : α), p x) ∨ q :=
  decidable.forall_or_distrib_right

/-- A predicate holds everywhere on the image of a surjective functions iff
    it holds everywhere. -/
theorem forall_iff_forall_surj {α : Type u_1} {β : Type u_2} {f : α → β} (h : function.surjective f)
    {P : β → Prop} : (∀ (a : α), P (f a)) ↔ ∀ (b : β), P b :=
  sorry

@[simp] theorem exists_prop {p : Prop} {q : Prop} : (∃ (h : p), q) ↔ p ∧ q := sorry

@[simp] theorem exists_false {α : Sort u_1} : ¬∃ (a : α), False :=
  fun (_x : ∃ (a : α), False) =>
    (fun (_a : ∃ (a : α), False) => Exists.dcases_on _a fun (w : α) (h : False) => idRhs False h) _x

@[simp] theorem exists_unique_false {α : Sort u_1} : ¬exists_unique fun (a : α) => False := sorry

theorem Exists.fst {b : Prop} {p : b → Prop} : Exists p → b :=
  fun (ᾰ : Exists p) => Exists.dcases_on ᾰ fun (ᾰ_w : b) (ᾰ_h : p ᾰ_w) => idRhs b ᾰ_w

theorem Exists.snd {b : Prop} {p : b → Prop} (h : Exists p) : p (Exists.fst h) :=
  Exists.dcases_on h fun (h_w : b) (h_h : p h_w) => idRhs (p h_w) h_h

@[simp] theorem forall_prop_of_true {p : Prop} {q : p → Prop} (h : p) : (∀ (h' : p), q h') ↔ q h :=
  forall_const p

@[simp] theorem exists_prop_of_true {p : Prop} {q : p → Prop} (h : p) : (∃ (h' : p), q h') ↔ q h :=
  exists_const p

@[simp] theorem forall_prop_of_false {p : Prop} {q : p → Prop} (hn : ¬p) :
    (∀ (h' : p), q h') ↔ True :=
  iff_true_intro fun (h : p) => not.elim hn h

@[simp] theorem exists_prop_of_false {p : Prop} {q : p → Prop} : ¬p → ¬∃ (h' : p), q h' :=
  mt Exists.fst

theorem exists_unique.exists {α : Sort u_1} {p : α → Prop} (h : exists_unique fun (x : α) => p x) :
    ∃ (x : α), p x :=
  exists.elim h
    fun (x : α) (hx : (fun (x : α) => p x) x ∧ ∀ (y : α), p y → y = x) =>
      Exists.intro x (and.left hx)

theorem exists_unique.unique {α : Sort u_1} {p : α → Prop} (h : exists_unique fun (x : α) => p x)
    {y₁ : α} {y₂ : α} (py₁ : p y₁) (py₂ : p y₂) : y₁ = y₂ :=
  unique_of_exists_unique h py₁ py₂

@[simp] theorem exists_unique_iff_exists {α : Sort u_1} [subsingleton α] {p : α → Prop} :
    (exists_unique fun (x : α) => p x) ↔ ∃ (x : α), p x :=
  { mp := fun (h : exists_unique fun (x : α) => p x) => exists_unique.exists h,
    mpr :=
      Exists.imp
        fun (x : α) (hx : p x) =>
          { left := hx, right := fun (y : α) (_x : p y) => subsingleton.elim y x } }

theorem exists_unique.elim2 {α : Sort u_1} {p : α → Sort u_2} [∀ (x : α), subsingleton (p x)]
    {q : (x : α) → p x → Prop} {b : Prop}
    (h₂ : exists_unique fun (x : α) => exists_unique fun (h : p x) => q x h)
    (h₁ : ∀ (x : α) (h : p x), q x h → (∀ (y : α) (hy : p y), q y hy → y = x) → b) : b :=
  sorry

theorem exists_unique.intro2 {α : Sort u_1} {p : α → Sort u_2} [∀ (x : α), subsingleton (p x)]
    {q : (x : α) → p x → Prop} (w : α) (hp : p w) (hq : q w hp)
    (H : ∀ (y : α) (hy : p y), q y hy → y = w) :
    exists_unique fun (x : α) => exists_unique fun (hx : p x) => q x hx :=
  sorry

theorem exists_unique.exists2 {α : Sort u_1} {p : α → Sort u_2} {q : (x : α) → p x → Prop}
    (h : exists_unique fun (x : α) => exists_unique fun (hx : p x) => q x hx) :
    ∃ (x : α), ∃ (hx : p x), q x hx :=
  Exists.imp (fun (x : α) (hx : exists_unique fun (hx : p x) => q x hx) => exists_unique.exists hx)
    (exists_unique.exists h)

theorem exists_unique.unique2 {α : Sort u_1} {p : α → Sort u_2} [∀ (x : α), subsingleton (p x)]
    {q : (x : α) → p x → Prop}
    (h : exists_unique fun (x : α) => exists_unique fun (hx : p x) => q x hx) {y₁ : α} {y₂ : α}
    (hpy₁ : p y₁) (hqy₁ : q y₁ hpy₁) (hpy₂ : p y₂) (hqy₂ : q y₂ hpy₂) : y₁ = y₂ :=
  sorry

/-! ### Classical lemmas -/

namespace classical

theorem cases {p : Prop → Prop} (h1 : p True) (h2 : p False) (a : Prop) : p a := cases_on a h1 h2

/- use shortened names to avoid conflict when classical namespace is open. -/

theorem dec (p : Prop) : Decidable p := prop_decidable p

theorem dec_pred {α : Sort u_1} (p : α → Prop) : decidable_pred p :=
  fun (a : α) => prop_decidable (p a)

theorem dec_rel {α : Sort u_1} (p : α → α → Prop) : DecidableRel p :=
  fun (a b : α) => prop_decidable (p a b)

theorem dec_eq (α : Sort u_1) : DecidableEq α := fun (a b : α) => prop_decidable (a = b)

/--
We make decidability results that depends on `classical.choice` noncomputable lemmas.
* We have to mark them as noncomputable, because otherwise Lean will try to generate bytecode
  for them, and fail because it depends on `classical.choice`.
* We make them lemmas, and not definitions, because otherwise later definitions will raise
  \"failed to generate bytecode\" errors when writing something like
  `letI := classical.dec_eq _`.
Cf. <https://leanprover-community.github.io/archive/113488general/08268noncomputabletheorem.html>
-/
/-- Construct a function from a default value `H0`, and a function to use if there exists a value
satisfying the predicate. -/
def exists_cases {α : Sort u_1} {p : α → Prop} {C : Sort u} (H0 : C) (H : (a : α) → p a → C) : C :=
  dite (∃ (a : α), p a) (fun (h : ∃ (a : α), p a) => H (some h) sorry)
    fun (h : ¬∃ (a : α), p a) => H0

theorem some_spec2 {α : Sort u_1} {p : α → Prop} {h : ∃ (a : α), p a} (q : α → Prop)
    (hpq : ∀ (a : α), p a → q a) : q (some h) :=
  hpq (some h) (some_spec h)

/-- A version of classical.indefinite_description which is definitionally equal to a pair -/
def subtype_of_exists {α : Type u_1} {P : α → Prop} (h : ∃ (x : α), P x) :
    Subtype fun (x : α) => P x :=
  { val := some h, property := sorry }

end classical

/-- This function has the same type as `exists.rec_on`, and can be used to case on an equality,
but `exists.rec_on` can only eliminate into Prop, while this version eliminates into any universe
using the axiom of choice. -/
def exists.classical_rec_on {α : Sort u_1} {p : α → Prop} (h : ∃ (a : α), p a) {C : Sort u}
    (H : (a : α) → p a → C) : C :=
  H (classical.some h) sorry

/-! ### Declarations about bounded quantifiers -/

theorem bex_def {α : Sort u_1} {p : α → Prop} {q : α → Prop} :
    (∃ (x : α), ∃ (h : p x), q x) ↔ ∃ (x : α), p x ∧ q x :=
  sorry

theorem bex.elim {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} {b : Prop} :
    (∃ (x : α), ∃ (h : p x), P x h) → (∀ (a : α) (h : p a), P a h → b) → b :=
  sorry

theorem bex.intro {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} (a : α) (h₁ : p a)
    (h₂ : P a h₁) : ∃ (x : α), ∃ (h : p x), P x h :=
  Exists.intro a (Exists.intro h₁ h₂)

theorem ball_congr {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} (H : ∀ (x : α) (h : p x), P x h ↔ Q x h) :
    (∀ (x : α) (h : p x), P x h) ↔ ∀ (x : α) (h : p x), Q x h :=
  forall_congr fun (x : α) => forall_congr (H x)

theorem bex_congr {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} (H : ∀ (x : α) (h : p x), P x h ↔ Q x h) :
    (∃ (x : α), ∃ (h : p x), P x h) ↔ ∃ (x : α), ∃ (h : p x), Q x h :=
  exists_congr fun (x : α) => exists_congr (H x)

theorem bex_eq_left {α : Sort u_1} {p : α → Prop} {a : α} :
    (∃ (x : α), ∃ (_x : x = a), p x) ↔ p a :=
  sorry

theorem ball.imp_right {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} (H : ∀ (x : α) (h : p x), P x h → Q x h)
    (h₁ : ∀ (x : α) (h : p x), P x h) (x : α) (h : p x) : Q x h :=
  H x h (h₁ x h)

theorem bex.imp_right {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} (H : ∀ (x : α) (h : p x), P x h → Q x h) :
    (∃ (x : α), ∃ (h : p x), P x h) → ∃ (x : α), ∃ (h : p x), Q x h :=
  sorry

theorem ball.imp_left {α : Sort u_1} {r : α → Prop} {p : α → Prop} {q : α → Prop}
    (H : ∀ (x : α), p x → q x) (h₁ : ∀ (x : α), q x → r x) (x : α) (h : p x) : r x :=
  h₁ x (H x h)

theorem bex.imp_left {α : Sort u_1} {r : α → Prop} {p : α → Prop} {q : α → Prop}
    (H : ∀ (x : α), p x → q x) : (∃ (x : α), ∃ (_x : p x), r x) → ∃ (x : α), ∃ (_x : q x), r x :=
  sorry

theorem ball_of_forall {α : Sort u_1} {p : α → Prop} (h : ∀ (x : α), p x) (x : α) : p x := h x

theorem forall_of_ball {α : Sort u_1} {p : α → Prop} {q : α → Prop} (H : ∀ (x : α), p x)
    (h : ∀ (x : α), p x → q x) (x : α) : q x :=
  h x (H x)

theorem bex_of_exists {α : Sort u_1} {p : α → Prop} {q : α → Prop} (H : ∀ (x : α), p x) :
    (∃ (x : α), q x) → ∃ (x : α), ∃ (_x : p x), q x :=
  fun (ᾰ : ∃ (x : α), q x) =>
    Exists.dcases_on ᾰ
      fun (ᾰ_w : α) (ᾰ_h : q ᾰ_w) =>
        idRhs (∃ (x : α), ∃ (_x : p x), q x) (Exists.intro ᾰ_w (Exists.intro (H ᾰ_w) ᾰ_h))

theorem exists_of_bex {α : Sort u_1} {p : α → Prop} {q : α → Prop} :
    (∃ (x : α), ∃ (_x : p x), q x) → ∃ (x : α), q x :=
  sorry

@[simp] theorem bex_imp_distrib {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {b : Prop} : (∃ (x : α), ∃ (h : p x), P x h) → b ↔ ∀ (x : α) (h : p x), P x h → b :=
  sorry

theorem not_bex {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} :
    (¬∃ (x : α), ∃ (h : p x), P x h) ↔ ∀ (x : α) (h : p x), ¬P x h :=
  bex_imp_distrib

theorem not_ball_of_bex_not {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} :
    (∃ (x : α), ∃ (h : p x), ¬P x h) → ¬∀ (x : α) (h : p x), P x h :=
  sorry

-- See Note [decidable namespace]

protected theorem decidable.not_ball {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    [Decidable (∃ (x : α), ∃ (h : p x), ¬P x h)] [(x : α) → (h : p x) → Decidable (P x h)] :
    (¬∀ (x : α) (h : p x), P x h) ↔ ∃ (x : α), ∃ (h : p x), ¬P x h :=
  sorry

theorem not_ball {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} :
    (¬∀ (x : α) (h : p x), P x h) ↔ ∃ (x : α), ∃ (h : p x), ¬P x h :=
  decidable.not_ball

theorem ball_true_iff {α : Sort u_1} (p : α → Prop) : (∀ (x : α), p x → True) ↔ True :=
  iff_true_intro fun (h : α) (hrx : p h) => trivial

theorem ball_and_distrib {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} :
    (∀ (x : α) (h : p x), P x h ∧ Q x h) ↔
        (∀ (x : α) (h : p x), P x h) ∧ ∀ (x : α) (h : p x), Q x h :=
  iff.trans (forall_congr fun (x : α) => forall_and_distrib) forall_and_distrib

theorem bex_or_distrib {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop}
    {Q : (x : α) → p x → Prop} :
    (∃ (x : α), ∃ (h : p x), P x h ∨ Q x h) ↔
        (∃ (x : α), ∃ (h : p x), P x h) ∨ ∃ (x : α), ∃ (h : p x), Q x h :=
  iff.trans (exists_congr fun (x : α) => exists_or_distrib) exists_or_distrib

theorem ball_or_left_distrib {α : Sort u_1} {r : α → Prop} {p : α → Prop} {q : α → Prop} :
    (∀ (x : α), p x ∨ q x → r x) ↔ (∀ (x : α), p x → r x) ∧ ∀ (x : α), q x → r x :=
  iff.trans (forall_congr fun (x : α) => or_imp_distrib) forall_and_distrib

theorem bex_or_left_distrib {α : Sort u_1} {r : α → Prop} {p : α → Prop} {q : α → Prop} :
    (∃ (x : α), ∃ (_x : p x ∨ q x), r x) ↔
        (∃ (x : α), ∃ (_x : p x), r x) ∨ ∃ (x : α), ∃ (_x : q x), r x :=
  sorry

namespace classical

theorem not_ball {α : Sort u_1} {p : α → Prop} {P : (x : α) → p x → Prop} :
    (¬∀ (x : α) (h : p x), P x h) ↔ ∃ (x : α), ∃ (h : p x), ¬P x h :=
  not_ball

end classical

theorem ite_eq_iff {α : Sort u_1} {p : Prop} [Decidable p] {a : α} {b : α} {c : α} :
    ite p a b = c ↔ p ∧ a = c ∨ ¬p ∧ b = c :=
  sorry

@[simp] theorem ite_eq_left_iff {α : Sort u_1} {p : Prop} [Decidable p] {a : α} {b : α} :
    ite p a b = a ↔ ¬p → b = a :=
  sorry

@[simp] theorem ite_eq_right_iff {α : Sort u_1} {p : Prop} [Decidable p] {a : α} {b : α} :
    ite p a b = b ↔ p → a = b :=
  sorry

/-! ### Declarations about `nonempty` -/

protected instance has_zero.nonempty {α : Type u} [HasZero α] : Nonempty α := Nonempty.intro 0

protected instance has_one.nonempty {α : Type u} [HasOne α] : Nonempty α := Nonempty.intro 1

theorem exists_true_iff_nonempty {α : Sort u_1} : (∃ (a : α), True) ↔ Nonempty α := sorry

@[simp] theorem nonempty_Prop {p : Prop} : Nonempty p ↔ p :=
  { mp :=
      fun (_x : Nonempty p) =>
        (fun (_a : Nonempty p) => nonempty.dcases_on _a fun (val : p) => idRhs p val) _x,
    mpr := fun (h : p) => Nonempty.intro h }

theorem not_nonempty_iff_imp_false {α : Type u} : ¬Nonempty α ↔ α → False := sorry

@[simp] theorem nonempty_sigma {α : Type u} {γ : α → Type w} :
    Nonempty (sigma fun (a : α) => γ a) ↔ ∃ (a : α), Nonempty (γ a) :=
  sorry

@[simp] theorem nonempty_subtype {α : Sort u} {p : α → Prop} :
    Nonempty (Subtype p) ↔ ∃ (a : α), p a :=
  sorry

@[simp] theorem nonempty_prod {α : Type u} {β : Type v} :
    Nonempty (α × β) ↔ Nonempty α ∧ Nonempty β :=
  sorry

@[simp] theorem nonempty_pprod {α : Sort u} {β : Sort v} :
    Nonempty (PProd α β) ↔ Nonempty α ∧ Nonempty β :=
  sorry

@[simp] theorem nonempty_sum {α : Type u} {β : Type v} :
    Nonempty (α ⊕ β) ↔ Nonempty α ∨ Nonempty β :=
  sorry

@[simp] theorem nonempty_psum {α : Sort u} {β : Sort v} :
    Nonempty (psum α β) ↔ Nonempty α ∨ Nonempty β :=
  sorry

@[simp] theorem nonempty_psigma {α : Sort u} {β : α → Sort v} :
    Nonempty (psigma β) ↔ ∃ (a : α), Nonempty (β a) :=
  sorry

@[simp] theorem nonempty_empty : ¬Nonempty empty :=
  fun (_x : Nonempty empty) =>
    (fun (_a : Nonempty empty) =>
        nonempty.dcases_on _a fun (val : empty) => idRhs False (empty.elim val))
      _x

@[simp] theorem nonempty_ulift {α : Type u} : Nonempty (ulift α) ↔ Nonempty α := sorry

@[simp] theorem nonempty_plift {α : Sort u} : Nonempty (plift α) ↔ Nonempty α := sorry

@[simp] theorem nonempty.forall {α : Sort u} {p : Nonempty α → Prop} :
    (∀ (h : Nonempty α), p h) ↔ ∀ (a : α), p (Nonempty.intro a) :=
  sorry

@[simp] theorem nonempty.exists {α : Sort u} {p : Nonempty α → Prop} :
    (∃ (h : Nonempty α), p h) ↔ ∃ (a : α), p (Nonempty.intro a) :=
  sorry

theorem classical.nonempty_pi {α : Sort u} {β : α → Sort v} :
    Nonempty ((a : α) → β a) ↔ ∀ (a : α), Nonempty (β a) :=
  sorry

/-- Using `classical.choice`, lifts a (`Prop`-valued) `nonempty` instance to a (`Type`-valued)
  `inhabited` instance. `classical.inhabited_of_nonempty` already exists, in
  `core/init/classical.lean`, but the assumption is not a type class argument,
  which makes it unsuitable for some applications. -/
def classical.inhabited_of_nonempty' {α : Sort u} [h : Nonempty α] : Inhabited α :=
  { default := Classical.choice h }

/-- Using `classical.choice`, extracts a term from a `nonempty` type. -/
protected def nonempty.some {α : Sort u} (h : Nonempty α) : α := Classical.choice h

/-- Using `classical.choice`, extracts a term from a `nonempty` type. -/
protected def classical.arbitrary (α : Sort u) [h : Nonempty α] : α := Classical.choice h

/-- Given `f : α → β`, if `α` is nonempty then `β` is also nonempty.
  `nonempty` cannot be a `functor`, because `functor` is restricted to `Type`. -/
theorem nonempty.map {α : Sort u} {β : Sort v} (f : α → β) : Nonempty α → Nonempty β :=
  fun (ᾰ : Nonempty α) =>
    nonempty.dcases_on ᾰ fun (ᾰ : α) => idRhs (Nonempty β) (Nonempty.intro (f ᾰ))

protected theorem nonempty.map2 {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} (f : α → β → γ) :
    Nonempty α → Nonempty β → Nonempty γ :=
  fun (ᾰ : Nonempty α) (ᾰ_1 : Nonempty β) =>
    nonempty.dcases_on ᾰ
      fun (ᾰ_1_1 : α) =>
        nonempty.dcases_on ᾰ_1 fun (ᾰ : β) => idRhs (Nonempty γ) (Nonempty.intro (f ᾰ_1_1 ᾰ))

protected theorem nonempty.congr {α : Sort u} {β : Sort v} (f : α → β) (g : β → α) :
    Nonempty α ↔ Nonempty β :=
  { mp := nonempty.map f, mpr := nonempty.map g }

theorem nonempty.elim_to_inhabited {α : Sort u_1} [h : Nonempty α] {p : Prop}
    (f : Inhabited α → p) : p :=
  nonempty.elim h (f ∘ Inhabited.mk)

protected instance prod.nonempty {α : Type u_1} {β : Type u_2} [h : Nonempty α] [h2 : Nonempty β] :
    Nonempty (α × β) :=
  nonempty.elim h fun (g : α) => nonempty.elim h2 fun (g2 : β) => Nonempty.intro (g, g2)

/-- A function applied to a `dite` is a `dite` of that function applied to each of the branches. -/
theorem apply_dite {α : Sort u_1} {β : Sort u_2} (f : α → β) (P : Prop) [Decidable P] (x : P → α)
    (y : ¬P → α) : f (dite P x y) = dite P (fun (h : P) => f (x h)) fun (h : ¬P) => f (y h) :=
  sorry

/-- A function applied to a `ite` is a `ite` of that function applied to each of the branches. -/
theorem apply_ite {α : Sort u_1} {β : Sort u_2} (f : α → β) (P : Prop) [Decidable P] (x : α)
    (y : α) : f (ite P x y) = ite P (f x) (f y) :=
  apply_dite f P (fun (_x : P) => x) fun (_x : ¬P) => y

/-- A two-argument function applied to two `dite`s is a `dite` of that two-argument function
applied to each of the branches. -/
theorem apply_dite2 {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} (f : α → β → γ) (P : Prop)
    [Decidable P] (a : P → α) (b : ¬P → α) (c : P → β) (d : ¬P → β) :
    f (dite P a b) (dite P c d) =
        dite P (fun (h : P) => f (a h) (c h)) fun (h : ¬P) => f (b h) (d h) :=
  sorry

/-- A two-argument function applied to two `ite`s is a `ite` of that two-argument function
applied to each of the branches. -/
theorem apply_ite2 {α : Sort u_1} {β : Sort u_2} {γ : Sort u_3} (f : α → β → γ) (P : Prop)
    [Decidable P] (a : α) (b : α) (c : β) (d : β) :
    f (ite P a b) (ite P c d) = ite P (f a c) (f b d) :=
  apply_dite2 f P (fun (_x : P) => a) (fun (_x : ¬P) => b) (fun (_x : P) => c) fun (_x : ¬P) => d

/-- A 'dite' producing a `Pi` type `Π a, β a`, applied to a value `x : α`
is a `dite` that applies either branch to `x`. -/
theorem dite_apply {α : Sort u_1} {β : α → Sort u_2} (P : Prop) [Decidable P]
    (f : P → (a : α) → β a) (g : ¬P → (a : α) → β a) (x : α) :
    dite P f g x = dite P (fun (h : P) => f h x) fun (h : ¬P) => g h x :=
  sorry

/-- A 'ite' producing a `Pi` type `Π a, β a`, applied to a value `x : α`
is a `ite` that applies either branch to `x` -/
theorem ite_apply {α : Sort u_1} {β : α → Sort u_2} (P : Prop) [Decidable P] (f : (a : α) → β a)
    (g : (a : α) → β a) (x : α) : ite P f g x = ite P (f x) (g x) :=
  dite_apply P (fun (_x : P) => f) (fun (_x : ¬P) => g) x

/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp] theorem dite_not {α : Sort u_1} (P : Prop) [Decidable P] (x : ¬P → α) (y : ¬¬P → α) :
    dite (¬P) x y = dite P (fun (h : P) => y (not_not_intro h)) x :=
  sorry

/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp] theorem ite_not {α : Sort u_1} (P : Prop) [Decidable P] (x : α) (y : α) :
    ite (¬P) x y = ite P y x :=
  dite_not P (fun (_x : ¬P) => x) fun (_x : ¬¬P) => y

theorem ite_and {α : Sort u_1} {p : Prop} {q : Prop} [Decidable p] [Decidable q] {x : α} {y : α} :
    ite (p ∧ q) x y = ite p (ite q x y) y :=
  sorry

end Mathlib


/-
Copyright (c) 2019 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.equiv.basic
import Mathlib.data.list.basic
import Mathlib.algebra.star.basic
import Mathlib.PostPort

universes u_1 u_2 u_4 u_5 u_3

namespace Mathlib

/-!
# Free monoid over a given alphabet

## Main definitions

* `free_monoid α`: free monoid over alphabet `α`; defined as a synonym for `list α`
  with multiplication given by `(++)`.
* `free_monoid.of`: embedding `α → free_monoid α` sending each element `x` to `[x]`;
* `free_monoid.lift`: natural equivalence between `α → M` and `free_monoid α →* M`
* `free_monoid.map`: embedding of `α → β` into `free_monoid α →* free_monoid β` given by `list.map`.
-/

/-- Free monoid over a given alphabet. -/
def free_add_monoid (α : Type u_1) := List α

namespace free_monoid

protected instance Mathlib.free_add_monoid.add_monoid {α : Type u_1} :
    add_monoid (free_add_monoid α) :=
  add_monoid.mk (fun (x y : free_add_monoid α) => x ++ y) sorry [] sorry sorry

protected instance inhabited {α : Type u_1} : Inhabited (free_monoid α) := { default := 1 }

theorem one_def {α : Type u_1} : 1 = [] := rfl

theorem Mathlib.free_add_monoid.add_def {α : Type u_1} (xs : List α) (ys : List α) :
    xs + ys = xs ++ ys :=
  rfl

/-- Embeds an element of `α` into `free_monoid α` as a singleton list. -/
def of {α : Type u_1} (x : α) : free_monoid α := [x]

theorem of_def {α : Type u_1} (x : α) : of x = [x] := rfl

theorem of_injective {α : Type u_1} : function.injective of :=
  fun (a b : α) => list.head_eq_of_cons_eq

/-- Recursor for `free_monoid` using `1` and `of x * xs` instead of `[]` and `x :: xs`. -/
def rec_on {α : Type u_1} {C : free_monoid α → Sort u_2} (xs : free_monoid α) (h0 : C 1)
    (ih : (x : α) → (xs : free_monoid α) → C xs → C (of x * xs)) : C xs :=
  list.rec_on xs h0 ih

theorem hom_eq {α : Type u_1} {M : Type u_4} [monoid M] {f : free_monoid α →* M}
    {g : free_monoid α →* M} (h : ∀ (x : α), coe_fn f (of x) = coe_fn g (of x)) : f = g :=
  sorry

/-- Equivalence between maps `α → M` and monoid homomorphisms `free_monoid α →* M`. -/
def lift {α : Type u_1} {M : Type u_4} [monoid M] : (α → M) ≃ (free_monoid α →* M) :=
  equiv.mk
    (fun (f : α → M) =>
      monoid_hom.mk (fun (l : free_monoid α) => list.prod (list.map f l)) sorry sorry)
    (fun (f : free_monoid α →* M) (x : α) => coe_fn f (of x)) sorry sorry

@[simp] theorem Mathlib.free_add_monoid.lift_symm_apply {α : Type u_1} {M : Type u_4} [add_monoid M]
    (f : free_add_monoid α →+ M) :
    coe_fn (equiv.symm free_add_monoid.lift) f = ⇑f ∘ free_add_monoid.of :=
  rfl

theorem lift_apply {α : Type u_1} {M : Type u_4} [monoid M] (f : α → M) (l : free_monoid α) :
    coe_fn (coe_fn lift f) l = list.prod (list.map f l) :=
  rfl

theorem Mathlib.free_add_monoid.lift_comp_of {α : Type u_1} {M : Type u_4} [add_monoid M]
    (f : α → M) : ⇑(coe_fn free_add_monoid.lift f) ∘ free_add_monoid.of = f :=
  equiv.symm_apply_apply free_add_monoid.lift f

@[simp] theorem Mathlib.free_add_monoid.lift_eval_of {α : Type u_1} {M : Type u_4} [add_monoid M]
    (f : α → M) (x : α) : coe_fn (coe_fn free_add_monoid.lift f) (free_add_monoid.of x) = f x :=
  congr_fun (free_add_monoid.lift_comp_of f) x

@[simp] theorem lift_restrict {α : Type u_1} {M : Type u_4} [monoid M] (f : free_monoid α →* M) :
    coe_fn lift (⇑f ∘ of) = f :=
  equiv.apply_symm_apply lift f

theorem comp_lift {α : Type u_1} {M : Type u_4} [monoid M] {N : Type u_5} [monoid N] (g : M →* N)
    (f : α → M) : monoid_hom.comp g (coe_fn lift f) = coe_fn lift (⇑g ∘ f) :=
  sorry

theorem hom_map_lift {α : Type u_1} {M : Type u_4} [monoid M] {N : Type u_5} [monoid N] (g : M →* N)
    (f : α → M) (x : free_monoid α) :
    coe_fn g (coe_fn (coe_fn lift f) x) = coe_fn (coe_fn lift (⇑g ∘ f)) x :=
  iff.mp monoid_hom.ext_iff (comp_lift g f) x

/-- The unique monoid homomorphism `free_monoid α →* free_monoid β` that sends
each `of x` to `of (f x)`. -/
def map {α : Type u_1} {β : Type u_2} (f : α → β) : free_monoid α →* free_monoid β :=
  monoid_hom.mk (list.map f) sorry sorry

@[simp] theorem map_of {α : Type u_1} {β : Type u_2} (f : α → β) (x : α) :
    coe_fn (map f) (of x) = of (f x) :=
  rfl

theorem Mathlib.free_add_monoid.lift_of_comp_eq_map {α : Type u_1} {β : Type u_2} (f : α → β) :
    (coe_fn free_add_monoid.lift fun (x : α) => free_add_monoid.of (f x)) = free_add_monoid.map f :=
  free_add_monoid.hom_eq fun (x : α) => rfl

theorem map_comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} (g : β → γ) (f : α → β) :
    map (g ∘ f) = monoid_hom.comp (map g) (map f) :=
  hom_eq fun (x : α) => rfl

protected instance star_monoid {α : Type u_1} : star_monoid (free_monoid α) :=
  star_monoid.mk list.reverse_append

@[simp] theorem star_of {α : Type u_1} (x : α) : star (of x) = of x := rfl

/-- Note that `star_one` is already a global simp lemma, but this one works with dsimp too -/
@[simp] theorem star_one {α : Type u_1} : star 1 = 1 := rfl

end Mathlib


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

A computable model of hereditarily finite sets with atoms
(ZFA without infinity). This is useful for calculations in naive
set theory.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.list.basic
import Mathlib.data.sigma.default
import Mathlib.PostPort

universes u l u_1 u_2 u_3

namespace Mathlib

inductive lists' (α : Type u) : Bool → Type u
where
| atom : α → lists' α false
| nil : lists' α tt
| cons' : {b : Bool} → lists' α b → lists' α tt → lists' α tt

def lists (α : Type u_1) :=
  sigma fun (b : Bool) => lists' α b

namespace lists'

protected instance inhabited {α : Type u_1} [Inhabited α] (b : Bool) : Inhabited (lists' α b) :=
  sorry

def cons {α : Type u_1} : lists α → lists' α tt → lists' α tt :=
  sorry

@[simp] def to_list {α : Type u_1} {b : Bool} : lists' α b → List (lists α) :=
  sorry

@[simp] theorem to_list_cons {α : Type u_1} (a : lists α) (l : lists' α tt) : to_list (cons a l) = a :: to_list l := sorry

@[simp] def of_list {α : Type u_1} : List (lists α) → lists' α tt :=
  sorry

@[simp] theorem to_of_list {α : Type u_1} (l : List (lists α)) : to_list (of_list l) = l := sorry

@[simp] theorem of_to_list {α : Type u_1} (l : lists' α tt) : of_list (to_list l) = l := sorry

end lists'

def lists'.subset {α : Type u_1} : lists' α tt → lists' α tt → Prop :=
  fun (ᾰ ᾰ_1 : lists' α tt) =>
    lists.equiv._mut_
      ((fun (idx : psigma fun (ᾰ : lists' α tt) => psigma fun (ᾰ : lists' α tt) => Unit) => psum.inr idx)
        ((fun (ᾰ ᾰ_2 : lists' α tt) => psigma.mk ᾰ (psigma.mk ᾰ_2 Unit.unit)) ᾰ ᾰ_1))

namespace lists'

protected instance has_subset {α : Type u_1} : has_subset (lists' α tt) :=
  has_subset.mk subset

protected instance has_mem {α : Type u_1} {b : Bool} : has_mem (lists α) (lists' α b) :=
  has_mem.mk fun (a : lists α) (l : lists' α b) => ∃ (a' : lists α), ∃ (H : a' ∈ to_list l), lists.equiv a a'

theorem mem_def {α : Type u_1} {b : Bool} {a : lists α} {l : lists' α b} : a ∈ l ↔ ∃ (a' : lists α), ∃ (H : a' ∈ to_list l), lists.equiv a a' :=
  iff.rfl

@[simp] theorem mem_cons {α : Type u_1} {a : lists α} {y : lists α} {l : lists' α tt} : a ∈ cons y l ↔ lists.equiv a y ∨ a ∈ l := sorry

theorem cons_subset {α : Type u_1} {a : lists α} {l₁ : lists' α tt} {l₂ : lists' α tt} : cons a l₁ ⊆ l₂ ↔ a ∈ l₂ ∧ l₁ ⊆ l₂ := sorry

theorem of_list_subset {α : Type u_1} {l₁ : List (lists α)} {l₂ : List (lists α)} (h : l₁ ⊆ l₂) : of_list l₁ ⊆ of_list l₂ := sorry

theorem subset.refl {α : Type u_1} {l : lists' α tt} : l ⊆ l :=
  eq.mpr (id (Eq._oldrec (Eq.refl (l ⊆ l)) (Eq.symm (of_to_list l)))) (of_list_subset (list.subset.refl (to_list l)))

theorem subset_nil {α : Type u_1} {l : lists' α tt} : l ⊆ nil → l = nil := sorry

theorem mem_of_subset' {α : Type u_1} {a : lists α} {l₁ : lists' α tt} {l₂ : lists' α tt} (s : l₁ ⊆ l₂) (h : a ∈ to_list l₁) : a ∈ l₂ := sorry

theorem subset_def {α : Type u_1} {l₁ : lists' α tt} {l₂ : lists' α tt} : l₁ ⊆ l₂ ↔ ∀ (a : lists α), a ∈ to_list l₁ → a ∈ l₂ := sorry

end lists'

namespace lists

def atom {α : Type u_1} (a : α) : lists α :=
  sigma.mk false (lists'.atom a)

def of' {α : Type u_1} (l : lists' α tt) : lists α :=
  sigma.mk tt l

@[simp] def to_list {α : Type u_1} : lists α → List (lists α) :=
  sorry

def is_list {α : Type u_1} (l : lists α) :=
  ↥(sigma.fst l)

def of_list {α : Type u_1} (l : List (lists α)) : lists α :=
  of' (lists'.of_list l)

theorem is_list_to_list {α : Type u_1} (l : List (lists α)) : is_list (of_list l) :=
  Eq.refl (sigma.fst (of_list l))

theorem to_of_list {α : Type u_1} (l : List (lists α)) : to_list (of_list l) = l := sorry

theorem of_to_list {α : Type u_1} {l : lists α} : is_list l → of_list (to_list l) = l := sorry

protected instance inhabited {α : Type u_1} : Inhabited (lists α) :=
  { default := of' lists'.nil }

protected instance decidable_eq {α : Type u_1} [DecidableEq α] : DecidableEq (lists α) :=
  eq.mpr sorry fun (a b : sigma fun (b : Bool) => lists' α b) => sigma.decidable_eq a b

protected instance has_sizeof {α : Type u_1} [SizeOf α] : SizeOf (lists α) :=
  eq.mpr sorry (sigma.has_sizeof Bool fun (b : Bool) => lists' α b)

def induction_mut {α : Type u_1} (C : lists α → Sort u_2) (D : lists' α tt → Sort u_3) (C0 : (a : α) → C (atom a)) (C1 : (l : lists' α tt) → D l → C (of' l)) (D0 : D lists'.nil) (D1 : (a : lists α) → (l : lists' α tt) → C a → D l → D (lists'.cons a l)) : PProd ((l : lists α) → C l) ((l : lists' α tt) → D l) :=
  { fst := fun (_x : lists α) => sorry,
    snd :=
      fun (l : lists' α tt) =>
        pprod.snd
          ((fun (b : Bool) (l : lists' α b) =>
              lists'.rec (fun (a : α) => { fst := C0 a, snd := PUnit.unit }) { fst := C1 lists'.nil D0, snd := D0 }
                (fun {b : Bool} (a : lists' α b) (l : lists' α tt) (IH₁ : PProd (C (sigma.mk b a)) sorry)
                  (IH₂ : PProd (C (sigma.mk tt l)) sorry) =>
                  { fst := C1 (lists'.cons' a l) (D1 (sigma.mk b a) l (pprod.fst IH₁) (pprod.snd IH₂)),
                    snd := D1 (sigma.mk b a) l (pprod.fst IH₁) (pprod.snd IH₂) })
                l)
            tt l) }

def mem {α : Type u_1} (a : lists α) : lists α → Prop :=
  sorry

protected instance has_mem {α : Type u_1} : has_mem (lists α) (lists α) :=
  has_mem.mk mem

theorem is_list_of_mem {α : Type u_1} {a : lists α} {l : lists α} : a ∈ l → is_list l := sorry

theorem equiv.antisymm_iff {α : Type u_1} {l₁ : lists' α tt} {l₂ : lists' α tt} : equiv (of' l₁) (of' l₂) ↔ l₁ ⊆ l₂ ∧ l₂ ⊆ l₁ := sorry

theorem equiv_atom {α : Type u_1} {a : α} {l : lists α} : equiv (atom a) l ↔ atom a = l := sorry

theorem equiv.symm {α : Type u_1} {l₁ : lists α} {l₂ : lists α} (h : equiv l₁ l₂) : equiv l₂ l₁ := sorry

theorem equiv.trans {α : Type u_1} {l₁ : lists α} {l₂ : lists α} {l₃ : lists α} : equiv l₁ l₂ → equiv l₂ l₃ → equiv l₁ l₃ := sorry

protected instance setoid {α : Type u_1} : setoid (lists α) :=
  setoid.mk equiv sorry

@[simp] def equiv.decidable_meas {α : Type u_1} : psum (psigma fun (l₁ : lists α) => lists α)
    (psum (psigma fun (l₁ : lists' α tt) => lists' α tt) (psigma fun (a : lists α) => lists' α tt)) →
  ℕ :=
  sorry

theorem sizeof_pos {α : Type u_1} {b : Bool} (l : lists' α b) : 0 < sizeof l := sorry

theorem lt_sizeof_cons' {α : Type u_1} {b : Bool} (a : lists' α b) (l : lists' α tt) : sizeof (sigma.mk b a) < sizeof (lists'.cons' a l) := sorry

instance mem.decidable {α : Type u_1} [DecidableEq α] (a : lists α) (l : lists' α tt) : Decidable (a ∈ l) :=
  sorry

end lists

namespace lists'

theorem mem_equiv_left {α : Type u_1} {l : lists' α tt} {a : lists α} {a' : lists α} : lists.equiv a a' → (a ∈ l ↔ a' ∈ l) := sorry

theorem mem_of_subset {α : Type u_1} {a : lists α} {l₁ : lists' α tt} {l₂ : lists' α tt} (s : l₁ ⊆ l₂) : a ∈ l₁ → a ∈ l₂ := sorry

theorem subset.trans {α : Type u_1} {l₁ : lists' α tt} {l₂ : lists' α tt} {l₃ : lists' α tt} (h₁ : l₁ ⊆ l₂) (h₂ : l₂ ⊆ l₃) : l₁ ⊆ l₃ :=
  iff.mpr subset_def fun (a₁ : lists α) (m₁ : a₁ ∈ to_list l₁) => mem_of_subset h₂ (mem_of_subset' h₁ m₁)

end lists'

def finsets (α : Type u_1) :=
  quotient lists.setoid

namespace finsets

protected instance has_emptyc {α : Type u_1} : has_emptyc (finsets α) :=
  has_emptyc.mk (quotient.mk (lists.of' lists'.nil))

protected instance inhabited {α : Type u_1} : Inhabited (finsets α) :=
  { default := ∅ }

protected instance decidable_eq {α : Type u_1} [DecidableEq α] : DecidableEq (finsets α) :=
  eq.mpr sorry fun (a b : quotient lists.setoid) => quotient.decidable_eq a b


import tactic --hide

/-Lemma
This is getting silly now!
-/
lemma lemma_9 (P Q R : Prop) : ((Q → P) → P) → (Q → R) → (R → P) → P :=
begin
  intros h1 h2 h3,
  apply h1,
  intro hQ,
  apply h3,
  apply h2,
  exact hQ,

end


/-
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.monoidal.coherence

/-!
# Monoidal opposites

We write `Cᵐᵒᵖ` for the monoidal opposite of a monoidal category `C`.
-/

universes v₁ v₂ u₁ u₂

variables {C : Type u₁}

namespace category_theory

open category_theory.monoidal_category

/-- A type synonym for the monoidal opposite. Use the notation `Cᴹᵒᵖ`. -/
@[nolint has_inhabited_instance]
def monoidal_opposite (C : Type u₁) := C

namespace monoidal_opposite

notation C `ᴹᵒᵖ`:std.prec.max_plus := monoidal_opposite C

/-- Think of an object of `C` as an object of `Cᴹᵒᵖ`. -/
@[pp_nodot]
def mop (X : C) : Cᴹᵒᵖ := X

/-- Think of an object of `Cᴹᵒᵖ` as an object of `C`. -/
@[pp_nodot]
def unmop (X : Cᴹᵒᵖ) : C := X

lemma op_injective : function.injective (mop : C → Cᴹᵒᵖ) := λ _ _, id
lemma unop_injective : function.injective (unmop : Cᴹᵒᵖ → C) := λ _ _, id

@[simp] lemma op_inj_iff (x y : C) : mop x = mop y ↔ x = y := iff.rfl
@[simp]

attribute [irreducible] monoidal_opposite

@[simp] lemma mop_unmop (X : Cᴹᵒᵖ) : mop (unmop X) = X := rfl
@[simp] lemma unmop_mop (X : C) : unmop (mop X) = X := rfl

instance monoidal_opposite_category [I : category.{v₁} C] : category Cᴹᵒᵖ :=
{ hom := λ X Y, unmop X ⟶ unmop Y,
  id := λ X, 𝟙 (unmop X),
  comp := λ X Y Z f g, f ≫ g, }

end monoidal_opposite

end category_theory

open category_theory
open category_theory.monoidal_opposite

variables [category.{v₁} C]

/-- The monoidal opposite of a morphism `f : X ⟶ Y` is just `f`, thought of as `mop X ⟶ mop Y`. -/
def quiver.hom.mop {X Y : C} (f : X ⟶ Y) : @quiver.hom Cᴹᵒᵖ _ (mop X) (mop Y) := f
/-- We can think of a morphism `f : mop X ⟶ mop Y` as a morphism `X ⟶ Y`. -/
def quiver.hom.unmop {X Y : Cᴹᵒᵖ} (f : X ⟶ Y) : unmop X ⟶ unmop Y := f

namespace category_theory

lemma mop_inj {X Y : C} :
  function.injective (quiver.hom.mop : (X ⟶ Y) → (mop X ⟶ mop Y)) :=
λ _ _ H, congr_arg quiver.hom.unmop H

lemma unmop_inj {X Y : Cᴹᵒᵖ} :
  function.injective (quiver.hom.unmop : (X ⟶ Y) → (unmop X ⟶ unmop Y)) :=
λ _ _ H, congr_arg quiver.hom.mop H

@[simp] lemma unmop_mop {X Y : C} {f : X ⟶ Y} : f.mop.unmop = f := rfl
@[simp] lemma mop_unmop {X Y : Cᴹᵒᵖ} {f : X ⟶ Y} : f.unmop.mop = f := rfl

@[simp] lemma mop_comp {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} :
  (f ≫ g).mop = f.mop ≫ g.mop := rfl
@[simp] lemma mop_id {X : C} : (𝟙 X).mop = 𝟙 (mop X) := rfl

@[simp] lemma unmop_comp {X Y Z : Cᴹᵒᵖ} {f : X ⟶ Y} {g : Y ⟶ Z} :
  (f ≫ g).unmop = f.unmop ≫ g.unmop := rfl
@[simp] lemma unmop_id {X : Cᴹᵒᵖ} : (𝟙 X).unmop = 𝟙 (unmop X) := rfl

@[simp] lemma unmop_id_mop {X : C} : (𝟙 (mop X)).unmop = 𝟙 X := rfl
@[simp] lemma mop_id_unmop {X : Cᴹᵒᵖ} : (𝟙 (unmop X)).mop = 𝟙 X := rfl

namespace iso

variables {X Y : C}

/-- An isomorphism in `C` gives an isomorphism in `Cᴹᵒᵖ`. -/
@[simps]
def mop (f : X ≅ Y) : mop X ≅ mop Y :=
{ hom := f.hom.mop,
  inv := f.inv.mop,
  hom_inv_id' := unmop_inj f.hom_inv_id,
  inv_hom_id' := unmop_inj f.inv_hom_id }

end iso

variables [monoidal_category.{v₁} C]

open opposite monoidal_category

instance monoidal_category_op : monoidal_category Cᵒᵖ :=
{ tensor_obj := λ X Y, op (unop X ⊗ unop Y),
  tensor_hom := λ X₁ Y₁ X₂ Y₂ f g, (f.unop ⊗ g.unop).op,
  tensor_unit := op (𝟙_ C),
  associator := λ X Y Z, (α_ (unop X) (unop Y) (unop Z)).symm.op,
  left_unitor := λ X, (λ_ (unop X)).symm.op,
  right_unitor := λ X, (ρ_ (unop X)).symm.op,
  associator_naturality' := by { intros, apply quiver.hom.unop_inj, simp, },
  left_unitor_naturality' := by { intros, apply quiver.hom.unop_inj, simp, },
  right_unitor_naturality' := by { intros, apply quiver.hom.unop_inj, simp, },
  triangle' := by { intros, apply quiver.hom.unop_inj, coherence, },
  pentagon' := by { intros, apply quiver.hom.unop_inj, coherence, }, }

lemma op_tensor_obj (X Y : Cᵒᵖ) : X ⊗ Y = op (unop X ⊗ unop Y) := rfl
lemma op_tensor_unit : (𝟙_ Cᵒᵖ) = op (𝟙_ C) := rfl

instance monoidal_category_mop : monoidal_category Cᴹᵒᵖ :=
{ tensor_obj := λ X Y, mop (unmop Y ⊗ unmop X),
  tensor_hom := λ X₁ Y₁ X₂ Y₂ f g, (g.unmop ⊗ f.unmop).mop,
  tensor_unit := mop (𝟙_ C),
  associator := λ X Y Z, (α_ (unmop Z) (unmop Y) (unmop X)).symm.mop,
  left_unitor := λ X, (ρ_ (unmop X)).mop,
  right_unitor := λ X, (λ_ (unmop X)).mop,
  associator_naturality' := by { intros, apply unmop_inj, simp, },
  left_unitor_naturality' := by { intros, apply unmop_inj, simp, },
  right_unitor_naturality' := by { intros, apply unmop_inj, simp, },
  triangle' := by { intros, apply unmop_inj, coherence, },
  pentagon' := by { intros, apply unmop_inj, coherence, }, }

lemma mop_tensor_obj (X Y : Cᴹᵒᵖ) : X ⊗ Y = mop (unmop Y ⊗ unmop X) := rfl
lemma mop_tensor_unit : (𝟙_ Cᴹᵒᵖ) = mop (𝟙_ C) := rfl

end category_theory


/-
Copyright (c) 2020 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author(s): Simon Hudon
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.control.monad.basic
import Mathlib.control.monad.cont
import Mathlib.control.monad.writer
import Mathlib.data.equiv.basic
import Mathlib.tactic.interactive
import Mathlib.PostPort

universes u₀ u₁ v₀ v₁ l u_1 u_2 u_3 u_4 u_5 u_6

namespace Mathlib

/-!
# Universe lifting for type families

Some functors such as `option` and `list` are universe polymorphic. Unlike
type polymorphism where `option α` is a function application and reasoning and
generalizations that apply to functions can be used, `option.{u}` and `option.{v}`
are not one function applied to two universe names but one polymorphic definition
instantiated twice. This means that whatever works on `option.{u}` is hard
to transport over to `option.{v}`. `uliftable` is an attempt at improving the situation.

`uliftable option.{u} option.{v}` gives us a generic and composable way to use
`option.{u}` in a context that requires `option.{v}`. It is often used in tandem with
`ulift` but the two are purposefully decoupled.

## Main definitions
  * `uliftable` class

## Tags

universe polymorphism functor

-/

/-- Given a universe polymorphic type family `M.{u} : Type u₁ → Type
u₂`, this class convert between instantiations, from
`M.{u} : Type u₁ → Type u₂` to `M.{v} : Type v₁ → Type v₂` and back -/
class uliftable (f : Type u₀ → Type u₁) (g : Type v₀ → Type v₁) where
  congr : {α : Type u₀} → {β : Type v₀} → α ≃ β → f α ≃ g β

namespace uliftable

/-- The most common practical use `uliftable` (together with `up`), this function takes
`x : M.{u} α` and lifts it to M.{max u v} (ulift.{v} α) -/
def up {f : Type u₀ → Type u₁} {g : Type (max u₀ v₀) → Type v₁} [uliftable f g] {α : Type u₀} :
    f α → g (ulift α) :=
  equiv.to_fun (congr f g (equiv.symm equiv.ulift))

/-- The most common practical use of `uliftable` (together with `up`), this function takes
`x : M.{max u v} (ulift.{v} α)` and lowers it to `M.{u} α` -/
def down {f : Type u₀ → Type u₁} {g : Type (max u₀ v₀) → Type v₁} [uliftable f g] {α : Type u₀} :
    g (ulift α) → f α :=
  equiv.inv_fun (congr f g (equiv.symm equiv.ulift))

/-- convenient shortcut to avoid manipulating `ulift` -/
def adapt_up (F : Type v₀ → Type v₁) (G : Type (max v₀ u₀) → Type u₁) [uliftable F G] [Monad G]
    {α : Type v₀} {β : Type (max v₀ u₀)} (x : F α) (f : α → G β) : G β :=
  up x >>= f ∘ ulift.down

/-- convenient shortcut to avoid manipulating `ulift` -/
def adapt_down {F : Type (max u₀ v₀) → Type u₁} {G : Type v₀ → Type v₁} [L : uliftable G F]
    [Monad F] {α : Type (max u₀ v₀)} {β : Type v₀} (x : F α) (f : α → G β) : G β :=
  down (x >>= up ∘ f)

/-- map function that moves up universes -/
def up_map {F : Type u₀ → Type u₁} {G : Type (max u₀ v₀) → Type v₁} [inst : uliftable F G]
    [Functor G] {α : Type u₀} {β : Type (max u₀ v₀)} (f : α → β) (x : F α) : G β :=
  (f ∘ ulift.down) <$> up x

/-- map function that moves down universes -/
def down_map {F : Type (max u₀ v₀) → Type u₁} {G : Type → Type v₁} [inst : uliftable G F]
    [Functor F] {α : Type (max u₀ v₀)} {β : Type} (f : α → β) (x : F α) : G β :=
  down ((ulift.up ∘ f) <$> x)

@[simp] theorem up_down {f : Type u₀ → Type u₁} {g : Type (max u₀ v₀) → Type v₁} [uliftable f g]
    {α : Type u₀} (x : g (ulift α)) : up (down x) = x :=
  equiv.right_inv (congr f g (equiv.symm equiv.ulift)) x

@[simp] theorem down_up {f : Type u₀ → Type u₁} {g : Type (max u₀ v₀) → Type v₁} [uliftable f g]
    {α : Type u₀} (x : f α) : down (up x) = x :=
  equiv.left_inv (congr f g (equiv.symm equiv.ulift)) x

end uliftable

protected instance id.uliftable : uliftable id id :=
  uliftable.mk fun (α : Type u_1) (β : Type u_2) (F : α ≃ β) => F

/-- for specific state types, this function helps to create a uliftable instance -/
def state_t.uliftable' {s : Type u₀} {s' : Type u₁} {m : Type u₀ → Type v₀} {m' : Type u₁ → Type v₁}
    [uliftable m m'] (F : s ≃ s') : uliftable (state_t s m) (state_t s' m') :=
  uliftable.mk
    fun (α : Type u₀) (β : Type u₁) (G : α ≃ β) =>
      state_t.equiv (equiv.Pi_congr F fun (_x : s) => uliftable.congr m m' (equiv.prod_congr G F))

protected instance state_t.uliftable {s : Type u_1} {m : Type u_1 → Type u_2}
    {m' : Type (max u_1 u_3) → Type u_4} [uliftable m m'] :
    uliftable (state_t s m) (state_t (ulift s) m') :=
  state_t.uliftable' (equiv.symm equiv.ulift)

/-- for specific reader monads, this function helps to create a uliftable instance -/
def reader_t.uliftable' {s : Type u_1} {s' : Type u_2} {m : Type u_1 → Type u_3}
    {m' : Type u_2 → Type u_4} [uliftable m m'] (F : s ≃ s') :
    uliftable (reader_t s m) (reader_t s' m') :=
  uliftable.mk
    fun (α : Type u_1) (β : Type u_2) (G : α ≃ β) =>
      reader_t.equiv (equiv.Pi_congr F fun (_x : s) => uliftable.congr m m' G)

protected instance reader_t.uliftable {s : Type u_1} {m : Type u_1 → Type u_2}
    {m' : Type (max u_1 u_3) → Type u_4} [uliftable m m'] :
    uliftable (reader_t s m) (reader_t (ulift s) m') :=
  reader_t.uliftable' (equiv.symm equiv.ulift)

/-- for specific continuation passing monads, this function helps to create a uliftable instance -/
def cont_t.uliftable' {r : Type u_1} {r' : Type u_2} {m : Type u_1 → Type u_3}
    {m' : Type u_2 → Type u_4} [uliftable m m'] (F : r ≃ r') :
    uliftable (cont_t r m) (cont_t r' m') :=
  uliftable.mk fun (α : Type u_1) (β : Type u_2) => cont_t.equiv (uliftable.congr m m' F)

protected instance cont_t.uliftable {s : Type u_1} {m : Type u_1 → Type u_2}
    {m' : Type (max u_1 u_3) → Type u_4} [uliftable m m'] :
    uliftable (cont_t s m) (cont_t (ulift s) m') :=
  cont_t.uliftable' (equiv.symm equiv.ulift)

/-- for specific writer monads, this function helps to create a uliftable instance -/
def writer_t.uliftable' {w : Type (max u_1 u_2)} {w' : Type (max u_3 u_4)}
    {m : Type (max u_1 u_2) → Type u_5} {m' : Type (max u_3 u_4) → Type u_6} [uliftable m m']
    (F : w ≃ w') : uliftable (writer_t w m) (writer_t w' m') :=
  uliftable.mk
    fun (α : Type (max u_1 u_2)) (β : Type (max u_3 u_4)) (G : α ≃ β) =>
      writer_t.equiv (uliftable.congr m m' (equiv.prod_congr G F))

protected instance writer_t.uliftable {s : Type (max u_1 u_2)} {m : Type (max u_1 u_2) → Type u_3}
    {m' : Type (max (max u_1 u_2) u_4) → Type u_5} [uliftable m m'] :
    uliftable (writer_t s m) (writer_t (ulift s) m') :=
  writer_t.uliftable' (equiv.symm equiv.ulift)

end Mathlib


import Lean
open Lean Widget

/-!
# The user-widgets system

Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
structures are visual in nature. *User widgets* let you associate custom interactive UIs with
sections of a Lean document. User widgets are rendered in the Lean infoview.

![Rubik's cube](../images/widgets_rubiks.png)

## Trying it out

To try it out, simply type in the following code and place your cursor over the `#widget` command.
-/

@[widget]
def helloWidget : UserWidgetDefinition where
  name := "Hello"
  javascript := "
    import * as React from 'react';
    export default function(props) {
      const name = props.name || 'world'
      return React.createElement('p', {}, name + '!')
    }"

#widget helloWidget .null

/-!
If you want to dive into a full sample right away, check out
[`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
Below, we'll explain the system piece by piece.

⚠ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.

## Widget sources and instances

A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
React, the module must use `import * as React from 'react'`. Our first example of a widget source
is of course the value of `helloWidget.javascript`.

We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
in the `name` field. This is bundled together in a `UserWidgetDefinition`.

A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
are stored in the *infotree* in the same manner as other information about the source file
such as the type of every expression. In our example, the `#widget` command stores a widget instance
with the entire line as its range. We can think of a widget instance as an instruction for the
infoview: "when the user places their cursor here, please render the following widget".

Every widget instance also contains a `props : Json` value. This value is passed as an argument
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
happens when you type in:
-/

#widget helloWidget (Json.mkObj [("name", "<your name here>")])

/-!
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
is the web-based infoview in VSCode.

## Querying the Lean server

Besides enabling us to create cool client-side visualizations, user widgets come with the ability
to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
command as a web input form. This example assumes some familiarity with React.

The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
is basically a Lean function callable from widget code (possibly remotely over the internet).
Our method will take in the `name : Name` of a constant in the environment and return its type.
By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
-/

structure GetTypeParams where
  /-- Name of a constant to get the type of. -/
  name : Name
  /-- Position of our widget instance in the Lean file. -/
  pos : Lsp.Position
  deriving FromJson, ToJson

/-!
After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
options:
- One is to send a *reference* which points to an object residing on the server. From JavaScript's
  point of view, references are entirely opaque, but they can be sent back to other RPC methods for
  further processing.
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
  This representation contains extra data which the infoview uses for interactivity. We take this
  strategy here.

RPC methods execute in the context of a file, but not any particular `Environment` so they don't
know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
method launches a concurrent computation whose job is to find such an `Environment` and a bit
more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
`MetaM` procedures to find out the type of `name` and pretty-print it.
-/

open Server RequestM in
@[server_rpc_method]
def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
  withWaitFindSnapAtPos params.pos fun snap => do
    runTermElabM snap do
      let name ← resolveGlobalConstNoOverloadCore params.name
      let some c ← Meta.getConst? name
        | throwThe RequestError ⟨.invalidParams, s!"no constant named '{name}'"⟩
      Widget.ppExprTagged c.type

/-!
## Using infoview components

Now that we have all we need on the server side, let's write the widget source. By importing
`@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
as seen in the goal view. We will use it to implement our custom `#check` display.

⚠ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.

The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
whether the call has resolved successfully, has returned an error, or is still in-flight. Based
on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
to turn it into a readable message, or show a `Loading..` message, respectively.
-/

@[widget]
def checkWidget : UserWidgetDefinition where
  name := "#check as a service"
  javascript := "
import * as React from 'react';
const e = React.createElement;
import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';

export default function(props) {
  const rs = React.useContext(RpcContext)
  const [name, setName] = React.useState('getType')

  const st = useAsync(() =>
    rs.call('getType', { name, pos: props.pos }), [name, rs, props.pos])

  const type = st.state === 'resolved' ? st.value && e(InteractiveCode, {fmt: st.value})
    : st.state === 'rejected' ? e('p', null, mapRpcError(st.error).message)
    : e('p', null, 'Loading..')
  const onChange = (event) => { setName(event.target.value) }
  return e('div', null,
    e('input', { value: name, onChange }), ' : ', type)
}
"

/-!
Finally we can try out the widget.
-/

#widget checkWidget .null

/-!
![`#check` as a service](../images/widgets_caas.png)

## Building widget sources

While typing JavaScript inline is fine for a simple example, for real developments we want to use
packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
by default. On the other hand a widget source must be a single, self-contained ESModule in the form
of a string. Readers familiar with web development may already have guessed that to obtain such a
string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
the infoview we need to:
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
  These libraries are already loaded by the infoview so they should not be bundled.

In the RubiksCube sample, we provide a working `rollup.js` build configuration in
[rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).

## Inserting text

We can also instruct the editor to insert text, copy text to the clipboard, or
reveal a certain location in the document.
To do this, use the `React.useContext(EditorContext)` React context.
This will return an `EditorConnection` whose `api` field contains a number of methods to
interact with the text editor.

You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
-/

@[widget]
def insertTextWidget : UserWidgetDefinition where
  name := "textInserter"
  javascript := "
import * as React from 'react';
const e = React.createElement;
import { EditorContext } from '@leanprover/infoview';

export default function(props) {
  const editorConnection = React.useContext(EditorContext)
  function onClick() {
    editorConnection.api.insertText('-- hello!!!', 'above')
  }

  return e('div', null, e('button', { value: name, onClick }, 'insert'))
}
"

/-! Finally, we can try this out: -/

#widget insertTextWidget .null


def x := 1

section
  variable (α : Type)
  variable (x : α)
  notation "A" => id x
  #check A
  theorem test : A = A := rfl

  section
    variable (x : Nat)
    #check A  -- should use shadowed `x`
  end
end

#check A  -- escaping section variable, should fail

section
  variable (x : Nat)
  #check A  -- should fail
end