/-
Copyright (c) 2022 Antoine Labelle. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Antoine Labelle
-/
import representation_theory.fdRep
import linear_algebra.trace
import representation_theory.basic

/-!
# Characters of representations

This file introduces characters of representation and proves basic lemmas about how characters
behave under various operations on representations.

# TODO
* Once we have the monoidal closed structure on `fdRep k G` and a better API for the rigid
structure, `char_dual` and `char_lin_hom` should probably be stated in terms of `Vᘁ` and `ihom V W`.
-/

noncomputable theory

universes u

open linear_map category_theory.monoidal_category representation

variables {k G : Type u} [field k]

namespace fdRep

section monoid

variables [monoid G]

/-- The character of a representation `V : fdRep k G` is the function associating to `g : G` the
trace of the linear map `V.ρ g`.-/
def character (V : fdRep k G) (g : G) := linear_map.trace k V (V.ρ g)

lemma char_mul_comm (V : fdRep k G) (g : G) (h : G) : V.character (h * g) = V.character (g * h) :=
by simp only [trace_mul_comm, character, map_mul]

@[simp] lemma char_one  (V : fdRep k G) : V.character 1 = finite_dimensional.finrank k V :=
by simp only [character, map_one, trace_one]

/-- The character is multiplicative under the tensor product. -/
@[simp] lemma char_tensor (V W : fdRep k G) : (V ⊗ W).character = V.character * W.character :=
by { ext g, convert trace_tensor_product' (V.ρ g) (W.ρ g) }

/-- The character of isomorphic representations is the same. -/
lemma char_iso  {V W : fdRep k G} (i : V ≅ W) : V.character = W.character :=
by { ext g, simp only [character, fdRep.iso.conj_ρ i], exact (trace_conj' (V.ρ g) _).symm }

end monoid

section group

variables [group G]

/-- The character of a representation is constant on conjugacy classes. -/
@[simp] lemma char_conj (V : fdRep k G) (g : G) (h : G) :
  V.character (h * g * h⁻¹) = V.character g :=
by rw [char_mul_comm, inv_mul_cancel_left]

@[simp] lemma char_dual (V : fdRep k G) (g : G) : (of (dual V.ρ)).character g = V.character g⁻¹ :=
  trace_transpose' (V.ρ g⁻¹)

@[simp] lemma char_lin_hom (V W : fdRep k G) (g : G) :
  (of (lin_hom V.ρ W.ρ)).character g = (V.character g⁻¹) * (W.character g) :=
by { rw [←char_iso (dual_tensor_iso_lin_hom _ _), char_tensor, pi.mul_apply, char_dual], refl }

end group

end fdRep


import data.nat.basic

namespace nat

lemma lt_iff_add_one_le {m n : ℕ} :
  m < n ↔ m + 1 ≤ n  := by rw succ_le_iff

lemma add_lt_add_of_le_of_le_of_lt_or_lt {a b c d : nat} :
  a ≤ b → c ≤ d → (a < b ∨ c < d) → a + c < b + d :=
begin
  intros h1 h2 h3, cases h3,
  apply add_lt_add_of_lt_of_le; assumption,
  apply add_lt_add_of_le_of_lt; assumption
end

lemma max_succ_succ {m n} :
  max (succ m) (succ n) = succ (max m n) :=
begin
  by_cases h1 : m ≤ n,
  rw [max_eq_right h1, max_eq_right (succ_le_succ h1)],
  { rw not_le at h1, have h2 := le_of_lt h1,
    rw [max_eq_left h2, max_eq_left (succ_le_succ h2)] }
end

end nat


/- Formalisation of Groups from the module Linear Algebra and Groups -/
import tactic
/- Chapter 1. Groups and Subgroups -/

namespace M40004

-- Definition of a group
@[simp] def is_assoc (G : Type*) (operat : G → G → G) :=
∀ g h k : G, operat (operat g h) k = operat g (operat h k)

@[simp] def is_identity (G : Type*) (operat : G → G → G) (e : G) :=
∀ g : G, operat g e = operat e g ∧ operat g e = g

@[simp] def has_identity (G : Type*) (operat : G → G → G) :=
∃ e : G, is_identity G operat e

@[simp] def is_inverse (G : Type*) (operat : G → G → G) (g h : G) :=
operat g h = operat h g ∧ is_identity G operat (operat g h)

@[simp] def has_inverse (G : Type*) (operat : G → G → G) :=
∀ g : G, ∃ h : G, is_inverse G operat g h

structure is_group (G : Type*) (operat : G → G → G) :=
(group_assoc : is_assoc G operat)
(group_identity : has_identity G operat)
(group_inverse : has_inverse G operat)

-- A group has a unique identity
theorem unique_identity {G : Type*} {operat : G → G → G} :
∀ e₁ e₂ : G, is_identity G operat e₁ ∧ is_identity G operat e₂ →  e₁ = e₂ :=
begin
    rintros e₁ e₂ ⟨id₁, id₂⟩,
    rw [←(id₂ e₁).right, (id₂ e₁).left],
    from (id₁ e₂).right
end

-- An element in a group has an unique inverse
theorem unique_inverse {G : Type*} {operat : G → G → G} (hgp : is_group G operat) :
∀ g h k : G, is_inverse G operat g h ∧ is_inverse G operat g k → h = k :=
begin
    rintros g h k ⟨invh, invk⟩,
    have : operat (operat k g) h = h :=
        by {cases invk with heq hid,by
            rw [←heq, ←(hid h).left, (hid h).right]
        },
    rw [←this, hgp.group_assoc],
    repeat {swap, assumption},
    suffices : is_identity G operat (operat g h),
        rwa (this k).right,
    from invh.right
end

-- Some lemmas that makes working easier
lemma operat_both_sides {G : Type*} {operat : G → G → G} {hgp : is_group G operat} (a b c : G) :
b = c → operat a b = operat a c := λ h, by {rw h}

-- Some properties of a group

-- If ab = ac then b = c
theorem left_operat_cancel {G : Type*} {operat : G → G → G} (hgp : is_group G operat) :
∀ a b c : G, operat a b = operat a c → b = c :=
begin
    intros a b c h,
    cases hgp.group_inverse a with ainv hainv,
    suffices : operat ainv (operat a b) = operat ainv (operat a c),
        repeat {rw ←hgp.group_assoc at this},
        sorry,
    sorry
end

end M40004


import tactic --hide
lemma not_iff_imp_false (P : Prop) : ¬ P ↔ P → false := iff.rfl -- hide
lemma contra (P Q : Prop) : (P ∧ ¬ P) → Q := by {cc} --hide

/-Lemma
If $P$ is a logical statement then $¬(¬P) \implies P$.
-/
lemma not_not_P_imp_P (P : Prop) :¬ (¬ P) → P :=
begin
  rw not_iff_imp_false,
  intro hp,
  by_cases hp2 : P,
  exact hp2,
  exfalso,
  apply hp,
  exact hp2,

end


-- Pruebas de (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j)
-- ============================================

-- ----------------------------------------------------
-- Ej. 1. Demostrar
--    (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j)
-- ----------------------------------------------------

import data.set

open set

variables {I J U : Type}
variables (A : I → J → set U)

-- 1ª demostración
example : (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j) :=
begin
  intros x h,
  rw mem_Union at h,
  cases h with i hi,
  rw mem_Inter at hi,
  apply mem_Inter.mpr,
  intro j,
  apply mem_Union.mpr,
  use i,
  exact (hi j),
end

-- 2ª demostración
example : (⋃i, ⋂j, A i j) ⊆ (⋂j, ⋃i, A i j) :=
begin
  intros x h,
  simp * at *,
  cases h with i hi,
  intro j,
  use i,
  exact (hi j),
end


/-
Justin Ngo
jmn4fms
5/4/20
Sullivan 2102-001
-/

/-
CS2102 Spring 2020 Final Exam. You are
to take this exam entirely on your own.
You may not discuss it with anyone but
the instructor and TAs. The exam has 6
qustions, some with several parts. Read
the entire exam first to get a sense of
the easy and hard parts. Do the easier
parts first while letting your mind work
in the background on the harder parts.
Submit your completed exam on Collab.
The exam is due no later than 5PM sharp
***EDT*** on May 8. Please set yourself
a reminder.
-/

/-
1. You know from our study of the Boolean
satisfiability problem that there are 2^n
possible combinations of Boolean values for
n Boolean variables.

Your task here is to give an English
language (quasi-formal) proof of this fact
*by induction*.

To get started, let's make the property of
natural numbers that is at issue clear by
defining "P n" to be proposition that "the
number of possible combinations of values
for *n* Boolean variables is *2^n*."

What you are to prove is the proposition
that this is true for any value of n. That
is, you are to prove ∀ n, P n.

Next, recall that a proof of a universal
generalization (such as ∀ n, P n) *by induction*
is based on the application of the *induction
principle* for for the given data type. Here
the data type is ℕ, and the induction rule for
ℕ is as follows:

∀ {P : ℕ → Prop},
P 0 →
(∀ n', P n' → P (n' + 1)) →
∀ n, P n.

** You should assume that the number of
** combinations of zero boolean variables
** is 1.

Reading backwards, this says that if you
want to prove ∀ n, P n, it will *suffice*
to prove P 0 and ∀ n', P n' → P (n' + 1)).
The reason is that you can then apply the
rule to deduce that ∀ n, P n must be true.
In other words, you can reduce the task of
proving ∀ n, P n to the tasks of proving
the two antecedents of this conclusion in
the induction rule.

This induction principles tells you exactly
what needs to be done. We give you the start
of a quasi-formal proof. You must complete it.
-/

-- Answer
/-
Theorem: For any natural number, n, the
number of combinations of values for n
Boolean variables is 2^n.

Proof: By induction. To prove ∀ n, P n,
where P is defined to be the proposition,
"the number of possible combinations of
values for n Boolean variables is 2^n,"
it will suffice ... <the rest of your
answer here>.
-/

/- **** ANSWER ****
It will suffice to show that "∀ n, P n" is true for
all natural numbers.

Assume that n is an arbitrary but specific natural number.

1. It will sufffice to show that it is true for the base case
    n = 0 or 0 (P 0 →)
2. And to show that it is true for n+1 ((∀ n, P n → P (n + 1)) → ),
    assuming it is true for n.

For any natural number n, if the number of combinations
of n boolean variables is 2^n, then the number of combinations
of n+1 boolean variables is 2^(n+1).
And thus holds true for all natural numbers.

PROOF:
Given
-- Number of combinations of 0 boolean variables is 1
And assumming
-- P n = 2^n

-- 2^n = 2^(n-1) * 2 [by property of exponents]
Therefore,
-- 2^(n+1) = 2^n * 2
-- P (n+1) = (P n) * 2
-- P (n+1) = 2^n * 2
and since
-- 2^n * 2 = 2^(n+1),
the number of combinations of n+1 boolean variables is 2^(n+1)

Thus
P (n+1) = 2^(n+1)
QED
-/

/- 2.
Consider the following proposition.
-/

def aProp :=    ∀ (α : Type), ∀ (Heavy Charmed: α → Prop),
                (∃ (a : α), Heavy a ∧ Charmed a) →
                (∃ (a : α), Charmed a)

/- 2a. Give an English language rendition
of this proposition. In plain English, what
does it say?
-/

/-  **** Answer ****
Suppose we have an object of type α;
Suppose "heavy" and "charmed" are objects/ properties
of type alpha
If there exists an object "a" of this type
and "a" is both heavy and charmed,
THEN there exists an object that is charmed
-/

/- 2b. Give a formal proof of it.
-/

example : aProp :=
    λ a,
        λ H C, -- Heavy, Charmed
            λ heavy_and_charmed,
                match heavy_and_charmed with
                    | exists.intro fred pf_fred_charmed :=
                        match pf_fred_charmed with
                            | and.intro c h := (exists.intro fred pf_fred_charmed.right)
                        end
                end

/- 2c. Give a quasi-formal, English-language
proof of it. Try to be guided by your formal
proof. Justify each step in your proof by
naming the inference rule that you're using.
-/

-- Your answer here
/- **** Answer ****
Assume we have an object a of type alpha
Assume that the object a of type alpha has properties
heavy and charmed.
Assumming there exists an object a that is both heavy
and charmed.
Deconstruct this assumption to prove that there
exists an object fred which is heavy and charmed
by doing case analysis.
And further deconstruct the assumption of a heavy
and charmed object, fred, to get a proof that
there exists an object that is charmed.
-/

/- 3.
Consider the following proposition.
-/

def aProp2 :=   ∀ (α : Type), ∀ (Heavy Charmed: α → Prop),
                (∃ (a : α), Heavy a ∨ Charmed a) →
                (∃ (a : α), Heavy a) ∨ (∃ (a : α), Charmed a)

/- 3a. Give an English language rendition
of this proposition. In plain English, what
does it say?
-/

/- **** Answer ****
Suppose we have an object of type α;
Suppose "heavy" and "charmed" are objects/ properties
of type alpha
If there exists an object "a" of this type
such that "a" is heavy or charmed,
THEN there either exists an object that is heavy OR
there exists an object that is charmed
-/

/- 3b. Give a formal proof of it.
-/

example : aProp2 :=
    λ a,
        λ H C, -- Heavy, Charmed
            λ heavy_or_charmed,
                match heavy_or_charmed with
                    | exists.intro fred pf_fred_either_heavy_or_charmed :=
                        match pf_fred_either_heavy_or_charmed with
                            | or.inl h := or.inl (exists.intro fred h)
                            | or.inr c := or.inr (exists.intro fred c)
                        end
                end

/- 3c. Give a quasi-formal, English-language
proof of it. Try to be guided by your formal
proof. Justify each step in your proof by
naming the inference rule that you're using.
-/

-- Your answer here
/- **** Answer ****
Assume we have an object a of type alpha
Assume that the object a of type alpha has properties
heavy and charmed.
Assumming there exists an object a that is either heavy
or an object a that is charmed.
Deconstruct this assumption to prove that there
exists an object fred which is heavy or there exists
an object fred that is charmed by doing case analysis.

And further deconstruct the assumption of a heavy
or charmed object, fred, to get a proof that
there exists an object that is heavy or there exists
an object that is charmed.
-/

/- 4.
Formally specify the syntax and semantics
of a language of *arithmetic* expressions that
can include variables, where the meaning of
an expression is (reduces to) a natural number.

Hint: model your answer on our specification
of the syntax and semantics of *propositional
logic* expressions.
-/

/- In this language, an interpretation will
map variables to natural numbers rather than
to Boolean values. We'll give you a start on
a solution by defining

(1) a type of variables that are distinguished
    from one another by a ℕ-valued index,

(2) specifying the type of an interpretation, and

(3) giving an example of an interpretation
    in which all variables have the value zero.
-/

-- our variable type
structure a_var : Type := mk :: (index : ℕ)

-- friendly names for a few variables
def X_var := a_var.mk 0
def Y_var := a_var.mk 1
def Z_var := a_var.mk 2

-- the type of an interpretation
def interp := a_var → ℕ

-- one possible interpretation, "all zero"
def all_zero_interp : interp :=
    λ v, 0

/-
4a. Define an interpretation in which X has
value 3, Y has value 7, and Z has value 1,
and all other variables have value 0.
-/

def an_interp : interp :=
    λ (v : a_var),
        match v with
            | a_var.mk 0 := 3 -- X
            | a_var.mk 1 := 7 -- Y
            | a_var.mk 2 := 1 -- Z
            | _ := 0
        end

/-
4b. Define the syntax of your language to have
the following kinds of expressions.

- ℕ literal expression
- ℕ variable expression
- expression + expression
- expression * expression

Do this by defining an inductive type, aexp,
the values of which are arithmetic expressions
in our language. Call your constructors lit,
var, add, and mul. When you succeed, the test
expressions we give you should type check.
-/

inductive aexp : Type
-- fill in constructors here
| lit : ℕ → aexp
| var : a_var → aexp
| add : aexp → aexp → aexp
| mul : aexp → aexp → aexp
open aexp

-- These expressions should type-check
def X := aexp.var X_var
def Y := aexp.var Y_var
def Z := aexp.var Z_var
def l6 := aexp.lit 6
def e1 := aexp.add X Y
def e2 := aexp.mul X Z
def e3 := aexp.mul e1 l6

/-
4c. Define a semantics for your
language so that expressions evaluate
to natural numbers as they would using
the standard notions of addition and
multiplication. Furthermore, literal
expressions should evaluate to their
natural numbers arguments, and variable
expressions should evaluate the natural
numbers according to an interpretation
given to the evaluation function (call
it aEval). Remember to put constructor
expressions in parentheses when using
pattern matching / destructuring. When
you've succeeded, the test cases we've
provided should all pass.
-/

-- Your answer here
def aEval : aexp → interp → ℕ
-- fill in cases here
| (lit v) i := v
| (var v) i := i v
| (add e1 e2) i := (aEval e1 i) + (aEval e2 i)
| (mul e1 e2) i := (aEval e1 i) * (aEval e2 i)

-- Test cases that should pass when you've succeeded
example : aEval X an_interp = 3 := rfl
example : aEval Y an_interp = 7 := rfl
example : aEval Z an_interp = 1 := rfl
example : aEval l6 all_zero_interp = 6 := rfl
example : aEval e1 all_zero_interp = 0  := rfl
example : aEval e2 an_interp = 3 := rfl
example : aEval e3 an_interp = 60 := rfl

/-
5. Explain concisely but also precisely how a
proof of a proposition, P, "by contradiction"
would be carried out. Be sure to point out
exactly where negation elimination is involved.

Then explain concisely and precisely how a
proof of a proposition, ¬ P, would be carried
out.
-/

/- **** Answer ****
To prove proposition P "by contradiction",
Assume P is false which also assumes that ¬P
is assumed to be true.
Prove that the results of these assumptions
contradict each other because it violates the
law of the excluded middle: either P is true OR
¬P is true. Once the proof is reduced to ¬ (¬ P),
negation elimination can be used to deduce that P
is true.

To prove ¬ P, assume P is true and show that
this leads to a contradiction, then from that
derive a proof of P being false,
and use false.elim to finish the proof.
This is called a "proof by negation".
This shows ¬ (¬ P) is false.
And then, according to the the classical principle of
negation elimination, ¬P can be deduced true.
-/

/- 6. A simplish proof. Give a formal proof
of the following proposition. Then explain
briefly in English why the proposition must
be true no matter what propositions P and Q
are.
-/

example : ∀ (P Q R : Prop), ¬ ((P ∧ ¬ Q) ∧ (Q ∧ ¬ R)) :=
    λ P Q R,
        (λ wholeprop,
           (let pandnq := wholeprop.left in
            let qandnr := wholeprop.right in
            let nq := pandnq.right in
            let q := qandnr.left in
            false.elim (nq q)
            )
        )

-- Your explanation
/- **** Answer ****
A proof of a negation is a proof that some proposition
implies false, in this case
¬ ((P ∧ ¬ Q) ∧ (Q ∧ ¬ R)) where ((P ∧ ¬ Q) ∧ (Q ∧ ¬ R))
must be proven false.
If X = ((P ∧ ¬ Q) ∧ (Q ∧ ¬ R)),
In order to prove a proposition ¬X, we have to prove
X implies false. So assume X can lead to a contradiction.

Isolating Q and ¬Q in this proof X, the two contradict
each other and both Q and ¬Q cannot be true because of
the law of the excluded middle. Q and ¬Q are isolated
using let...in statements to retrieve the separated
Q and ¬Q in the proof.
Thus, Q ∧ ¬Q will always be false
no matter what P and R are.
And by appyling false.elim, the proof of a
negation Q and ¬Q evaluates to false,
and the entire proof is deduced to be true.
-/


import topology.basic
import topology.bases
open topological_space
open set filter classical
variables {X : Type} [topological_space X] {A E F U V : set X} {B C : set(set X)}

-- some terms from the lectures
def cond_pt (A : set X) (x : X) := ∀ (N ∈ nhds x), (N ∩ A).nonempty
def cond_points (A : set X) := { x : X | cond_pt A x}
def limit_pt (A : set X) (x : X) := cond_pt (A ∩ {x}ᶜ) x
def limit_points (A : set X) := { x : X | limit_pt A x}

def lecture_closure (A : set X) := A ∪ limit_points A
def lecture_interior (A : set X) := {x : X | ∃ (N ∈ nhds x) (H: is_open N), N ⊆ A}

-- Bullet point 1
lemma subset_closed_subset_lecture_closure
  (A F : set X) (hAF : A ⊆ F) (hF : is_closed F) :
  lecture_closure A ⊆ F:=
begin
  intros x hA,
  cases hA,
  {exact hAF hA},
  {
    -- Re-using the complement neiborhood arguement used in proving lecture_closure_to_lean_closure

    -- since A is subset of F,
    have obvious_fact : A ∩ Fᶜ= ∅ := diff_eq_empty.mpr hAF,

    have more_specifically : Fᶜ ∩ (A ∩ {x}ᶜ) = ∅ ,
    -- since this is definately smaller than A ∩ Fᶜ
      rw set.inter_comm,
      apply diff_eq_empty.mpr,
      exact subset.trans (by finish) hAF,

    -- if x not in F, Fᶜ is an open neiborhood of x.
    -- Since x is a limit point of A,
    -- Fᶜ must contain an element of A but A ⊆ F. Contradiction.
    by_contradiction,

    have : Fᶜ ∈ nhds x := is_open.mem_nhds hF.is_open_compl h,
    cases hA Fᶜ this with N bad,
    finish,
  },
end

-- Bullet point 2
lemma open_subset_subset_interior
  (U A : set X) (Uopen : is_open U) (UsubA : U ⊆ A) : U ⊆ lecture_interior A :=
begin
  intros x xinU,
  use U,
  apply and.intro,
  exact is_open.mem_nhds Uopen xinU,
  exact exists.intro Uopen UsubA,
end

-- Basis question
-- Exercise 2.1.6 part 2
-- Interestingly basis in Lean is defined 'using' B1 and B2 (and one other)
example (B : set(set X)) : is_topological_basis B → B.sUnion = set.univ ∧
  ∀ B1 B2 ∈ B, ∀ x ∈ B1 ∩ B2, (∃ B3 ∈ B, x ∈ B3 ∧ B3 ⊆ B1 ∩ B2):=
begin
  intros basis,
  split,
  exact basis.sUnion_eq,
  exact basis.exists_subset_inter,
end

-- Well, that was boring

structure is_lecture_basis (B : set(set X)) : Prop :=
  (open_sets : ∀ Bi ∈ B, is_open Bi)
  (construcable : ∀ (s : set X), is_open s → ∃ C ⊆ B, s = C.sUnion)

-- The real deal
example (B : set(set X)) : is_lecture_basis B → B.sUnion = set.univ ∧
  ∀ B1 B2 ∈ B, ∀ x ∈ B1 ∩ B2, (∃ B3 ∈ B, x ∈ B3 ∧ B3 ⊆ B1 ∩ B2):=
begin
  intros basis,
  split,
  {
    have Xisopen : is_open set.univ,      finish,
    have henceConstrucable :=             basis.construcable set.univ Xisopen,
    rcases henceConstrucable with ⟨ C, CinB, CconstX ⟩,

    -- univ = ⋃₀C ⊆ ⋃₀B ⊆ univ → ⋃₀B = univ
    have UCinUB : ⋃₀C ⊆ ⋃₀B :=          sUnion_mono CinB,
    have : ⋃₀B ⊆ set.univ :=            (⋃₀ B).subset_univ,
    rw ← CconstX at UCinUB,
    exact set.subset.antisymm this UCinUB,
  },
  {
    intros B1 hB1 B2 hB2 x hx,
    have Biopen := basis.open_sets,
    have B1open : is_open B1 :=           Biopen B1 hB1,
    have B2open : is_open B2 :=           Biopen B2 hB2,
    have inter_open :=                    is_open.inter B1open B2open,
    have henceConstrucable :=             basis.construcable (B1 ∩ B2) inter_open,
    rcases henceConstrucable with ⟨ C, CinB, CconstInter ⟩,
    rw CconstInter at hx,
    have Done : ∃ C1 ∈ C, x ∈ C1 :=          mem_sUnion.mp hx,

    rcases Done with ⟨ C1, C1inC, xinC1 ⟩,
    use C1,
    split,
    exact CinB C1inC,
    split,
    exact xinC1,
    rw CconstInter,
    exact subset_sUnion_of_mem C1inC,
  },
end


-- ---------------------------------------------------------------------
-- Ejercicio. Sean a y b números reales. Demostrar que
--    min a b = min b a
-- ----------------------------------------------------------------------

import data.real.basic

variables a b : ℝ

-- 1ª demostración
-- ===============

example : min a b = min b a :=
begin
  apply le_antisymm,
  { show min a b ≤ min b a,
    apply le_min,
    { apply min_le_right },
    { apply min_le_left }},
  { show min b a ≤ min a b,
    apply le_min,
    { apply min_le_right },
    { apply min_le_left }},
end

-- Nota: Se usa "show" para indicar lo que se demuestra en cada bloque.

-- El desarrollo de la prueba es
--
--    ⊢ min a b = min b a
-- apply le_antisymm,
-- |    ⊢ min a b ≤ min b a
-- | apply le_min,
-- | |    ⊢ min a b ≤ b
-- | | { apply min_le_right },
-- | |    ⊢ min a b ≤ a
-- | apply min_le_left,
-- |    ⊢ min b a ≤ min a b
-- | apply le_min,
-- | |    ⊢ min b a ≤ a
-- | { apply min_le_right },
-- | |    ⊢ min b a ≤ b
-- | apply min_le_left }
--    no goals

-- 2ª demostración (con lema local)
-- ================================

example : min a b = min b a :=
begin
  have h : ∀ x y : ℝ, min x y ≤ min y x,
  { intros x y,
    apply le_min,
    { apply min_le_right },
    { apply min_le_left }},
  apply le_antisymm,
  apply h,
  apply h,
end

-- Nota: La táctica "intros" introduce las variables en el contexto. Ver
-- https://bit.ly/2UF1EdL

-- 3ª demostración
-- ===============

example : min a b = min b a :=
begin
  have h : ∀ {x y : ℝ}, min x y ≤ min y x,
  { intros x y,
    exact le_min (min_le_right x y) (min_le_left x y) },
  exact le_antisymm h h,
end

-- 4ª demostración (con repeat)
-- ============================

example : min a b = min b a :=
begin
  apply le_antisymm,
  repeat {
    apply le_min,
    apply min_le_right,
    apply min_le_left },
end

-- Nota. La táctica "repeat" aplica una táctica recursivamente a todos los
-- subobjetivos. Ver https://bit.ly/2YuO5P9

-- Lemas usados
-- ============

-- #check (le_antisymm : a ≤ b → b ≤ a → a = b)
-- #check (le_min : c ≤ a → c ≤ b → c ≤ min a b)
-- #check (min_le_left a b : min a b ≤ a)
-- #check (min_le_right a b : min a b ≤ b)


import Mathlib.Algebra.BigOperators.Basic

/-!
# IMO 2018 Q3

An anti-Pascal triangle is an equilateral triangular array of numbers such that,
except for the numbers in the bottom row, each number is the absolute value
of the difference of the two numbers immediately below it. For example,
the following array is an anti-Pascal triangle with four rows
which contains every integer from 1 to 10:

                  4
                2   6
              5   7   1
            8   3  10   9

Does there exist an anti-Pascal triangle with 2018 rows which contains every
integer from 1 to 1 + 2 + ... + 2018?

# Solution
No.

-/

open BigOperators

structure Coords where
(row : Nat) (col : Nat)

def left_child (c : Coords) : Coords :=
 ⟨c.row.succ, c.col⟩

def right_child (c : Coords) : Coords :=
  ⟨c.row.succ, c.col.succ⟩

/--
antipascal triangle with n rows
-/
structure antipascal_triangle (n : Nat) where
(f : Coords → Nat)
(antipascal : ∀ x: Coords, x.row.succ < n ∧ x.col ≤ x.row →
  f x + f (left_child x) = f (right_child x) ∨
  f x + f (right_child x) = f (left_child x))

structure a_and_b where
(a : Coords) (b : Coords)

theorem imo2018_q3 (t : antipascal_triangle 2018)
    (h_contains_all : ∀ n, n ≤ ∑ i in Finset.range 2018, (i + 1) →
                      ∃ r, r ≤ 2018 ∧ ∃ c, c < r ∧ t.f ⟨r,c⟩ = n) :
    False := by sorry


import analysis.real tactic.norm_num
local infix ` ^ ` := monoid.pow

theorem Q1 : 1=1 := sorry
theorem Q2 : 1=1 := sorry

lemma avg_lt_max {mn mx: ℝ} (H : mn < mx) : (mn+mx) / 2 < mx :=
begin
  apply (mul_lt_mul_right (show (0:ℝ)<2, by norm_num)).1,
  rw [div_mul_cancel _ (two_ne_zero)],
  simp [H,mul_two],
end

lemma min_lt_avg {mn mx: ℝ} (H : mn < mx) : mn < (mn+mx) / 2 :=
begin
  apply (mul_lt_mul_right (show (0:ℝ)<2, by norm_num)).1,
  rw [div_mul_cancel _ (two_ne_zero)],
  simp [H,mul_two],
end

lemma lub_open (y : ℝ) : is_lub {x : ℝ | x < y} y :=
begin
split,
{ intro a,
  exact le_of_lt },
unfold lower_bounds,
intro b,
intro Hb,
refine le_of_not_gt _,
intro Hnb,
let c:=(b+y)/2,
unfold upper_bounds at Hb,
have H2 := Hb c,
clear Hb,
have H : c ∈ {x : ℝ | x < y},
{ exact avg_lt_max Hnb,
},
have Hcleb := H2 H,
have Hbltc : b < c := min_lt_avg Hnb,
exact not_lt.2 Hcleb Hbltc,
end

def S3a : set ℝ := {x : ℝ | x<0}

theorem Q3a : is_lub (S3a) 0 := lub_open 0

def S3b : set ℝ := {x : ℝ | ∃ y : ℚ, x = ↑y}

theorem Q3b : ∀ b : ℝ, ¬ (is_lub (S3b) b) :=
begin
intros b Hlub,
have Hbub : b ∈ upper_bounds S3b := Hlub.left,
have H : b < (b+1) := calc b = b+0 : (add_zero _).symm
                         ... < b+1 : add_lt_add_left zero_lt_one _,
--have H2 := exists_rat_btwn H,
--cases H2 with q Hq,
cases (exists_rat_btwn H) with q Hq,
have Hwrong := Hbub ↑q,
have Hqin : ↑q ∈ S3b := ⟨q,rfl⟩,
exact not_lt.2 (Hwrong Hqin) (Hq.left),
end

lemma pow_two_eq_mul_self {x : ℝ} : x^2=x*x :=
begin
unfold monoid.pow,simp,
end

def S3c : set ℝ := {x : ℝ | (x+1)^2 < x^2} -- x<-0.5

theorem Q3c : is_lub (S3c) (-1/2) :=
begin
have : S3c = {x : ℝ | x < -1/2},
{ apply set.ext,
  intro x,
  show (x+1)^2 < x^2 ↔ x < -1/2,
  rw [←add_zero (x^2),pow_two_eq_mul_self,mul_add,add_mul,add_mul,pow_two_eq_mul_self,mul_one,one_mul],
  rw [add_assoc],
  -- ask Mario why I can't do this -- I only want
  -- to change one thing
  exact calc
    x * x + (x + (x + 1 * 1)) < x * x + 0 ↔ (x + (x + 1 * 1)) < 0 : add_lt_add_iff_left (x*x)
    ... ↔ x * 2 + 1 < 0 : by rw [mul_one,←add_assoc,←mul_two]
    ... ↔ x * 2 < -1 : by rw[←lt_sub_right_iff_add_lt,zero_sub]
    ... ↔ x < (-1) / 2 : (lt_div_iff (show (0:ℝ) < 2, by norm_num)).symm },
rw this,
exact lub_open ((-1)/2),
end

def S3d : set ℝ := {x : ℝ | (∃ q : ℚ, x=↑q) ∧ 1 < x ∧ x < 2}

theorem Q3d : is_lub S3d 2 :=
begin
split,
{ intros z Hz,
  exact le_of_lt Hz.right.right },
intros y Hy,
refine le_of_not_gt _,
intro Hylt2,
have onelt2 : (1:ℝ) < 2 := by norm_num,
have := max_lt onelt2 Hylt2,
cases (exists_rat_btwn this) with q Hq,
have := Hy ↑q,
have Hq_in : ↑q ∈ S3d,
{ split,
  { existsi q,refl},
  split,
  { exact lt_of_le_of_lt (le_max_left _ _) Hq.left },
  exact Hq.right,
},
have this2 := this Hq_in,

unfold upper_bounds at Hy,
apply (not_lt.2 this2),
exact lt_of_le_of_lt (le_max_right 1 y) Hq.left,
end

theorem Q4 (S : set ℝ) (x : ℝ) (H1 : x ∈ upper_bounds S) (H2 : x ∈ S) : is_lub S x :=
begin
split,exact H1,
intro y,
intro H,
have := H x,
exact this H2,
end

theorem Q5a1 (S : set ℝ) : (∃ x : ℝ, x ∈ lower_bounds S)
    ↔ (∃ y : ℝ, y ∈ upper_bounds {t : ℝ | ∃ s ∈ S, t = -s }) :=
begin
split,
{ intros H,
  cases H with x Hx,
  existsi (-x),
  intro w,
  have Hmw := Hx (-w),
  intro Hw,
  cases Hw with t Ht,
  cases Ht with u Hu,
  refine le_neg_of_le_neg _,
  apply Hmw _,
  rw [Hu],
  rwa neg_neg },
{ intros H,
  cases H with y Hy,
  existsi (-y),
  intro mt,
  have Ht := Hy (-mt),
  intro Hmt,
  refine neg_le_of_neg_le _,
  apply Ht _,
  existsi mt,
  existsi Hmt,
  refl
}
end

theorem Q5a2 (S : set ℝ) (x : ℝ) : is_glb S x ↔
    is_lub {t : ℝ | ∃ s ∈ S, t = -s} (-x) :=
begin
split,
{ intro HSx,
  split,
  { intros ms Hms,
    refine le_neg_of_le_neg _,
    refine HSx.left _ _,
    cases Hms with s Hs,
    cases Hs with H1 H2,
    rw H2,
    rwa neg_neg },
  { intros b Hb,
    apply neg_le_of_neg_le _,
    apply HSx.2 (-b),
    intros c Hc,
    apply neg_le_of_neg_le _,
    apply Hb (-c),
    existsi c,
    existsi Hc,
    refl },
},
{ intro HSx,
  split,
  { intros ms Hms,
    refine le_of_neg_le_neg _,
    refine HSx.left _ _,
    existsi [ms,Hms],
    refl },
  { intros b Hb,
    apply le_of_neg_le_neg _,
    apply HSx.2 (-b),
    intros c Hc,
    cases Hc with mc Hmc,
    cases Hmc with H1 H2,
    apply le_neg_of_le_neg _,
    apply Hb (-c),
    rw H2,
    rwa neg_neg },
},
end

lemma Q5bhelper (S : set ℝ) (x₁ x₂ : ℝ) : is_glb S x₁ ∧ is_glb S x₂ → x₁ ≤ x₂ :=
begin
intro H,
have Hglb1 := H.left,
have Hlb1 := Hglb1.left,
have Hglb2 := H.right,
have H1 := Hglb2.right,
exact H1 _ Hlb1,
end

theorem Q5b (S : set ℝ) (x₁ x₂ : ℝ) : is_glb S x₁ ∧ is_glb S x₂ → x₁ = x₂ :=
begin
intro H,
have H1 := Q5bhelper _ _ _ H,
have H2 := Q5bhelper _ _ _ ⟨H.right,H.left⟩,
--exact eq_iff_le_and_le. 2 ⟨H1,H2⟩,
-- TODO : did this used to work? What do I do now??
exact le_antisymm H1 H2,
end

theorem Q5c :  (∀ S : set ℝ, (∃ w : ℝ, w ∈ S) → (∃ x : ℝ, x ∈ upper_bounds S) → ∃ y : ℝ, is_lub S y)
   →   (∀ T : set ℝ, (∃ w₁ : ℝ, w₁ ∈ T) → (∃ x₁ : ℝ, x₁ ∈ lower_bounds T) → ∃ y₁ : ℝ, is_glb T y₁) :=
begin
intro H,
intro T,
have H1 := H {x : ℝ | ∃ y : ℝ, y ∈ T ∧ x = -y},
clear H,
intro J2,
cases J2 with w2 Jw2,
have H2 := H1 _,
{ intro J3,
  clear H1,
  cases J3 with w3 Jw3,
  have H3 := H2 _,
  { clear H2,
    cases H3 with y3 Hy3,
    existsi (-y3),
    split,
    { intro t,
      have H4 := Hy3.left (-t),
      intro J5,
      rw neg_le,
      apply H4,
      clear H4,
      existsi t,
      simp [J5]
    },
    intros t Ht,
    have H4 := Hy3.right (-t),
    rw le_neg,
    apply H4,
    clear H4,
    intros u Hu,
    cases Hu with v Hv,
    rw [Hv.right],
    refine neg_le_neg _,
    apply Ht,
    exact Hv.left,
  },
  existsi (-w3),
  intros z Hz,
  rw le_neg,
  apply Jw3,
  cases Hz with u Hu,
  rw Hu.right,
  rw neg_neg,
  exact Hu.left
},
existsi (-w2),
existsi w2,
simp [Jw2],
end

def S (a : {n : ℕ // n ≥ 1} → ℝ)
  (n : ℕ) (H : n ≥ 1) := { r: ℝ | ∃ (m : ℕ) (Hm : m ≥ n), r = a ⟨m,ge_trans Hm H⟩ }

/- HORRIBLE ERROR
theorem Q6a1 (a : {n : ℕ // n ≥ 1} → ℝ)
  (HB : ∃ B : ℝ, ∀ n : ℕ, ∀ H : n ≥ 1, a ⟨n,H⟩ ≤ B) : ∀ (n : ℕ) (H : n ≥ 1),
  (∃ r : ℝ, r ∈ S a HB n H) ∧ (∃ B : ℝ, ∀ x : ℝ, x ∈ S a HB n H → x ≤ B) :=
begin
intros n Hn,
split,
{ existsi a ⟨n Hn⟩,

}
end
-/

--set_option trace.class_instances true

theorem Q6a1 (a : {n : ℕ // n ≥ 1} → ℝ)
  (HB : ∃ B : ℝ, ∀ n : ℕ, ∀ H : n ≥ 1, a ⟨n,H⟩ ≤ B) : ∀ (n : ℕ) (H : n ≥ 1),
  (∃ r : ℝ, r ∈ S a n H) ∧ (∃ B : ℝ, ∀ x : ℝ, x ∈ S a n H → x ≤ B) :=
begin
intros n Hn,
split,
{ existsi a ⟨n,Hn⟩,
  existsi n,
  existsi (le_of_eq (refl n)),
  refl },
cases HB with B H,
existsi B,
intro x,
intro Hx,
cases Hx with m Hm,
cases Hm with H1 Hx,
rw Hx,
refine H _ _,
end

theorem Q6a2 (a : {n : ℕ // n ≥ 1} → ℝ)
  (HB : ∃ B : ℝ, ∀ n : ℕ, ∀ H : n ≥ 1, a ⟨n,H⟩ ≤ B) (n : ℕ) (H : n ≥ 1) :
  ∃ x : ℝ, is_lub (S a n H) x :=
begin
have H1 := Q6a1 a HB n H,
cases H1.left with y Hy,
cases H1.right with B H2,
refine exists_supremum_real Hy H2
end

theorem Q6b1 (a : {n : ℕ // n ≥ 1} → ℝ)
  (HB : ∃ B : ℝ, ∀ n : ℕ, ∀ H : n ≥ 1, a ⟨n,H⟩ ≤ B) (n : ℕ) (H : n ≥ 1) :
  ∀ bnp1 bn : ℝ, is_lub (S a n H) bn ∧ is_lub (S a (n+1) (le_trans H (nat.le_succ _))) bnp1
  → bnp1 ≤ bn :=
begin
introv H1,
suffices : bn ∈ upper_bounds (S a (n+1) (le_trans H (nat.le_succ _))),
refine (H1.right).right bn this,
intros x Hx,
suffices : x ∈ (S a n H),
exact H1.left.left x this,
cases Hx with m Hm,
cases Hm with H2 H3,
existsi m,
existsi (le_trans (nat.le_succ _) H2),
assumption
end

def limsup (a : {n : ℕ // n ≥ 1} → ℝ) (lsup : ℝ) : Prop :=
  ∃ b : { n : ℕ // n ≥ 1} → ℝ, is_glb { x : ℝ | ∃ (n : ℕ) (H : n ≥ 1), x = b ⟨n,H⟩} lsup
  ∧ ∀ (n : ℕ) (H : n ≥ 1), is_lub (S a n H) (b ⟨n,H⟩)

def a1 : {n : ℕ // n ≥ 1} → ℝ := λ _, 1
theorem Q6c1 : limsup a1 1 :=
begin
existsi (λ _,(1:ℝ)),
split,
{ split,
  { intros y Hy,
    cases Hy with n Hn,
    cases Hn with H1 H2,
    rw H2,
    show (1:ℝ) ≤ 1,
    norm_num
  },
  intros y Hy,
  have H1 := Hy 1,
  refine H1 _,
  existsi 1,
  existsi (show 1 ≤ 1, by norm_num),
  simp

},
intros n Hn,
show is_lub (S a1 n Hn) 1,
split,
{ intros x Hx,
  cases Hx with m Hm,
  cases Hm with H1 H2,
  rw H2,
  show (1:ℝ)≤1,
  norm_num
},
{ intros x Hx,
  have H1 := Hx 1,
  apply H1,
  clear H1, -- ask Mario how to do apply then clear
  existsi n,
  existsi _,
  {refl},
  show n≤n,
  refl,

}
end

--set_option pp.all true

noncomputable def a2 : {n : ℕ // n ≥ 1} → ℝ := λ N, 1/N.val
theorem Q6c2 : limsup a2 0 :=
begin
existsi a2,
split,
{ split,
  {
  intros x Hx,
  cases Hx with m Hm,
  cases Hm with H1 H2,
  rw H2,
  show (0:ℝ) ≤ 1/m,
  refine div_nonneg_of_nonneg_of_pos _ _,
  {norm_num},
  refine lt_of_lt_of_le zero_lt_one _,
  rwa [←nat.cast_one,nat.cast_le] },
  { intros y Hy,
    refine not_lt.1 _,
    intro Hny,
    cases (exists_lt_nat (1/y)) with m Hm,
    have H1 := Hy (1/m),
    rw ←inv_eq_one_div at Hm,
    have H2 : m ≥ 1,
    { have H3 : ↑0 < ↑m := lt_trans (inv_pos Hny) Hm,
      rw [nat.cast_lt] at H3,
      exact nat.succ_le_of_lt H3 },
    have H4 : 1 / (↑m:ℝ) ∈ {x : ℝ | ∃ (n : ℕ) (H : n ≥ 1), x = a2 ⟨n, H⟩},
    { existsi [m,H2],
      refl },
    have H5 := H1 H4,
    have H6 := inv_le_inv _ _ Hny H5,
    rw [←inv_eq_one_div,inv_inv'] at H6,
    apply lt_irrefl (m:ℝ),
    exact lt_of_le_of_lt H6 Hm,
  }
},
{
intros n H,
split,
{ intros x Hx,
  cases Hx with d Hd,
  cases Hd with Hm H1,
  rw H1,
  show 1/(d:ℝ) ≤ 1/n,
  rw [←inv_eq_one_div,←inv_eq_one_div],
  refine inv_le_inv _ _ _ _,
  { rw [←nat.cast_zero,nat.cast_lt], exact lt_of_lt_of_le zero_lt_one H},
  rwa [nat.cast_le]
  },
intros x Hx,
have := Hx (1/(n:ℝ)),
apply this,
existsi n,
existsi _,
refl,
show n ≤ n,
refl
}
end

noncomputable def a3 : {n : ℕ // n ≥ 1} → ℝ := λ N, 1 - ((((N.val % (2:ℕ))):ℤ):ℝ)
--#eval a3 ⟨1,dec_trivial⟩

theorem Q6c3 : limsup a3 1 :=
begin
existsi (λ _, (1:ℝ)),
split,
{ split,
  { intros x Hx,
    cases Hx with m Hm,
    cases Hm with H1 H2,
    rw H2,
    simp [le_refl],
  },
  intros x Hx,
  apply Hx,
  existsi 2,
  existsi _,
  refl,
  exact dec_trivial,

},
intros n Hn,
split,
{ intros y Hy,
  cases Hy with m Hm,
  show y ≤ 1,
  cases Hm with H1 H2,
  rw H2,
  unfold a3,
  refine sub_le_self _ _,
  rw [←int.cast_zero],
  unfold ge,
  rw int.cast_le,
  refine int.mod_nonneg _ _,
  exact dec_trivial,
},
intros x Hx,
show 1 ≤ x,
apply Hx,
existsi (2*n),
existsi _,
{ unfold a3,
  simp,
},
rw [mul_comm,mul_two],
exact nat.le_add_left _ _,
end

/-
Say we have a sequence of real numbers a 1 , a 2 , a 3 , . . ., which is
 bounded above in the sense
that there exists some real number B such that a i ≤ B for all i.
Now let’s define some sets S 1 , S 2 , S 3 , . . . by
S n = {a n , a n+1 , a n+2 , . . .}.
For example S 3 = {a 3 , a 4 , a 5 , . . .}.
a) Prove that for all n ≥ 1, S n is a non-empty set which is bounded above, and hence has a
least upper bound b n .
b) Prove that b n+1 ≤ b n and hence b 1 , b 2 , b 3 is a decreasing sequence.
If the set {b 1 , b 2 , b 3 , . . .} is bounded below, then its greatest lower bound ` is called the limsup
of the sequence (a 1 , a 2 , a 3 , . . .) (this is an abbreviation for Limit Superior).
c) Find the limsup of the following sequences (they do exist).
i) 1, 1, 1, 1, 1, . . .
ii) 1, 2 1 , 13 , 14 , . . .
iii) 0, 1, 0, 1, 0, 1, 0, 1, . . .
d) If you like, then guess the definition of liminf (Limit Inferior) and compute it for examples
(i) to (iii) of (c) above. Which of these sequences converges? Can you tell just from looking at
the limsup and liminf?-/

def liminf (a : {n : ℕ // n ≥ 1} → ℝ) (linf : ℝ) : Prop :=
  ∃ c : { n : ℕ // n ≥ 1} → ℝ, is_lub { x : ℝ | ∃ (n : ℕ) (H : n ≥ 1), x = c ⟨n,H⟩} linf
  ∧ ∀ (n : ℕ) (H : n ≥ 1), is_glb (S a n H) (c ⟨n,H⟩)

theorem Q6c1' : liminf a1 1 :=
begin
existsi (λ _,(1:ℝ)),
split,
{ split,
  { intros y Hy,
    cases Hy with n Hn,
    cases Hn with H1 H2,
    rw H2,
    show (1:ℝ) ≤ 1,
    exact le_refl _,
  },
  intros y Hy,
  have H1 := Hy 1,
  refine H1 _,
  existsi 1,
  existsi (show 1 ≤ 1, by exact le_refl _),
  simp
},
intros n Hn,
show is_glb (S a1 n Hn) 1,
split,
{ intros x Hx,
  cases Hx with m Hm,
  cases Hm with H1 H2,
  rw H2,
  show (1:ℝ)≤1,
  exact le_refl _,
},
{ intros x Hx,
  have H1 := Hx 1,
  apply H1,
  clear H1, -- ask Mario how to do apply then clear
  existsi n,
  existsi _,
  {refl},
  show n≤n,
  refl,
}
end

theorem Q6c2' : liminf a2 0 :=
begin
existsi (λ _,(0:ℝ)),
split,
{ split,
  {
  intros x Hx,
  cases Hx with m Hm,
  cases Hm with H1 H2,
  rw H2,
  exact le_refl _,
  },
  intros y Hy,
  apply Hy,
  existsi 1,
  existsi _,refl,
  show 1 ≤ 1,
  exact le_refl _
},
intros n Hn,
split,
{ intros x Hx,
  cases Hx with m Hm,
  cases Hm with H1 H2,
  show 0 ≤ x,
  rw H2,
  unfold a2,
  show 0 ≤ 1/(↑m:ℝ),
  rw [←inv_eq_one_div],
  apply le_of_lt _,
  apply inv_pos,
  rw [←nat.cast_zero,nat.cast_lt],
  exact calc 0 < 1 : zero_lt_one ... ≤ n : Hn ... ≤ m : H1
},
intros x Hx,
show x ≤ 0,
    refine not_lt.1 _,
    intro Hny,
    cases (exists_lt_nat (max (1/x) n)) with m Hm,
    have H1 := Hx (1/m),
    rw ←inv_eq_one_div at Hm,
    have H2 : n ≤ m,
    { suffices : ↑n < (↑m:ℝ),exact le_of_lt (nat.cast_lt.1 this), exact lt_of_le_of_lt (le_max_right _ _) Hm },
    have H4 : 1 / (↑m:ℝ) ∈ {x : ℝ | ∃ (m : ℕ) (H : m ≥ n), x = a2 ⟨m, (le_trans Hn H)⟩},
    { existsi [m,H2],
      refl },
    have H5 := H1 H4,
    have H6 := inv_le_inv _ _ Hny H5,
    rw [←inv_eq_one_div,inv_inv'] at H6,
    apply lt_irrefl (m:ℝ),
    refine lt_of_le_of_lt H6 _,
    refine lt_of_le_of_lt (le_max_left _ _) Hm,
end

--noncomputable def a3 : {n : ℕ // n ≥ 1} → ℝ := λ N, 1 - ((((N.val % (2:ℕ))):ℤ):ℝ)
theorem Q6c3' : liminf a3 0 :=
begin
existsi (λ _,(0:ℝ)),
split,
{ split,
  { intros x₁ Hx₁,
    cases Hx₁ with n₁ Hn₁,
    cases Hn₁ with H1 H2,
    rw H2,
    exact le_refl 0 },
  intros x₂ Hx₂,
  apply Hx₂,
  existsi 1,
  existsi _,refl,
  exact le_refl 1 },
intros n H,
split,
{ intro x₁,
  intro H1,
  show 0 ≤ x₁,
  cases H1 with n₁ Hn₁,
  cases Hn₁ with H1 H2,
  rw H2,
  unfold a3,
  rw [sub_nonneg],
  show ↑((↑n₁:ℤ) % (↑2:ℤ)) ≤ (1:ℝ),
  have H3 : (1:ℝ) = ↑(1:ℤ), rw [←int.cast_one],
  rw [H3],
  rw [int.cast_le],
  apply int.le_of_lt_add_one,
  show ↑n₁ % ↑2 < (2:ℤ),
  refine int.mod_lt_of_pos ↑n₁ _,
  norm_num },
intros x₁ Hx₁,
show x₁ ≤ 0,
apply Hx₁,
existsi (2*n+1),
existsi _,
{ unfold a3,
  apply eq.symm,
  apply sub_eq_zero_of_eq,
  rw ←int.cast_one,
  rw int.cast_inj,
  show (1:ℤ) = ↑((2*n+1) % 2),
  suffices : 1 = 1 % 2,simpa,
  refl },
rw [mul_comm,mul_two,add_assoc],
apply nat.le_add_right
end


-- La_suma_de_no_negativos_es_expansiva.lean
-- La suma de no negativos es expansiva
-- José A. Alonso Jiménez
-- Sevilla, 21 de agosto de 2020
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Ejercicio 1.. Demostrar si a y b son números reales y a es no
-- negativo, entonces b ≤ a + b
-- ----------------------------------------------------------------------

import data.real.basic
variables {a b : ℝ}

-- 1ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
begin
  calc b = 0 + b : by rw zero_add
     ... ≤ a + b : by exact add_le_add_right ha b,
end

-- Comentario: Se ha usado el lema
-- + zero_add : 0 + a = a

-- 2ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
begin
  calc b = 0 + b : (zero_add b).symm
     ... ≤ a + b : add_le_add_right ha b,
end

-- 3ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
begin
  calc b = 0 + b : by ring
     ... ≤ a + b : by exact add_le_add_right ha b,
end

-- 4ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
by simp [ha]

-- 5ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
by linarith

-- 6ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
by finish

-- 7ª demostración
-- ===============

example
  (ha : 0 ≤ a)
  : b ≤ a + b :=
le_add_of_nonneg_left ha

-- Comentario: Se ha usado el lema
-- + le_add_of_nonneg_left : 0 ≤ b → a ≤ b + a

-- ---------------------------------------------------------------------
-- Ejercicio 2. Demostrar si a y b son números reales y b es no
-- negativo, entonces a ≤ a + b
-- ----------------------------------------------------------------------

-- 1ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
begin
  calc a = a + 0 : by rw add_zero
     ... ≤ a + b : by exact add_le_add_left hb a,
end

-- Comentario: Se ha usado el lema
-- + add_le_add_left : a ≤ b → ∀ (c : ℝ), c + a ≤ c + b

-- 2ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
begin
  calc a = a + 0 : (add_zero a).symm
     ... ≤ a + b : add_le_add_left hb a,
end

-- 3ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
begin
  calc a = a + 0 : by ring
     ... ≤ a + b : add_le_add_left hb a,
end

-- 4ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
by simp [hb]

-- 5ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
by linarith

-- 6ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
by finish

-- 7ª demostración
-- ===============

example
  (hb : 0 ≤ b)
  : a ≤ a + b :=
le_add_of_nonneg_right hb

-- Comentario: Se usa el lema
-- + le_add_of_nonneg_right : 0 ≤ b → a ≤ a + b


import .src_04_theorems

variables p q r : Prop

namespace mth1001

section if_and_only_if

/-
The symbol `↔`, written `\iff` is read 'if and only if'. From `p ↔ q`, one can deduce `p → q` and
`p → q`. Likewise, from `p → q` and `q → p`, one can deduce `p ↔ q`.

Given propositions `p` and `q`, if `p ↔ q`, we say that `p` is _equivalent_ to `q`.
-/

-- Here is a term-style derivation of `p ↔ q` from `h₁ : p → q` and `h₂ : q → p`.
example (h₁ : p → q) (h₂ : q → p) : p ↔ q :=
iff.intro h₁ h₂

/-
Equally, we can use the `split` tactic to decompose the goal `p ↔ q` into two goals: one
to prove `p → q` and the other to prove `q → p`.
-/
example (h₁ : p → q) (h₂ : q → p) : p ↔ q :=
begin
  split,
  { exact h₁, },
  { exact h₂, },
end

-- Exercise 033:
-- We combine the previous results `and_assoc` and `and_assoc2`.
theorem and_assoc : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
begin
  split,
  { sorry},
  { sorry, },
end

/-
Below is a tactic mode proof of `p → q` from `h : p ↔ q`. Note how `cases` is used to decompose
the premise `h` into two new premises, `hpq : p → q` and `hqp : q → p`.
-/
example (h : p ↔ q) : p → q :=
begin
  cases h with hpq hqp,
  exact hpq,
end

-- Exercise 034:
example (h : p ↔ q) : q → p :=
begin
  sorry
end

/-
Alternatively, we can give a term-style proof. If `h : p ↔ q`, then `h.mp` is a proof of `p → q`
and `h.mpr` is a proof of `q → p`. Here `mp` is an abbreviation of 'modus ponens', the Latin name
for implication elimination.
-/

example (h : p ↔ q) : p → q :=
h.mp

example (h : p ↔ q) : q → p :=
h.mpr

-- Exercise 035:
-- We prove `p ∧ q ↔ q ∧ p` using the theorem `and_of_and` from the previous section.
theorem and_iff_and : p ∧ q ↔ q ∧ p :=
begin
  split,
  { exact and_of_and p q },
  { sorry, },
end

end if_and_only_if

/-
SUMMARY:

* Iff introduction.
* Term-style introduction using `iff.intro`.
* Tactic-style introduction using `split`.

* Iff elimination.
* Tactic-style elimination using `cases`.
* Term-style elimination using `.mp` or `.mpr`.
-/

end mth1001


import data.real.basic

open_locale classical

/-
Theoretical negations.

This file is for people interested in logic who want to fully understand
negations.

Here we don't use `contrapose` or `push_neg`. The goal is to prove lemmas
that are used by those tactics. Of course we can use
`exfalso`, `by_contradiction` and `by_cases`.

If this doesn't sound like fun then skip ahead to the next file.
-/

section negation_prop

variables P Q : Prop

-- 0055
example : (P → Q) ↔ (¬ Q → ¬ P) :=
begin
  sorry
end

-- 0056
lemma non_imp (P Q : Prop) : ¬ (P → Q) ↔ P ∧ ¬ Q :=
begin
  sorry
end

-- In the next one, let's use the axiom
-- propext {P Q : Prop} : (P ↔ Q) → P = Q

-- 0057
example (P : Prop) : ¬ P ↔ P = false :=
begin
  sorry
end

end negation_prop

section negation_quantifiers
variables (X : Type) (P : X → Prop)

-- 0058
example : ¬ (∀ x, P x) ↔ ∃ x, ¬ P x :=
begin
  sorry
end

-- 0059
example : ¬ (∃ x, P x) ↔ ∀ x, ¬ P x :=
begin
  sorry
end

-- 0060
example (P : ℝ → Prop) : ¬ (∃ ε > 0, P ε) ↔ ∀ ε > 0, ¬ P ε :=
begin
  sorry
end

-- 0061
example (P : ℝ → Prop) : ¬ (∀ x > 0, P x) ↔ ∃ x > 0, ¬ P x :=
begin
  sorry
end

end negation_quantifiers


/-
Conjunctions, disjunctions, implication, iff,
negation
-/

/-
1. Prove that 3 + 3 = 6 and 2 + 6 = 8 implies
that 1 + 1 = 2.
-/

-- answer:
example : 3 + 3 = 6 ∧ 2 + 6 = 8 → 1 + 1 = 2 :=
begin
     assume ttts,

     exact rfl,
end
/-
2. Prove that 2 + 5 = 3 or 9 + 1 = 5 implies
that 2 + 3 = 9.
-/

-- answer:
example : 2 + 5 = 0 ∨ 9 + 1 = 0 → 2 + 3 = 9 :=
begin
     assume tof,
     cases tof,
          have contra : 2 + 5 ≠ 0 :=
          begin
               apply nat.no_confusion,
          end,
          contradiction,
          have contra : 9 + 1 ≠ 0 :=
          begin
               apply nat.no_confusion,
          end,
          contradiction,
end
/-
3. Prove that ¬(A ∧ B ∧ C) ↔ (¬A ∨ ¬B ∨ ¬C).
You may use the axiom of the excluded middle.
-/
open classical
--axiom em: ∀(P: Prop), P ∨ ¬P

-- answer:
example : ∀ (A B C : Prop), ¬(A ∧ B ∧ C) ↔ (¬A ∨ ¬B ∨ ¬C) :=
begin
     intros A B C,
     apply iff.intro,
     /- forward -/
          assume nabc,
          cases em A with a na,
          cases em B with b nb,
          cases em C with c nc,
          have f : false := nabc (and.intro a (and.intro b c)),
          exact false.elim f,
          exact or.inr (or.inr nc),
          exact or.inr (or.inl nb),
          exact or.inl na,
     /- backward -/
          assume nanbnc,
          assume abc,
          cases nanbnc with na nbnc,
          have f : false := na abc.1,
          exact f,
          cases nbnc with nb nc,
          have f : false := nb abc.2.1,
          exact f,
          have f : false := nc abc.2.2,
          exact f,
end

/-
4. Prove that ¬(A ∨ B ∨ C) ↔ (¬A ∧ ¬B ∧ ¬C).
You may *NOT* use the axiom of the excluded middle.
-/
example : ∀ (A B C : Prop), ¬(A ∨ B ∨ C) ↔ (¬A ∧ ¬B ∧ ¬C) :=
begin
     intros,
     split,
          assume nabc,
          split,
          assume a,
          exact nabc (or.inl a),
          split,
          assume b,
          exact nabc (or.inr (or.inl b)),
          assume c,
          exact nabc (or.inr (or.inr c)),
          assume nabc,
          assume abc,
          cases abc with a bc,
          exact (nabc.1 a),
          cases bc with b c,
          exact (nabc.2.1 b),
          exact (nabc.2.2 c),
end
-- answer:
example : ∀ (A B C : Prop), ¬(A ∨ B ∨ C) ↔ (¬A ∧ ¬B ∧ ¬C) :=
begin
     intros A B C,
     apply iff.intro,
     /- forward -/
          assume nabc,
          apply and.intro,
          assume a,
          have f : false := nabc (or.inl a),
          exact f,
          apply and.intro,
          assume b,
          have f : false := nabc (or.inr (or.inl b)),
          exact f,
          assume c,
          have f : false := nabc (or.inr (or.inr c)),
          exact f,
     /-backward-/
          assume nanbnc,
          assume abc,
          cases abc with a bc,
          have na := nanbnc.1,
          exact false.elim (na a),
          cases bc with b c,
          have nb := nanbnc.2.1,
          exact false.elim (nb b),
          have nc := nanbnc.2.2,
          exact false.elim (nc c),
end
/-
5a. Prove that ¬(A ∨ ¬B) ↔ (¬A ∧ B).
You may use the axiom of the excluded middle.
-/

-- answer:
example : ∀ (A B : Prop), ¬(A ∨ ¬B) ↔ (¬A ∧ B) :=
begin
     intros A B,
     apply iff.intro,
     /- forward-/
          assume nanb,
          apply and.intro,
          assume a,
          have f : false := nanb (or.inl a),
          exact f,
          cases em B with b nb,
          exact b,
          have f : false := nanb (or.inr nb),
          exact false.elim f,
     /-backward-/
          assume nab,
          assume anb,
          cases anb with a nb,
          have na := nab.1,
          exact false.elim (na a),
          have b := nab.2,
          exact false.elim (nb b),
end
/-
5b. Prove that ¬(A ∧ ¬B) ↔ (¬A ∨ B).
You may use the axiom of the excluded middle.
-/

-- answer:
example : ∀ (A B : Prop), ¬(A ∧ ¬B) ↔ (¬A ∨ B) :=
begin
     intros A B,
     apply iff.intro,
     /-forward-/
          assume nanb,
          cases em A with a na,
          cases em B with b nb,
          apply or.inr b,
          have f : false := nanb (and.intro a nb),
          exact false.elim f,
          apply or.inl na,
     /-backward-/
          assume nab,
          assume anb,
          cases nab with na b,
          have a := anb.1,
          exact na a,
          have nb := anb.2,
          exact nb b,
end
/-
6. Prove that ¬P ∨ ¬Q ∨ R is true if and only
if P → Q → R. You may use the axiom of the
excluded middle.
-/
-- answer:
example : ∀ (P Q R : Prop ), (P → Q → R) ↔ (¬P ∨ ¬Q ∨ R) :=
begin
     intros P Q R,
     split,
          assume pqr,
          cases em P with p np,
          have QR := pqr p,
          cases em Q with q nq,
          have r := QR q,
          exact or.inr (or.inr r),
          exact or.inr (or.inl nq),
          exact or.inl np,

          assume npnqr,
          assume p,
          assume q,
          cases npnqr with np nqr,
               contradiction,
               cases nqr with nq r,
               contradiction,
               exact r,
end
/-
7. Prove that ¬¬¬P → ¬P. You may use the axiom
of the excluded middle.
-/

-- answer:
example : ∀ P : Prop, ¬¬¬P → ¬P :=
begin
     intro P,
     assume nnnp,
     assume p,
     cases em ¬P with p np,
          contradiction,
          exact nnnp np,
end
/-
Universal Quantifiers, Existential Quantifiers, and
Satisfiability.
-/

/-
8. Prove the following statements are satisfiable
or prove that they are not. You may use the axiom
of the excluded middle to prove cases where the
statements are not satisfiable.
-/

/-
8a. (P ∨ Q) ∧ (¬P ∨ Q) ∧ (P ∨ ¬Q)
-/
example : ∃ P Q : Prop, (P ∨ Q) ∧ (¬P ∨ Q) ∧ (P ∨ ¬Q) :=
begin
     apply exists.intro true,
     apply exists.intro true,
     simp,
end
-- answer:
example : ∃ P Q : Prop, (P ∨ Q) ∧ (¬P ∨ Q) ∧ (P ∨ ¬Q) :=
begin
     apply exists.intro true,
     apply exists.intro true,
     split,
          exact or.inl true.intro,
          split,
          exact or.inr true.intro,
          exact or.inl true.intro,
end

/-
8b. (P ∨ Q) ∧ (¬P ∨ Q) ∧ (P ∨ ¬Q) ∧ (¬P ∨ ¬Q)
-/

-- answer:
example : ¬∃ (P Q : Prop), (P ∨ Q) ∧ (¬P ∨ Q) ∧ (P ∨ ¬Q) ∧ (¬P ∨ ¬Q) :=
begin
     assume func,
     apply exists.elim func,
     assume P,
     assume Pf,
     apply exists.elim Pf,
     assume Q,
     assume Qf,
     cases em P with p np,
          cases em Q with q nq,
               have npnq := Qf.2.2.2,
               cases npnq with np nq,
               contradiction,
               contradiction,
               have npq := Qf.2.1,
               cases npq with np q,
               contradiction,
               contradiction,
               cases em Q with q nq,
               have pnq := Qf.2.2.1,
               cases pnq with p nq,
               contradiction,
               contradiction,
               have pq := Qf.1,
               cases pq with p q,
               contradiction,
               contradiction,
end
/-
8c. (P ∨ Q ∨ R) ∧ (¬P ∨ Q ∨ R) ∧
     (P ∨ ¬Q ∨ ¬R) ∧ (¬P ∨ ¬Q ∨ ¬R)
-/
example : ∃ P Q R: Prop, (P ∨ Q ∨ R) ∧ (¬P ∨ Q ∨ R) ∧
     (P ∨ ¬Q ∨ ¬R) ∧ (¬P ∨ ¬Q ∨ ¬R) :=
begin
     apply exists.intro true,
     apply exists.intro true,
     apply exists.intro false,
     simp,
end
-- answer:
example : ∃ (P Q R : Prop), (P ∨ Q ∨ R) ∧ (¬P ∨ Q ∨ R) ∧
     (P ∨ ¬Q ∨ ¬R) ∧ (¬P ∨ ¬Q ∨ ¬R) :=
begin
     apply exists.intro true,
     apply exists.intro true,
     apply exists.intro false,
     split,
     exact or.inl true.intro,
     split,
     exact or.inr (or.inl true.intro),
     split,
     exact or.inl true.intro,
     exact or.inr (or.inr false.elim),

end
/-
9. Prove that exists a number such that
it is both even and a multiple of 3.
Use the supplied definitions of even and prime.
-/
def isEven' (n: ℕ ) := ∃ m :ℕ, n = m*2
def isMult3' (n: ℕ ) := ∃ m: ℕ, n = m*3

example : ∃ (n: ℕ ), isEven' n ∧ isMult3' n :=
begin
     apply exists.intro 6,
     split,
     unfold isEven',
     apply exists.intro 3,
     trivial,
     unfold isMult3',
     apply exists.intro 2,
     trivial,
end

def isEven(n: ℕ) := (∃(m: ℕ), m * 2 = n)

def isMult3(n: ℕ) := (∃(m: ℕ), m * 3 = n)

-- answer
example : ∃ (n: ℕ ), isEven n ∧ isMult3 n :=
begin
     have isEven6 : isEven 6 := ⟨3, rfl⟩,
     have isPrime6 : isMult3 6 := ⟨ 2, rfl ⟩ ,
     have both := and.intro isEven6 isPrime6,
     exact ⟨ 6, both ⟩ ,
end
/-
10a. Write the lemma that if there exists
 someone that you can fool all of the time,
 then there always exists someone you can fool.
 Use the supplied axioms, and make sure you use
 at least as many parentheses as needed. (It's
 okay to use more than you need.)
10b. Prove the above lemma.
-/

axioms Person Time: Type
axiom fool: Person → Time → Prop
example : (∃ p: Person, ∀ t: Time, fool p t) →
     (∀ t : Time, ∃ p : Person, fool p t):=
begin
     assume foolish,
     assume T,
     apply exists.elim foolish,
     assume P,
     assume func,
     have ifunc := func T,
     apply exists.intro P,
     assumption,
end

-- answers:
example : (∃(p : Person), ∀ (t : Time), fool p t) →
          (∀ (t : Time), ∃ (p : Person), fool p t) :=
begin
     assume idiot,
     assume t,
     apply exists.elim idiot,
     assume p,
     assume tpfunc,
     have idiotfunc := tpfunc t,
     exact ⟨ p, idiotfunc ⟩ ,
end


import .love04_functional_programming_demo

/- # LoVe Homework 4: Functional Programming

Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1 (2 points): Maps

1.1 (1 point). Complete the following definition. The `map_btree` function
should apply its argument `f` to all values of type `α` stored in the tree and
otherwise preserve the tree's structure. -/

def map_btree {α β : Type} (f : α → β) : btree α → btree β :=
sorry

/- 1.2 (1 point). Prove the following lemma about your `map_btree` function. -/

lemma map_btree_iden {α : Type} :
  ∀t : btree α, map_btree (λa, a) t = t :=
sorry

/- ## Question 2 (4 points): Tail-Recursive Factorials -/

/- Recall the definition of the factorial functions -/

#check fact

/- 2.1 (2 points). Experienced functional programmers tend to avoid definitions
such as the above, because they lead to a deep call stack. Tail recursion can be
used to avoid this. Results are accumulated in an argument and returned. This
can be optimized by compilers. For factorials, this gives the following
definition: -/

def accufact : ℕ → ℕ → ℕ
| a 0       := a
| a (n + 1) := accufact ((n + 1) * a) n

/- Prove that, given 1 as the accumulator `a`, `accufact` computes `fact`.

Hint: You will need to prove a generalized version of the statement as a
separate lemma or `have`, because the desired propery fixes `a` to 1, which
yields a too weak induction hypothesis. -/

lemma accufact_1_eq_fact (n : ℕ) :
  accufact 1 n = fact n :=
sorry

/- 2.2 (2 points). Prove the same property as above again, this time as a
"paper" proof. Follow the guidelines given in question 1.4 of the exercise. -/

-- enter your paper proof here

/- ## Question 3 (3 points): Gauss's Summation Formula -/

-- `sum_upto f n = f 0 + f 1 + ⋯ + f n`
def sum_upto (f : ℕ → ℕ) : ℕ → ℕ
| 0       := f 0
| (m + 1) := sum_upto m + f (m + 1)

/- 3.1 (2 point). Prove the following lemma, discovered by Carl Friedrich Gauss
as a pupil.

Hint: The `mul_add` and `add_mul` lemmas might be useful to reason about
multiplication. -/

#check mul_add
#check add_mul

lemma sum_upto_eq :
  ∀m : ℕ, 2 * sum_upto (λi, i) m = m * (m + 1) :=
sorry

/- 3.2 (1 point). Prove the following property of `sum_upto`. -/

lemma sum_upto_mul (f g : ℕ → ℕ) :
  ∀n : ℕ, sum_upto (λi, f i + g i) n = sum_upto f n + sum_upto g n :=
sorry

end LoVe


/-
CPDT Chapter 2, Introducing Inductive Types
-/
import mini_crush
universe variable u

export nat (succ)

def is_zero : ℕ → bool
| 0        := tt
| (succ _) := ff

def plus : ℕ → ℕ → ℕ
| 0 m         := m
| (succ n') m := succ (plus n' m)

theorem n_plus_0 (n : ℕ) : plus n 0 = n :=
by mini_crush

lemma plus_S (n1 n2 : nat) : plus n1 (succ n2) = succ (plus n1 n2) :=
by mini_crush

inductive nat_list : Type
| NNil  : nat_list
| NCons : nat → nat_list → nat_list

open nat_list

def nlength : nat_list → ℕ
| NNil          := 0
| (NCons _ ls') := succ (nlength ls')

def napp : nat_list → nat_list → nat_list
| NNil ls2 := ls2
| (NCons n ls1') ls2 := NCons n (napp ls1' ls2)

theorem nlength_napp (ls1 ls2 : nat_list) : nlength (napp ls1 ls2) = plus (nlength ls1) (nlength ls2) :=
by mini_crush

inductive nat_btree : Type
| NLeaf : nat_btree
| NNode : nat_btree → ℕ → nat_btree → nat_btree

open nat_btree

def nsize : nat_btree → ℕ
| NLeaf             := succ 0
| (NNode tr1 _ tr2) := plus (nsize tr1) (nsize tr2)

def nsplice : nat_btree → nat_btree → nat_btree
| NLeaf tr2               := NNode tr2 0 NLeaf
| (NNode tr1' n tr2') tr2 := NNode (nsplice tr1' tr2) n tr2'

@[simp] theorem plus_assoc (n1 n2 n3 : nat) : plus (plus n1 n2) n3 = plus n1 (plus n2 n3) :=
by mini_crush

theorem nsize_nsplice (tr1 tr2 : nat_btree) : nsize (nsplice tr1 tr2) = plus (nsize tr2) (nsize tr1) :=
by mini_crush

export list (nil cons)

def length {α : Type u} : list α → ℕ
| nil          := 0
| (cons _ ls') := succ (length ls')

def app {α : Type u} : list α → list α → list α
| nil ls2           := ls2
| (cons x ls1') ls2 := cons x (app ls1' ls2)

theorem length_app {α : Type u} (ls1 ls2 : list α) : length (app ls1 ls2) = plus (length ls1) (length ls2) :=
by mini_crush

inductive pformula : Type
| Truth : pformula
| Falsehood : pformula
| Conjunction : pformula → pformula → pformula.

open pformula

def pformula_denote : pformula → Prop
| Truth               := true
| Falsehood           := false
| (Conjunction f1 f2) := pformula_denote f1 ∧ pformula_denote f2

open pformula

inductive formula : Type
| Eq     : nat → nat → formula
| And    : formula → formula → formula
| Forall : (nat → formula) → formula

open formula

example : formula := Forall (λ x, Eq x x)

def formula_denote : formula → Prop
| (Eq n1 n2)  := n1 = n2
| (And f1 f2) := formula_denote f1 ∧ formula_denote f2
| (Forall f') := ∀ n : nat, formula_denote (f' n)

def swapper : formula → formula
| (Eq n1 n2)  := Eq n2 n1
| (And f1 f2) := And (swapper f2) (swapper f1)
| (Forall f') := Forall (λ n, swapper (f' n))

theorem swapper_preserves_truth (f) : formula_denote f → formula_denote (swapper f) :=
by mini_crush


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

! This file was ported from Lean 3 source module data.int.basic
! leanprover-community/mathlib commit 728baa2f54e6062c5879a3e397ac6bac323e506f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Nat.Basic
import Mathbin.Order.Monotone.Basic

/-!
# Basic instances on the integers

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

This file contains:
* instances on `ℤ`. The stronger one is `int.comm_ring`.
  See `data/int/defs/order` for `int.linear_ordered_comm_ring`.
* basic lemmas about the integers, but which do not use the ordered algebra hierarchy.
-/

open Nat

namespace Int

instance : Inhabited ℤ :=
  ⟨Int.zero⟩

instance : Nontrivial ℤ :=
  ⟨⟨0, 1, Int.zero_ne_one⟩⟩

instance : CommRing ℤ where
  add := Int.add
  add_assoc := Int.add_assoc
  zero := Int.zero
  zero_add := Int.zero_add
  add_zero := Int.add_zero
  neg := Int.neg
  add_left_neg := Int.add_left_neg
  add_comm := Int.add_comm
  mul := Int.mul
  mul_assoc := Int.mul_assoc
  one := Int.one
  one_mul := Int.one_mul
  mul_one := Int.mul_one
  sub := Int.sub
  left_distrib := Int.mul_add
  right_distrib := Int.add_mul
  mul_comm := Int.mul_comm
  natCast := Int.ofNat
  natCast_zero := rfl
  natCast_succ n := rfl
  intCast n := n
  intCast_ofNat n := rfl
  intCast_negSucc n := rfl
  zsmul := (· * ·)
  zsmul_zero' := Int.zero_mul
  zsmul_succ' n x := by
    rw [Nat.succ_eq_add_one, Nat.add_comm, of_nat_add, Int.add_mul, of_nat_one, Int.one_mul]
  zsmul_neg' n x := Int.neg_mul_eq_neg_mul_symm (n.succ : ℤ) x

/-! ### Extra instances to short-circuit type class resolution

These also prevent non-computable instances like `int.normed_comm_ring` being used to construct
these instances non-computably.
-/

-- instance : has_sub int            := by apply_instance -- This is in core
instance : AddCommMonoid ℤ := by infer_instance

instance : AddMonoid ℤ := by infer_instance

instance : Monoid ℤ := by infer_instance

instance : CommMonoid ℤ := by infer_instance

instance : CommSemigroup ℤ := by infer_instance

instance : Semigroup ℤ := by infer_instance

instance : AddCommGroup ℤ := by infer_instance

instance : AddGroup ℤ := by infer_instance

instance : AddCommSemigroup ℤ := by infer_instance

instance : AddSemigroup ℤ := by infer_instance

instance : CommSemiring ℤ := by infer_instance

instance : Semiring ℤ := by infer_instance

instance : Ring ℤ := by infer_instance

instance : Distrib ℤ := by infer_instance

end Int

namespace Int

#print Int.add_neg_one /-
@[simp]
theorem add_neg_one (i : ℤ) : i + -1 = i - 1 :=
  rfl
#align int.add_neg_one Int.add_neg_one
-/

#print Int.sign_coe_add_one /-
@[simp]
theorem sign_coe_add_one (n : ℕ) : Int.sign (n + 1) = 1 :=
  rfl
#align int.sign_coe_add_one Int.sign_coe_add_one
-/

#print Int.sign_negSucc /-
@[simp]
theorem sign_negSucc (n : ℕ) : Int.sign -[n+1] = -1 :=
  rfl
#align int.sign_neg_succ_of_nat Int.sign_negSucc
-/

#print Int.default_eq_zero /-
@[simp]
theorem default_eq_zero : default = (0 : ℤ) :=
  rfl
#align int.default_eq_zero Int.default_eq_zero
-/

unsafe instance : has_to_format ℤ :=
  ⟨fun z => toString z⟩

section

-- Note that here we are disabling the "safety" of reflected, to allow us to reuse `int.mk_numeral`.
-- The usual way to provide the required `reflected` instance would be via rewriting to prove that
-- the expression we use here is equivalent.
attribute [local semireducible] reflected

unsafe instance reflect : has_reflect ℤ :=
  int.mk_numeral q(ℤ) q((by infer_instance : Zero ℤ)) q((by infer_instance : One ℤ))
    q((by infer_instance : Add ℤ)) q((by infer_instance : Neg ℤ))
#align int.reflect int.reflect

end

attribute [simp] Int.bodd

#print Int.add_def /-
@[simp]
theorem add_def {a b : ℤ} : Int.add a b = a + b :=
  rfl
#align int.add_def Int.add_def
-/

#print Int.mul_def /-
@[simp]
theorem mul_def {a b : ℤ} : Int.mul a b = a * b :=
  rfl
#align int.mul_def Int.mul_def
-/

#print Int.negSucc_not_nonneg /-
@[simp]
theorem negSucc_not_nonneg (n : ℕ) : 0 ≤ -[n+1] ↔ False :=
  by
  simp only [not_le, iff_false_iff]
  exact Int.negSucc_lt_zero n
#align int.neg_succ_not_nonneg Int.negSucc_not_nonneg
-/

#print Int.negSucc_not_pos /-
@[simp]
theorem negSucc_not_pos (n : ℕ) : 0 < -[n+1] ↔ False := by simp only [not_lt, iff_false_iff]
#align int.neg_succ_not_pos Int.negSucc_not_pos
-/

#print Int.negSucc_sub_one /-
@[simp]
theorem negSucc_sub_one (n : ℕ) : -[n+1] - 1 = -[n + 1+1] :=
  rfl
#align int.neg_succ_sub_one Int.negSucc_sub_one
-/

#print Int.ofNat_mul_negSucc /-
@[simp]
theorem ofNat_mul_negSucc (m n : ℕ) : (m : ℤ) * -[n+1] = -(m * succ n) :=
  rfl
#align int.coe_nat_mul_neg_succ Int.ofNat_mul_negSucc
-/

#print Int.negSucc_mul_ofNat /-
@[simp]
theorem negSucc_mul_ofNat (m n : ℕ) : -[m+1] * n = -(succ m * n) :=
  rfl
#align int.neg_succ_mul_coe_nat Int.negSucc_mul_ofNat
-/

#print Int.negSucc_mul_negSucc /-
@[simp]
theorem negSucc_mul_negSucc (m n : ℕ) : -[m+1] * -[n+1] = succ m * succ n :=
  rfl
#align int.neg_succ_mul_neg_succ Int.negSucc_mul_negSucc
-/

/- warning: int.coe_nat_le clashes with int.coe_nat_le_coe_nat_iff -> Int.ofNat_le
Case conversion may be inaccurate. Consider using '#align int.coe_nat_le Int.ofNat_leₓ'. -/
#print Int.ofNat_le /-
theorem ofNat_le {m n : ℕ} : (↑m : ℤ) ≤ ↑n ↔ m ≤ n :=
  ofNat_le m n
#align int.coe_nat_le Int.ofNat_le
-/

/- warning: int.coe_nat_lt clashes with int.coe_nat_lt_coe_nat_iff -> Int.ofNat_lt
Case conversion may be inaccurate. Consider using '#align int.coe_nat_lt Int.ofNat_ltₓ'. -/
#print Int.ofNat_lt /-
theorem ofNat_lt {m n : ℕ} : (↑m : ℤ) < ↑n ↔ m < n :=
  ofNat_lt m n
#align int.coe_nat_lt Int.ofNat_lt
-/

#print Int.coe_nat_inj' /-
theorem coe_nat_inj' {m n : ℕ} : (↑m : ℤ) = ↑n ↔ m = n :=
  Int.ofNat_inj m n
#align int.coe_nat_inj' Int.coe_nat_inj'
-/

/- warning: int.coe_nat_strict_mono -> Int.coe_nat_strictMono is a dubious translation:
lean 3 declaration is
  StrictMono.{0, 0} Nat Int (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder)) (PartialOrder.toPreorder.{0} Int (LinearOrder.toPartialOrder.{0} Int Int.linearOrder)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))))
but is expected to have type
  StrictMono.{0, 0} Nat Int (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder)) (PartialOrder.toPreorder.{0} Int (LinearOrder.toPartialOrder.{0} Int Int.instLinearOrderInt)) (fun (x._@.Mathlib.Data.Int.Basic._hyg.840 : Nat) => Nat.cast.{0} Int instNatCastInt x._@.Mathlib.Data.Int.Basic._hyg.840)
Case conversion may be inaccurate. Consider using '#align int.coe_nat_strict_mono Int.coe_nat_strictMonoₓ'. -/
theorem coe_nat_strictMono : StrictMono (coe : ℕ → ℤ) := fun _ _ => Int.ofNat_lt.2
#align int.coe_nat_strict_mono Int.coe_nat_strictMono

#print Int.coe_nat_nonneg /-
theorem coe_nat_nonneg (n : ℕ) : 0 ≤ (n : ℤ) :=
  ofNat_le.2 (Nat.zero_le _)
#align int.coe_nat_nonneg Int.coe_nat_nonneg
-/

#print Int.negSucc_ne_zero /-
@[simp]
theorem negSucc_ne_zero (n : ℕ) : -[n+1] ≠ 0 := fun h => Int.noConfusion h
#align int.neg_of_nat_ne_zero Int.negSucc_ne_zero
-/

#print Int.zero_ne_negSucc /-
@[simp]
theorem zero_ne_negSucc (n : ℕ) : 0 ≠ -[n+1] := fun h => Int.noConfusion h
#align int.zero_ne_neg_of_nat Int.zero_ne_negSucc
-/

/-! ### succ and pred -/

#print Int.succ /-
/-- Immediate successor of an integer: `succ n = n + 1` -/
def succ (a : ℤ) :=
  a + 1
#align int.succ Int.succ
-/

#print Int.pred /-
/-- Immediate predecessor of an integer: `pred n = n - 1` -/
def pred (a : ℤ) :=
  a - 1
#align int.pred Int.pred
-/

#print Int.nat_succ_eq_int_succ /-
theorem nat_succ_eq_int_succ (n : ℕ) : (Nat.succ n : ℤ) = Int.succ n :=
  rfl
#align int.nat_succ_eq_int_succ Int.nat_succ_eq_int_succ
-/

#print Int.pred_succ /-
theorem pred_succ (a : ℤ) : pred (succ a) = a :=
  add_sub_cancel _ _
#align int.pred_succ Int.pred_succ
-/

#print Int.succ_pred /-
theorem succ_pred (a : ℤ) : succ (pred a) = a :=
  sub_add_cancel _ _
#align int.succ_pred Int.succ_pred
-/

#print Int.neg_succ /-
theorem neg_succ (a : ℤ) : -succ a = pred (-a) :=
  neg_add _ _
#align int.neg_succ Int.neg_succ
-/

#print Int.succ_neg_succ /-
theorem succ_neg_succ (a : ℤ) : succ (-succ a) = -a := by rw [neg_succ, succ_pred]
#align int.succ_neg_succ Int.succ_neg_succ
-/

#print Int.neg_pred /-
theorem neg_pred (a : ℤ) : -pred a = succ (-a) := by
  rw [neg_eq_iff_eq_neg.mp (neg_succ (-a)), neg_neg]
#align int.neg_pred Int.neg_pred
-/

#print Int.pred_neg_pred /-
theorem pred_neg_pred (a : ℤ) : pred (-pred a) = -a := by rw [neg_pred, pred_succ]
#align int.pred_neg_pred Int.pred_neg_pred
-/

#print Int.pred_nat_succ /-
theorem pred_nat_succ (n : ℕ) : pred (Nat.succ n) = n :=
  pred_succ n
#align int.pred_nat_succ Int.pred_nat_succ
-/

#print Int.neg_nat_succ /-
theorem neg_nat_succ (n : ℕ) : -(Nat.succ n : ℤ) = pred (-n) :=
  neg_succ n
#align int.neg_nat_succ Int.neg_nat_succ
-/

#print Int.succ_neg_nat_succ /-
theorem succ_neg_nat_succ (n : ℕ) : succ (-Nat.succ n) = -n :=
  succ_neg_succ n
#align int.succ_neg_nat_succ Int.succ_neg_nat_succ
-/

#print Int.add_one_le_iff /-
theorem add_one_le_iff {a b : ℤ} : a + 1 ≤ b ↔ a < b :=
  Iff.rfl
#align int.add_one_le_iff Int.add_one_le_iff
-/

#print Int.coe_pred_of_pos /-
@[norm_cast]
theorem coe_pred_of_pos {n : ℕ} (h : 0 < n) : ((n - 1 : ℕ) : ℤ) = (n : ℤ) - 1 :=
  by
  cases n
  cases h
  simp
#align int.coe_pred_of_pos Int.coe_pred_of_pos
-/

#print Int.induction_on /-
@[elab_as_elim]
protected theorem induction_on {p : ℤ → Prop} (i : ℤ) (hz : p 0) (hp : ∀ i : ℕ, p i → p (i + 1))
    (hn : ∀ i : ℕ, p (-i) → p (-i - 1)) : p i :=
  by
  induction i
  · induction i
    · exact hz
    · exact hp _ i_ih
  · have : ∀ n : ℕ, p (-n) := by
      intro n
      induction n
      · simp [hz]
      · convert hn _ n_ih using 1
        simp [sub_eq_neg_add]
    exact this (i + 1)
#align int.induction_on Int.induction_on
-/

/-! ### nat abs -/

variable {a b : ℤ} {n : ℕ}

attribute [simp] nat_abs_of_nat nat_abs_zero nat_abs_one

#print Int.natAbs_add_le /-
theorem natAbs_add_le (a b : ℤ) : natAbs (a + b) ≤ natAbs a + natAbs b :=
  by
  have : ∀ a b : ℕ, nat_abs (sub_nat_nat a (Nat.succ b)) ≤ Nat.succ (a + b) :=
    by
    refine' fun a b : ℕ =>
      sub_nat_nat_elim a b.succ (fun m n i => n = b.succ → nat_abs i ≤ (m + b).succ) _
        (fun i n e => _) rfl
    · rintro i n rfl
      rw [add_comm _ i, add_assoc]
      exact Nat.le_add_right i (b.succ + b).succ
    · apply succ_le_succ
      rw [← succ.inj e, ← add_assoc, add_comm]
      apply Nat.le_add_right
  cases a <;> cases' b with b b <;> simp [nat_abs, Nat.succ_add] <;> try rfl <;> [skip,
      rw [add_comm a b]] <;>
    apply this
#align int.nat_abs_add_le Int.natAbs_add_le
-/

#print Int.natAbs_sub_le /-
theorem natAbs_sub_le (a b : ℤ) : natAbs (a - b) ≤ natAbs a + natAbs b :=
  by
  rw [sub_eq_add_neg, ← Int.natAbs_neg b]
  apply nat_abs_add_le
#align int.nat_abs_sub_le Int.natAbs_sub_le
-/

#print Int.natAbs_negOfNat /-
theorem natAbs_negOfNat (n : ℕ) : natAbs (negOfNat n) = n := by cases n <;> rfl
#align int.nat_abs_neg_of_nat Int.natAbs_negOfNat
-/

#print Int.natAbs_mul /-
theorem natAbs_mul (a b : ℤ) : natAbs (a * b) = natAbs a * natAbs b := by
  cases a <;> cases b <;>
    simp only [← Int.mul_def, Int.mul, nat_abs_neg_of_nat, eq_self_iff_true, Int.natAbs]
#align int.nat_abs_mul Int.natAbs_mul
-/

#print Int.natAbs_mul_natAbs_eq /-
theorem natAbs_mul_natAbs_eq {a b : ℤ} {c : ℕ} (h : a * b = (c : ℤ)) : a.natAbs * b.natAbs = c := by
  rw [← nat_abs_mul, h, nat_abs_of_nat]
#align int.nat_abs_mul_nat_abs_eq Int.natAbs_mul_natAbs_eq
-/

#print Int.natAbs_mul_self' /-
theorem natAbs_mul_self' (a : ℤ) : (natAbs a * natAbs a : ℤ) = a * a := by
  rw [← Int.ofNat_mul, nat_abs_mul_self]
#align int.nat_abs_mul_self' Int.natAbs_mul_self'
-/

#print Int.negSucc_eq' /-
theorem negSucc_eq' (m : ℕ) : -[m+1] = -m - 1 := by simp [neg_succ_of_nat_eq, sub_eq_neg_add]
#align int.neg_succ_of_nat_eq' Int.negSucc_eq'
-/

#print Int.natAbs_ne_zero_of_ne_zero /-
theorem natAbs_ne_zero_of_ne_zero {z : ℤ} (hz : z ≠ 0) : z.natAbs ≠ 0 := fun h =>
  hz <| Int.eq_zero_of_natAbs_eq_zero h
#align int.nat_abs_ne_zero_of_ne_zero Int.natAbs_ne_zero_of_ne_zero
-/

#print Int.natAbs_eq_zero /-
@[simp]
theorem natAbs_eq_zero {a : ℤ} : a.natAbs = 0 ↔ a = 0 :=
  ⟨Int.eq_zero_of_natAbs_eq_zero, fun h => h.symm ▸ rfl⟩
#align int.nat_abs_eq_zero Int.natAbs_eq_zero
-/

#print Int.natAbs_ne_zero /-
theorem natAbs_ne_zero {a : ℤ} : a.natAbs ≠ 0 ↔ a ≠ 0 :=
  not_congr Int.natAbs_eq_zero
#align int.nat_abs_ne_zero Int.natAbs_ne_zero
-/

#print Int.natAbs_lt_natAbs_of_nonneg_of_lt /-
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) :
    a.natAbs < b.natAbs := by
  lift b to ℕ using le_trans w₁ (le_of_lt w₂)
  lift a to ℕ using w₁
  simpa [coe_nat_lt] using w₂
#align int.nat_abs_lt_nat_abs_of_nonneg_of_lt Int.natAbs_lt_natAbs_of_nonneg_of_lt
-/

#print Int.natAbs_eq_natAbs_iff /-
theorem natAbs_eq_natAbs_iff {a b : ℤ} : a.natAbs = b.natAbs ↔ a = b ∨ a = -b :=
  by
  constructor <;> intro h
  ·
    cases' Int.natAbs_eq a with h₁ h₁ <;> cases' Int.natAbs_eq b with h₂ h₂ <;> rw [h₁, h₂] <;>
      simp [h]
  · cases h <;> rw [h]
    rw [Int.natAbs_neg]
#align int.nat_abs_eq_nat_abs_iff Int.natAbs_eq_natAbs_iff
-/

#print Int.natAbs_eq_iff /-
theorem natAbs_eq_iff {a : ℤ} {n : ℕ} : a.natAbs = n ↔ a = n ∨ a = -n := by
  rw [← Int.natAbs_eq_natAbs_iff, Int.natAbs_ofNat]
#align int.nat_abs_eq_iff Int.natAbs_eq_iff
-/

/-! ### `/`  -/

#print Int.ofNat_div /-
@[simp]
theorem ofNat_div (m n : ℕ) : ofNat (m / n) = ofNat m / ofNat n :=
  rfl
#align int.of_nat_div Int.ofNat_div
-/

#print Int.coe_nat_div /-
@[simp, norm_cast]
theorem coe_nat_div (m n : ℕ) : ((m / n : ℕ) : ℤ) = m / n :=
  rfl
#align int.coe_nat_div Int.coe_nat_div
-/

#print Int.negSucc_ediv /-
theorem negSucc_ediv (m : ℕ) {b : ℤ} (H : 0 < b) : -[m+1] / b = -(m / b + 1) :=
  match b, eq_succ_of_zero_lt H with
  | _, ⟨n, rfl⟩ => rfl
#align int.neg_succ_of_nat_div Int.negSucc_ediv
-/

#print Int.zero_div /-
-- Will be generalized to Euclidean domains.
@[local simp]
protected theorem zero_div : ∀ b : ℤ, 0 / b = 0
  | (n : ℕ) => show ofNat _ = _ by simp
  | -[n+1] => show -ofNat _ = _ by simp
#align int.zero_div Int.zero_div
-/

#print Int.div_zero /-
-- Will be generalized to Euclidean domains.
@[local simp]
protected theorem div_zero : ∀ a : ℤ, a / 0 = 0
  | (n : ℕ) => show ofNat _ = _ by simp
  | -[n+1] => rfl
#align int.div_zero Int.div_zero
-/

@[simp]
protected theorem div_neg : ∀ a b : ℤ, a / -b = -(a / b)
  | (m : ℕ), 0 => show ofNat (m / 0) = -(m / 0 : ℕ) by rw [Nat.div_zero] <;> rfl
  | (m : ℕ), (n + 1 : ℕ) => rfl
  | (m : ℕ), -[n+1] => (neg_neg _).symm
  | -[m+1], 0 => rfl
  | -[m+1], (n + 1 : ℕ) => rfl
  | -[m+1], -[n+1] => rfl
#align int.div_neg Int.div_negₓ

#print Int.ediv_of_neg_of_pos /-
theorem ediv_of_neg_of_pos {a b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b = -((-a - 1) / b + 1) :=
  match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩ => by
    change (- -[m+1] : ℤ) with (m + 1 : ℤ) <;> rw [add_sub_cancel] <;> rfl
#align int.div_of_neg_of_pos Int.ediv_of_neg_of_pos
-/

protected theorem div_nonneg {a b : ℤ} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b :=
  match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩ => ofNat_zero_le _
#align int.div_nonneg Int.div_nonnegₓ

#print Int.ediv_neg' /-
theorem ediv_neg' {a b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
  match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩ => negSucc_lt_zero _
#align int.div_neg' Int.ediv_neg'
-/

@[simp]
protected theorem div_one : ∀ a : ℤ, a / 1 = a
  | (n : ℕ) => congr_arg ofNat (Nat.div_one _)
  | -[n+1] => congr_arg negSucc (Nat.div_one _)
#align int.div_one Int.div_oneₓ

theorem div_eq_zero_of_lt {a b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a / b = 0 :=
  match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (lt_of_le_of_lt H1 H2), H2 with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩, H2 => congr_arg ofNat <| Nat.div_eq_of_lt <| lt_of_ofNat_lt_ofNat H2
#align int.div_eq_zero_of_lt Int.div_eq_zero_of_ltₓ

/-! ### mod -/

#print Int.ofNat_mod_ofNat /-
theorem ofNat_mod_ofNat (m n : Nat) : (m % n : ℤ) = ofNat (m % n) :=
  rfl
#align int.of_nat_mod Int.ofNat_mod_ofNat
-/

#print Int.coe_nat_mod /-
@[simp, norm_cast]
theorem coe_nat_mod (m n : ℕ) : (↑(m % n) : ℤ) = ↑m % ↑n :=
  rfl
#align int.coe_nat_mod Int.coe_nat_mod
-/

#print Int.negSucc_emod /-
theorem negSucc_emod (m : ℕ) {b : ℤ} (bpos : 0 < b) : -[m+1] % b = b - 1 - m % b := by
  rw [sub_sub, add_comm] <;>
    exact
      match b, eq_succ_of_zero_lt bpos with
      | _, ⟨n, rfl⟩ => rfl
#align int.neg_succ_of_nat_mod Int.negSucc_emod
-/

@[simp]
theorem mod_neg : ∀ a b : ℤ, a % -b = a % b
  | (m : ℕ), n => @congr_arg ℕ ℤ _ _ (fun i => ↑(m % i)) (natAbs_neg _)
  | -[m+1], n => @congr_arg ℕ ℤ _ _ (fun i => subNatNat i (Nat.succ (m % i))) (natAbs_neg _)
#align int.mod_neg Int.mod_negₓ

-- Will be generalized to Euclidean domains.
@[local simp]
theorem zero_mod (b : ℤ) : 0 % b = 0 :=
  rfl
#align int.zero_mod Int.zero_modₓ

-- Will be generalized to Euclidean domains.
@[local simp]
theorem mod_zero : ∀ a : ℤ, a % 0 = a
  | (m : ℕ) => congr_arg ofNat <| Nat.mod_zero _
  | -[m+1] => congr_arg negSucc <| Nat.mod_zero _
#align int.mod_zero Int.mod_zeroₓ

-- Will be generalized to Euclidean domains.
@[local simp]
theorem mod_one : ∀ a : ℤ, a % 1 = 0
  | (m : ℕ) => congr_arg ofNat <| Nat.mod_one _
  | -[m+1] => show (1 - (m % 1).succ : ℤ) = 0 by rw [Nat.mod_one] <;> rfl
#align int.mod_one Int.mod_oneₓ

#print Int.emod_eq_of_lt /-
theorem emod_eq_of_lt {a b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a % b = a :=
  match a, b, eq_ofNat_of_zero_le H1, eq_ofNat_of_zero_le (le_trans H1 (le_of_lt H2)), H2 with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩, H2 => congr_arg ofNat <| Nat.mod_eq_of_lt (lt_of_ofNat_lt_ofNat H2)
#align int.mod_eq_of_lt Int.emod_eq_of_lt
-/

theorem mod_add_div_aux (m n : ℕ) : (n - (m % n + 1) - (n * (m / n) + n) : ℤ) = -[m+1] :=
  by
  rw [← sub_sub, neg_succ_of_nat_coe, sub_sub (n : ℤ), eq_comm, neg_eq_iff_eq_neg, neg_sub,
    sub_sub_self, add_right_comm]
  exact @congr_arg ℕ ℤ _ _ (fun i => (i + 1 : ℤ)) (Nat.mod_add_div _ _).symm
#align int.mod_add_div_aux Int.mod_add_div_aux

#print Int.emod_add_ediv /-
theorem emod_add_ediv : ∀ a b : ℤ, a % b + b * (a / b) = a
  | (m : ℕ), (n : ℕ) => congr_arg ofNat (Nat.mod_add_div _ _)
  | (m : ℕ), -[n+1] =>
    show (_ + -(n + 1) * -(m / (n + 1) : ℕ) : ℤ) = _ by
      rw [neg_mul_neg] <;> exact congr_arg of_nat (Nat.mod_add_div _ _)
  | -[m+1], 0 => by rw [mod_zero, Int.div_zero] <;> rfl
  | -[m+1], (n + 1 : ℕ) => mod_add_div_aux m n.succ
  | -[m+1], -[n+1] => mod_add_div_aux m n.succ
#align int.mod_add_div Int.emod_add_ediv
-/

theorem div_add_mod (a b : ℤ) : b * (a / b) + a % b = a :=
  (add_comm _ _).trans (emod_add_ediv _ _)
#align int.div_add_mod Int.div_add_modₓ

theorem mod_add_div' (m k : ℤ) : m % k + m / k * k = m :=
  by
  rw [mul_comm]
  exact mod_add_div _ _
#align int.mod_add_div' Int.mod_add_div'ₓ

theorem div_add_mod' (m k : ℤ) : m / k * k + m % k = m :=
  by
  rw [mul_comm]
  exact div_add_mod _ _
#align int.div_add_mod' Int.div_add_mod'ₓ

theorem mod_def (a b : ℤ) : a % b = a - b * (a / b) :=
  eq_sub_of_add_eq (emod_add_ediv _ _)
#align int.mod_def Int.mod_defₓ

/-! ### properties of `/` and `%` -/

#print Int.mul_ediv_mul_of_pos /-
@[simp]
theorem mul_ediv_mul_of_pos {a : ℤ} (b c : ℤ) (H : 0 < a) : a * b / (a * c) = b / c :=
  suffices ∀ (m k : ℕ) (b : ℤ), (m.succ * b / (m.succ * k) : ℤ) = b / k from
    match a, eq_succ_of_zero_lt H, c, eq_nat_or_neg c with
    | _, ⟨m, rfl⟩, _, ⟨k, Or.inl rfl⟩ => this _ _ _
    | _, ⟨m, rfl⟩, _, ⟨k, Or.inr rfl⟩ => by
      rw [mul_neg, Int.div_neg, Int.div_neg] <;> apply congr_arg Neg.neg <;> apply this
  fun m k b =>
  match b, k with
  | (n : ℕ), k => congr_arg ofNat (Nat.mul_div_mul _ _ m.succ_pos)
  | -[n+1], 0 => by rw [Int.ofNat_zero, MulZeroClass.mul_zero, Int.div_zero, Int.div_zero]
  | -[n+1], k + 1 =>
    congr_arg negSucc <|
      show (m.succ * n + m) / (m.succ * k.succ) = n / k.succ
        by
        apply Nat.div_eq_of_lt_le
        · refine' le_trans _ (Nat.le_add_right _ _)
          rw [← Nat.mul_div_mul _ _ m.succ_pos]
          apply Nat.div_mul_le_self
        · change m.succ * n.succ ≤ _
          rw [mul_left_comm]
          apply Nat.mul_le_mul_left
          apply (Nat.div_lt_iff_lt_mul k.succ_pos).1
          apply Nat.lt_succ_self
#align int.mul_div_mul_of_pos Int.mul_ediv_mul_of_pos
-/

/- warning: int.mul_div_mul_of_pos_left -> Int.mul_ediv_mul_of_pos_left is a dubious translation:
lean 3 declaration is
  forall (a : Int) {b : Int}, (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) b) -> (forall (c : Int), Eq.{1} Int (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.hasDiv) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a b) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) c b)) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.hasDiv) a c))
but is expected to have type
  forall (a : Int) {b : Int} (H : Int), (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) b) -> (Eq.{1} Int (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.instDivInt_1) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a b) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) H b)) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.instDivInt_1) a H))
Case conversion may be inaccurate. Consider using '#align int.mul_div_mul_of_pos_left Int.mul_ediv_mul_of_pos_leftₓ'. -/
@[simp]
theorem mul_ediv_mul_of_pos_left (a : ℤ) {b : ℤ} (H : 0 < b) (c : ℤ) : a * b / (c * b) = a / c := by
  rw [mul_comm, mul_comm c, mul_div_mul_of_pos _ _ H]
#align int.mul_div_mul_of_pos_left Int.mul_ediv_mul_of_pos_left

/- warning: int.mul_mod_mul_of_pos -> Int.mul_emod_mul_of_pos is a dubious translation:
lean 3 declaration is
  forall {a : Int}, (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) a) -> (forall (b : Int) (c : Int), Eq.{1} Int (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.hasMod) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a b) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a c)) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.hasMod) b c)))
but is expected to have type
  forall {a : Int} (H : Int) (b : Int), (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) a) -> (Eq.{1} Int (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.instModInt_1) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a H) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a b)) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.instModInt_1) H b)))
Case conversion may be inaccurate. Consider using '#align int.mul_mod_mul_of_pos Int.mul_emod_mul_of_posₓ'. -/
@[simp]
theorem mul_emod_mul_of_pos {a : ℤ} (H : 0 < a) (b c : ℤ) : a * b % (a * c) = a * (b % c) := by
  rw [mod_def, mod_def, mul_div_mul_of_pos _ _ H, mul_sub_left_distrib, mul_assoc]
#align int.mul_mod_mul_of_pos Int.mul_emod_mul_of_pos

theorem mul_div_cancel_of_mod_eq_zero {a b : ℤ} (H : a % b = 0) : b * (a / b) = a := by
  have := mod_add_div a b <;> rwa [H, zero_add] at this
#align int.mul_div_cancel_of_mod_eq_zero Int.mul_div_cancel_of_mod_eq_zeroₓ

theorem div_mul_cancel_of_mod_eq_zero {a b : ℤ} (H : a % b = 0) : a / b * b = a := by
  rw [mul_comm, mul_div_cancel_of_mod_eq_zero H]
#align int.div_mul_cancel_of_mod_eq_zero Int.div_mul_cancel_of_mod_eq_zeroₓ

/- warning: int.nat_abs_sign -> Int.natAbs_sign is a dubious translation:
lean 3 declaration is
  forall (z : Int), Eq.{1} Nat (Int.natAbs (Int.sign z)) (ite.{1} Nat (Eq.{1} Int z (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) (Int.decidableEq z (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall (z : Int), Eq.{1} Nat (Int.natAbs (Int.sign z)) (ite.{1} Nat (Eq.{1} Int z (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) (Int.instDecidableEqInt z (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align int.nat_abs_sign Int.natAbs_signₓ'. -/
theorem natAbs_sign (z : ℤ) : z.sign.natAbs = if z = 0 then 0 else 1 := by
  rcases z with ((_ | _) | _) <;> rfl
#align int.nat_abs_sign Int.natAbs_sign

#print Int.natAbs_sign_of_nonzero /-
theorem natAbs_sign_of_nonzero {z : ℤ} (hz : z ≠ 0) : z.sign.natAbs = 1 := by
  rw [Int.natAbs_sign, if_neg hz]
#align int.nat_abs_sign_of_nonzero Int.natAbs_sign_of_nonzero
-/

#print Int.sign_coe_nat_of_nonzero /-
theorem sign_coe_nat_of_nonzero {n : ℕ} (hn : n ≠ 0) : Int.sign n = 1 :=
  by
  obtain ⟨n, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hn
  exact Int.sign_of_succ n
#align int.sign_coe_nat_of_nonzero Int.sign_coe_nat_of_nonzero
-/

#print Int.sign_neg /-
@[simp]
theorem sign_neg (z : ℤ) : Int.sign (-z) = -Int.sign z := by rcases z with ((_ | _) | _) <;> rfl
#align int.sign_neg Int.sign_neg
-/

/- warning: int.div_sign -> Int.div_sign is a dubious translation:
lean 3 declaration is
  forall (a : Int) (b : Int), Eq.{1} Int (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.hasDiv) a (Int.sign b)) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a (Int.sign b))
but is expected to have type
  forall (a : Int) (b : Int), Eq.{1} Int (Int.div a (Int.sign b)) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a (Int.sign b))
Case conversion may be inaccurate. Consider using '#align int.div_sign Int.div_signₓ'. -/
theorem div_sign : ∀ a b, a / sign b = a * sign b
  | a, (n + 1 : ℕ) => by unfold SignType.sign <;> simp
  | a, 0 => by simp [SignType.sign]
  | a, -[n+1] => by simp [SignType.sign]
#align int.div_sign Int.div_sign

#print Int.sign_mul /-
@[simp]
theorem sign_mul : ∀ a b, sign (a * b) = sign a * sign b
  | a, 0 => by simp
  | 0, b => by simp
  | (m + 1 : ℕ), (n + 1 : ℕ) => rfl
  | (m + 1 : ℕ), -[n+1] => rfl
  | -[m+1], (n + 1 : ℕ) => rfl
  | -[m+1], -[n+1] => rfl
#align int.sign_mul Int.sign_mul
-/

#print Int.mul_sign /-
theorem mul_sign : ∀ i : ℤ, i * sign i = natAbs i
  | (n + 1 : ℕ) => mul_one _
  | 0 => MulZeroClass.mul_zero _
  | -[n+1] => mul_neg_one _
#align int.mul_sign Int.mul_sign
-/

#print Int.ofNat_add_negSucc_of_lt /-
theorem ofNat_add_negSucc_of_lt {m n : ℕ} (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
  by
  change sub_nat_nat _ _ = _
  have h' : n.succ - m = (n - m).succ
  apply succ_sub
  apply le_of_lt_succ h
  simp [*, sub_nat_nat]
#align int.of_nat_add_neg_succ_of_nat_of_lt Int.ofNat_add_negSucc_of_lt
-/

/- warning: int.neg_add_neg clashes with int.neg_succ_of_nat_add_neg_succ_of_nat -> Int.negSucc_add_negSucc
Case conversion may be inaccurate. Consider using '#align int.neg_add_neg Int.negSucc_add_negSuccₓ'. -/
#print Int.negSucc_add_negSucc /-
@[simp]
theorem negSucc_add_negSucc (m n : ℕ) : -[m+1] + -[n+1] = -[Nat.succ (m + n)+1] :=
  rfl
#align int.neg_add_neg Int.negSucc_add_negSucc
-/

/-! ### to_nat -/

/- warning: int.to_nat_eq_max -> Int.toNat_eq_max is a dubious translation:
lean 3 declaration is
  forall (a : Int), Eq.{1} Int ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Int.toNat a)) (LinearOrder.max.{0} Int Int.linearOrder a (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))))
but is expected to have type
  forall (a : Int), Eq.{1} Int (Nat.cast.{0} Int instNatCastInt (Int.toNat a)) (Max.max.{0} Int Int.instMaxInt a (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)))
Case conversion may be inaccurate. Consider using '#align int.to_nat_eq_max Int.toNat_eq_maxₓ'. -/
theorem toNat_eq_max : ∀ a : ℤ, (toNat a : ℤ) = max a 0
  | (n : ℕ) => (max_eq_left (ofNat_zero_le n)).symm
  | -[n+1] => (max_eq_right (le_of_lt (negSucc_lt_zero n))).symm
#align int.to_nat_eq_max Int.toNat_eq_max

#print Int.toNat_zero /-
@[simp]
theorem toNat_zero : (0 : ℤ).toNat = 0 :=
  rfl
#align int.to_nat_zero Int.toNat_zero
-/

#print Int.toNat_one /-
@[simp]
theorem toNat_one : (1 : ℤ).toNat = 1 :=
  rfl
#align int.to_nat_one Int.toNat_one
-/

#print Int.toNat_of_nonneg /-
@[simp]
theorem toNat_of_nonneg {a : ℤ} (h : 0 ≤ a) : (toNat a : ℤ) = a := by
  rw [to_nat_eq_max, max_eq_left h]
#align int.to_nat_of_nonneg Int.toNat_of_nonneg
-/

#print Int.toNat_coe_nat /-
@[simp]
theorem toNat_coe_nat (n : ℕ) : toNat ↑n = n :=
  rfl
#align int.to_nat_coe_nat Int.toNat_coe_nat
-/

#print Int.toNat_coe_nat_add_one /-
@[simp]
theorem toNat_coe_nat_add_one {n : ℕ} : ((n : ℤ) + 1).toNat = n + 1 :=
  rfl
#align int.to_nat_coe_nat_add_one Int.toNat_coe_nat_add_one
-/

#print Int.self_le_toNat /-
theorem self_le_toNat (a : ℤ) : a ≤ toNat a := by rw [to_nat_eq_max] <;> apply le_max_left
#align int.le_to_nat Int.self_le_toNat
-/

#print Int.le_toNat /-
@[simp]
theorem le_toNat {n : ℕ} {z : ℤ} (h : 0 ≤ z) : n ≤ z.toNat ↔ (n : ℤ) ≤ z := by
  rw [← Int.ofNat_le, Int.toNat_of_nonneg h]
#align int.le_to_nat_iff Int.le_toNat
-/

#print Int.toNat_add /-
theorem toNat_add {a b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a + b).toNat = a.toNat + b.toNat :=
  by
  lift a to ℕ using ha
  lift b to ℕ using hb
  norm_cast
#align int.to_nat_add Int.toNat_add
-/

#print Int.toNat_add_nat /-
theorem toNat_add_nat {a : ℤ} (ha : 0 ≤ a) (n : ℕ) : (a + n).toNat = a.toNat + n :=
  by
  lift a to ℕ using ha
  norm_cast
#align int.to_nat_add_nat Int.toNat_add_nat
-/

#print Int.pred_toNat /-
@[simp]
theorem pred_toNat : ∀ i : ℤ, (i - 1).toNat = i.toNat - 1
  | (0 : ℕ) => rfl
  | (n + 1 : ℕ) => by simp
  | -[n+1] => rfl
#align int.pred_to_nat Int.pred_toNat
-/

#print Int.toNat_sub_toNat_neg /-
@[simp]
theorem toNat_sub_toNat_neg : ∀ n : ℤ, ↑n.toNat - ↑(-n).toNat = n
  | (0 : ℕ) => rfl
  | (n + 1 : ℕ) => show ↑(n + 1) - (0 : ℤ) = n + 1 from sub_zero _
  | -[n+1] => show 0 - (n + 1 : ℤ) = _ from zero_sub _
#align int.to_nat_sub_to_nat_neg Int.toNat_sub_toNat_neg
-/

#print Int.toNat_add_toNat_neg_eq_natAbs /-
@[simp]
theorem toNat_add_toNat_neg_eq_natAbs : ∀ n : ℤ, n.toNat + (-n).toNat = n.natAbs
  | (0 : ℕ) => rfl
  | (n + 1 : ℕ) => show n + 1 + 0 = n + 1 from add_zero _
  | -[n+1] => show 0 + (n + 1) = n + 1 from zero_add _
#align int.to_nat_add_to_nat_neg_eq_nat_abs Int.toNat_add_toNat_neg_eq_natAbs
-/

#print Int.toNat' /-
/-- If `n : ℕ`, then `int.to_nat' n = some n`, if `n : ℤ` is negative, then `int.to_nat' n = none`.
-/
def toNat' : ℤ → Option ℕ
  | (n : ℕ) => some n
  | -[n+1] => none
#align int.to_nat' Int.toNat'
-/

#print Int.mem_toNat' /-
theorem mem_toNat' : ∀ (a : ℤ) (n : ℕ), n ∈ toNat' a ↔ a = n
  | (m : ℕ), n => Option.some_inj.trans coe_nat_inj'.symm
  | -[m+1], n => by constructor <;> intro h <;> cases h
#align int.mem_to_nat' Int.mem_toNat'
-/

#print Int.toNat_neg_nat /-
@[simp]
theorem toNat_neg_nat : ∀ n : ℕ, (-(n : ℤ)).toNat = 0
  | 0 => rfl
  | n + 1 => rfl
#align int.to_nat_neg_nat Int.toNat_neg_nat
-/

end Int


/-
Copyright (c) 2021 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anatole Dedecker
-/
import topology.continuous_on

/-!
# Left and right continuity

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

In this file we prove a few lemmas about left and right continuous functions:

* `continuous_within_at_Ioi_iff_Ici`: two definitions of right continuity
  (with `(a, ∞)` and with `[a, ∞)`) are equivalent;
* `continuous_within_at_Iio_iff_Iic`: two definitions of left continuity
  (with `(-∞, a)` and with `(-∞, a]`) are equivalent;
* `continuous_at_iff_continuous_left_right`, `continuous_at_iff_continuous_left'_right'` :
  a function is continuous at `a` if and only if it is left and right continuous at `a`.

## Tags

left continuous, right continuous
-/

open set filter
open_locale topology

section partial_order

variables {α β : Type*} [topological_space α] [partial_order α] [topological_space β]

lemma continuous_within_at_Ioi_iff_Ici {a : α} {f : α → β} :
  continuous_within_at f (Ioi a) a ↔ continuous_within_at f (Ici a) a :=
by simp only [← Ici_diff_left, continuous_within_at_diff_self]

lemma continuous_within_at_Iio_iff_Iic {a : α} {f : α → β} :
  continuous_within_at f (Iio a) a ↔ continuous_within_at f (Iic a) a :=
@continuous_within_at_Ioi_iff_Ici αᵒᵈ _ ‹topological_space α› _ _ _ f

lemma nhds_left'_le_nhds_ne (a : α) :
  𝓝[<] a ≤ 𝓝[≠] a :=
nhds_within_mono a (λ y hy, ne_of_lt hy)

lemma nhds_right'_le_nhds_ne (a : α) :
  𝓝[>] a ≤ 𝓝[≠] a :=
nhds_within_mono a (λ y hy, ne_of_gt hy)

end partial_order

section topological_space

variables {α β : Type*} [topological_space α] [linear_order α] [topological_space β]

lemma nhds_left_sup_nhds_right (a : α) :
  𝓝[≤] a ⊔ 𝓝[≥] a = 𝓝 a :=
by rw [← nhds_within_union, Iic_union_Ici, nhds_within_univ]

lemma nhds_left'_sup_nhds_right (a : α) :
  𝓝[<] a ⊔ 𝓝[≥] a = 𝓝 a :=
by rw [← nhds_within_union, Iio_union_Ici, nhds_within_univ]

lemma nhds_left_sup_nhds_right' (a : α) :
  𝓝[≤] a ⊔ 𝓝[>] a = 𝓝 a :=
by rw [← nhds_within_union, Iic_union_Ioi, nhds_within_univ]

lemma nhds_left'_sup_nhds_right' (a : α) :
  𝓝[<] a ⊔ 𝓝[>] a = 𝓝[≠] a :=
by rw [← nhds_within_union, Iio_union_Ioi]

lemma continuous_at_iff_continuous_left_right {a : α} {f : α → β} :
  continuous_at f a ↔ continuous_within_at f (Iic a) a ∧ continuous_within_at f (Ici a) a :=
by simp only [continuous_within_at, continuous_at, ← tendsto_sup, nhds_left_sup_nhds_right]

lemma continuous_at_iff_continuous_left'_right' {a : α} {f : α → β} :
  continuous_at f a ↔ continuous_within_at f (Iio a) a ∧ continuous_within_at f (Ioi a) a :=
by rw [continuous_within_at_Ioi_iff_Ici, continuous_within_at_Iio_iff_Iic,
  continuous_at_iff_continuous_left_right]

end topological_space


/-
Copyright (c) 2023 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic

/-

# `finset X` is a lattice

Recall that a lattice is just a partially ordered set where every pair {a,b} of elements has
an inf `a ⊓ b` and a sup `a ⊔ b`. The type of finite subsets of `X`, ordered by inclusion,
has this property (because the union or intersection of two finite sets is finite).
This lattice structure is inbuilt in Lean.

-/

-- Let X be a type
variables (X : Type)

-- Assume the law of the excluded middle
open_locale classical

-- Don't worry about whether functions are computable
noncomputable theory

-- Then, finally, the type of finite subsets of X has a lattice structure
example : lattice (finset X) := infer_instance -- the square bracket system knows this

example (a b : finset X) : finset X := a ⊔ b -- sups (and infs) make sense

-- The lattice also has a `⊥`, the smallest finite subset of `X`, namely the empty set.

example : finset X := ⊥

-- But for general `X` it does not have a `⊤`, because if `X` is infinite then it doesn't
-- have a largest finite subset

-- example : finset X := ⊤ -- error

-- If `Y` is another type, then you can push finsets forward along maps from X to Y
variables (Y : Type) (f : X → Y)

example (S : finset X) : finset Y := finset.image f S

-- See if you can prove these. You'll have to figure out the basic API
-- for `finset.image`.

example (S : finset X) (y : Y) : y ∈ S.image f ↔ ∃ x ∈ S, f x = y :=
begin
  apply finset.mem_image,
end

example (S : finset X) (x : X) (hx : x ∈ S) : f x ∈ S.image f :=
begin
  apply finset.mem_image_of_mem,
  exact hx,
end