/-
Copyright (c) 2021 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa
-/
import algebra.group_power.basic
import algebra.hom.iterate
import algebra.regular.basic

/-!
# Regular elements

## Implementation details

Group powers and other definitions import a lot of the algebra hierarchy.
Lemmas about them are kept separate to be able to provide `is_regular` early in the
algebra hierarchy.

-/

variables {R : Type*} {a b : R}

section monoid

variable [monoid R]

/--  Any power of a left-regular element is left-regular. -/
lemma is_left_regular.pow (n : ℕ) (rla : is_left_regular a) : is_left_regular (a ^ n) :=
by simp only [is_left_regular, ← mul_left_iterate, rla.iterate n]

/--  Any power of a right-regular element is right-regular. -/
lemma is_right_regular.pow (n : ℕ) (rra : is_right_regular a) : is_right_regular (a ^ n) :=
by { rw [is_right_regular, ← mul_right_iterate], exact rra.iterate n }

/--  Any power of a regular element is regular. -/
lemma is_regular.pow (n : ℕ) (ra : is_regular a) : is_regular (a ^ n) :=
⟨is_left_regular.pow n ra.left, is_right_regular.pow n ra.right⟩

/--  An element `a` is left-regular if and only if a positive power of `a` is left-regular. -/
lemma is_left_regular.pow_iff {n : ℕ} (n0 : 0 < n) :
  is_left_regular (a ^ n) ↔ is_left_regular a :=
begin
  refine ⟨_, is_left_regular.pow n⟩,
  rw [← nat.succ_pred_eq_of_pos n0, pow_succ'],
  exact is_left_regular.of_mul,
end

/--  An element `a` is right-regular if and only if a positive power of `a` is right-regular. -/
lemma is_right_regular.pow_iff {n : ℕ} (n0 : 0 < n) :
  is_right_regular (a ^ n) ↔ is_right_regular a :=
begin
  refine ⟨_, is_right_regular.pow n⟩,
  rw [← nat.succ_pred_eq_of_pos n0, pow_succ],
  exact is_right_regular.of_mul,
end

/--  An element `a` is regular if and only if a positive power of `a` is regular. -/
lemma is_regular.pow_iff {n : ℕ} (n0 : 0 < n) :
  is_regular (a ^ n) ↔ is_regular a :=
⟨λ h, ⟨(is_left_regular.pow_iff n0).mp h.left, (is_right_regular.pow_iff n0).mp h.right⟩,
  λ h, ⟨is_left_regular.pow n h.left, is_right_regular.pow n h.right⟩⟩

end monoid


/- Comments -/

import Lean.Parser.Term

#check Prop

variable (A B C D E : Prop)

#check D

/- truth and falsity -/

#check True
#check False

/- Connectives -/

/- Negation -/

#check ¬ A
/- type \neg -/
#check Not
#reduce ¬ A

/- Implication -/

def Implies (P Q : Prop) : Prop := P → Q
#check A → B
#check Implies A B
/- \to  -/
#check A -> B

/- And -/

#check And A B
#check A /\ B
#check A ∧ B
/- \and -/

/- Or -/

#check Or A B
#check A \/ B
#check A ∨ B
/- \or -/

/- Bi-implication -/

#check Iff A B
#check A ↔ B
#check Iff
/- \iff -/

/- Examples -/

#check A ∧ B → C
#check ¬ A ∨ C ↔ A → B
#check (A → B) → False

#check Classical.em A
#check em A

/- What is a proof? -/

variable (h : A)

#check h

example (h : A) : A := h

theorem identity (h : A) : A := h

variable {G H : Prop}

theorem superProof (h : G) : H := sorry

#check superProof

example : A → B := fun (h : A) => (superProof h)

#check And

#check Or.elim
#check Or.inl
example (h : A ∨ B) : B ∨ A := Or.elim h (fun (a:A) => Or.inr a) _

#reduce Not A

#check Iff.intro

open Lean Parser Term
macro "assume " var:funBinder ", " exp:term : term => `(fun $var => $exp)

#check assume (a:A)

example : (A → A) :=
  assume (a : A), a
  show A from a

example (h : A → B ∧ C) : A → C := fun (a : A) => And.right (h a)

example : A ∧ B → A ∨ B := fun (p : A ∧ B) => Or.inl (And.left p)

example  (h : A ∧ B) : (B ∧ B) := And.intro (And.right h) (And.right h)

example : (A → B) → (¬ B → ¬ A) :=
  fun (f: A → B) => fun (h : ¬ B) => fun (a : A) => h (f a)

#check @Classical.byContradiction A

example : ¬ ¬ A → A := fun (h : ¬ ¬ A) => Classical.byContradiction (fun (n : ¬ A) => h n)

#check Classical.em

#check False.elim

example (h : ¬ B → ¬ A) : (A → B) :=
  Or.elim (Classical.em B) (fun (b : B) (_ : A) => b) (fun (n : ¬ B) (a : A) => False.elim (h n a))

example : False := by sorry

example (A B C : Prop) : (A → B ∧ C) → (A → B) ∧ (A → C) := by
  intro (h : A → B ∧ C)
  have (f : A → B) := fun (a:A) => And.left (h a)
  have (g : A → C) := fun (a:A) => And.right (h a)
  exact And.intro f g

example (a : A) (b : B) : A ∧ B := by
  apply And.intro
  case left => exact a
  case right => exact b

example (h : A ∨ B) : B ∨ A := by
  cases h with
  | inl a => exact Or.inr a
  | inr b => exact Or.inl b

example (p q : Prop) (h : p ∨ q): q ∨ p := by
  cases h with
  | inr hq => apply Or.inl; exact hq
  | inl hp => apply Or.inr; exact hp


/-
Copyright (c) 2020 Bryan Gin-ge Chen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Lacker, Bryan Gin-ge Chen
-/
import data.nat.prime
import data.int.basic
/-!
# Lemmas about nat.prime using `int`s
-/

open nat

namespace int

lemma not_prime_of_int_mul {a b : ℤ} {c : ℕ}
  (ha : 1 < a.nat_abs) (hb : 1 < b.nat_abs) (hc : a*b = (c : ℤ)) : ¬ nat.prime c :=
not_prime_mul' (nat_abs_mul_nat_abs_eq hc) ha hb

end int


/-
Copyright (c) 2020 Fox Thomson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Fox Thomson
-/
import tactic.rcases
import computability.language

/-!
# Regular Expressions

This file contains the formal definition for regular expressions and basic lemmas. Note these are
regular expressions in terms of formal language theory. Note this is different to regex's used in
computer science such as the POSIX standard.

TODO
* Show that this regular expressions and DFA/NFA's are equivalent.
* `attribute [pattern] has_mul.mul` has been added into this file, it could be moved.
-/

universe u

variables {α : Type u} [dec : decidable_eq α]

/--
This is the definition of regular expressions. The names used here is to mirror the definition
of a Kleene algebra (https://en.wikipedia.org/wiki/Kleene_algebra).
* `0` (`zero`) matches nothing
* `1` (`epsilon`) matches only the empty string
* `char a` matches only the string 'a'
* `star P` matches any finite concatenation of strings which match `P`
* `P + Q` (`plus P Q`) matches anything which match `P` or `Q`
* `P * Q` (`comp P Q`) matches `x ++ y` if `x` matches `P` and `y` matches `Q`
-/
inductive regular_expression (α : Type u) : Type u
| zero : regular_expression
| epsilon : regular_expression
| char : α → regular_expression
| plus : regular_expression → regular_expression → regular_expression
| comp : regular_expression → regular_expression → regular_expression
| star : regular_expression → regular_expression

namespace regular_expression

instance : inhabited (regular_expression α) := ⟨zero⟩

instance : has_add (regular_expression α) := ⟨plus⟩
instance : has_mul (regular_expression α) := ⟨comp⟩
instance : has_one (regular_expression α) := ⟨epsilon⟩
instance : has_zero (regular_expression α) := ⟨zero⟩

attribute [pattern] has_mul.mul

@[simp] lemma zero_def : (zero : regular_expression α) = 0 := rfl
@[simp] lemma one_def : (epsilon : regular_expression α) = 1 := rfl

@[simp] lemma plus_def (P Q : regular_expression α) : plus P Q = P + Q := rfl
@[simp] lemma comp_def (P Q : regular_expression α) : comp P Q = P * Q := rfl

/-- `matches P` provides a language which contains all strings that `P` matches -/
def matches : regular_expression α → language α
| 0 := 0
| 1 := 1
| (char a) := {[a]}
| (P + Q) := P.matches + Q.matches
| (P * Q) := P.matches * Q.matches
| (star P) := P.matches.star

@[simp] lemma matches_zero_def : (0 : regular_expression α).matches = 0 := rfl
@[simp] lemma matches_epsilon_def : (1 : regular_expression α).matches = 1 := rfl
@[simp] lemma matches_add_def (P Q : regular_expression α) :
  (P + Q).matches = P.matches + Q.matches := rfl
@[simp] lemma matches_mul_def (P Q : regular_expression α) :
  (P * Q).matches = P.matches * Q.matches := rfl
@[simp] lemma matches_star_def (P : regular_expression α) : P.star.matches = P.matches.star := rfl

/-- `match_epsilon P` is true if and only if `P` matches the empty string -/
def match_epsilon : regular_expression α → bool
| 0 := ff
| 1 := tt
| (char _) := ff
| (P + Q) := P.match_epsilon || Q.match_epsilon
| (P * Q) := P.match_epsilon && Q.match_epsilon
| (star P) := tt

include dec

/-- `P.deriv a` matches `x` if `P` matches `a :: x`, the Brzozowski derivative of `P` with respect
  to `a` -/
def deriv : regular_expression α → α → regular_expression α
| 0 _ := 0
| 1 _ := 0
| (char a₁) a₂ := if a₁ = a₂ then 1 else 0
| (P + Q) a := deriv P a + deriv Q a
| (P * Q) a :=
  if P.match_epsilon then
    deriv P a * Q + deriv Q a
  else
    deriv P a * Q
| (star P) a := deriv P a * star P

/-- `P.rmatch x` is true if and only if `P` matches `x`. This is a computable definition equivalent
  to `matches`. -/
def rmatch : regular_expression α → list α → bool
| P [] := match_epsilon P
| P (a::as) := rmatch (P.deriv a) as

@[simp] lemma zero_rmatch (x : list α) : rmatch 0 x = ff :=
by induction x; simp [rmatch, match_epsilon, deriv, *]

lemma char_rmatch_iff (a : α) (x : list α) : rmatch (char a) x ↔ x = [a] :=
begin
  cases x with _ x,
    dec_trivial,
  cases x,
    rw [rmatch, deriv],
    split_ifs;
    tauto,
  rw [rmatch, deriv],
  split_ifs,
    rw one_rmatch_iff,
    tauto,
  rw zero_rmatch,
  tauto
end

lemma add_rmatch_iff (P Q : regular_expression α) (x : list α) :
  (P + Q).rmatch x ↔ P.rmatch x ∨ Q.rmatch x :=
begin
  induction x with _ _ ih generalizing P Q,
  { repeat {rw rmatch},
    rw match_epsilon,
    finish },
  { repeat {rw rmatch},
    rw deriv,
    exact ih _ _ }
end

lemma mul_rmatch_iff (P Q : regular_expression α) (x : list α) :
  (P * Q).rmatch x ↔ ∃ t u : list α, x = t ++ u ∧ P.rmatch t ∧ Q.rmatch u :=
begin
  induction x with a x ih generalizing P Q,
  { rw [rmatch, match_epsilon],
    split,
    { intro h,
      refine ⟨ [], [], rfl, _ ⟩,
      rw [rmatch, rmatch],
      rwa band_coe_iff at h },
    { rintro ⟨ t, u, h₁, h₂ ⟩,
      cases list.append_eq_nil.1 h₁.symm with ht hu,
      subst ht,
      subst hu,
      repeat {rw rmatch at h₂},
      finish } },
  { rw [rmatch, deriv],
    split_ifs with hepsilon,
    { rw [add_rmatch_iff, ih],
      split,
      { rintro (⟨ t, u, _ ⟩ | h),
        { exact ⟨ a :: t, u, by tauto ⟩ },
        { exact ⟨ [], a :: x, rfl, hepsilon, h ⟩ } },
      { rintro ⟨ t, u, h, hP, hQ ⟩,
        cases t with b t,
        { right,
          rw list.nil_append at h,
          rw ←h at hQ,
          exact hQ },
        { left,
          refine ⟨ t, u, by finish, _, hQ ⟩,
          rw rmatch at hP,
          convert hP,
          finish } } },
    { rw ih,
      split;
      rintro ⟨ t, u, h, hP, hQ ⟩,
      { exact ⟨ a :: t, u, by tauto ⟩ },
      { cases t with b t,
        { contradiction },
        { refine ⟨ t, u, by finish, _, hQ ⟩,
          rw rmatch at hP,
          convert hP,
          finish } } } }
end

lemma star_rmatch_iff (P : regular_expression α) : ∀ (x : list α),
  (star P).rmatch x ↔ ∃ S : list (list α), x = S.join ∧ ∀ t ∈ S, t ≠ [] ∧ P.rmatch t
| x :=
begin
  have A : ∀ (m n : ℕ), n < m + n + 1,
  { assume m n,
    convert add_lt_add_of_le_of_lt (add_le_add (zero_le m) (le_refl n)) zero_lt_one,
    simp },
  have IH := λ t (h : list.length t < list.length x), star_rmatch_iff t,
  clear star_rmatch_iff,
  split,
  { cases x with a x,
    { intro,
      fconstructor,
      exact [],
      tauto },
    { rw [rmatch, deriv, mul_rmatch_iff],
      rintro ⟨ t, u, hs, ht, hu ⟩,
      have hwf : u.length < (list.cons a x).length,
      { rw [hs, list.length_cons, list.length_append],
        apply A },
      rw IH _ hwf at hu,
      rcases hu with ⟨ S', hsum, helem ⟩,
      use (a :: t) :: S',
      split,
      { finish },
      { intros t' ht',
        cases ht' with ht' ht',
        { rw ht',
          exact ⟨ dec_trivial, ht ⟩ },
        { exact helem _ ht' } } } },
  { rintro ⟨ S, hsum, helem ⟩,
    cases x with a x,
    { dec_trivial },
    { rw [rmatch, deriv, mul_rmatch_iff],
      cases S with t' U,
      { exact ⟨ [], [], by tauto ⟩ },
      { cases t' with b t,
        { finish },
        refine ⟨ t, U.join, by finish, _, _ ⟩,
        { specialize helem (b :: t) _,
          { finish },
          rw rmatch at helem,
          convert helem.2,
          finish },
        { have hwf : U.join.length < (list.cons a x).length,
          { rw hsum,
            simp only
              [list.join, list.length_append, list.cons_append, list.length_join, list.length],
            apply A },
          rw IH _ hwf,
          refine ⟨ U, rfl, λ t h, helem t _ ⟩,
          right,
          assumption } } } }
end
using_well_founded
{ rel_tac := λ _ _, `[exact ⟨(λ L₁ L₂ : list _, L₁.length < L₂.length), inv_image.wf _ nat.lt_wf⟩] }

@[simp] lemma rmatch_iff_matches (P : regular_expression α) :
  ∀ x : list α, P.rmatch x ↔ x ∈ P.matches :=
begin
  intro x,
  induction P generalizing x,
  all_goals
  { try {rw zero_def},
    try {rw one_def},
    try {rw plus_def},
    try {rw comp_def},
    rw matches },
  case zero :
  { rw zero_rmatch,
    tauto },
  case epsilon :
  { rw one_rmatch_iff,
    refl },
  case char :
  { rw char_rmatch_iff,
    refl },
  case plus : _ _ ih₁ ih₂
  { rw [add_rmatch_iff, ih₁, ih₂],
    refl },
  case comp : P Q ih₁ ih₂
  { simp only [mul_rmatch_iff, comp_def, language.mul_def, exists_and_distrib_left, set.mem_image2,
      set.image_prod],
    split,
    { rintro ⟨ x, y, hsum, hmatch₁, hmatch₂ ⟩,
      rw ih₁ at hmatch₁,
      rw ih₂ at hmatch₂,
      exact ⟨ x, hmatch₁, y, hmatch₂, hsum.symm ⟩ },
    { rintro ⟨ x, hmatch₁, y, hmatch₂, hsum ⟩,
      rw ←ih₁ at hmatch₁,
      rw ←ih₂ at hmatch₂,
      exact ⟨ x, y, hsum.symm, hmatch₁, hmatch₂ ⟩ } },
  case star : _ ih
  { rw [star_rmatch_iff, language.star_def_nonempty],
    split,
    all_goals
    { rintro ⟨ S, hx, hS ⟩,
      refine ⟨ S, hx, _ ⟩,
      intro y,
      specialize hS y },
    { rw ←ih y,
      tauto },
    { rw ih y,
      tauto } }
end

instance (P : regular_expression α) : decidable_pred P.matches :=
begin
  intro x,
  change decidable (x ∈ P.matches),
  rw ←rmatch_iff_matches,
  exact eq.decidable _ _
end

end regular_expression


/-
Copyright (c) 2023 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import data.set.image
import data.list.basic
import data.fin.basic

/-!
# Lemmas about `list`s and `set.range`

In this file we prove lemmas about range of some operations on lists.
-/

open list
variables {α β : Type*} (l : list α)

namespace set

lemma range_list_map (f : α → β) : range (map f) = {l | ∀ x ∈ l, x ∈ range f} :=
begin
  refine subset.antisymm (range_subset_iff.2 $ λ l, forall_mem_map_iff.2 $ λ y _, mem_range_self _)
    (λ l hl, _),
  induction l with a l ihl, { exact ⟨[], rfl⟩ },
  rcases ihl (λ x hx, hl x $ subset_cons _ _ hx) with ⟨l, rfl⟩,
  rcases hl a (mem_cons_self _ _) with ⟨a, rfl⟩,
  exact ⟨a :: l, map_cons _ _ _⟩
end

lemma range_list_map_coe (s : set α) : range (map (coe : s → α)) = {l | ∀ x ∈ l, x ∈ s} :=
by rw [range_list_map, subtype.range_coe]

@[simp] lemma range_list_nth_le : range (λ k : fin l.length, l.nth_le k k.2) = {x | x ∈ l} :=
begin
  ext x,
  rw [mem_set_of_eq, mem_iff_nth_le],
  exact ⟨λ ⟨⟨n, h₁⟩, h₂⟩, ⟨n, h₁, h₂⟩, λ ⟨n, h₁, h₂⟩, ⟨⟨n, h₁⟩, h₂⟩⟩
end

lemma range_list_nth : range l.nth = insert none (some '' {x | x ∈ l}) :=
begin
  rw [← range_list_nth_le, ← range_comp],
  refine (range_subset_iff.2 $ λ n, _).antisymm (insert_subset.2 ⟨_, _⟩),
  exacts [(le_or_lt l.length n).imp nth_eq_none_iff.2 (λ hlt, ⟨⟨_, _⟩, (nth_le_nth hlt).symm⟩),
    ⟨_, nth_eq_none_iff.2 le_rfl⟩, range_subset_iff.2 $ λ k, ⟨_, nth_le_nth _⟩]
end

@[simp] lemma range_list_nthd (d : α) : range (λ n, l.nthd n d) = insert d {x | x ∈ l} :=
calc range (λ n, l.nthd n d) = (λ o : option α, o.get_or_else d) '' range l.nth :
  by simp only [← range_comp, (∘), nthd_eq_get_or_else_nth]
... = insert d {x | x ∈ l} :
  by simp only [range_list_nth, image_insert_eq, option.get_or_else, image_image, image_id']

@[simp]
lemma range_list_inth [inhabited α] (l : list α) : range l.inth = insert default {x | x ∈ l} :=
range_list_nthd l default

end set

/-- If each element of a list can be lifted to some type, then the whole list can be lifted to this
type. -/
instance list.can_lift (c) (p) [can_lift α β c p] :
  can_lift (list α) (list β) (list.map c) (λ l, ∀ x ∈ l, p x) :=
{ prf  := λ l H,
    begin
      rw [← set.mem_range, set.range_list_map],
      exact λ a ha, can_lift.prf a (H a ha),
    end}


/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios
-/
import order.rel_classes
import data.set.intervals.basic

/-!
# Bounded and unbounded sets

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

We prove miscellaneous lemmas about bounded and unbounded sets. Many of these are just variations on
the same ideas, or similar results with a few minor differences. The file is divided into these
different general ideas.
-/

namespace set
variables {α : Type*} {r : α → α → Prop} {s t : set α}

/-! ### Subsets of bounded and unbounded sets -/

theorem bounded.mono (hst : s ⊆ t) (hs : bounded r t) : bounded r s :=
hs.imp $ λ a ha b hb, ha b (hst hb)

theorem unbounded.mono (hst : s ⊆ t) (hs : unbounded r s) : unbounded r t :=
λ a, let ⟨b, hb, hb'⟩ := hs a in ⟨b, hst hb, hb'⟩

/-! ### Alternate characterizations of unboundedness on orders -/

lemma unbounded_le_of_forall_exists_lt [preorder α] (h : ∀ a, ∃ b ∈ s, a < b) : unbounded (≤) s :=
λ a, let ⟨b, hb, hb'⟩ := h a in ⟨b, hb, λ hba, hba.not_lt hb'⟩

lemma unbounded_le_iff [linear_order α] : unbounded (≤) s ↔ ∀ a, ∃ b ∈ s, a < b :=
by simp only [unbounded, not_le]

lemma unbounded_lt_of_forall_exists_le [preorder α] (h : ∀ a, ∃ b ∈ s, a ≤ b) : unbounded (<) s :=
λ a, let ⟨b, hb, hb'⟩ := h a in ⟨b, hb, λ hba, hba.not_le hb'⟩

lemma unbounded_lt_iff [linear_order α] : unbounded (<) s ↔ ∀ a, ∃ b ∈ s, a ≤ b :=
by simp only [unbounded, not_lt]

lemma unbounded_ge_of_forall_exists_gt [preorder α] (h : ∀ a, ∃ b ∈ s, b < a) : unbounded (≥) s :=
@unbounded_le_of_forall_exists_lt αᵒᵈ _ _ h

lemma unbounded_ge_iff [linear_order α] : unbounded (≥) s ↔ ∀ a, ∃ b ∈ s, b < a :=
⟨λ h a, let ⟨b, hb, hba⟩ := h a in ⟨b, hb, lt_of_not_ge hba⟩, unbounded_ge_of_forall_exists_gt⟩

lemma unbounded_gt_of_forall_exists_ge [preorder α] (h : ∀ a, ∃ b ∈ s, b ≤ a) : unbounded (>) s :=
λ a, let ⟨b, hb, hb'⟩ := h a in ⟨b, hb, λ hba, not_le_of_gt hba hb'⟩

lemma unbounded_gt_iff [linear_order α] : unbounded (>) s ↔ ∀ a, ∃ b ∈ s, b ≤ a :=
⟨λ h a, let ⟨b, hb, hba⟩ := h a in ⟨b, hb, le_of_not_gt hba⟩, unbounded_gt_of_forall_exists_ge⟩

/-! ### Relation between boundedness by strict and nonstrict orders. -/

/-! #### Less and less or equal -/

lemma bounded.rel_mono {r' : α → α → Prop} (h : bounded r s) (hrr' : r ≤ r') : bounded r' s :=
let ⟨a, ha⟩ := h in ⟨a, λ b hb, hrr' b a (ha b hb)⟩

lemma bounded_le_of_bounded_lt [preorder α] (h : bounded (<) s) : bounded (≤) s :=
h.rel_mono $ λ _ _, le_of_lt

lemma unbounded.rel_mono {r' : α → α → Prop} (hr : r' ≤ r) (h : unbounded r s) : unbounded r' s :=
λ a, let ⟨b, hb, hba⟩ := h a in ⟨b, hb, λ hba', hba (hr b a hba')⟩

lemma unbounded_lt_of_unbounded_le [preorder α] (h : unbounded (≤) s) :
  unbounded (<) s :=
h.rel_mono $ λ _ _, le_of_lt

lemma bounded_le_iff_bounded_lt [preorder α] [no_max_order α] : bounded (≤) s ↔ bounded (<) s :=
begin
  refine ⟨λ h, _, bounded_le_of_bounded_lt⟩,
  cases h with a ha,
  cases exists_gt a with b hb,
  exact ⟨b, λ c hc, lt_of_le_of_lt (ha c hc) hb⟩
end

lemma unbounded_lt_iff_unbounded_le [preorder α] [no_max_order α] :
  unbounded (<) s ↔ unbounded (≤) s :=
by simp_rw [← not_bounded_iff, bounded_le_iff_bounded_lt]

/-! #### Greater and greater or equal -/

lemma bounded_ge_of_bounded_gt [preorder α] (h : bounded (>) s) : bounded (≥) s :=
let ⟨a, ha⟩ := h in ⟨a, λ b hb, le_of_lt (ha b hb)⟩

lemma unbounded_gt_of_unbounded_ge [preorder α] (h : unbounded (≥) s) : unbounded (>) s :=
λ a, let ⟨b, hb, hba⟩ := h a in ⟨b, hb, λ hba', hba (le_of_lt hba')⟩

lemma bounded_ge_iff_bounded_gt [preorder α] [no_min_order α] : bounded (≥) s ↔ bounded (>) s :=
@bounded_le_iff_bounded_lt αᵒᵈ _ _ _

lemma unbounded_gt_iff_unbounded_ge [preorder α] [no_min_order α] :
  unbounded (>) s ↔ unbounded (≥) s :=
@unbounded_lt_iff_unbounded_le αᵒᵈ _ _ _

/-! ### The universal set -/

theorem unbounded_le_univ [has_le α] [no_top_order α] : unbounded (≤) (@set.univ α) :=
λ a, let ⟨b, hb⟩ := exists_not_le a in ⟨b, ⟨⟩, hb⟩

theorem unbounded_lt_univ [preorder α] [no_top_order α] : unbounded (<) (@set.univ α) :=
unbounded_lt_of_unbounded_le unbounded_le_univ

theorem unbounded_ge_univ [has_le α] [no_bot_order α] : unbounded (≥) (@set.univ α) :=
λ a, let ⟨b, hb⟩ := exists_not_ge a in ⟨b, ⟨⟩, hb⟩

theorem unbounded_gt_univ [preorder α] [no_bot_order α] : unbounded (>) (@set.univ α) :=
unbounded_gt_of_unbounded_ge unbounded_ge_univ

/-! ### Bounded and unbounded intervals -/

theorem bounded_self (a : α) : bounded r {b | r b a} :=
⟨a, λ x, id⟩

/-! #### Half-open bounded intervals -/

theorem bounded_le_Iio [preorder α] (a : α) : bounded (≤) (set.Iio a) :=
bounded_le_of_bounded_lt (bounded_lt_Iio a)

theorem bounded_le_Iic [preorder α] (a : α) : bounded (≤) (set.Iic a) :=
bounded_self a

theorem bounded_lt_Iic [preorder α] [no_max_order α] (a : α) : bounded (<) (set.Iic a) :=
by simp only [← bounded_le_iff_bounded_lt, bounded_le_Iic]

theorem bounded_gt_Ioi [preorder α] (a : α) : bounded (>) (set.Ioi a) :=
bounded_self a

theorem bounded_ge_Ioi [preorder α] (a : α) : bounded (≥) (set.Ioi a) :=
bounded_ge_of_bounded_gt (bounded_gt_Ioi a)

theorem bounded_ge_Ici [preorder α] (a : α) : bounded (≥) (set.Ici a) :=
bounded_self a

theorem bounded_gt_Ici [preorder α] [no_min_order α] (a : α) : bounded (>) (set.Ici a) :=
by simp only [← bounded_ge_iff_bounded_gt, bounded_ge_Ici]

/-! #### Other bounded intervals -/

theorem bounded_lt_Ioo [preorder α] (a b : α) : bounded (<) (set.Ioo a b) :=
(bounded_lt_Iio b).mono set.Ioo_subset_Iio_self

theorem bounded_lt_Ico [preorder α] (a b : α) : bounded (<) (set.Ico a b) :=
(bounded_lt_Iio b).mono set.Ico_subset_Iio_self

theorem bounded_lt_Ioc [preorder α] [no_max_order α] (a b : α) : bounded (<) (set.Ioc a b) :=
(bounded_lt_Iic b).mono set.Ioc_subset_Iic_self

theorem bounded_lt_Icc [preorder α] [no_max_order α] (a b : α) : bounded (<) (set.Icc a b) :=
(bounded_lt_Iic b).mono set.Icc_subset_Iic_self

theorem bounded_le_Ioo [preorder α] (a b : α) : bounded (≤) (set.Ioo a b) :=
(bounded_le_Iio b).mono set.Ioo_subset_Iio_self

theorem bounded_le_Ico [preorder α] (a b : α) : bounded (≤) (set.Ico a b) :=
(bounded_le_Iio b).mono set.Ico_subset_Iio_self

theorem bounded_le_Ioc [preorder α] (a b : α) : bounded (≤) (set.Ioc a b) :=
(bounded_le_Iic b).mono set.Ioc_subset_Iic_self

theorem bounded_le_Icc [preorder α] (a b : α) : bounded (≤) (set.Icc a b) :=
(bounded_le_Iic b).mono set.Icc_subset_Iic_self

theorem bounded_gt_Ioo [preorder α] (a b : α) : bounded (>) (set.Ioo a b) :=
(bounded_gt_Ioi a).mono set.Ioo_subset_Ioi_self

theorem bounded_gt_Ioc [preorder α] (a b : α) : bounded (>) (set.Ioc a b) :=
(bounded_gt_Ioi a).mono set.Ioc_subset_Ioi_self

theorem bounded_gt_Ico [preorder α] [no_min_order α] (a b : α) : bounded (>) (set.Ico a b) :=
(bounded_gt_Ici a).mono set.Ico_subset_Ici_self

theorem bounded_gt_Icc [preorder α] [no_min_order α] (a b : α) : bounded (>) (set.Icc a b) :=
(bounded_gt_Ici a).mono set.Icc_subset_Ici_self

theorem bounded_ge_Ioo [preorder α] (a b : α) : bounded (≥) (set.Ioo a b) :=
(bounded_ge_Ioi a).mono set.Ioo_subset_Ioi_self

theorem bounded_ge_Ioc [preorder α] (a b : α) : bounded (≥) (set.Ioc a b) :=
(bounded_ge_Ioi a).mono set.Ioc_subset_Ioi_self

theorem bounded_ge_Ico [preorder α] (a b : α) : bounded (≥) (set.Ico a b) :=
(bounded_ge_Ici a).mono set.Ico_subset_Ici_self

theorem bounded_ge_Icc [preorder α] (a b : α) : bounded (≥) (set.Icc a b) :=
(bounded_ge_Ici a).mono set.Icc_subset_Ici_self

/-! #### Unbounded intervals -/

theorem unbounded_le_Ioi [semilattice_sup α] [no_max_order α] (a : α) : unbounded (≤) (set.Ioi a) :=
λ b, let ⟨c, hc⟩ := exists_gt (a ⊔ b) in
  ⟨c, le_sup_left.trans_lt hc, (le_sup_right.trans_lt hc).not_le⟩

theorem unbounded_le_Ici [semilattice_sup α] [no_max_order α] (a : α) : unbounded (≤) (set.Ici a) :=
(unbounded_le_Ioi a).mono set.Ioi_subset_Ici_self

theorem unbounded_lt_Ioi [semilattice_sup α] [no_max_order α] (a : α) : unbounded (<) (set.Ioi a) :=
unbounded_lt_of_unbounded_le (unbounded_le_Ioi a)

theorem unbounded_lt_Ici [semilattice_sup α] (a : α) : unbounded (<) (set.Ici a) :=
λ b, ⟨a ⊔ b, le_sup_left, le_sup_right.not_lt⟩

/-! ### Bounded initial segments -/

theorem bounded_inter_not (H : ∀ a b, ∃ m, ∀ c, r c a ∨ r c b → r c m) (a : α) :
  bounded r (s ∩ {b | ¬ r b a}) ↔ bounded r s :=
begin
  refine ⟨_, bounded.mono (set.inter_subset_left s _)⟩,
  rintro ⟨b, hb⟩,
  cases H a b with m hm,
  exact ⟨m, λ c hc, hm c (or_iff_not_imp_left.2 (λ hca, (hb c ⟨hc, hca⟩)))⟩
end

theorem unbounded_inter_not (H : ∀ a b, ∃ m, ∀ c, r c a ∨ r c b → r c m) (a : α) :
  unbounded r (s ∩ {b | ¬ r b a}) ↔ unbounded r s :=
by simp_rw [← not_bounded_iff, bounded_inter_not H]

/-! #### Less or equal -/

theorem bounded_le_inter_not_le [semilattice_sup α] (a : α) :
  bounded (≤) (s ∩ {b | ¬ b ≤ a}) ↔ bounded (≤) s :=
bounded_inter_not (λ x y, ⟨x ⊔ y, λ z h, h.elim le_sup_of_le_left le_sup_of_le_right⟩) a

theorem unbounded_le_inter_not_le [semilattice_sup α] (a : α) :
  unbounded (≤) (s ∩ {b | ¬ b ≤ a}) ↔ unbounded (≤) s :=
begin
  rw [←not_bounded_iff, ←not_bounded_iff, not_iff_not],
  exact bounded_le_inter_not_le a
end

theorem bounded_le_inter_lt [linear_order α] (a : α) :
  bounded (≤) (s ∩ {b | a < b}) ↔ bounded (≤) s :=
by simp_rw [← not_le, bounded_le_inter_not_le]

theorem unbounded_le_inter_lt [linear_order α] (a : α) :
  unbounded (≤) (s ∩ {b | a < b}) ↔ unbounded (≤) s :=
by { convert unbounded_le_inter_not_le a, ext, exact lt_iff_not_le }

theorem bounded_le_inter_le [linear_order α] (a : α) :
  bounded (≤) (s ∩ {b | a ≤ b}) ↔ bounded (≤) s :=
begin
  refine ⟨_, bounded.mono (set.inter_subset_left s _)⟩,
  rw ←@bounded_le_inter_lt _ s _ a,
  exact bounded.mono (λ x ⟨hx, hx'⟩, ⟨hx, le_of_lt hx'⟩)
end

theorem unbounded_le_inter_le [linear_order α] (a : α) :
  unbounded (≤) (s ∩ {b | a ≤ b}) ↔ unbounded (≤) s :=
begin
  rw [←not_bounded_iff, ←not_bounded_iff, not_iff_not],
  exact bounded_le_inter_le a
end

/-! #### Less than -/

theorem bounded_lt_inter_not_lt [semilattice_sup α] (a : α) :
  bounded (<) (s ∩ {b | ¬ b < a}) ↔ bounded (<) s :=
bounded_inter_not (λ x y, ⟨x ⊔ y, λ z h, h.elim lt_sup_of_lt_left lt_sup_of_lt_right⟩) a

theorem unbounded_lt_inter_not_lt [semilattice_sup α] (a : α) :
  unbounded (<) (s ∩ {b | ¬ b < a}) ↔ unbounded (<) s :=
begin
  rw [←not_bounded_iff, ←not_bounded_iff, not_iff_not],
  exact bounded_lt_inter_not_lt a
end

theorem bounded_lt_inter_le [linear_order α] (a : α) :
  bounded (<) (s ∩ {b | a ≤ b}) ↔ bounded (<) s :=
by { convert bounded_lt_inter_not_lt a, ext, exact not_lt.symm }

theorem unbounded_lt_inter_le [linear_order α] (a : α) :
  unbounded (<) (s ∩ {b | a ≤ b}) ↔ unbounded (<) s :=
by { convert unbounded_lt_inter_not_lt a, ext, exact not_lt.symm }

theorem bounded_lt_inter_lt [linear_order α] [no_max_order α] (a : α) :
  bounded (<) (s ∩ {b | a < b}) ↔ bounded (<) s :=
begin
  rw [←bounded_le_iff_bounded_lt, ←bounded_le_iff_bounded_lt],
  exact bounded_le_inter_lt a
end

theorem unbounded_lt_inter_lt [linear_order α] [no_max_order α] (a : α) :
  unbounded (<) (s ∩ {b | a < b}) ↔ unbounded (<) s :=
begin
  rw [←not_bounded_iff, ←not_bounded_iff, not_iff_not],
  exact bounded_lt_inter_lt a
end

/-! #### Greater or equal -/

theorem bounded_ge_inter_not_ge [semilattice_inf α] (a : α) :
  bounded (≥) (s ∩ {b | ¬ a ≤ b}) ↔ bounded (≥) s :=
@bounded_le_inter_not_le αᵒᵈ s _ a

theorem unbounded_ge_inter_not_ge [semilattice_inf α] (a : α) :
  unbounded (≥) (s ∩ {b | ¬ a ≤ b}) ↔ unbounded (≥) s :=
@unbounded_le_inter_not_le αᵒᵈ s _ a

theorem bounded_ge_inter_gt [linear_order α] (a : α) :
  bounded (≥) (s ∩ {b | b < a}) ↔ bounded (≥) s :=
@bounded_le_inter_lt αᵒᵈ s _ a

theorem unbounded_ge_inter_gt [linear_order α] (a : α) :
  unbounded (≥) (s ∩ {b | b < a}) ↔ unbounded (≥) s :=
@unbounded_le_inter_lt αᵒᵈ s _ a

theorem bounded_ge_inter_ge [linear_order α] (a : α) :
  bounded (≥) (s ∩ {b | b ≤ a}) ↔ bounded (≥) s :=
@bounded_le_inter_le αᵒᵈ s _ a

theorem unbounded_ge_iff_unbounded_inter_ge [linear_order α] (a : α) :
  unbounded (≥) (s ∩ {b | b ≤ a}) ↔ unbounded (≥) s :=
@unbounded_le_inter_le αᵒᵈ s _ a

/-! #### Greater than -/

theorem bounded_gt_inter_not_gt [semilattice_inf α] (a : α) :
  bounded (>) (s ∩ {b | ¬ a < b}) ↔ bounded (>) s :=
@bounded_lt_inter_not_lt αᵒᵈ s _ a

theorem unbounded_gt_inter_not_gt [semilattice_inf α] (a : α) :
  unbounded (>) (s ∩ {b | ¬ a < b}) ↔ unbounded (>) s :=
@unbounded_lt_inter_not_lt αᵒᵈ s _ a

theorem bounded_gt_inter_ge [linear_order α] (a : α) :
  bounded (>) (s ∩ {b | b ≤ a}) ↔ bounded (>) s :=
@bounded_lt_inter_le αᵒᵈ s _ a

theorem unbounded_inter_ge [linear_order α] (a : α) :
  unbounded (>) (s ∩ {b | b ≤ a}) ↔ unbounded (>) s :=
@unbounded_lt_inter_le αᵒᵈ s _ a

theorem bounded_gt_inter_gt [linear_order α] [no_min_order α] (a : α) :
  bounded (>) (s ∩ {b | b < a}) ↔ bounded (>) s :=
@bounded_lt_inter_lt αᵒᵈ s _ _ a

theorem unbounded_gt_inter_gt [linear_order α] [no_min_order α] (a : α) :
  unbounded (>) (s ∩ {b | b < a}) ↔ unbounded (>) s :=
@unbounded_lt_inter_lt αᵒᵈ s _ _ a

end set


/-
Copyright (c) 2022 John Nicol. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: John Nicol

! This file was ported from Lean 3 source module number_theory.wilson
! leanprover-community/mathlib commit e985d48324225202b17a7f9eb50b29ba09b77b44
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.LegendreSymbol.GaussEisensteinLemmas

/-!
# Wilson's theorem.

This file contains a proof of Wilson's theorem.

The heavy lifting is mostly done by the previous `wilsons_lemma`,
but here we also prove the other logical direction.

This could be generalized to similar results about finite abelian groups.

## References

* [Wilson's Theorem](https://en.wikipedia.org/wiki/Wilson%27s_theorem)

## TODO

* Move `wilsons_lemma` into this file, and give it a descriptive name.
-/

open Nat

namespace Nat

variable {n : ℕ}

/-- For `n ≠ 1`, `(n-1)!` is congruent to `-1` modulo `n` only if n is prime. -/
theorem prime_of_fac_equiv_neg_one (h : ((n - 1)! : ZMod n) = -1) (h1 : n ≠ 1) : Prime n :=
  by
  rcases eq_or_ne n 0 with (rfl | h0)
  · norm_num at h
  replace h1 : 1 < n := n.two_le_iff.mpr ⟨h0, h1⟩
  by_contra h2
  obtain ⟨m, hm1, hm2 : 1 < m, hm3⟩ := exists_dvd_of_not_prime2 h1 h2
  have hm : m ∣ (n - 1)! := Nat.dvd_factorial (pos_of_gt hm2) (le_pred_of_lt hm3)
  refine' hm2.ne' (nat.dvd_one.mp ((Nat.dvd_add_right hm).mp (hm1.trans _)))
  rw [← ZMod.nat_cast_zmod_eq_zero_iff_dvd, cast_add, cast_one, h, add_left_neg]
#align nat.prime_of_fac_equiv_neg_one Nat.prime_of_fac_equiv_neg_one

/-- **Wilson's Theorem**: For `n ≠ 1`, `(n-1)!` is congruent to `-1` modulo `n` iff n is prime. -/
theorem prime_iff_fac_equiv_neg_one (h : n ≠ 1) : Prime n ↔ ((n - 1)! : ZMod n) = -1 :=
  by
  refine' ⟨fun h1 => _, fun h2 => prime_of_fac_equiv_neg_one h2 h⟩
  haveI := Fact.mk h1
  exact ZMod.wilsons_lemma n
#align nat.prime_iff_fac_equiv_neg_one Nat.prime_iff_fac_equiv_neg_one

end Nat


/- Here we prove Edmonds' matroid intersection theorem: given two matroids M₁ and M₂ on α, the size
of the largest set that is independent in both matroids is equal to the minimum of M₁.r X + M₂.r Xᶜ,
taken over all X ⊆ α. The proof is really by induction on the size of the ground set, but to make
things easier we instead do induction on the number of nonloops, applying the induction hypothesis
to loopifications and projections of M₁ and M₂.  -/

import matroid.submatroid.projection
import prelim.minmax
import .basic

open_locale classical
noncomputable theory
open matroid set

variables {α : Type*} [fintype α]

section intersection

/-- the parameter ν is nonnegative -/
lemma ν_nonneg (M₁ M₂ : matroid α) :
  0 ≤ ν M₁ M₂ :=
by {apply lb_le_max, intro X, apply size_nonneg}

/-- function that provides an upper bound on ν M₁ M₂ -/
def matroid_inter_ub_fn (M₁ M₂ : matroid α) (X : set α): ℤ :=
  M₁.r X + M₂.r Xᶜ

/-- the easy direction of matroid intersection, stated for a specific pair of sets. -/
theorem matroid_intersection_pair_le {M₁ M₂ : matroid α} {I : common_ind M₁ M₂} (A : set α) :
  size (I : set α) ≤ M₁.r A + M₂.r Aᶜ :=
begin
  rcases I with ⟨I, ⟨h₁, h₂⟩⟩,
  unfold_coes, dsimp only,
  rw ←(compl_inter_size A I),
  have h₁i := indep_of_subset_indep (inter_subset_right A I) h₁,
  have h₂i := indep_of_subset_indep (inter_subset_right Aᶜ I) h₂,
  rw [←indep_iff_r.mp h₁i, ←indep_iff_r.mp h₂i],
  linarith [rank_mono_inter_left M₁ A I, rank_mono_inter_left M₂ Aᶜ I],
end

/-- the easy direction of matroid intersection, stated as an upper bound on ν -/
lemma ν_ub (M₁ M₂ : matroid α) :
  ν M₁ M₂ ≤ min_val (matroid_inter_ub_fn M₁ M₂)  :=
begin
  rcases max_spec (λ (X : common_ind M₁ M₂), size X.val) with ⟨X, hX1, hX2⟩,
  rcases min_spec (matroid_inter_ub_fn M₁ M₂) with ⟨A, hA1, hA2⟩,
  rw [ν, ←hX1, ←hA1],
  apply matroid_intersection_pair_le,
end

/-- Edmonds' matroid intersection theorem: the size of a largest common independent set
    is equal to the minimum value of a natural upper bound on the size of any such set.
    Implies many other minmax theorems in combinatorics.                             -/
theorem matroid_intersection (M₁ M₂ : matroid α) :
  ν M₁ M₂ = min_val (λ X, M₁.r X + M₂.r Xᶜ) :=
begin
  -- the hard direction suffices
  refine le_antisymm (ν_ub M₁ M₂) _,

  --induction boilerplate
  convert  nonneg_int_strong_induction_param
    (λ p : matroid α × matroid α, min_val (matroid_inter_ub_fn p.1 p.2) ≤ ν p.1 p.2)
    (λ p : matroid α × matroid α, size (nonloops p.1 ∩ nonloops p.2))
    (λ p, size_nonneg _)
    _ _ ⟨M₁,M₂⟩,

  -- base case, when everything is a loop. Here the LHS is obviously 0.
  rintros ⟨N₁,N₂⟩ hN, dsimp only at ⊢ hN,

  have h' : (matroid_inter_ub_fn N₁ N₂) (loops N₁) = 0 :=  by
  { rw [size_zero_iff_empty, N₂.nonloops_eq_compl_loops, ← subset_iff_disjoint_compl] at hN,
    rw [matroid_inter_ub_fn, rank_loops, ← nonloops_eq_compl_loops, zero_add,
      rank_zero_of_subset_rank_zero hN N₂.rank_loops]},

  linarith [ν_nonneg N₁ N₂, min_is_lb (matroid_inter_ub_fn N₁ N₂) (loops N₁)],

  -- we now assume that the result holds for any strictly loopier pair of matroids,
  -- and that there is at least one common nonloop; call it e.

  rintros ⟨N₁,N₂⟩ hsize IH, dsimp only at hsize IH ⊢,
  set k := ν N₁ N₂ with hk,
  --rw ←hsize at hn,
  obtain ⟨e, he_mem⟩  := exists_mem_of_size_pos hsize,
  have  h_e_nl := he_mem,
  rw [mem_inter_iff, ← nonloop_iff_mem_nonloops] at h_e_nl,

  -- contract and delete (loopify/project) e from both elements of the pairs, to get
  -- strictly loopier pairs to which we'll apply the IH, along with the associated maximizers
  set N₁d := N₁ ⟍ {e} with hN₁d,
  set N₂d := N₂ ⟍ {e} with hN₂d,
  set N₁c := N₁ ⟋ {e} with hN₁c,
  set N₂c := N₂ ⟋ {e} with hN₂c,

  obtain ⟨⟨Id,hId_ind⟩, ⟨hId_eq_max, hId_ub⟩⟩ := max_spec (λ (X : common_ind N₁d N₂d), size X.val),
  obtain ⟨⟨Ic,hIc_ind⟩, ⟨hIc_eq_max, hIc_ub⟩⟩ := max_spec (λ (X : common_ind N₁c N₂c), size X.val),

  -- e doesn't belong to Ic, because Ic is independent in M/e
  have heIc : e ∉ Ic := λ heIc, by
  { have := projected_set_rank_zero N₁ {e},
    rw [←hN₁c, mem_indep_r heIc hIc_ind.1] at this,
    exact one_ne_zero this},

  -- ν does not get larger upon deletion
  have h_nu_d : ν N₁d N₂d ≤ k :=  by
  { rw [ν, ←hId_eq_max, hk, ν],
    convert max_is_ub (λ (X : common_ind N₁ N₂), size X.val) ⟨Id, _⟩,
    from ⟨indep_of_loopify_indep hId_ind.1, indep_of_loopify_indep hId_ind.2⟩},

  -- ν goes down upon contraction
  have h_nu_c : ν N₁c N₂c ≤ k-1 := by
  { rw [hk, ν, ν, ←hIc_eq_max],
    have := max_is_ub (λ (X : common_ind N₁ N₂), size X.val) ⟨Ic ∪ {e}, _⟩,
    dsimp only at this ⊢,
    linarith only [size_union_nonmem_singleton heIc, this],
    split, all_goals {apply indep_union_project_set_of_project_indep},
    exact hIc_ind.1, exact (nonloop_iff_indep.mp h_e_nl.1),
    exact hIc_ind.2, exact (nonloop_iff_indep.mp h_e_nl.2)},

  -- `(N₁ ⟍ e, N₂ ⟍ e)` is loopier than `(N₁, N₂)`.
  have h_fewer_nonloops_d : size (N₁d.nonloops ∩ N₂d.nonloops) < size (N₁.nonloops ∩ N₂.nonloops),
  { rw [hN₁d, hN₂d, loopify_nonloops_eq, loopify_nonloops_eq, diff_inter_diff_right,
        size_remove_mem he_mem],
    apply sub_one_lt, },

  -- so is `(N₁ ⟋ e , N₂ ⟋ e)`.
  have h_fewer_nonloops_c : size (N₁c.nonloops ∩ N₂c.nonloops) < size (N₁.nonloops ∩ N₂.nonloops),
  { rw [hN₁c, hN₂c, project_nonloops_eq, project_nonloops_eq],
    refine size_strict_monotone ((ssubset_iff_of_subset _).mpr ⟨e, he_mem, _⟩),
    { intros x hx, simp_rw [mem_inter_iff, mem_diff_iff] at hx, exact ⟨hx.1.1, hx.2.1⟩},
    refine nonmem_of_nonmem_supset (nonmem_diff_of_mem _ _) (inter_subset_left _ _),
    apply mem_cl_single},

  -- apply IH to deletion and then contraction, getting minimizers Ac and Ad
  have hd := IH ⟨N₁d, N₂d⟩ h_fewer_nonloops_d,
  obtain ⟨Ad, ⟨hAd_eq_min, hAd_lb⟩⟩ := min_spec (matroid_inter_ub_fn N₁d N₂d),
  rw [←hAd_eq_min] at hd,

  have hc := IH ⟨N₁c, N₂c⟩ h_fewer_nonloops_c,
  obtain ⟨Ac, ⟨hAc_eq_min, hAc_lb⟩⟩ := min_spec (matroid_inter_ub_fn N₁c N₂c),
  rw [←hAc_eq_min] at hc,

  -- this gives upper bounds on certain rank expressions
  have hAd_ub : N₁.r (Ad \ {e}) + N₂.r (Adᶜ \ {e}) ≤ k := le_trans hd h_nu_d,

  have hAc_ub : N₁.r (Ac ∪ {e}) + N₂.r (Acᶜ ∪ {e}) ≤ k+1 := by
  { suffices : (N₁.r (Ac ∪ {e}) - N₁.r {e}) + (N₂.r (Acᶜ ∪ {e}) - N₂.r {e}) ≤ k-1,
      by linarith [rank_nonloop h_e_nl.1, rank_nonloop h_e_nl.2],
    from le_trans hc h_nu_c},

  -- use contradiction, and replace the IH with a bound applying to all sets
  by_contra h_contr, push_neg at h_contr,
  replace h_contr : ∀ X, k + 1 ≤ matroid_inter_ub_fn N₁ N₂ X :=
    λ X, by linarith [min_is_lb (matroid_inter_ub_fn N₁ N₂) X],

  -- apply the bound to sets for which we know a bound in the other direction;
  have hi := h_contr (Ac ∩ Ad \ {e}),
  have hu := h_contr (Ac ∪ Ad ∪ {e}),
  unfold matroid_inter_ub_fn at hi hu,
  rw [compl_union, compl_union, ←diff_eq] at hu,
  rw [compl_diff, compl_inter] at hi,

  -- contradict submodularity.
  have sm1 := N₁.rank_submod (Ac ∪ {e}) (Ad \ {e}),
  have sm2 := N₂.rank_submod (Acᶜ ∪ {e}) (Adᶜ \ {e}),
  rw [union_union_diff, union_inter_diff] at sm1 sm2,
  linarith only [sm1, sm2, hi, hu, hAd_ub, hAc_ub],
end

/-- restatement of matroid intersection theorem as the existence of a matching maximizer/minimizer-/
theorem matroid_intersection_exists_pair_eq (M₁ M₂ : matroid α) :
  ∃ I A, is_common_ind M₁ M₂ I ∧ size I =  M₁.r A + M₂.r Aᶜ  :=
begin
  rcases max_spec (λ (I : common_ind M₁ M₂), size I.val) with ⟨⟨I,h_ind⟩,h_eq_max, hI_ub⟩,
  rcases min_spec (λ X, M₁.r X + M₂.r Xᶜ) with ⟨A, hA_eq_min, hA_lb⟩,
  refine ⟨I, A, ⟨h_ind,_⟩⟩,
  dsimp only at *,
  rw [h_eq_max, hA_eq_min],
  apply matroid_intersection,
end

end intersection


import .love10_denotational_semantics_demo

/-! # LoVe Homework 10: Denotational Semantics

Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1 (7 points): Denotational Semantics of DOWHILE -/

namespace do_while

/-! Consider the following DOWHILE language: -/

inductive stmt : Type
| skip     : stmt
| assign   : string → (state → ℕ) → stmt
| seq      : stmt → stmt → stmt
| unless   : stmt → (state → Prop) → stmt
| while    : (state → Prop) → stmt → stmt
| do_while : stmt → (state → Prop) → stmt

infixr ` ;; ` : 90 := stmt.seq

/-! The `skip`, `assign`, `seq`, and `while` constructs are as for the WHILE
language.

`unless S b` executes `S` if `b` is false in the current state; otherwise, it
does nothing. This statement is inspired by Perl's `unless` conditional.

`do_while S b` first executes `S`. Then, if `b` is true in the resulting state,
it re-enters the loop and executes `S` again, and continues executing `S` until
`b` becomes `false`. The semantics is almost the same as `while b S`, except
that `p` is always executed at least once, even if the condition is not true
initially. This statement is inspired by C's and Java's `do { … } while (…)`
loop.

1.1 (2 points). Give a denotational semantics of DOWHILE.

Hint: Your definition should make it easy to prove lemma `do_while_while` in
question 1.2. -/

def denote : stmt → set (state × state)
| stmt.skip           := Id
-- enter the missing cases here

notation `⟦` S `⟧`:= denote S

/-! 1.2 (1 point). Prove the following correspondence between `do_while` and
`while`. -/

lemma do_while_while (S : stmt) (b : state → Prop) :
  ⟦stmt.do_while S b⟧ = ⟦S⟧ ◯ ⟦stmt.while b S⟧ :=
sorry

/-! 1.3 (4 points). Prove the following lemmas.

Hint: For all of these, short proofs are possible. It may help, however, to
know what basic expressions such as `p ⇃ (λx, false)` and `p ⇃ (λx, true)` mean.
Make sure to simplify the expressions involving `⇃` before trying to figure out
what to do about `lfp`. -/

lemma lfp_const {α : Type} [complete_lattice α] (a : α) :
  lfp (λX, a) = a :=
sorry

lemma while_false (S : stmt) :
  ⟦stmt.while (λ_, false) S⟧ = Id :=
sorry

lemma comp_Id {α : Type} (r : set (α × α)) :
  r ◯ Id = r :=
sorry

lemma do_while_false (S : stmt) :
  ⟦stmt.do_while S (λ_, false)⟧ = ⟦S⟧ :=
sorry

end do_while

/-! ## Question 2 (2 points + 2 bonus points): Kleene's Theorem

We can compute the fixpoint by iteration by taking the union of all finite
iterations of `f`:

    `lfp f = ⋃i, (f ^^ i) ∅`

where

    `f ^^ i = f ∘ ⋯ ∘ f`

iterates the function `f` `i` times. However, the above characterization of
`lfp` only holds for continuous functions, a concept we will introduce below. -/

def iterate {α : Type} (f : α → α) : ℕ → α → α
| 0       a := a
| (n + 1) a := f (iterate n a)

notation f`^^`n := iterate f n

/-! 2.1 (2 points). Fill in the missing proofs below.

Hint: Bear in mind that `≤` works on lattices in general, including sets. On
sets, it can be unfolded to `⊆` using `simp [(≤)]`. Moreover, when you see
`h : A ⊆ B` in a goal, you can imagine that you have `?a ∈ A → ?a ∈ B` by
definition, or unfold the definition using `simp [(⊆), set.subset]`. -/

def Union {α : Type} (s : ℕ → set α) : set α :=
{a | ∃n, a ∈ s n}

lemma Union_le {α : Type} {s : ℕ → set α} (A : set α) (h : ∀i, s i ≤ A) :
  Union s ≤ A :=
sorry

/-! A continuous function `f` is a function that commutes with the union of any
monotone sequence `s`: -/

def continuous {α : Type} (f : set α → set α) : Prop :=
∀s : ℕ → set α, monotone s → f (Union s) = Union (λi, f (s i))

/-! We need to prove that each continuous function is monotone. To achieve this,
we will need the following sequence: -/

def bi_seq {α : Type} (A B : set α) : ℕ → set α
| 0       := A
| (n + 1) := B

/-! For example, `bi_seq A B` is the sequence A, B, B, B, …. -/

lemma monotone_bi_seq {α : Type} (A B : set α) (h : A ≤ B) :
  monotone (bi_seq A B)
| 0       0       _ := le_refl _
| 0       (n + 1) _ := h
| (n + 1) (m + 1) _ := le_refl _

lemma Union_bi_seq {α : Type} (A B : set α) (ha : A ≤ B) :
  Union (bi_seq A B) = B :=
begin
  apply le_antisymm,
  { sorry },
  { sorry }
end

lemma monotone_of_continuous {α : Type} (f : set α → set α)
    (hf : continuous f) :
  monotone f :=
sorry

/-! 2.2 (1 bonus point). Provide the following proof, using a similar case
distinction as for `monotone_bi_seq` above. -/

lemma monotone_iterate {α : Type} (f : set α → set α) (hf : monotone f) :
  monotone (λi, (f ^^ i) ∅) :=
sorry

/-! 2.3 (1 bonus point). Prove the main theorem. A proof sketch is given below.

We break the proof into two proofs of inclusion.

Case 1. `lfp f ≤ Union (λi, (f ^^ i) ∅)`: The key is to use the lemma `lfp_le`
together with continuity of `f`.

Case 2. `Union (λi, (f ^^ i) ∅) ≤ lfp f`: The lemma `Union_le` gives us a
natural number `i`, on which you can perform induction. We also need the lemma
`lfp_eq` to unfold one iteration of `lfp f`. -/

lemma lfp_Kleene {α : Type} (f : set α → set α) (hf : continuous f) :
  lfp f = Union (λi, (f ^^ i) ∅) :=
sorry

end LoVe


/-
Copyright (c) 2022 Julian Kuelshammer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Julian Kuelshammer
-/

import solutions.sheet01

/-! Two-by-two matrices

This file defines two-by-two matrices and shows that they form a vector space.
-/

/- Here is one way to define a 2x2 matrix, via specifying its two rows. In hard mode you could try to take a
  different approach (either as its two columns or as its four entries and see what gets easier and what gets
  harder). -/
structure two_matrix : Type :=
(fst_row : ℝ²)
(snd_row : ℝ²)

namespace two_matrix

notation `Mat₂` := two_matrix

/-- Two matrices are equal if and only if their first and second rows coincide. -/
@[ext] theorem ext {A B : Mat₂}
  (h_first_row : A.fst_row = B.fst_row )
  (h_second_row : A.snd_row = B.snd_row ) :
  A = B :=
begin
  cases A,
  cases B,
  simp * at *,
end

/- Again we want to be able to write `A + B` if `A` and `B` are matrices without too complicated notation. -/
instance : has_add Mat₂ := ⟨λ A B, ⟨A.fst_row + B.fst_row, A.snd_row + B.snd_row⟩⟩

@[simp] lemma add_fst_row (A B : Mat₂) : (A + B).fst_row = A.fst_row + B.fst_row := rfl
@[simp] lemma add_snd_row (A B : Mat₂) : (A + B).snd_row = A.snd_row + B.snd_row := rfl

lemma add_assoc (A B C : Mat₂) : A + B + C = A + (B + C) :=
begin
  ext;
  simp;
  ring,
end

lemma add_comm (A B : Mat₂) : A + B = B + A :=
begin
  ext;
  simp;
  ring,
end

def zero_matrix : Mat₂ := ⟨0,0⟩

/- We even want to be able to write `0` for the zero matrix.-/
instance : has_zero Mat₂ := ⟨zero_matrix⟩

/- The following lemmas have each two zeros in them, see which is which. -/
@[simp] lemma zero_fst_row : (0 : Mat₂).fst_row = 0 := rfl
@[simp] lemma zero_snd_row : (0 : Mat₂).snd_row = 0 := rfl

@[simp] lemma add_zero (A : Mat₂) : A + 0 = A :=
begin
  ext;
  simp,
end

@[simp] lemma zero_add (A : Mat₂) : 0 + A = A :=
begin
  ext;
  simp,
end

/- We want to define the negation of a matrix. -/
instance : has_neg Mat₂ := ⟨λ A, ⟨-A.fst_row, -A.snd_row⟩⟩

@[simp] lemma neg_fst_row (A : Mat₂) : (-A).fst_row = -A.fst_row := rfl
@[simp] lemma neg_snd_row (A : Mat₂) : (-A).snd_row = -A.snd_row := rfl

@[simp] lemma add_neg_self (A : Mat₂) : A + -A = 0 :=
begin
  ext;
  simp,
end

@[simp] lemma neg_add_self (A : Mat₂) : -A + A = 0 :=
begin
  ext;
  simp,
end

/- Finally we set up subtraction and scalar multiplication of matrices. -/
instance : has_sub Mat₂ := ⟨λ A B, A + (-B)⟩

instance : has_scalar ℝ Mat₂ := ⟨λ a A, ⟨a • A.fst_row, a • A.snd_row⟩⟩

@[simp] lemma smul_fst_row (a : ℝ) (A : Mat₂) : (a • A).fst_row = a • A.fst_row := rfl
@[simp] lemma smul_snd_row (a : ℝ) (A : Mat₂) : (a • A).snd_row = a • A.snd_row := rfl

lemma smul_assoc (a b : ℝ) (A : Mat₂) : (a * b) • A = a • (b • A) :=
begin
  ext;
  simp;
  ring,
end

@[simp] lemma one_smul (A : Mat₂) : (1 : ℝ) • A = A :=
begin
  ext;
  simp,
end

@[simp] lemma smul_add (a : ℝ) (A B : Mat₂) : a • (A + B) = a • A + a • B :=
begin
  ext;
  simp,
end

@[simp] lemma add_smul (a b : ℝ) (A : Mat₂) : (a + b) • A = a • A + b • A :=
begin
  ext;
  simp,
end

end two_matrix


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

import data.W

/-!
# W types

The file `data/W.lean` shows that if `α` is an an encodable fintype and for every `a : α`,
`β a` is encodable, then `W β` is encodable.

As an example of how this can be used, we show that the type of propositional formulas with
variables labeled from an encodable type is encodable.

The strategy is to define a type of labels corresponding to the constructors.
From the definition (using `sum`, `unit`, and an encodable type), Lean can infer
that it is encodable. We then define a map from propositional formulas to the
corresponding `Wfin` type, and show that map has a left inverse.

We mark the auxiliary constructions `private`, since their only purpose is to
show encodability.
-/

/-- Propositional formulas with labels from `α`. -/
inductive prop_form (α : Type*)
| var : α → prop_form
| not : prop_form → prop_form
| and : prop_form → prop_form → prop_form
| or  : prop_form → prop_form → prop_form

/-!
The next three functions make it easier to construct functions from a small
`fin`.
-/

section
variable {α : Type*}

/-- the trivial function out of `fin 0`. -/
def mk_fn0 : fin 0 → α
| ⟨_, h⟩ := absurd h dec_trivial

/-- defines a function out of `fin 1` -/
def mk_fn1 (t : α) : fin 1 → α
| ⟨0, _⟩   := t
| ⟨n+1, h⟩ := absurd h dec_trivial

/-- defines a function out of `fin 2` -/
def mk_fn2 (s t : α) : fin 2 → α
| ⟨0, _⟩   := s
| ⟨1, _⟩   := t
| ⟨n+2, h⟩ := absurd h dec_trivial

attribute [simp] mk_fn0 mk_fn1 mk_fn2
end

namespace prop_form

private def constructors (α : Type*) := α ⊕ unit ⊕ unit ⊕ unit

local notation `cvar` a := sum.inl a
local notation `cnot`   := sum.inr (sum.inl unit.star)
local notation `cand`   := sum.inr (sum.inr (sum.inr unit.star))
local notation `cor`    := sum.inr (sum.inr (sum.inl unit.star))

@[simp]
private def arity (α : Type*) : constructors α → nat
| (cvar a) := 0
| cnot     := 1
| cand     := 2
| cor      := 2

variable {α : Type*}

private def f : prop_form α → W_type (λ i, fin (arity α i))
| (var a)   := ⟨cvar a, mk_fn0⟩
| (not p)   := ⟨cnot, mk_fn1 (f p)⟩
| (and p q) := ⟨cand, mk_fn2 (f p) (f q)⟩
| (or  p q) := ⟨cor, mk_fn2 (f p) (f q)⟩

private def finv : W_type (λ i, fin (arity α i)) → prop_form α
| ⟨cvar a, fn⟩ := var a
| ⟨cnot, fn⟩   := not (finv (fn ⟨0, dec_trivial⟩))
| ⟨cand, fn⟩   := and (finv (fn ⟨0, dec_trivial⟩)) (finv (fn ⟨1, dec_trivial⟩))
| ⟨cor, fn⟩    := or  (finv (fn ⟨0, dec_trivial⟩)) (finv (fn ⟨1, dec_trivial⟩))

instance [encodable α] : encodable (prop_form α) :=
begin
  haveI : encodable (constructors α) :=
    by { unfold constructors, apply_instance },
  exact encodable.of_left_inverse f finv
    (by { intro p, induction p; simp [f, finv, *] })
end

end prop_form


-- import the reals...
import data.real.basic

-- ...and now we can build the complex numbers as an ordered pair of reals.
structure complex : Type :=
(re : ℝ) (im : ℝ)

notation `ℂ` := complex

namespace complex

/-
What we have so far:

*) a new type called `complex` or ℂ for short;
*) Two functions `complex.re` and `complex.im` from ℂ to ℝ,
   plus the cool abbreviation z.re for complex.re z and z.im similarly;
*) A way of making a complex number from two real numbers x and y;
   the official name is of this function is `complex.mk x y`
   but in practice we will just use the abbreviation ⟨x, y⟩
   as you're about to see.
-/

-- how to make 3 + 4i
example : ℂ := ⟨3, 4⟩

-- In type theory, "eta conversion" is about simplifying a term
-- which involves a constructor applied to an eliminator.
-- In this context, our eliminators are re and im, and our constructor
-- is ⟨x, y⟩. So `eta z` should be the theorem that ⟨re z, im z⟩ = z.
theorem eta (z : ℂ) : (⟨z.re, z.im⟩ : ℂ) = z := by cases z with x y; refl

-- Now we should prove the extensionality lemma for complex numbers;
-- two complex numbers are equal if and only if their real and imaginary
-- parts are equal. One way is trivial; here's the other way.
@[extensionality] theorem ext (z w : ℂ) (Hre : re z = re w) (Him : im z = im w) :
z = w :=
begin
  cases w with x y,
  rw ←eta z,
  -- this is the goal now:
  show (⟨re z, im z⟩ : ℂ) = ⟨x, y⟩,
  rw Hre, -- z.re = x
  rw Him, -- z.im = y
  -- goal is now true by definition, so gets automatically closed.
end

-- Now we start on the data we need to make the complexes a ring,
-- namely 0, 1, addition and multiplication.

-- Here the 0's are (0 : ℝ)
definition zero : ℂ := ⟨0, 0⟩

-- zero notation
instance : has_zero ℂ := ⟨complex.zero⟩

-- For our simp lemmas we will use the numeral 0 rather than complex.zero .
-- It's important that we stick to one convention!
@[simp] lemma zero_re : (0 : ℂ).re = 0 := rfl
@[simp] lemma zero_im : (0 : ℂ).im = 0 := rfl

-- Now  `simp` will expand out 0.re and 0.im as the real number 0.

-- Same for 1:
definition one : ℂ := ⟨1, 0⟩

instance : has_one ℂ := ⟨complex.one⟩

@[simp] lemma one_re : (1 : ℂ).re = 1 := rfl
@[simp] lemma one_im : (1 : ℂ).im = 0 := rfl

-- Next addition
definition add (z w : ℂ) : ℂ := ⟨z.re + w.re, z.im + w.im⟩

-- add the notation
instance : has_add ℂ := ⟨complex.add⟩

-- These lemmas is true by definition, but we need to tell
-- them to Lean explicitly so we can train `simp` to expand
-- out whenever it sees the left hand side.
@[simp] lemma add_re (a b : ℂ) : (a + b).re = a.re + b.re := rfl

@[simp] lemma add_im (a b : ℂ) : (a + b).im = a.im + b.im := rfl

-- Next negation
definition neg (z : ℂ) : ℂ := ⟨-z.re, -z.im⟩

instance : has_neg ℂ := ⟨complex.neg⟩

@[simp] lemma neg_re (a : ℂ) : (-a).re = -a.re := rfl
@[simp] lemma neg_im (a : ℂ) : (-a).im = -a.im := rfl

definition mul (z w : ℂ) : ℂ :=
⟨z.re * w.re - z.im * w.im, z.re * w.im + z.im * w.re⟩

-- add the notation
instance : has_mul ℂ := ⟨complex.mul⟩

@[simp] lemma mul_re (a b : ℂ) :
(a * b).re = a.re * b.re - a.im * b.im := rfl

@[simp] lemma mul_im (a b : ℂ) :
(a * b).im = a.re * b.im + a.im * b.re := rfl

 -- Sanity check!
example : (⟨1, 2⟩ : ℂ) * (⟨1, 2⟩ : ℂ) = (⟨-3, 4⟩ : ℂ) :=
begin
  apply complex.ext, -- "suffices to prove real and imag parts are equal"
    -- long-winded method for real part:
    rw mul_re, norm_num,
    -- automation for imag part
    simp, norm_num,
    -- actually just norm_num seems to work.
end

-- For a general theorem, simp is very useful
example (a b c : ℂ) :
(a + b) * c = a * c + b * c :=
begin
  apply ext,
  -- again let's do the real part by hand
  { rw [add_re,mul_re,add_re,add_im, mul_re, mul_re],
    ring },
  -- and now let's note that automation also works
  { simp, ring },
end

-- Now let's do it in term mode:
example (a b c : ℂ) :
a * (b + c) = a * b + a * c := by apply ext; simp; ring

-- and now let's prove all of the axioms of a commutative ring using
-- the same technique.
instance : comm_ring ℂ :=
by refine { zero := 0, add := (+), neg := has_neg.neg, one := 1, mul := (*), ..};
{ intros, apply ext; simp; ring }

def conj (z : ℂ) : ℂ := ⟨z.re, -z.im⟩

noncomputable def inv (z : ℂ) : ℂ :=
  ⟨z.re*(z.re*z.re+z.im*z.im)⁻¹, -z.im*(z.re*z.re+z.im*z.im)⁻¹⟩

noncomputable instance : has_inv ℂ := ⟨complex.inv⟩

example : zero_ne_one_class ℝ := by apply_instance

example : (0 : ℝ) ≠ (1 : ℝ) := zero_ne_one_class.zero_ne_one ℝ

--set_option pp.numerals false
instance : zero_ne_one_class ℂ := {
  zero := 0,
  one := 1,
  zero_ne_one := begin
    intro h,
    apply zero_ne_one_class.zero_ne_one ℝ,
    show (0 : ℂ).re = (1 : ℂ).re,
    rw h,
  end }

lemma norm_sq_ne_zero_of_ne_zero {x y : ℝ} (h : (⟨x, y⟩ : ℂ) ≠ 0) : x * x + y * y ≠ 0 :=
begin
  intro h2,
  apply h,
  ext,
    dsimp,
    exact eq_zero_of_mul_self_add_mul_self_eq_zero h2,
  rw add_comm at h2,
  exact eq_zero_of_mul_self_add_mul_self_eq_zero h2,
end

theorem mul_inv_cancel {z : ℂ} (hz : z ≠ 0) : z * z⁻¹ = 1 :=
begin
  cases z with x y,
  unfold has_inv.inv inv,
  dsimp,
  ext;dsimp,
  { rw ←mul_assoc,
    rw neg_mul_eq_neg_mul,
    rw ←mul_assoc,
    rw neg_mul_neg,
    rw ←add_mul,
    apply div_self,
    apply norm_sq_ne_zero_of_ne_zero,
    assumption,
  },
  { ring,
  }
end

theorem inv_mul_cancel {z : ℂ} (hz : z ≠ 0) : z⁻¹ * z = 1 := (mul_comm z z⁻¹) ▸ mul_inv_cancel hz

/-
(has_decidable_eq : decidable_eq α)
(inv_zero : inv zero = zero)
(mul_inv_cancel : ∀ {a : α}, a ≠ 0 → a * a⁻¹ = 1)
(inv_mul_cancel : ∀ {a : α}, a ≠ 0 → a⁻¹ * a = 1)
-/
noncomputable instance : field ℂ :=
begin
  refine {
    inv := has_inv.inv,
    zero_ne_one := zero_ne_one_class.zero_ne_one _,
    mul_inv_cancel := λ _, mul_inv_cancel,
    inv_mul_cancel := λ _, inv_mul_cancel,
    ..complex.comm_ring,
    ..},
end

noncomputable instance : discrete_field ℂ :=
begin
  refine {..complex.field, ..},
    intros x y,
    apply classical.prop_decidable,
  show (0 : ℂ) ⁻¹ = 0,
  ext,
    refine zero_mul _,
  dsimp,
  unfold has_inv.inv inv,
  dsimp,
  rw neg_zero,
  rw zero_mul,
end

end complex


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Chris Hughes, Floris van Doorn, Yaël Dillies
-/
import data.nat.basic
import data.nat.pow

/-!
# Factorial and variants

This file defines the factorial, along with the ascending and descending variants.

## Main declarations

* `factorial`: The factorial.
* `asc_factorial`: The ascending factorial. Note that it runs from `n + 1` to `n + k` and *not*
  from`n`
  to `n + k - 1`. We might want to change that in the future.
* `desc_factorial`: The descending factorial. It runs from `n - k` to `n`.
-/

namespace nat

/-- `nat.factorial n` is the factorial of `n`. -/
@[simp] def factorial : ℕ → ℕ
| 0        := 1
| (succ n) := succ n * factorial n

localized "notation n `!`:10000 := nat.factorial n" in nat

section factorial

variables {m n : ℕ}

@[simp] theorem factorial_zero : 0! = 1 := rfl

@[simp] theorem factorial_succ (n : ℕ) : n.succ! = (n + 1) * n! := rfl

@[simp] theorem factorial_one : 1! = 1 := rfl

@[simp] theorem factorial_two : 2! = 2 := rfl

theorem mul_factorial_pred (hn : 0 < n) : n * (n - 1)! = n! :=
tsub_add_cancel_of_le (nat.succ_le_of_lt hn) ▸ rfl

theorem factorial_pos : ∀ n, 0 < n!
| 0        := zero_lt_one
| (succ n) := mul_pos (succ_pos _) (factorial_pos n)

theorem factorial_ne_zero (n : ℕ) : n! ≠ 0 := ne_of_gt (factorial_pos _)

theorem factorial_dvd_factorial {m n} (h : m ≤ n) : m! ∣ n! :=
begin
  induction n with n IH; simp,
  { have := nat.eq_zero_of_le_zero h, subst m, simp },
  obtain he | hl := h.eq_or_lt,
  { subst m, simp },
  exact (IH (le_of_lt_succ hl)).mul_left _,
end

theorem dvd_factorial : ∀ {m n}, 0 < m → m ≤ n → m ∣ n!
| (succ m) n _ h := dvd_of_mul_right_dvd (factorial_dvd_factorial h)

@[mono] theorem factorial_le {m n} (h : m ≤ n) : m! ≤ n! :=
le_of_dvd (factorial_pos _) (factorial_dvd_factorial h)

lemma factorial_mul_pow_le_factorial : ∀ {m n : ℕ}, m! * m.succ ^ n ≤ (m + n)!
| m 0     := by simp
| m (n+1) :=
by  rw [← add_assoc, nat.factorial_succ, mul_comm (nat.succ _), pow_succ', ← mul_assoc];
  exact mul_le_mul factorial_mul_pow_le_factorial
    (nat.succ_le_succ (nat.le_add_right _ _)) (nat.zero_le _) (nat.zero_le _)

lemma monotone_factorial : monotone factorial := λ n m, factorial_le

lemma one_lt_factorial : 1 < n! ↔ 1 < n :=
by { convert factorial_lt _, refl, exact one_pos }

lemma factorial_eq_one : n! = 1 ↔ n ≤ 1 :=
begin
  split; intro h,
  { rw [← not_lt, ← one_lt_factorial, h],
    apply lt_irrefl },
  cases h with h h, refl, cases h, refl,
end

lemma factorial_inj (hn : 1 < n!) : n! = m! ↔ n = m :=
begin
  split; intro h,
  { obtain hnm | hnm | hnm := lt_trichotomy n m,
    { exfalso, rw [← factorial_lt, h] at hnm, exact lt_irrefl _ hnm,
      rw [one_lt_factorial] at hn, exact lt_trans one_pos hn },
    { exact hnm },
    exfalso,
    rw [h, one_lt_factorial] at hn,
    rw [←factorial_lt (lt_trans one_pos hn), h] at hnm, exact lt_irrefl _ hnm, },
  { rw h },
end

lemma self_le_factorial : ∀ n : ℕ, n ≤ n!
| 0       := zero_le_one
| (k + 1) := le_mul_of_one_le_right k.zero_lt_succ.le (nat.one_le_of_lt $ nat.factorial_pos _)

lemma lt_factorial_self {n : ℕ} (hi : 3 ≤ n) : n < n! :=
begin
  rw [← succ_pred_eq_of_pos ((zero_lt_two.trans (lt.base 2)).trans_le hi), factorial_succ],
  exact lt_mul_of_one_lt_right ((pred n).succ_pos) ((one_lt_two.trans_le
    (le_pred_of_lt (succ_le_iff.mp hi))).trans_le (self_le_factorial _)),
end

lemma add_factorial_succ_lt_factorial_add_succ {i : ℕ} (n : ℕ) (hi : 2 ≤ i) :
  i + (n + 1)! < (i + n + 1)! :=
begin
  rw [factorial_succ (i + _), add_mul, one_mul],
  have : i ≤ i + n := le.intro rfl,
  exact add_lt_add_of_lt_of_le (this.trans_lt ((lt_mul_iff_one_lt_right (zero_lt_two.trans_le
    (hi.trans this))).mpr (lt_iff_le_and_ne.mpr ⟨(i + n).factorial_pos, λ g,
    nat.not_succ_le_self 1 ((hi.trans this).trans (factorial_eq_one.mp g.symm))⟩))) (factorial_le
    ((le_of_eq (add_comm n 1)).trans ((add_le_add_iff_right n).mpr (one_le_two.trans hi)))),
end

lemma add_factorial_lt_factorial_add {i n : ℕ} (hi : 2 ≤ i) (hn : 1 ≤ n) :
  i + n! < (i + n)! :=
begin
  cases hn,
  { rw factorial_one,
    exact lt_factorial_self (succ_le_succ hi) },
  exact add_factorial_succ_lt_factorial_add_succ _ hi,
end

lemma add_factorial_succ_le_factorial_add_succ (i : ℕ) (n : ℕ) :
  i + (n + 1)! ≤ (i + (n + 1))! :=
begin
  obtain i2 | (_ | ⟨_, i0⟩) := le_or_lt 2 i,
  { exact (n.add_factorial_succ_lt_factorial_add_succ i2).le },
  { change 1 + (n + 1)! ≤ (1 + n + 1) * (1 + n)!,
    rw [add_mul, one_mul, add_comm 1 n],
    exact (add_le_add_iff_right _).mpr (one_le_mul (nat.le_add_left 1 n) (n + 1).factorial_pos) },
  rw [nat.le_zero_iff.mp (nat.succ_le_succ_iff.mp i0), zero_add, zero_add]
end

lemma add_factorial_le_factorial_add (i : ℕ) {n : ℕ} (n1 : 1 ≤ n) :
  i + n! ≤ (i + n)! :=
begin
  cases n1 with h,
  { exact self_le_factorial _ },
  exact add_factorial_succ_le_factorial_add_succ i h,
end

lemma factorial_mul_pow_sub_le_factorial {n m : ℕ} (hnm : n ≤ m) : n! * n ^ (m - n) ≤ m! :=
begin
  suffices : n! * (n + 1) ^ (m - n) ≤ m!,
  { apply trans _ this,
    rw mul_le_mul_left,
    apply pow_le_pow_of_le_left (zero_le n) (le_succ n),
    exact factorial_pos n,},
  convert nat.factorial_mul_pow_le_factorial,
  exact (add_tsub_cancel_of_le hnm).symm,
end

end factorial

/-! ### Ascending and descending factorials -/

section asc_factorial

/-- `n.asc_factorial k = (n + k)! / n!` (as seen in `nat.asc_factorial_eq_div`), but implemented
recursively to allow for "quick" computation when using `norm_num`. This is closely related to
`pochhammer`, but much less general. -/
def asc_factorial (n : ℕ) : ℕ → ℕ
| 0 := 1
| (k + 1) := (n + k + 1) * asc_factorial k

@[simp] lemma asc_factorial_zero (n : ℕ) : n.asc_factorial 0 = 1 := rfl

@[simp] lemma zero_asc_factorial (k : ℕ) : (0 : ℕ).asc_factorial k = k! :=
begin
  induction k with t ht, refl,
  unfold asc_factorial, rw [ht, zero_add, nat.factorial_succ],
end

lemma asc_factorial_succ {n k : ℕ} : n.asc_factorial k.succ = (n + k + 1) * n.asc_factorial k := rfl

lemma succ_asc_factorial (n : ℕ) :
  ∀ k, (n + 1) * n.succ.asc_factorial k = (n + k + 1) * n.asc_factorial k
| 0 := by rw [add_zero, asc_factorial_zero, asc_factorial_zero]
| (k + 1) := by rw [asc_factorial, mul_left_comm, succ_asc_factorial, asc_factorial, succ_add,
  ←add_assoc]

/-- `n.asc_factorial k = (n + k)! / n!` but without ℕ-division. See `nat.asc_factorial_eq_div` for
the version with ℕ-division. -/
theorem factorial_mul_asc_factorial (n : ℕ) : ∀ k, n! * n.asc_factorial k = (n + k)!
| 0 := by rw [asc_factorial, add_zero, mul_one]
| (k + 1) := by rw [asc_factorial_succ, mul_left_comm, factorial_mul_asc_factorial, ← add_assoc,
  factorial]

/-- Avoid in favor of `nat.factorial_mul_asc_factorial` if you can. ℕ-division isn't worth it. -/
lemma asc_factorial_eq_div (n k : ℕ) : n.asc_factorial k = (n + k)! / n! :=
begin
  apply mul_left_cancel₀ (factorial_ne_zero n),
  rw factorial_mul_asc_factorial,
  exact (nat.mul_div_cancel' $ factorial_dvd_factorial $ le.intro rfl).symm
end

lemma asc_factorial_of_sub {n k : ℕ} (h : k < n) :
  (n - k) * (n - k).asc_factorial k = (n - (k + 1)).asc_factorial (k + 1) :=
begin
  set t := n - k.succ with ht,
  suffices h' : n - k = t.succ, by rw [←ht, h', succ_asc_factorial, asc_factorial_succ],
  rw [ht, succ_eq_add_one, ←tsub_tsub_assoc (succ_le_of_lt h) (succ_pos _), succ_sub_one],
end

lemma pow_succ_le_asc_factorial (n : ℕ) : ∀ (k : ℕ), (n + 1)^k ≤ n.asc_factorial k
| 0 := by rw [asc_factorial_zero, pow_zero]
| (k + 1) := begin
  rw pow_succ,
  exact nat.mul_le_mul (nat.add_le_add_right le_self_add _) (pow_succ_le_asc_factorial k),
end

lemma pow_lt_asc_factorial' (n k : ℕ) : (n + 1)^(k + 2) < n.asc_factorial (k + 2) :=
begin
  rw pow_succ,
  exact nat.mul_lt_mul (nat.add_lt_add_right (nat.lt_add_of_pos_right succ_pos') 1)
    (pow_succ_le_asc_factorial n _) (pow_pos succ_pos' _),
end

lemma pow_lt_asc_factorial (n : ℕ) : ∀ {k : ℕ}, 2 ≤ k → (n + 1)^k < n.asc_factorial k
| 0 := by rintro ⟨⟩
| 1 := by rintro (_ | ⟨_, ⟨⟩⟩)
| (k + 2) := λ _, pow_lt_asc_factorial' n k

lemma asc_factorial_le_pow_add (n : ℕ) : ∀ (k : ℕ), n.asc_factorial k ≤ (n + k)^k
| 0 := by rw [asc_factorial_zero, pow_zero]
| (k + 1) := begin
  rw [asc_factorial_succ, pow_succ],
  exact nat.mul_le_mul_of_nonneg_left ((asc_factorial_le_pow_add k).trans (nat.pow_le_pow_of_le_left
  (le_succ _) _)),
end

lemma asc_factorial_lt_pow_add (n : ℕ) : ∀ {k : ℕ}, 2 ≤ k → n.asc_factorial k < (n + k)^k
| 0 := by rintro ⟨⟩
| 1 := by rintro (_ | ⟨_, ⟨⟩⟩)
| (k + 2) := λ _, begin
  rw [asc_factorial_succ, pow_succ],
  refine nat.mul_lt_mul' (le_refl _) ((asc_factorial_le_pow_add n _).trans_lt
    (pow_lt_pow_of_lt_left (lt_add_one _) (succ_pos _))) (succ_pos _),
end

lemma asc_factorial_pos (n k : ℕ) : 0 < n.asc_factorial k :=
(pow_pos (succ_pos n) k).trans_le (pow_succ_le_asc_factorial n k)

end asc_factorial

section desc_factorial

/-- `n.desc_factorial k = n! / (n - k)!` (as seen in `nat.desc_factorial_eq_div`), but
implemented recursively to allow for "quick" computation when using `norm_num`. This is closely
related to `pochhammer`, but much less general. -/
def desc_factorial (n : ℕ) : ℕ → ℕ
| 0 := 1
| (k + 1) := (n - k) * desc_factorial k

@[simp] lemma desc_factorial_zero (n : ℕ) : n.desc_factorial 0 = 1 := rfl

@[simp] lemma desc_factorial_succ (n k : ℕ) :
  n.desc_factorial k.succ = (n - k) * n.desc_factorial k := rfl

lemma zero_desc_factorial_succ (k : ℕ) :
  (0 : ℕ).desc_factorial k.succ = 0 :=
by rw [desc_factorial_succ, zero_tsub, zero_mul]

@[simp] lemma desc_factorial_one (n : ℕ) :
  n.desc_factorial 1 = n :=
by rw [desc_factorial_succ, desc_factorial_zero, mul_one, tsub_zero]

@[simp] lemma succ_desc_factorial_succ (n : ℕ) :
  ∀ k : ℕ, (n + 1).desc_factorial (k + 1) = (n + 1) * n.desc_factorial k
| 0        := by rw [desc_factorial_zero, desc_factorial_one, mul_one]
| (succ k) := by rw [desc_factorial_succ, succ_desc_factorial_succ, desc_factorial_succ,
  succ_sub_succ, mul_left_comm]

lemma succ_desc_factorial (n : ℕ) :
  ∀ k, (n + 1 - k) * (n + 1).desc_factorial k = (n + 1) * n.desc_factorial k
| 0 := by rw [tsub_zero, desc_factorial_zero, desc_factorial_zero]
| (k + 1) := by rw [desc_factorial, succ_desc_factorial, desc_factorial_succ, succ_sub_succ,
  mul_left_comm]

lemma desc_factorial_self : ∀ n : ℕ, n.desc_factorial n = n!
| 0        := by rw [desc_factorial_zero, factorial_zero]
| (succ n) := by rw [succ_desc_factorial_succ, desc_factorial_self, factorial_succ]

@[simp] lemma desc_factorial_eq_zero_iff_lt {n : ℕ} : ∀ {k : ℕ}, n.desc_factorial k = 0 ↔ n < k
| 0        := by simp only [desc_factorial_zero, nat.one_ne_zero, nat.not_lt_zero]
| (succ k) := begin
  rw [desc_factorial_succ, mul_eq_zero, desc_factorial_eq_zero_iff_lt, lt_succ_iff,
    tsub_eq_zero_iff_le, lt_iff_le_and_ne, or_iff_left_iff_imp, and_imp],
  exact λ h _, h,
end

alias nat.desc_factorial_eq_zero_iff_lt ↔ _ nat.desc_factorial_of_lt

lemma add_desc_factorial_eq_asc_factorial (n : ℕ) :
  ∀ k : ℕ, (n + k).desc_factorial k = n.asc_factorial k
| 0        := by rw [asc_factorial_zero, desc_factorial_zero]
| (succ k) := by rw [nat.add_succ, succ_desc_factorial_succ, asc_factorial_succ,
  add_desc_factorial_eq_asc_factorial]

/-- `n.desc_factorial k = n! / (n - k)!` but without ℕ-division. See `nat.desc_factorial_eq_div`
for the version using ℕ-division. -/
theorem factorial_mul_desc_factorial : ∀ {n k : ℕ}, k ≤ n → (n - k)! * n.desc_factorial k = n!
| n        0        := λ _, by rw [desc_factorial_zero, mul_one, tsub_zero]
| 0        (succ k) := λ h, by { exfalso, exact not_succ_le_zero k h }
| (succ n) (succ k) := λ h, by rw [succ_desc_factorial_succ, succ_sub_succ, ←mul_assoc,
  mul_comm (n - k)!, mul_assoc, factorial_mul_desc_factorial (nat.succ_le_succ_iff.1 h),
    factorial_succ]

/-- Avoid in favor of `nat.factorial_mul_desc_factorial` if you can. ℕ-division isn't worth it. -/
lemma desc_factorial_eq_div {n k : ℕ} (h : k ≤ n) : n.desc_factorial k = n! / (n - k)! :=
begin
  apply mul_left_cancel₀ (factorial_ne_zero (n - k)),
  rw factorial_mul_desc_factorial h,
  exact (nat.mul_div_cancel' $ factorial_dvd_factorial $ nat.sub_le n k).symm,
end

lemma pow_sub_le_desc_factorial (n : ℕ) : ∀ (k : ℕ), (n + 1 - k)^k ≤ n.desc_factorial k
| 0 := by rw [desc_factorial_zero, pow_zero]
| (k + 1) := begin
  rw [desc_factorial_succ, pow_succ, succ_sub_succ],
  exact nat.mul_le_mul_of_nonneg_left (le_trans (nat.pow_le_pow_of_le_left
    (tsub_le_tsub_right (le_succ _) _) k) (pow_sub_le_desc_factorial k)),
end

lemma pow_sub_lt_desc_factorial' {n : ℕ} :
  ∀ {k : ℕ}, k + 2 ≤ n → (n - (k + 1))^(k + 2) < n.desc_factorial (k + 2)
| 0 := λ h, begin
  rw [desc_factorial_succ, pow_succ, pow_one, desc_factorial_one],
  exact nat.mul_lt_mul_of_pos_left (tsub_lt_self (lt_of_lt_of_le zero_lt_two h) zero_lt_one)
    (tsub_pos_of_lt h),
end
| (k + 1) := λ h, begin
  rw [desc_factorial_succ, pow_succ],
  refine nat.mul_lt_mul_of_pos_left ((nat.pow_le_pow_of_le_left (tsub_le_tsub_right
    (le_succ n) _) _).trans_lt _) (tsub_pos_of_lt h),
  rw succ_sub_succ,
  exact (pow_sub_lt_desc_factorial' ((le_succ _).trans h)),
end

lemma pow_sub_lt_desc_factorial {n : ℕ} :
  ∀ {k : ℕ}, 2 ≤ k → k ≤ n → (n + 1 - k)^k < n.desc_factorial k
| 0 := by rintro ⟨⟩
| 1 := by rintro (_ | ⟨_, ⟨⟩⟩)
| (k + 2) := λ _ h, by { rw succ_sub_succ, exact pow_sub_lt_desc_factorial' h }

lemma desc_factorial_le_pow (n : ℕ) : ∀ (k : ℕ), n.desc_factorial k ≤ n^k
| 0 := by rw [desc_factorial_zero, pow_zero]
| (k + 1) := begin
  rw [desc_factorial_succ, pow_succ],
  exact nat.mul_le_mul (nat.sub_le _ _) (desc_factorial_le_pow k),
end

lemma desc_factorial_lt_pow {n : ℕ} (hn : 1 ≤ n) : ∀ {k : ℕ}, 2 ≤ k → n.desc_factorial k < n^k
| 0 := by rintro ⟨⟩
| 1 := by rintro (_ | ⟨_, ⟨⟩⟩)
| (k + 2) := λ _, begin
  rw [desc_factorial_succ, pow_succ', mul_comm],
  exact nat.mul_lt_mul' (desc_factorial_le_pow _ _) (tsub_lt_self hn k.zero_lt_succ)
    (pow_pos hn _),
end

end desc_factorial

end nat


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin

! This file was ported from Lean 3 source module data.list.nat_antidiagonal
! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Nodup
import Mathbin.Data.List.Range

/-!
# Antidiagonals in ℕ × ℕ as lists

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

This file defines the antidiagonals of ℕ × ℕ as lists: the `n`-th antidiagonal is the list of
pairs `(i, j)` such that `i + j = n`. This is useful for polynomial multiplication and more
generally for sums going from `0` to `n`.

## Notes

Files `data.multiset.nat_antidiagonal` and `data.finset.nat_antidiagonal` successively turn the
`list` definition we have here into `multiset` and `finset`.
-/

open List Function Nat

namespace List

namespace Nat

#print List.Nat.antidiagonal /-
/-- The antidiagonal of a natural number `n` is the list of pairs `(i, j)` such that `i + j = n`. -/
def antidiagonal (n : ℕ) : List (ℕ × ℕ) :=
  (range (n + 1)).map fun i => (i, n - i)
#align list.nat.antidiagonal List.Nat.antidiagonal
-/

#print List.Nat.mem_antidiagonal /-
/-- A pair (i, j) is contained in the antidiagonal of `n` if and only if `i + j = n`. -/
@[simp]
theorem mem_antidiagonal {n : ℕ} {x : ℕ × ℕ} : x ∈ antidiagonal n ↔ x.1 + x.2 = n :=
  by
  rw [antidiagonal, mem_map]; constructor
  · rintro ⟨i, hi, rfl⟩
    rw [mem_range, lt_succ_iff] at hi
    exact add_tsub_cancel_of_le hi
  · rintro rfl
    refine' ⟨x.fst, _, _⟩
    · rw [mem_range, add_assoc, lt_add_iff_pos_right]
      exact zero_lt_succ _
    · exact Prod.ext rfl (add_tsub_cancel_left _ _)
#align list.nat.mem_antidiagonal List.Nat.mem_antidiagonal
-/

#print List.Nat.length_antidiagonal /-
/-- The length of the antidiagonal of `n` is `n + 1`. -/
@[simp]
theorem length_antidiagonal (n : ℕ) : (antidiagonal n).length = n + 1 := by
  rw [antidiagonal, length_map, length_range]
#align list.nat.length_antidiagonal List.Nat.length_antidiagonal
-/

#print List.Nat.antidiagonal_zero /-
/-- The antidiagonal of `0` is the list `[(0, 0)]` -/
@[simp]
theorem antidiagonal_zero : antidiagonal 0 = [(0, 0)] :=
  rfl
#align list.nat.antidiagonal_zero List.Nat.antidiagonal_zero
-/

#print List.Nat.nodup_antidiagonal /-
/-- The antidiagonal of `n` does not contain duplicate entries. -/
theorem nodup_antidiagonal (n : ℕ) : Nodup (antidiagonal n) :=
  (nodup_range _).map (@LeftInverse.injective ℕ (ℕ × ℕ) Prod.fst (fun i => (i, n - i)) fun i => rfl)
#align list.nat.nodup_antidiagonal List.Nat.nodup_antidiagonal
-/

#print List.Nat.antidiagonal_succ /-
@[simp]
theorem antidiagonal_succ {n : ℕ} :
    antidiagonal (n + 1) = (0, n + 1) :: (antidiagonal n).map (Prod.map Nat.succ id) :=
  by
  simp only [antidiagonal, range_succ_eq_map, map_cons, true_and_iff, Nat.add_succ_sub_one,
    add_zero, id.def, eq_self_iff_true, tsub_zero, map_map, Prod.map_mk]
  apply congr (congr rfl _) rfl
  ext <;> simp
#align list.nat.antidiagonal_succ List.Nat.antidiagonal_succ
-/

#print List.Nat.antidiagonal_succ' /-
theorem antidiagonal_succ' {n : ℕ} :
    antidiagonal (n + 1) = (antidiagonal n).map (Prod.map id Nat.succ) ++ [(n + 1, 0)] :=
  by
  simp only [antidiagonal, range_succ, add_tsub_cancel_left, map_append, append_assoc, tsub_self,
    singleton_append, map_map, map]
  congr 1
  apply map_congr
  simp (config := { contextual := true }) [le_of_lt, Nat.succ_eq_add_one, Nat.sub_add_comm]
#align list.nat.antidiagonal_succ' List.Nat.antidiagonal_succ'
-/

#print List.Nat.antidiagonal_succ_succ' /-
theorem antidiagonal_succ_succ' {n : ℕ} :
    antidiagonal (n + 2) =
      (0, n + 2) :: (antidiagonal n).map (Prod.map Nat.succ Nat.succ) ++ [(n + 2, 0)] :=
  by
  rw [antidiagonal_succ']
  simpa
#align list.nat.antidiagonal_succ_succ' List.Nat.antidiagonal_succ_succ'
-/

#print List.Nat.map_swap_antidiagonal /-
theorem map_swap_antidiagonal {n : ℕ} : (antidiagonal n).map Prod.swap = (antidiagonal n).reverse :=
  by
  rw [antidiagonal, map_map, Prod.swap, ← List.map_reverse, range_eq_range', reverse_range', ←
    range_eq_range', map_map]
  apply map_congr
  simp (config := { contextual := true }) [Nat.sub_sub_self, lt_succ_iff]
#align list.nat.map_swap_antidiagonal List.Nat.map_swap_antidiagonal
-/

end Nat

end List


-- El_limite_de_u_es_a_syss_el_de_u-a_es_0.lean
-- El límite de u es a syss el de u-a es 0.
-- José A. Alonso Jiménez
-- Sevilla, 16 de julio de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- En Lean, una sucesión u₀, u₁, u₂, ... se puede representar mediante
-- una función (u : ℕ → ℝ) de forma que u(n) es uₙ.
--
-- Se define que a es el límite de la sucesión u, por
--    def limite : (ℕ → ℝ) → ℝ → Prop :=
--    λ u a, ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - a| < ε
-- donde se usa la notación |x| para el valor absoluto de x
--    notation `|`x`|` := abs x
--
-- Demostrar que el límite de u(i) es a si y solo si el de u(i)-a es
-- 0.
-- ---------------------------------------------------------------------

import data.real.basic
import tactic

variable  {u : ℕ → ℝ}
variables {a c x : ℝ}

notation `|`x`|` := abs x

def limite : (ℕ → ℝ) → ℝ → Prop :=
λ u c, ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - c| < ε

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

example
  : limite u a ↔ limite (λ i, u i - a) 0 :=
begin
  rw iff_eq_eq,
  calc limite u a
       = ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - a| < ε       : rfl
   ... = ∀ ε > 0, ∃ N, ∀ n ≥ N, |(u n - a) - 0| < ε : by simp
   ... = limite (λ i, u i - a) 0                    : rfl,
end

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

example
  : limite u a ↔ limite (λ i, u i - a) 0 :=
begin
  split,
  { intros h ε hε,
    convert h ε hε,
    norm_num, },
  { intros h ε hε,
    convert h ε hε,
    norm_num, },
end

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

example
  : limite u a ↔ limite (λ i, u i - a) 0 :=
begin
  split;
  { intros h ε hε,
    convert h ε hε,
    norm_num, },
end

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

lemma limite_con_suma
  (c : ℝ)
  (h : limite u a)
  : limite (λ i, u i + c) (a + c) :=
λ ε hε, (by convert h ε hε; norm_num)

lemma CNS_limite_con_suma
  (c : ℝ)
  : limite u a ↔ limite (λ i, u i + c) (a + c) :=
begin
  split,
  { apply limite_con_suma },
  { intro h,
    convert limite_con_suma (-c) h; simp, },
end

example
  (u : ℕ → ℝ)
  (a : ℝ)
  : limite u a ↔ limite (λ i, u i - a) 0 :=
begin
  convert CNS_limite_con_suma (-a),
  simp,
end


-- Razonamiento sobre árboles binarios: La función espejo es involutiva
-- ====================================================================

import tactic

variable {α : Type}

-- ----------------------------------------------------
-- Ejercicio 1. Definir un tipo de dato para los
-- árboles binarios, con los constructores hoja y nodo.
-- ----------------------------------------------------

inductive arbol (α : Type) : Type
| hoja : α → arbol
| nodo : α → arbol → arbol → arbol

-- ----------------------------------------------------
-- Ejercicio 2. Abrir el espacio de nombres arbol
-- ----------------------------------------------------

namespace arbol

-- #print prefix arbol

-- ----------------------------------------------------
-- Ejercicio 3. Definir el árbol correspondiente a
--        3
--       / \
--      2   4
--     / \
--    1   5
-- ----------------------------------------------------

def ejArbol : arbol ℕ :=
  nodo 3 (nodo 2 (hoja 1) (hoja 5)) (hoja 4)

-- ----------------------------------------------------
-- Ejercicio 3. Definir la función
--    repr : arbol α → string
-- tal que (repr a) es la cadena que representa al
-- árbol a. Por ejemplo,
--     #eval repr ejArbol
--     Da: "N 3 (N 2 (H 1) (H 5)) (H 4)"
-- ----------------------------------------------------

def repr [has_repr α] : arbol α → string
| (hoja x)     := "H " ++ has_repr.repr x
| (nodo x i d) := "N " ++ has_repr.repr x ++ " (" ++ repr i ++ ") (" ++ repr d ++ ")"

-- #eval repr ejArbol

-- ----------------------------------------------------
-- Ejercicio 4. Declarar repr la función para
-- representar los árboles. Por ejemplo,
--    #eval ejArbol
--    -- Da: N 3 (N 2 (H 1) (H 5)) (H 4)
-- ----------------------------------------------------

instance [has_repr α] : has_repr (arbol α) := ⟨repr⟩

-- #eval ejArbol
-- -- Da: N 3 (N 2 (H 1) (H 5)) (H 4)

-- --------------------------------------------------------------
-- Ejercicio 5. Definir la función
--    espejo : arbol α → arbol α
-- tal que (espejo a) es la imagen especular de a. Por ejmplo,
--    #eval espejo ejArbol
--    -- Da: N 3 (H 4) (N 2 (H 5) (H 1))
-- ----------------------------------------------------

def espejo : arbol α → arbol α
| (hoja x)     := hoja x
| (nodo x i d) := nodo x (espejo d) (espejo i)

-- #eval espejo ejArbol
-- -- Da: N 3 (H 4) (N 2 (H 5) (H 1))

-- ----------------------------------------------------
-- Ejercicio 6. Declarar las siguientes variables:
-- + a i d como variables sobre árboles de tipo α.
-- + x como variable sobre elementos de tipo α.
-- ----------------------------------------------------

variables (a i d : arbol α)
variable  (x : α)

-- ----------------------------------------------------
-- Ejercicio 7. Demostrar los siguientes lemas
-- + espejo_1 :
--      espejo (hoja x) = hoja x
-- + espejo_2 :
--      espejo (nodo x i d) = nodo x (espejo d) (espejo i)
-- ----------------------------------------------------

@[simp]
lemma espejo_1 :
  espejo (hoja x) = hoja x :=
espejo.equations._eqn_1 x

@[simp]
lemma espejo_2 :
  espejo (nodo x i d) = nodo x (espejo d) (espejo i) :=
espejo.equations._eqn_2 x i d

-- ----------------------------------------------------
-- Ejercicio 8. Demostrar que
--    espejo (espejo a) = a
-- ----------------------------------------------------

-- 1ª demostración
example :
  espejo (espejo a) = a :=
begin
  induction a with x x i d Hi Hd,
  { rw espejo_1,
    rw espejo_1, },
  { rw espejo_2,
    rw espejo_2,
    rw Hi,
    rw Hd, },
end

-- 2ª demostración
example :
  espejo (espejo a) = a :=
begin
  induction a with x x i d Hi Hd,
  { calc espejo (espejo (hoja x))
         = espejo (hoja x)
             : by exact congr_arg espejo (espejo_1 x)
     ... = hoja x
             : by rw espejo_1, },
  { calc espejo (espejo (nodo x i d))
         = espejo (nodo x (espejo d) (espejo i))
             :by exact congr_arg espejo (espejo_2 i d x)
     ... = nodo x (espejo (espejo i)) (espejo (espejo d))
             :by rw espejo_2
     ... = nodo x i (espejo (espejo d))
             :by rw Hi
     ... = nodo x i d
             :by rw Hd, },
end

-- 3ª demostración
example :
  espejo (espejo a) = a :=
begin
  induction a with _ x i d Hi Hd,
  { simp, },
  { simp [Hi, Hd], },
end

-- 4ª demostración
example :
  espejo (espejo a) = a :=
by induction a ; simp [*]

-- 5ª demostración
example :
  espejo (espejo a) = a :=
arbol.rec_on a
  ( assume x,
    calc espejo (espejo (hoja x))
         = espejo (hoja x)
             : by exact congr_arg espejo (espejo_1 x)
     ... = hoja x
             : by rw espejo_1 )
  ( assume x i d,
    assume Hi : espejo (espejo i) = i,
    assume Hd : espejo (espejo d) = d,
    calc espejo (espejo (nodo x i d))
         = espejo (nodo x (espejo d) (espejo i))
             :by exact congr_arg espejo (espejo_2 i d x)
     ... = nodo x (espejo (espejo i)) (espejo (espejo d))
             :by rw espejo_2
     ... = nodo x i (espejo (espejo d))
             :by rw Hi
     ... = nodo x i d
             :by rw Hd )

-- 6ª demostración
example :
  espejo (espejo a) = a :=
arbol.rec_on a
  (λ x, by simp )
  (λ x i d Hi Hd, by simp [Hi,Hd])

-- 7ª demostración
lemma espejo_espejo :
  ∀ a : arbol α, espejo (espejo a) = a
| (hoja x)     := by simp
| (nodo x i d) := by simp [espejo_espejo i, espejo_espejo d]

-- ----------------------------------------------------
-- Ejercicio 9. Cerrar el espacio de nombres arbol.
-- ----------------------------------------------------

end arbol