import analysis.real tactic.norm_num algebra.group_power

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 := sorry

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

theorem Q3b : ∀ b : ℝ, ¬ (is_lub (S3b) b) := sorry

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) := sorry

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

theorem Q3d : is_lub S3d 2 := sorry


/-
open classical
variables {A B C : Prop}

example (h : ¬ B → ¬ A) : A → B :=
sorry

example (h : A → B) : ¬ A ∨ B :=
sorry
-/

open classical
variables {A B C : Prop}

example (h : ¬ B → ¬ A) : A → B :=
  assume hA: A,
  show B, from (
    by_contradiction(
      assume hnB: ¬ B,
      have hnA: ¬ A, from h(hnB),
      show false, from hnA(hA)
    )
  )

example (h : A → B) : ¬ A ∨ B :=
  or.elim(em(A))(
    λ hA: A,
    have B, from h(hA),
    show ¬ A ∨ B, from or.inr this
  )(
    assume : ¬ A, or.inl this
  )


import tactic
import data.nat.prime
open nat

theorem simproot2 {x y : ℕ} (coprime : gcd x y = 1) : x^2 ≠ 2 * y^2 :=
  -- For Contradiction, will show false
  assume contr : x^2 = 2 * y^2,
  have twodivxsquare : 2 ∣ x^2,
  by exact dvd.intro (y ^ 2) (eq.symm contr),
  have twodivx: 2 ∣ x,
  from prime.dvd_of_dvd_pow prime_two twodivxsquare,
  exists.elim twodivx $
  assume (z : nat) (introz : x = 2 * z),
  have subst : 2 * (2 * z^2) = 2 * y^2,
  by
  begin
  rw introz at contr,
  simp[eq.symm contr, nat.pow],
  simp [nat.pow_succ, mul_comm, mul_assoc, mul_left_comm],
  end,
  have subst2 : 2 * z^2 = y^2,
  from eq_of_mul_eq_mul_left dec_trivial subst,
  have revsubst : y^2 = 2 * z^2,
  by rw ←subst2,
  have twodivysquare : 2 ∣ y^2,
  by exact dvd.intro (z ^ 2) (eq.symm revsubst),
  have twodivy: 2 ∣ y,
  from prime.dvd_of_dvd_pow prime_two twodivysquare,
  have fin : 2 ∣ gcd x y,
  from dvd_gcd twodivx twodivy,
  have last : 2 ∣ 1,
  from
  begin
  rw coprime at fin,
  use fin,
  end,
  show false,
  from absurd last dec_trivial


import basic_definitions.sub_module
import Tools.tools
import linear_algebra.matrix
import group_theory.group_action
import init.algebra.functions
--set_option trace.simplify.rewrite true   --- a reprednre peut etre un peu !
open_locale big_operators
namespace general
universes u v  w w'
variables (G : Type u) (R : Type v) (X : Type w) [group G] [ring R]  [mul_action G X]

/--
    Permutation representation : let `•` an action of `G` on `X`. Let `R` be a ring,
    let `M = R → X` be the free-module over `M` of base `X`. For `v : R → X` and `g ∈ G` the formula
    `ρ g v := g⁻¹ • v` define a permutation de representation.
 -/

def rho  (g : G)  : (X → R) → (X → R)  := λ  v x,  v (g⁻¹ •  x)

def rho_linear (g : G) : (X → R) →ₗ[R] (X → R) := {
  to_fun := rho G R X g,
  add := by { intros, exact rfl},
  smul := by {intros, exact rfl}
}

@[simp]lemma rho_apply (g : G)(v : X → R)(x : X) : rho G R  X g v x  = v (g⁻¹ • x) := rfl

@[simp]lemma rho_mul (σ τ  : G) : rho G R X (σ * τ) = rho G R X σ  ∘  rho G R X τ := begin
        ext v x, rw rho_apply, rw  mul_inv_rev, rw  mul_smul, exact rfl,
end
@[simp]lemma rho_one  : rho G R X (1 : G) = id := begin
   ext x v, rw rho_apply, rw one_inv, rw one_smul, exact rfl,
end
@[simp]lemma rho_right_inv (g : G) : (rho G R X g : (X → R) →  (X → R)) ∘  (rho G R X g⁻¹) = id  := begin
    rw ← rho_mul, rw mul_inv_self, rw rho_one,
end

@[simp]lemma rho_left_inv  (g : G) : (  rho G R X (g⁻¹ )  :
(X → R) → (X → R) ) ∘    (rho G R X g : (X → R) → (X → R))  = id  :=
begin
    rw ← rho_mul, rw inv_mul_self, rw rho_one,
end

def Perm : group_representation G R (X → R) := {
  to_fun    :=  rho_linear G R X,
  map_one'  :=
    begin
        unfold rho_linear,congr,
        exact rho_one G R X,
    end,
  map_mul'  :=
    begin
        unfold rho_linear, intros, congr,
        exact rho_mul G R X _ _,
    end
}
variables (g : G) (x y : X → R)

example (g : G) (x y : X → R) (r : R) : true := begin
        let ρ := @Perm G R X,
        have f : ρ 1 = 1,
            rw ρ.map_one,
        have : ρ g (x+y) = ρ g x +ρ g y,
            rw (ρ g).map_add,

        trivial,
        end
end general
namespace finite_action
open classical_basis general
universes u v  w w'
variables {G : Type u} (R : Type v) (X : Type w) [fintype X] [decidable_eq X][group G] [comm_ring R]  [mul_action G X]
/-!
    Goal : study more the representation
-/

@[simp]theorem action_on_basis (g : G)(x : X) : rho G R X g (ε x) = ε (g • x) := begin
    funext y, simp,
    unfold ε,
    split_ifs,
        {exact rfl},
        {rw [h, ← mul_smul,mul_inv_self,one_smul] at h_1, trivial},
        {rw [← h_1, ← mul_smul,inv_mul_self,one_smul] at h, trivial},
        {exact rfl},
end
@[simp]theorem action_on_basis_apply (g : G) (x y : X) : rho G R X g (ε x) y = if g • x = y then 1 else 0 :=
begin simp, exact rfl,
   -- rw action_on_basis, exact rfl,
end
@[simp]theorem trace (g : G) :  ∑ (x : X), rho G R X g (ε x) x =  fintype.card {x : X |  g • x = x } :=
begin simp, exact rfl,
    --have r :  (λ (x : X), rho G R X g (ε x) x) = λ x,if g • x = x then 1 else 0,
    --   funext, rw action_on_basis_apply,
    --rw r,
    --rw finset.sum_boole,simp, exact rfl, --- filter ?
end
variables (g : G)
@[simp]lemma  Perm_ext (g : G) :  rho G R X g = (Perm G R X) g    := rfl

end finite_action


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

/-!
# `max` and `min`

This file proves basic properties about maxima and minima on a `linear_order`.

## Tags

min, max
-/

universes u v
variables {α : Type u} {β : Type v}

attribute [simp] max_eq_left max_eq_right min_eq_left min_eq_right

section
variables [linear_order α] [linear_order β] {f : α → β} {s : set α} {a b c d : α}

-- translate from lattices to linear orders (sup → max, inf → min)
@[simp] lemma le_min_iff : c ≤ min a b ↔ c ≤ a ∧ c ≤ b := le_inf_iff
@[simp] lemma max_le_iff : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := sup_le_iff
lemma max_le_max : a ≤ c → b ≤ d → max a b ≤ max c d := sup_le_sup
lemma min_le_min : a ≤ c → b ≤ d → min a b ≤ min c d := inf_le_inf
lemma le_max_of_le_left : a ≤ b → a ≤ max b c := le_sup_of_le_left
lemma le_max_of_le_right : a ≤ c → a ≤ max b c := le_sup_of_le_right
lemma lt_max_of_lt_left (h : a < b) : a < max b c := h.trans_le (le_max_left b c)
lemma lt_max_of_lt_right (h : a < c) : a < max b c := h.trans_le (le_max_right b c)
lemma min_le_of_left_le : a ≤ c → min a b ≤ c := inf_le_of_left_le
lemma min_le_of_right_le : b ≤ c → min a b ≤ c := inf_le_of_right_le
lemma min_lt_of_left_lt (h : a < c) : min a b < c := (min_le_left a b).trans_lt h
lemma min_lt_of_right_lt (h : b < c) : min a b < c := (min_le_right a b).trans_lt h
lemma max_min_distrib_left : max a (min b c) = min (max a b) (max a c) := sup_inf_left
lemma max_min_distrib_right : max (min a b) c = min (max a c) (max b c) := sup_inf_right
lemma min_max_distrib_left : min a (max b c) = max (min a b) (min a c) := inf_sup_left
lemma min_max_distrib_right : min (max a b) c = max (min a c) (min b c) := inf_sup_right
lemma min_le_max : min a b ≤ max a b := le_trans (min_le_left a b) (le_max_left a b)

@[simp] lemma min_eq_left_iff : min a b = a ↔ a ≤ b := inf_eq_left
@[simp] lemma min_eq_right_iff : min a b = b ↔ b ≤ a := inf_eq_right
@[simp] lemma max_eq_left_iff : max a b = a ↔ b ≤ a := sup_eq_left
@[simp] lemma max_eq_right_iff : max a b = b ↔ a ≤ b := sup_eq_right

/-- For elements `a` and `b` of a linear order, either `min a b = a` and `a ≤ b`,
    or `min a b = b` and `b < a`.
    Use cases on this lemma to automate linarith in inequalities -/
lemma min_cases (a b : α) : min a b = a ∧ a ≤ b ∨ min a b = b ∧ b < a :=
begin
  by_cases a ≤ b,
  { left,
    exact ⟨min_eq_left h, h⟩ },
  { right,
    exact ⟨min_eq_right (le_of_lt (not_le.mp h)), (not_le.mp h)⟩ }
end

/-- For elements `a` and `b` of a linear order, either `max a b = a` and `b ≤ a`,
    or `max a b = b` and `a < b`.
    Use cases on this lemma to automate linarith in inequalities -/
lemma max_cases (a b : α) : max a b = a ∧ b ≤ a ∨ max a b = b ∧ a < b :=
@min_cases (order_dual α) _ a b

lemma min_eq_iff : min a b = c ↔ a = c ∧ a ≤ b ∨ b = c ∧ b ≤ a :=
begin
  split,
  { intro h,
    refine or.imp (λ h', _) (λ h', _) (le_total a b);
    exact ⟨by simpa [h'] using h, h'⟩ },
  { rintro (⟨rfl, h⟩|⟨rfl, h⟩);
    simp [h] }
end

lemma max_eq_iff : max a b = c ↔ a = c ∧ b ≤ a ∨ b = c ∧ a ≤ b :=
@min_eq_iff (order_dual α) _ a b c

/-- An instance asserting that `max a a = a` -/
instance max_idem : is_idempotent α max := by apply_instance -- short-circuit type class inference

/-- An instance asserting that `min a a = a` -/
instance min_idem : is_idempotent α min := by apply_instance -- short-circuit type class inference

@[simp] lemma max_lt_iff : max a b < c ↔ (a < c ∧ b < c) :=
sup_lt_iff

@[simp] lemma lt_min_iff : a < min b c ↔ (a < b ∧ a < c) :=
lt_inf_iff

@[simp] lemma lt_max_iff : a < max b c ↔ a < b ∨ a < c :=
lt_sup_iff

@[simp] lemma min_lt_iff : min a b < c ↔ a < c ∨ b < c :=
@lt_max_iff (order_dual α) _ _ _ _

@[simp] lemma min_le_iff : min a b ≤ c ↔ a ≤ c ∨ b ≤ c :=
inf_le_iff

@[simp] lemma le_max_iff : a ≤ max b c ↔ a ≤ b ∨ a ≤ c :=
@min_le_iff (order_dual α) _ _ _ _

lemma min_lt_max : min a b < max a b ↔ a ≠ b := inf_lt_sup

lemma max_lt_max (h₁ : a < c) (h₂ : b < d) : max a b < max c d :=
by simp [lt_max_iff, max_lt_iff, *]

lemma min_lt_min (h₁ : a < c) (h₂ : b < d) : min a b < min c d :=
@max_lt_max (order_dual α) _ _ _ _ _ h₁ h₂

theorem min_right_comm (a b c : α) : min (min a b) c = min (min a c) b :=
right_comm min min_comm min_assoc a b c

theorem max.left_comm (a b c : α) : max a (max b c) = max b (max a c) :=
left_comm max max_comm max_assoc a b c

theorem max.right_comm (a b c : α) : max (max a b) c = max (max a c) b :=
right_comm max max_comm max_assoc a b c

lemma monotone_on.map_max (hf : monotone_on f s) (ha : a ∈ s) (hb : b ∈ s) :
  f (max a b) = max (f a) (f b) :=
by cases le_total a b; simp only [max_eq_right, max_eq_left, hf ha hb, hf hb ha, h]

lemma monotone_on.map_min (hf : monotone_on f s) (ha : a ∈ s) (hb : b ∈ s) :
  f (min a b) = min (f a) (f b) :=
hf.dual.map_max ha hb

lemma antitone_on.map_max (hf : antitone_on f s) (ha : a ∈ s) (hb : b ∈ s) :
  f (max a b) = min (f a) (f b) :=
hf.dual_right.map_max ha hb

lemma antitone_on.map_min (hf : antitone_on f s) (ha : a ∈ s) (hb : b ∈ s) :
  f (min a b) = max (f a) (f b) :=
hf.dual.map_max ha hb

lemma monotone.map_max (hf : monotone f) : f (max a b) = max (f a) (f b) :=
by cases le_total a b; simp [h, hf h]

lemma monotone.map_min (hf : monotone f) : f (min a b) = min (f a) (f b) :=
hf.dual.map_max

lemma antitone.map_max (hf : antitone f) : f (max a b) = min (f a) (f b) :=
by cases le_total a b; simp [h, hf h]

lemma antitone.map_min (hf : antitone f) : f (min a b) = max (f a) (f b) :=
hf.dual.map_max

lemma min_rec {p : α → Prop} {x y : α} (hx : x ≤ y → p x) (hy : y ≤ x → p y) : p (min x y) :=
(le_total x y).rec (λ h, (min_eq_left h).symm.subst (hx h))
  (λ h, (min_eq_right h).symm.subst (hy h))

lemma max_rec {p : α → Prop} {x y : α} (hx : y ≤ x → p x) (hy : x ≤ y → p y) : p (max x y) :=
@min_rec (order_dual α) _ _ _ _ hx hy

lemma min_rec' (p : α → Prop) {x y : α} (hx : p x) (hy : p y) : p (min x y) :=
min_rec (λ _, hx) (λ _, hy)

lemma max_rec' (p : α → Prop) {x y : α} (hx : p x) (hy : p y) : p (max x y) :=
max_rec (λ _, hx) (λ _, hy)

theorem min_choice (a b : α) : min a b = a ∨ min a b = b :=
by cases le_total a b; simp *

theorem max_choice (a b : α) : max a b = a ∨ max a b = b :=
@min_choice (order_dual α) _ a b

lemma le_of_max_le_left {a b c : α} (h : max a b ≤ c) : a ≤ c :=
le_trans (le_max_left _ _) h

lemma le_of_max_le_right {a b c : α} (h : max a b ≤ c) : b ≤ c :=
le_trans (le_max_right _ _) h

lemma max_commutative : commutative (max : α → α → α) :=
max_comm

lemma max_associative : associative (max : α → α → α) :=
max_assoc

lemma max_left_commutative : left_commutative (max : α → α → α) :=
max_left_comm

lemma min_commutative : commutative (min : α → α → α) :=
min_comm

lemma min_associative : associative (min : α → α → α) :=
min_assoc

lemma min_left_commutative : left_commutative (min : α → α → α) :=
min_left_comm

end


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.lattice
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Accumulate

The function `accumulate` takes a set `s` and returns `⋃ y ≤ x, s y`.
-/

namespace set

/-- `accumulate s` is the union of `s y` for `y ≤ x`. -/
def accumulate {α : Type u_1} {β : Type u_2} [HasLessEq α] (s : α → set β) (x : α) : set β :=
  Union fun (y : α) => Union fun (H : y ≤ x) => s y

theorem accumulate_def {α : Type u_1} {β : Type u_2} {s : α → set β} [HasLessEq α] {x : α} : accumulate s x = Union fun (y : α) => Union fun (H : y ≤ x) => s y :=
  rfl

@[simp] theorem mem_accumulate {α : Type u_1} {β : Type u_2} {s : α → set β} [HasLessEq α] {x : α} {z : β} : z ∈ accumulate s x ↔ ∃ (y : α), ∃ (H : y ≤ x), z ∈ s y :=
  mem_bUnion_iff

theorem subset_accumulate {α : Type u_1} {β : Type u_2} {s : α → set β} [preorder α] {x : α} : s x ⊆ accumulate s x :=
  fun (z : β) => mem_bUnion le_rfl

theorem monotone_accumulate {α : Type u_1} {β : Type u_2} {s : α → set β} [preorder α] : monotone (accumulate s) :=
  fun (x y : α) (hxy : x ≤ y) =>
    bUnion_subset_bUnion_left fun (z : α) (hz : z ∈ fun (y : α) => preorder.le y x) => le_trans hz hxy

theorem bUnion_accumulate {α : Type u_1} {β : Type u_2} {s : α → set β} [preorder α] (x : α) : (Union fun (y : α) => Union fun (H : y ≤ x) => accumulate s y) = Union fun (y : α) => Union fun (H : y ≤ x) => s y :=
  subset.antisymm (bUnion_subset fun (x_1 : α) (hx : x_1 ∈ fun (y : α) => preorder.le y x) => monotone_accumulate hx)
    (bUnion_subset_bUnion_right fun (x_1 : α) (hx : x_1 ∈ fun (y : α) => preorder.le y x) => subset_accumulate)

theorem Union_accumulate {α : Type u_1} {β : Type u_2} {s : α → set β} [preorder α] : (Union fun (x : α) => accumulate s x) = Union fun (x : α) => s x := sorry


/-
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 set_theory.game.basic
import set_theory.ordinal.natural_ops

/-!
# Ordinals as games

We define the canonical map `ordinal → pgame`, where every ordinal is mapped to the game whose left
set consists of all previous ordinals.

The map to surreals is defined in `ordinal.to_surreal`.

# Main declarations

- `ordinal.to_pgame`: The canonical map between ordinals and pre-games.
- `ordinal.to_pgame_embedding`: The order embedding version of the previous map.
-/

universe u

open pgame

open_locale natural_ops pgame

namespace ordinal

/-- Converts an ordinal into the corresponding pre-game. -/
noncomputable! def to_pgame : Π o : ordinal.{u}, pgame.{u}
| o := ⟨o.out.α, pempty, λ x, let hwf := ordinal.typein_lt_self x in
        (typein (<) x).to_pgame, pempty.elim⟩
using_well_founded { dec_tac := tactic.assumption }

theorem to_pgame_def (o : ordinal) :
  o.to_pgame = ⟨o.out.α, pempty, λ x, (typein (<) x).to_pgame, pempty.elim⟩ :=
by rw to_pgame

@[simp] theorem to_pgame_left_moves (o : ordinal) : o.to_pgame.left_moves = o.out.α :=
by rw [to_pgame, left_moves]

@[simp] theorem to_pgame_right_moves (o : ordinal) : o.to_pgame.right_moves = pempty :=
by rw [to_pgame, right_moves]

instance : is_empty (to_pgame 0).left_moves :=
by { rw to_pgame_left_moves, apply_instance }

instance (o : ordinal) : is_empty o.to_pgame.right_moves :=
by { rw to_pgame_right_moves, apply_instance }

/-- Converts an ordinal less than `o` into a move for the `pgame` corresponding to `o`, and vice
versa. -/
noncomputable def to_left_moves_to_pgame {o : ordinal} : set.Iio o ≃ o.to_pgame.left_moves :=
(enum_iso_out o).to_equiv.trans (equiv.cast (to_pgame_left_moves o).symm)

@[simp] theorem to_left_moves_to_pgame_symm_lt {o : ordinal} (i : o.to_pgame.left_moves) :
  ↑(to_left_moves_to_pgame.symm i) < o :=
(to_left_moves_to_pgame.symm i).prop

theorem to_pgame_move_left_heq {o : ordinal} :
  o.to_pgame.move_left == λ x : o.out.α, (typein (<) x).to_pgame :=
by { rw to_pgame, refl }

@[simp] theorem to_pgame_move_left' {o : ordinal} (i) :
  o.to_pgame.move_left i = (to_left_moves_to_pgame.symm i).val.to_pgame :=
(congr_heq to_pgame_move_left_heq.symm (cast_heq _ i)).symm

theorem to_pgame_move_left {o : ordinal} (i) :
  o.to_pgame.move_left (to_left_moves_to_pgame i) = i.val.to_pgame :=
by simp

theorem to_pgame_lf {a b : ordinal} (h : a < b) : a.to_pgame ⧏ b.to_pgame :=
by { convert move_left_lf (to_left_moves_to_pgame ⟨a, h⟩), rw to_pgame_move_left }

theorem to_pgame_le {a b : ordinal} (h : a ≤ b) : a.to_pgame ≤ b.to_pgame :=
begin
  refine le_iff_forall_lf.2 ⟨λ i, _, is_empty_elim⟩,
  rw to_pgame_move_left',
  exact to_pgame_lf ((to_left_moves_to_pgame_symm_lt i).trans_le h)
end

theorem to_pgame_lt {a b : ordinal} (h : a < b) : a.to_pgame < b.to_pgame :=
⟨to_pgame_le h.le, to_pgame_lf h⟩

@[simp] theorem to_pgame_lf_iff {a b : ordinal} : a.to_pgame ⧏ b.to_pgame ↔ a < b :=
⟨by { contrapose, rw [not_lt, not_lf], exact to_pgame_le }, to_pgame_lf⟩

@[simp] theorem to_pgame_le_iff {a b : ordinal} : a.to_pgame ≤ b.to_pgame ↔ a ≤ b :=
⟨by { contrapose, rw [not_le, pgame.not_le], exact to_pgame_lf }, to_pgame_le⟩

@[simp] theorem to_pgame_lt_iff {a b : ordinal} : a.to_pgame < b.to_pgame ↔ a < b :=
⟨by { contrapose, rw not_lt, exact λ h, not_lt_of_le (to_pgame_le h) }, to_pgame_lt⟩

@[simp] theorem to_pgame_equiv_iff {a b : ordinal} : a.to_pgame ≈ b.to_pgame ↔ a = b :=
by rw [pgame.equiv, le_antisymm_iff, to_pgame_le_iff, to_pgame_le_iff]

theorem to_pgame_injective : function.injective ordinal.to_pgame :=
λ a b h, to_pgame_equiv_iff.1 $ equiv_of_eq h

@[simp] theorem to_pgame_eq_iff {a b : ordinal} : a.to_pgame = b.to_pgame ↔ a = b :=
to_pgame_injective.eq_iff

/-- The order embedding version of `to_pgame`. -/
@[simps] noncomputable def to_pgame_embedding : ordinal.{u} ↪o pgame.{u} :=
{ to_fun := ordinal.to_pgame,
  inj' := to_pgame_injective,
  map_rel_iff' := @to_pgame_le_iff }

/-- The sum of ordinals as games corresponds to natural addition of ordinals. -/
theorem to_pgame_add : ∀ a b : ordinal.{u}, a.to_pgame + b.to_pgame ≈ (a ♯ b).to_pgame
| a b := begin
  refine ⟨le_of_forall_lf (λ i, _) is_empty_elim, le_of_forall_lf (λ i, _) is_empty_elim⟩,
  { apply left_moves_add_cases i;
    intro i;
    let wf := to_left_moves_to_pgame_symm_lt i;
    try { rw add_move_left_inl }; try { rw add_move_left_inr };
    rw [to_pgame_move_left', lf_congr_left (to_pgame_add _ _), to_pgame_lf_iff],
    { exact nadd_lt_nadd_right wf _ },
    { exact nadd_lt_nadd_left wf _ } },
  { rw to_pgame_move_left',
    rcases lt_nadd_iff.1 (to_left_moves_to_pgame_symm_lt i) with ⟨c, hc, hc'⟩ | ⟨c, hc, hc'⟩;
    rw [←to_pgame_le_iff, ←le_congr_right (to_pgame_add _ _)] at hc';
    apply lf_of_le_of_lf hc',
    { apply add_lf_add_right,
      rwa to_pgame_lf_iff },
    { apply add_lf_add_left,
      rwa to_pgame_lf_iff } }
end
using_well_founded { dec_tac := `[solve_by_elim [psigma.lex.left, psigma.lex.right]] }

@[simp] theorem to_pgame_add_mk (a b : ordinal) :
  ⟦a.to_pgame⟧ + ⟦b.to_pgame⟧ = ⟦(a ♯ b).to_pgame⟧ :=
quot.sound (to_pgame_add a b)

end ordinal


import data.int.basic data.nat.gcd

namespace int

lemma dvd_mod_iff {k m n : ℤ} (h : k ∣ n) : k ∣ m % n ↔ k ∣ m :=
let t := @dvd_add_iff_left _ _ _ (m % n) _ (dvd_trans h (dvd_mul_right n (m / n))) in
by rwa mod_add_div at t

@[simp] lemma gcd_zero_left (a : ℤ) : gcd 0 a = a.nat_abs := nat.gcd_zero_left _

@[simp] lemma  gcd_zero_right (a : ℤ) : gcd a 0 = a.nat_abs := nat.gcd_zero_right _

@[simp] lemma gcd_one_left (a : ℤ) : gcd 1 a = 1 := nat.gcd_one_left _

@[simp] lemma gcd_one_right (a : ℤ) : gcd a 1 = 1 := nat.gcd_one_right _

lemma gcd_comm (a b : ℤ) : gcd a b = gcd b a := nat.gcd_comm _ _

lemma gcd_mul_left (a b c : ℤ) : gcd (a * b) (a * c) = a.nat_abs * gcd b c :=
by unfold gcd;
  rw [nat_abs_mul, nat_abs_mul, nat.gcd_mul_left]

lemma gcd_mul_right (a b c : ℤ) : gcd (b * a) (c * a) = gcd b c * a.nat_abs :=
by rw [mul_comm b, mul_comm c, gcd_mul_left, mul_comm (gcd b c)]

lemma gcd_pos_of_ne_zero_left {a : ℤ} (b : ℤ) (hb : b ≠ 0) : 0 < gcd a b :=
by unfold gcd; exact nat.gcd_pos_of_pos_right _ (nat_abs_pos_of_ne_zero hb)

@[simp] lemma gcd_neg (a b : ℤ) : gcd (-a) b = gcd a b :=
by unfold gcd; rw nat_abs_neg

@[simp] lemma gcd_neg' (a b : ℤ) : gcd a (-b) = gcd a b :=
by unfold gcd; rw nat_abs_neg

lemma gcd_dvd_left (a b : ℤ) : (gcd a b : ℤ) ∣ a :=
dvd_nat_abs.1 $ int.coe_nat_dvd.2 $ nat.gcd_dvd_left _ _

lemma gcd_dvd_right (a b : ℤ) : (gcd a b : ℤ) ∣ b :=
dvd_nat_abs.1 $ int.coe_nat_dvd.2 $ nat.gcd_dvd_right _ _

lemma dvd_gcd {a b c : ℤ} (hb : a ∣ b) (hc : a ∣ c) : a ∣ gcd b c :=
nat_abs_dvd.1 $ int.coe_nat_dvd.2 (begin
  rw [← nat_abs_dvd, ← dvd_nat_abs, int.coe_nat_dvd] at hb hc,
  exact nat.dvd_gcd hb hc
end)

lemma gcd_mod (a b : ℤ) : gcd (b % a) a = gcd a b:=
nat.dvd_antisymm
  (int.coe_nat_dvd.1 (dvd_gcd (gcd_dvd_right (b % a) a)
    ((dvd_mod_iff (gcd_dvd_right (b % a) a)).1 (gcd_dvd_left (b % a) a))))
  (int.coe_nat_dvd.1 (dvd_gcd ((dvd_mod_iff (gcd_dvd_left a b)).2
    (gcd_dvd_right a b)) (gcd_dvd_left a b)))

lemma gcd_assoc (a b c : ℤ) : gcd (gcd a b) c = gcd a (gcd b c) := nat.gcd_assoc _ _ _

end int


import data.real.basic

/-
 A large rectangle in the plane is partitioned into smaller rectangles, each of which has either integer
height or integer width (or both). Prove that the large rectangle also has this property.
-/

structure point := mk ::
(x : ℝ) (y : ℝ)

structure rect := mk ::
(bottom_left : point)
(top_right : point)
(positive_width : 0 < top_right.x - bottom_left.x)
(positive_height : 0 < top_right.y - bottom_left.y)

def rect_width (r : rect) : ℝ := r.top_right.x - r.bottom_left.x
def rect_height (r : rect) : ℝ := r.top_right.y - r.bottom_left.y

def rect_contains_point (r : rect) (p : point) : Prop :=
  r.bottom_left.x ≤ p.x ∧ p.x < r.top_right.x ∧
  r.bottom_left.y ≤ p.y ∧ p.y < r.top_right.y

def rects_intersect (r1 : rect) (r2 : rect) : Prop :=
  ∃ p : point, rect_contains_point r1 p ∧ rect_contains_point r2 p

theorem integers_and_rectangles
  (big_rect : rect)
  (n : ℕ)
  (small_rects : fin n → rect)
  (h_cover : ∀ p : point, rect_contains_point big_rect p →
             ∃ m : fin n, rect_contains_point (small_rects m) p)
  (h_no_overlap : ∀ m1 m2 : fin n, m1 ≠ m2 →
                  ¬ rects_intersect (small_rects m1) (small_rects m2))
  (h_integer_sides : ∀ m : fin n,
                     ∃ k : ℕ, rect_width (small_rects m) = k ∨
                              rect_height (small_rects m) = k) :
  ∃k : ℕ, rect_width big_rect = k ∨ rect_height big_rect = k :=
begin
  sorry
end


import tactic.norm_num

/-
Bulgarian Mathematical Olympiad 1998, Problem 1

Find the least natural number n (n ≥ 3) with the following property:
for any coloring in 2 colors of n distinct collinear points A_1, A_2, ..., A_n,
there exist three points A_i, A_j, A_{2j - i}, 1 ≤ i < 2j - i ≤ n, which are colored
the same color.

Solution: 9

-/

def coloring_has_desired_points (n: ℕ) (hn: 2 < n) (f: fin n → fin 2) : Prop :=
  ∃ i j : fin n,
  (∃ c : fin 2,
  (i < j ∧
   2 * j.val + 1 < n + i.val ∧
   f i = c ∧ f j = c ∧
   f (⟨2, hn⟩ * j + ⟨1, lt_trans one_lt_two hn⟩ - i) = c))

theorem bulgaria1998_q1a (f: fin 9 → fin 2) : coloring_has_desired_points 9 (by norm_num) f :=
begin
  sorry
end

def coloring_of_eight : fin 8 → fin 2
| ⟨0, _⟩ := 0
| ⟨1, _⟩ := 1
| ⟨2, _⟩ := 0
| ⟨3, _⟩ := 1
| ⟨4, _⟩ := 1
| ⟨5, _⟩ := 0
| ⟨6, _⟩ := 1
| ⟨7, _⟩ := 0
| _ := 0 -- unreachable

theorem bulgaria1998_q1b :
  ∃ f: fin 8 → fin 2, (¬ coloring_has_desired_points 8 (by norm_num) f) :=
begin
  use coloring_of_eight,
  intro h,
  obtain ⟨i, j, c, hij1, hij2, hc1, hc2, hc3⟩ := h,
  sorry
end


import number_theory.padics.padic_norm
import basic
import order.filter.basic
import analysis.special_functions.log.base
import analysis.normed.ring.seminorm
import data.nat.digits

open_locale big_operators

/-!
# Ostrowski's theorem for ℚ

This file states some basic lemmas about mul_ring_norm ℚ

-/

noncomputable theory

variable {f : mul_ring_norm ℚ}

-- TODO: remove this
-- I think this is a missing lemma in mathlib and maybe we can use this for now.
-- (Done)
lemma f_mul_eq : mul_eq f := f.map_mul'

-- The norm of -1 is 1
-- (Done)
lemma norm_neg_one_eq_one : f (-1) = 1 :=
begin
  have H₁ : f (-1) * f (-1) = 1,
  calc
    f (-1) * f (-1)  = f ((-1) * (-1)) : by simp
    ... = f 1 : by norm_num
    ... = 1 : f.map_one',
  have H₂: f (-1) ≥ 0 := map_nonneg f (-1),
  rw mul_self_eq_one_iff at H₁,
  cases H₁,
  { exact H₁ },
  { rw H₁ at H₂,
    have h' : ¬(-1 ≥ (0 : ℝ)) := by norm_num,
    contradiction },
end

-- If x is non-zero, then the norm of x is larger than zero.
-- (Done)
lemma norm_pos_of_ne_zero {x : ℚ} (h : x ≠ 0) : f x > 0 :=
lt_of_le_of_ne (map_nonneg f x) (λ h', h (f.eq_zero_of_map_eq_zero' x h'.symm))

--TODO: generalise to division rings, get rid of field_simp
-- (Done)
lemma ring_norm.div_eq (p : ℚ) {q : ℚ} (hq : q ≠ 0) : f (p / q) = (f p) / (f q) :=
begin
  have H : f q ≠ 0,
  { intro fq0,
    have := f.eq_zero_of_map_eq_zero' q fq0,
    exact hq this },
  calc f (p / q) = f (p / q) * f q / f q : by field_simp
  ... = f (p / q * q)  / f q : by simp
  ... = f p / f q : by field_simp,
end

-- This lemma look a bit strange to me.
-- (Done)
lemma int_norm_bound_iff_nat_norm_bound :
  (∀ n : ℕ, f n ≤ 1) ↔ (∀ z : ℤ, f z ≤ 1) :=
begin
  split,
  { intros h z,
    obtain ⟨n, rfl | rfl⟩ := z.eq_coe_or_neg,
    { exact h n },
    { have : ↑((-1 : ℤ) * n) = (-1 : ℚ) * n := by norm_cast,
      rw [neg_eq_neg_one_mul, this, f_mul_eq, norm_neg_one_eq_one, one_mul],
      exact h n } },
  { intros h n,
    exact_mod_cast (h n) },
end

-- (Done)
lemma mul_eq_pow {a : ℚ} {n : ℕ} : f (a ^ n) = (f a) ^ n :=
begin
  induction n with d hd,
  simp only [pow_zero],
  exact f.map_one',
  rw [pow_succ, pow_succ, ←hd, f_mul_eq],
end


import data.set
import data.finset
import logic.basic

namespace mth1001

open set

namespace set_membership

-- We use `finset ℕ` to denote the type of finite sets of elements of `ℕ`.

def A := ({1, 2, 3, 4} : finset ℕ) -- This defines `A` to be `{1, 2, 3, 4}`.
def B := ({1, 3, 5, 6} : finset ℕ)
def C := ({5, 7, 9} : finset ℕ)

/-
Type `∈` as `\in`,
type `∉` as `\notin`,
type `∩` as `\cap`,
type `∪` as `\cup`,
type `⊆` as `\sub`,
type `∅` as `\empty`
type `\` as `\`,
type `ℕ` as `\N` and `ℤ` as `\Z`.
-/

/-
`dec_trivial` proves results by 'decidability'. You don't need to know what this means.
Lean can use `dec_trivial` to prove results for particular finite sets.
-/

example : 2 ∈ A := dec_trivial

example : 5 ∉ A := dec_trivial

example : 3 ∈ (A ∩ B) := dec_trivial

end set_membership

section set_equality

/-
Whenever an mathematical object is defined by its external properties, the Lean tactic `ext`
transforms a proof of equality of two objects into a proof of checking that they have the same
external properties. This corresponds to the mathematical principle of *extensionality*.

In the case of proving two sets `S` and `T` on a type `A` are equal, the `ext` tactic transforms
`⊢ S = T` into an assumption `x : A` and a new goal `⊢ x ∈ S ↔ x ∈ T`.
-/

-- We'll look at sets over a type A.

variable A : Type*

theorem set_refl : ∀ S : set A, S = S :=
begin
  intro S, -- Assume S is a set.
  ext, -- Assume `x : A`. It suffices to prove `x ∈ S ↔ x ∈ S`.
  refl, -- The result follows by reflexivity of `↔`.
end

theorem set_symm : ∀ S T : set A, S = T → T = S:=
begin
  intros S T, -- Assume S and T are sets.
  intro h, -- Assume `h : ∀ x, x ∈ S ↔ x ∈ T`. It suffices to prove `∀ x, x ∈ T ↔ x ∈ S`.
  ext,       -- Assume `x : A`. It suffices to prove `x ∈ T ↔ x ∈ S`.
  symmetry, -- By symmetry of `↔`, it suffices to prove `x ∈ S ↔ x ∈ T`.
  rw h, -- This follows from `h`.
end

-- Exercise 127:
-- In this exercise, it will be helpful to use the `iff.trans` function. If
-- `h₁ : p ↔ q` and `h₂ : q ↔ r`, then `iff.trans h₁ h₂` is a proof of `p ↔ r`.
theorem set_trans : ∀ S T U : set A, (S = T) ∧ (T = U) → S = U :=
begin
  intros S T U, -- Assume S, T, and U are sets.
  intro h, -- Assume `h : S = T ∧ T = U`.
  ext, -- Assume `x : A`. It suffices to prove `x ∈ S ↔ x ∈ U`.
  have h₂ : x ∈ S ↔ x ∈ T, sorry,
  have h₃ : x ∈ T ↔ x ∈ U, sorry,
  sorry
end

end set_equality

section finite_set_equality

/-
In the next example, we show `{5, 6, 7, 8} = {5, 7, 5, 8, 6}`. Note that we must explicitly
specify the type of the set. For example, `({5, 6, 7, 8 } : set ℤ)` is the Lean way to
write `{5, 6, 7, 8}`.
-/

example : ({5, 6, 7, 8} : finset ℤ) = ({5, 7, 5, 8, 6} : finset ℤ) := dec_trivial

-- Likewise, we can prove two particular finite sets are not equal by `dec_trivial`.

example : ({5, 6, 7, 8} : finset ℤ) ≠ ({5, 5, 8, 6} : finset ℤ) := dec_trivial

/-
The proofs are a bit challenging if we don't use `dec_trivial`. The `finish` tactic proves goals
by applying rules of logic.
-/

example : ({5, 6, 7, 8} : set ℤ) = ({5, 7, 5, 8, 6} : set ℤ) :=
begin
  ext,        -- Use set extensionality.
  finish,     -- Finish using rules of logic.
end

/-
To prove set inequalities, we can use the `finish` tactic, but we also need to identify an element
that belongs to one set but not to the other. We also the set-specific function extenstionality
result `ext_iff`.
-/

example : ({5, 6, 7, 8} : set ℤ) ≠ ({5, 5, 8, 6} : set ℤ) :=
begin
  intro h, -- Assume `h : {5, 6, 7, 8} = {5, 5, 8, 6}`.-- It suffices to prove `⊥`
  rw ext_iff at h, -- By definition, `h : ∀ x, x ∈ {5, 6, 7, 8} ↔ x ∈ {5, 5, 8, 6}`.
  contrapose! h,   -- Negating this, the goal is `∃ x, ¬(x ∈ {5, 6, 7, 8} ↔ x ∈ {5, 5, 8, 6})`.
  use 7, -- By `∃` intro. on `7`, it suffices to prove `¬(7 ∈ {5, 6, 7, 8} ↔ 7 ∈ {5, 5, 8, 6})`.
  finish, -- Finish using rules of logic.
end

end finite_set_equality

end mth1001


import tactic basic

namespace complex

/-! # `ext` : A mathematical triviality -/

/-
Two complex numbers with the same and imaginary parts are equal.
This is an "extensionality lemma", i.e. a lemma of the form "if two things
are made from the same pieces, they are equal".
This is not hard to prove, but we want to give the result a name
so we can tag it with the `ext` attribute, meaning that the
`ext` tactic will know it. To add to the confusion, let's call the theorem `ext` :-)
-/

/-- If two complex numbers z and w have equal real and imaginary parts, they are equal -/
@[ext] theorem ext {z w : ℂ} (hre : re(z) = re(w)) (him : im(z) = im(w)) : z = w :=
begin
  cases z with zr zi,
  cases w with ww wi,
  simp at *,
  /- goal now a logic puzzle

  hre : zr = ww,
  him : zi = wi
  ⊢ zr = ww ∧ zi = wi

  -/
  cc,
end

/-! # Theorem:  The complex numbers are a commutative ring -/

-- Proof: we've defined all the structure, and every axiom can be checked by reducing it
-- to checking real and imaginary parts with `ext`, expanding everything out with `simp`
-- and then using the fact that the real numbers are a commutative ring.
instance : comm_ring ℂ :=
begin
  -- first the data
  refine_struct {
      zero := (0 : ℂ), add := (+), neg := has_neg.neg, one := 1, mul := (*),
  ..},
  -- now the axioms
  -- of which there seem to be 11
  -- introduce the variables
  all_goals {intros},
  -- we now have to prove an equality between two complex numbers.
  -- It suffices to check on real and imaginary parts
  all_goals {ext},
  -- the simplifier can simplify stuff like re(a+0)
  all_goals {simp},
  -- all the goals now are identities between *real* numbers,
  -- and the reals are already known to be a ring
  all_goals {ring},
end

-- That is the end of the proof that the complexes form a ring. We built
-- a basic API which was honed towards the general idea that to prove
-- certain statements about the complex numbers, for example distributivity,
-- we could just check on real and imaginary parts. We trained the
-- simplifier to expand out things like re(z*w) in terms
-- of re(z), im(z), re(w), im(w).

/-!

# Optional section for mathematicians : more basic infrastructure, and term mode

-/

/-!
## `ext` revisited

Recall extensionality:

`theorem ext {z w : ℂ} (hre : re(z) = re(w)) (him : im(z) = im(w)) : z = w := ...`

Here is another tactic mode proof of extensionality. Note that we have moved
the hypotheses to the other side of the colon; this does not
change the theorem. This proof shows the power
of the `rintros` tactic.
-/

theorem ext' : ∀ z w : ℂ, z.re = w.re → z.im = w.im → z = w :=
begin
  rintros ⟨zr, zi⟩ ⟨_, _⟩ ⟨rfl⟩ ⟨rfl⟩,
  refl,
end

/-
Explanation: `rintros` does `cases` as many times as you like using this cool `⟨ ⟩` syntax
for the case splits. Note that if you say that a proof of `a = b` is `rfl` then
Lean will define a to be b, or b to be a, and not even introduce new notation for it.
-/

-- Here is the same proof in term mode.

theorem ext'' : ∀ {z w : ℂ}, z.re = w.re → z.im = w.im → z = w
| ⟨zr, zi⟩ ⟨_, _⟩ rfl rfl := rfl

/-!
## `eta`
-/

/-
We prove the mathematically obvious statement that the
complex number whose real part is re(z) and whose imaginary
part is im(z) is of course equal to z.
-/

/-- ⟨z.re, z.im⟩ is equal to z -/
@[simp] theorem eta : ∀ z : ℂ, complex.mk z.re z.im = z :=
begin
  intro z,
  cases z with x y,
  /-
    goal now looks complicated, and contains terms which look
    like {re := a, im := b}.re which obviously simplify to a.
    The `dsimp` tactic will do some tidying up for us, although
    it is not logically necessary. `dsimp` does definitional simplification.
  -/
  dsimp,
  -- Now we see the goal can be solved by reflexivity
  refl,
end

/-
The proof was "unfold everything, and it's true by definition".
This proof does not teach a mathematician anything, so we may as well write
it in term mode. Many tactics have term mode equivalent.
The equation compiler does the `intro` and `cases` steps,
and `dsimp` was unnecessary -- the two sides of the equation
were definitionally equal.
-/

theorem eta' : ∀ z : ℂ, complex.mk z.re z.im = z
| ⟨x, y⟩ := rfl

/-!
## ext_iff
-/

/-
Note that `ext` is an implication -- if re(z)=re(w) and im(z)=im(w) then z=w.
The below variant `ext_iff` is the two-way implication: two complex
numbers are equal if and only if they have the same real and imaginary part.
Let's first see a tactic mode proof. See how the `ext` tactic is used?
After it is applied, we have two goals, both of which are hypotheses.
The semicolon means "apply the next tactic to all the goals produced by this one"
-/

theorem ext_iff {z w : ℂ} : z = w ↔ z.re = w.re ∧ z.im = w.im :=
begin
  split,
  { intro H,
    simp [H]},
  {
    rintro ⟨hre, him⟩,
    ext; assumption,
  }
end

-- Again this is easy to write in term mode, and no mathematician
-- wants to read the proof anyway.

theorem ext_iff' {z w : ℂ} : z = w ↔ z.re = w.re ∧ z.im = w.im :=
⟨λ H, by simp [H], and.rec ext⟩

end complex

/-!

# some last comments on the `simp` tactic

Some equalities, even if obvious, had to be given names, because we want `simp`
to be able to use them. In short, the `simp` tactic tries to solve
goals of the form A = B, when `refl` doesn't work (i.e. the goals are
not definitionally equal) but when any mathematician would be able
to simplify A and B via "obvious" steps such as `0 + x = x` or
`⟨z.re, z.im⟩ = z`. These things are sometimes not true by definition,
but they should be tagged as being well-known ways to simplify an equality.
When building our API for the complex numbers, if we prove a theorem of the
form `A = B` where `B` is a bit simpler than `A`, we should probably
tag it with the `@[simp]` attribute, so `simp` can use it.

Note: `simp` does *not* prove "all simple things". It proves *equalities*.
It proves `A = B` when, and only when, it can do it by applying
its "simplification rules", where a simplification rule is simply a proof
of a theorem of the form `A = B` and `B` is simpler than `A`.
-/

------------------ VERY INTERESTING BITS -------------------------

/-- The canonical map from ℝ to ℂ. -/
def of_real (r : ℝ) : ℂ := ⟨r, 0⟩

/-
We make this map into a *coercion*, which means that if `(r : ℝ)` is a real
number, then `(r : ℂ)` or `(↑r : ℂ)` will indicate the corresponding
complex number with no imaginary part. This is the notation we shall
use in our `simp` lemmas.
-/

/-- The coercion from ℝ to ℂ sending `r` to the complex number `⟨r, 0⟩` -/
instance : has_coe ℝ ℂ := ⟨of_real⟩


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

! This file was ported from Lean 3 source module measure_theory.measure.open_pos
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Measure.MeasureSpace

/-!
# Measures positive on nonempty opens

In this file we define a typeclass for measures that are positive on nonempty opens, see
`measure_theory.measure.is_open_pos_measure`. Examples include (additive) Haar measures, as well as
measures that have positive density with respect to a Haar measure. We also prove some basic facts
about these measures.

-/

open Topology ENNReal MeasureTheory

open Set Function Filter

namespace MeasureTheory

namespace Measure

section Basic

variable {X Y : Type _} [TopologicalSpace X] {m : MeasurableSpace X} [TopologicalSpace Y]
  [T2Space Y] (μ ν : Measure X)

/-- A measure is said to be `is_open_pos_measure` if it is positive on nonempty open sets. -/
class IsOpenPosMeasure : Prop where
  open_pos : ∀ U : Set X, IsOpen U → U.Nonempty → μ U ≠ 0
#align measure_theory.measure.is_open_pos_measure MeasureTheory.Measure.IsOpenPosMeasure

variable [IsOpenPosMeasure μ] {s U : Set X} {x : X}

theorem IsOpen.measure_ne_zero (hU : IsOpen U) (hne : U.Nonempty) : μ U ≠ 0 :=
  IsOpenPosMeasure.open_pos U hU hne
#align is_open.measure_ne_zero IsOpen.measure_ne_zero

theorem IsOpen.measure_pos (hU : IsOpen U) (hne : U.Nonempty) : 0 < μ U :=
  (hU.measure_ne_zero μ hne).bot_lt
#align is_open.measure_pos IsOpen.measure_pos

theorem IsOpen.measure_pos_iff (hU : IsOpen U) : 0 < μ U ↔ U.Nonempty :=
  ⟨fun h => nonempty_iff_ne_empty.2 fun he => h.ne' <| he.symm ▸ measure_empty, hU.measure_pos μ⟩
#align is_open.measure_pos_iff IsOpen.measure_pos_iff

theorem IsOpen.measure_eq_zero_iff (hU : IsOpen U) : μ U = 0 ↔ U = ∅ := by
  simpa only [not_lt, nonpos_iff_eq_zero, not_nonempty_iff_eq_empty] using
    not_congr (hU.measure_pos_iff μ)
#align is_open.measure_eq_zero_iff IsOpen.measure_eq_zero_iff

theorem measure_pos_of_nonempty_interior (h : (interior s).Nonempty) : 0 < μ s :=
  (isOpen_interior.measure_pos μ h).trans_le (measure_mono interior_subset)
#align measure_theory.measure.measure_pos_of_nonempty_interior MeasureTheory.Measure.measure_pos_of_nonempty_interior

theorem measure_pos_of_mem_nhds (h : s ∈ 𝓝 x) : 0 < μ s :=
  measure_pos_of_nonempty_interior _ ⟨x, mem_interior_iff_mem_nhds.2 h⟩
#align measure_theory.measure.measure_pos_of_mem_nhds MeasureTheory.Measure.measure_pos_of_mem_nhds

theorem isOpenPosMeasureSmul {c : ℝ≥0∞} (h : c ≠ 0) : IsOpenPosMeasure (c • μ) :=
  ⟨fun U Uo Une => mul_ne_zero h (Uo.measure_ne_zero μ Une)⟩
#align measure_theory.measure.is_open_pos_measure_smul MeasureTheory.Measure.isOpenPosMeasureSmul

variable {μ ν}

protected theorem AbsolutelyContinuous.isOpenPosMeasure (h : μ ≪ ν) : IsOpenPosMeasure ν :=
  ⟨fun U ho hne h₀ => ho.measure_ne_zero μ hne (h h₀)⟩
#align measure_theory.measure.absolutely_continuous.is_open_pos_measure MeasureTheory.Measure.AbsolutelyContinuous.isOpenPosMeasure

theorem LE.le.isOpenPosMeasure (h : μ ≤ ν) : IsOpenPosMeasure ν :=
  h.AbsolutelyContinuous.IsOpenPosMeasure
#align has_le.le.is_open_pos_measure LE.le.isOpenPosMeasure

theorem IsOpen.eq_empty_of_measure_zero (hU : IsOpen U) (h₀ : μ U = 0) : U = ∅ :=
  (hU.measure_eq_zero_iff μ).mp h₀
#align is_open.eq_empty_of_measure_zero IsOpen.eq_empty_of_measure_zero

theorem interior_eq_empty_of_null (hs : μ s = 0) : interior s = ∅ :=
  isOpen_interior.eq_empty_of_measure_zero <| measure_mono_null interior_subset hs
#align measure_theory.measure.interior_eq_empty_of_null MeasureTheory.Measure.interior_eq_empty_of_null

/-- If two functions are a.e. equal on an open set and are continuous on this set, then they are
equal on this set. -/
theorem eqOn_open_of_ae_eq {f g : X → Y} (h : f =ᵐ[μ.restrict U] g) (hU : IsOpen U)
    (hf : ContinuousOn f U) (hg : ContinuousOn g U) : EqOn f g U :=
  by
  replace h := ae_imp_of_ae_restrict h
  simp only [eventually_eq, ae_iff, not_imp] at h
  have : IsOpen (U ∩ { a | f a ≠ g a }) :=
    by
    refine' is_open_iff_mem_nhds.mpr fun a ha => inter_mem (hU.mem_nhds ha.1) _
    rcases ha with ⟨ha : a ∈ U, ha' : (f a, g a) ∈ diagonal Yᶜ⟩
    exact
      (hf.continuous_at (hU.mem_nhds ha)).prod_mk_nhds (hg.continuous_at (hU.mem_nhds ha))
        (is_closed_diagonal.is_open_compl.mem_nhds ha')
  replace := (this.eq_empty_of_measure_zero h).le
  exact fun x hx => Classical.not_not.1 fun h => this ⟨hx, h⟩
#align measure_theory.measure.eq_on_open_of_ae_eq MeasureTheory.Measure.eqOn_open_of_ae_eq

/-- If two continuous functions are a.e. equal, then they are equal. -/
theorem eq_of_ae_eq {f g : X → Y} (h : f =ᵐ[μ] g) (hf : Continuous f) (hg : Continuous g) : f = g :=
  suffices EqOn f g univ from funext fun x => this trivial
  eqOn_open_of_ae_eq (ae_restrict_of_ae h) isOpen_univ hf.ContinuousOn hg.ContinuousOn
#align measure_theory.measure.eq_of_ae_eq MeasureTheory.Measure.eq_of_ae_eq

theorem eqOn_of_ae_eq {f g : X → Y} (h : f =ᵐ[μ.restrict s] g) (hf : ContinuousOn f s)
    (hg : ContinuousOn g s) (hU : s ⊆ closure (interior s)) : EqOn f g s :=
  have : interior s ⊆ s := interior_subset
  (eqOn_open_of_ae_eq (ae_restrict_of_ae_restrict_of_subset this h) isOpen_interior (hf.mono this)
        (hg.mono this)).of_subset_closure
    hf hg this hU
#align measure_theory.measure.eq_on_of_ae_eq MeasureTheory.Measure.eqOn_of_ae_eq

variable (μ)

theorem Continuous.ae_eq_iff_eq {f g : X → Y} (hf : Continuous f) (hg : Continuous g) :
    f =ᵐ[μ] g ↔ f = g :=
  ⟨fun h => eq_of_ae_eq h hf hg, fun h => h ▸ EventuallyEq.rfl⟩
#align continuous.ae_eq_iff_eq Continuous.ae_eq_iff_eq

end Basic

section LinearOrder

variable {X Y : Type _} [TopologicalSpace X] [LinearOrder X] [OrderTopology X]
  {m : MeasurableSpace X} [TopologicalSpace Y] [T2Space Y] (μ : Measure X) [IsOpenPosMeasure μ]

theorem measure_Ioi_pos [NoMaxOrder X] (a : X) : 0 < μ (Ioi a) :=
  isOpen_Ioi.measure_pos μ nonempty_Ioi
#align measure_theory.measure.measure_Ioi_pos MeasureTheory.Measure.measure_Ioi_pos

theorem measure_Iio_pos [NoMinOrder X] (a : X) : 0 < μ (Iio a) :=
  isOpen_Iio.measure_pos μ nonempty_Iio
#align measure_theory.measure.measure_Iio_pos MeasureTheory.Measure.measure_Iio_pos

theorem measure_Ioo_pos [DenselyOrdered X] {a b : X} : 0 < μ (Ioo a b) ↔ a < b :=
  (isOpen_Ioo.measure_pos_iff μ).trans nonempty_Ioo
#align measure_theory.measure.measure_Ioo_pos MeasureTheory.Measure.measure_Ioo_pos

theorem measure_Ioo_eq_zero [DenselyOrdered X] {a b : X} : μ (Ioo a b) = 0 ↔ b ≤ a :=
  (isOpen_Ioo.measure_eq_zero_iff μ).trans (Ioo_eq_empty_iff.trans not_lt)
#align measure_theory.measure.measure_Ioo_eq_zero MeasureTheory.Measure.measure_Ioo_eq_zero

theorem eqOn_Ioo_of_ae_eq {a b : X} {f g : X → Y} (hfg : f =ᵐ[μ.restrict (Ioo a b)] g)
    (hf : ContinuousOn f (Ioo a b)) (hg : ContinuousOn g (Ioo a b)) : EqOn f g (Ioo a b) :=
  eqOn_of_ae_eq hfg hf hg Ioo_subset_closure_interior
#align measure_theory.measure.eq_on_Ioo_of_ae_eq MeasureTheory.Measure.eqOn_Ioo_of_ae_eq

theorem eqOn_Ioc_of_ae_eq [DenselyOrdered X] {a b : X} {f g : X → Y}
    (hfg : f =ᵐ[μ.restrict (Ioc a b)] g) (hf : ContinuousOn f (Ioc a b))
    (hg : ContinuousOn g (Ioc a b)) : EqOn f g (Ioc a b) :=
  eqOn_of_ae_eq hfg hf hg (Ioc_subset_closure_interior _ _)
#align measure_theory.measure.eq_on_Ioc_of_ae_eq MeasureTheory.Measure.eqOn_Ioc_of_ae_eq

theorem eqOn_Ico_of_ae_eq [DenselyOrdered X] {a b : X} {f g : X → Y}
    (hfg : f =ᵐ[μ.restrict (Ico a b)] g) (hf : ContinuousOn f (Ico a b))
    (hg : ContinuousOn g (Ico a b)) : EqOn f g (Ico a b) :=
  eqOn_of_ae_eq hfg hf hg (Ico_subset_closure_interior _ _)
#align measure_theory.measure.eq_on_Ico_of_ae_eq MeasureTheory.Measure.eqOn_Ico_of_ae_eq

theorem eqOn_Icc_of_ae_eq [DenselyOrdered X] {a b : X} (hne : a ≠ b) {f g : X → Y}
    (hfg : f =ᵐ[μ.restrict (Icc a b)] g) (hf : ContinuousOn f (Icc a b))
    (hg : ContinuousOn g (Icc a b)) : EqOn f g (Icc a b) :=
  eqOn_of_ae_eq hfg hf hg (closure_interior_Icc hne).symm.Subset
#align measure_theory.measure.eq_on_Icc_of_ae_eq MeasureTheory.Measure.eqOn_Icc_of_ae_eq

end LinearOrder

end Measure

end MeasureTheory

open MeasureTheory MeasureTheory.Measure

namespace Metric

variable {X : Type _} [PseudoMetricSpace X] {m : MeasurableSpace X} (μ : Measure X)
  [IsOpenPosMeasure μ]

theorem measure_ball_pos (x : X) {r : ℝ} (hr : 0 < r) : 0 < μ (ball x r) :=
  isOpen_ball.measure_pos μ (nonempty_ball.2 hr)
#align metric.measure_ball_pos Metric.measure_ball_pos

theorem measure_closedBall_pos (x : X) {r : ℝ} (hr : 0 < r) : 0 < μ (closedBall x r) :=
  (measure_ball_pos μ x hr).trans_le (measure_mono ball_subset_closedBall)
#align metric.measure_closed_ball_pos Metric.measure_closedBall_pos

end Metric

namespace Emetric

variable {X : Type _} [PseudoEMetricSpace X] {m : MeasurableSpace X} (μ : Measure X)
  [IsOpenPosMeasure μ]

theorem measure_ball_pos (x : X) {r : ℝ≥0∞} (hr : r ≠ 0) : 0 < μ (ball x r) :=
  isOpen_ball.measure_pos μ ⟨x, mem_ball_self hr.bot_lt⟩
#align emetric.measure_ball_pos Emetric.measure_ball_pos

theorem measure_closedBall_pos (x : X) {r : ℝ≥0∞} (hr : r ≠ 0) : 0 < μ (closedBall x r) :=
  (measure_ball_pos μ x hr).trans_le (measure_mono ball_subset_closedBall)
#align emetric.measure_closed_ball_pos Emetric.measure_closedBall_pos

end Emetric


/-
Copyright (c) 2018 Keji Neri, Blair Shi. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Keji Neri, Blair Shi

* `finite_free_module R n`: the set of maps from (fin n) to R (R ^ n)

-- Proved R^n is an abellian group and module R (R ^ n)

* `matrix_to_linear_map` : constructs a matrix based on the given linear map

* `linear_map_to_matrix` : constructs the linear map based on the given matrix

-- Proved the a x b matrices and R-linear maps R^b -> R^a are equivalent

-- Proved the product of two matrix is equivalent to the component of two
-- corresponding linear maps

-- Proved Hom(R^b,R^a)

* `linear_map_to_vec V n` : constructs the basis based on the linear map

* `vec_to_linear_map V n M` : construct the linear map based on the given basis

-- proved a basis v1,v2,...,vn of a fdvs V/k is just an isomorphism k^n -> V
-/

import xenalib.Ellen_Arlt_matrix_rings  algebra.big_operators
import data.set.finite algebra.module  data.finsupp
import algebra.group linear_algebra.basic data.fintype
import data.equiv.basic linear_algebra.linear_map_module
import algebra.pi_instances algebra.module data.list.basic

open function

universe u
variables {α : Type u}
variables {β : Type*} [add_comm_group α] [add_comm_group β]

/-- Predicate for group homomorphism. -/
class is_add_group_hom (f : α → β) : Prop :=
(add : ∀ a b : α, f (a + b) = (f a) + (f b))
namespace is_add_group_hom
variables (f : α → β) [is_add_group_hom f]

theorem zero : f 0 = 0 :=
add_self_iff_eq_zero.1 $ by simp [(add f 0 _).symm]

theorem inv (a : α) : f(-a)  = -(f a) :=
eq.symm $ neg_eq_of_add_eq_zero $ by simp [(add f a (-a)).symm, zero f]

instance id : is_add_group_hom (@id α) :=
⟨λ _ _, rfl⟩

instance comp {γ} [add_comm_group γ] (g : β → γ) [is_add_group_hom g] :
  is_add_group_hom (g ∘ f) :=
⟨λ x y, calc
  g (f (x + y)) = g (f x + f y)       : by rw add f
  ...           = g (f x) + g (f y)   : by rw add g⟩

end is_add_group_hom

definition finite_free_module (R : Type) (n : nat) := (fin n) → R

def add (R : Type) (n : nat) [ring R] :=
λ (a b :finite_free_module R n), (λ i, (a i) +(b i))

def smul {R : Type} {n : nat} [ring R] (s : R) (rn : finite_free_module R n) :
finite_free_module R n := λ I, s * (rn I)

theorem add__assoc {R : Type} {n : nat} [ring R] (a b c :(fin n) → R) :
  add R n (add R n a b) c = add R n a (add R n b c):=
begin
unfold add,
funext,
simp,
end
theorem add__comm {R : Type} {n : nat} [ring R] (a b :(fin n) → R):
 add R n a b =add R n b a :=
begin
unfold add,
funext,
exact add_comm (a i) (b i),
end

def zero (R : Type) (n : nat) [ring R]: finite_free_module R n := λ (i:fin n),(0 :R)
#check zero
theorem zero__add {R : Type} {n : nat} [ring R] (a:finite_free_module R n): add R n (zero R n) a = a:=
begin
unfold add,
funext,
unfold zero,
simp,
end

def neg (R : Type) (n : nat) [ring R]:= λ (a:finite_free_module R n),(λ i, -(a i))
theorem add__left__neg {R : Type} {n : nat} [ring R] (a :finite_free_module R n): add R n (neg R n a) a = zero R n:=
begin
unfold add,
unfold zero,
funext,
unfold neg,
simp,
end

def add__zero {R : Type} {n : nat} [ring R] (a :finite_free_module R n): add R n a (zero R n) =a:=
begin
unfold add,
funext,
unfold zero,
simp,
end

lemma is_add_group_hom_right_inv {α β : Type*} [add_comm_group α] [add_comm_group β]
{f: α → β} [is_add_group_hom f] (hf : injective f) { g :β → α} (h: right_inverse g f):
is_add_group_hom g:= ⟨ λ a b, hf $ by  rw[h(a+b),is_add_group_hom.add f,h a,h b]⟩

instance (R : Type) [ring R] (n : nat) : add_comm_group (finite_free_module R n) :=
{add:=add R n,
add_assoc := add__assoc,
zero := zero R n,
zero_add:= zero__add,
neg:=neg R n,
add_left_neg:= add__left__neg,
add_zero:= add__zero ,
add_comm:= add__comm,
}

namespace R_module
variables (R : Type) (n : nat)
variable [ring R]

theorem smul_add (s : R) (rn rm : finite_free_module R n) :
    smul s (add R n rn rm) = add R n (smul s rn) (smul s rm) :=
-- s • (rn + rm) = s • rn + s • rm
    begin
      apply funext,
      intro,
      unfold smul add,
      apply mul_add,
    end

theorem add_smul (s t : R) (rn: finite_free_module R n):
    smul (s + t) rn = add R n (smul s rn) (smul t rn) :=
    begin
      apply funext,
      intro,
      unfold smul add,
      apply add_mul,
    end

theorem mul_smul (s t : R) (rn : finite_free_module R n):
    smul (s * t) rn = smul s (smul t rn) :=
    begin
      apply funext,
      intro,
      unfold smul,
      apply mul_assoc,
    end

theorem one_smul (rn : finite_free_module R n):
    smul (1 : R) rn = rn :=
    begin
      apply funext,
      intro,
      unfold smul,
      apply one_mul,
    end
end R_module

instance (R : Type) [ring R] (n : nat) : has_scalar R (finite_free_module R n) :=
{
    smul := smul
}

instance {R : Type} {n : nat} [ring R] : module R (finite_free_module R n) :=
{
    smul_add := R_module.smul_add R n,
    add_smul := R_module.add_smul R n,
    mul_smul := R_module.mul_smul R n,
    one_smul := R_module.one_smul R n,
}

namespace map_matrix

definition matrix_to_map {R : Type} [ring R] {a b : nat} (M : matrix R a b) :
(finite_free_module R a) → (finite_free_module R b) := λ v ,(λ i,finset.sum finset.univ (λ K, (v K) *M K i ) )

instance hg {R : Type} [ring R] {a b : nat} (M : matrix R a b) : is_add_group_hom (matrix_to_map M) :=
⟨begin
intros,
funext,
unfold matrix_to_map,

show (finset.sum finset.univ (λ (K : fin a), (a_1 K + b_1 K)  * M K i) =_),
conv in ( (a_1 _ + b_1 _) * M _ i)
  begin
    rw [add_mul],
  end,

rw finset.sum_add_distrib,
refl,
end⟩

theorem smul_ {R: Type} [ring R] {a b : nat} (M : matrix R a b): ∀ (c : R) (x : finite_free_module R a),
matrix_to_map M (smul c x) = smul c (matrix_to_map M x):=
begin
intros,
unfold matrix_to_map,
funext,
unfold smul,
rw [finset.mul_sum],
simp[mul_assoc],
end

def module_hom {R: Type} [ring R] {a b : nat} (M : matrix R a b) :
  @is_linear_map R _ _ _ _ _ (matrix_to_map M) :=
 { add:=
 begin
exact is_add_group_hom.add _,
 end,
 smul:= smul_ _,
}

def matrix_to_linear_map {R : Type} [ring R] {a b : nat} (M : matrix R a b) :
(@linear_map R (finite_free_module R a)  (finite_free_module R b) _ _ _) :=
⟨matrix_to_map M, module_hom M⟩

def e (R : Type) [ring R] (a: nat) (i: fin a): finite_free_module R a:= λ j, if i =j then 1 else 0

definition linear_map_to_matrix {R : Type} [ring R] {a b : nat}
(f: @linear_map R (finite_free_module R a) (finite_free_module R b) _ _ _) : matrix R a b :=
    λ i j, f.1 (e R a i) j

theorem finset.sum_single {α : Type*} [fintype α]
  {β : Type*} [add_comm_monoid β]
  (f : α → β) {i : α}
  (h : ∀ (j : α), i ≠ j → f j = 0) :
f i = finset.sum finset.univ (λ (K : α), f K) :=
begin
  have H : finset.sum (finset.singleton i) (λ (K : α), f K)
    = finset.sum finset.univ (λ (K : α), f K),
  from finset.sum_subset (λ _ _, finset.mem_univ _)
    (λ _ _ H, h _ $ mt (λ h, finset.mem_singleton.2 h.symm) H),
  rw [← H, finset.sum_singleton]
end

theorem apply_function_to_sum {R : Type}[ring R] {n p : nat} (f: fin n → finite_free_module R p ) (i : fin p ):
(finset.sum finset.univ (λ (K : fin n),f K)) i = finset.sum finset.univ (λ (K : fin n), f K i):=
begin
rw finset.sum_hom (λ (v: finite_free_module R p), v i ) _,
intros,
simp,
refl,
end

theorem span {R : Type} {n : nat} [ring R] (v : finite_free_module R n):
v  = finset.sum finset.univ (λ K, smul (v K) (e R n K)):=
begin
funext,
rw [apply_function_to_sum (λ i, smul (v i) (e R n i))],
simp,
unfold smul,
  have H1 : ∀ (j : fin n), x ≠ j → v j * (e R n j) x = 0,
    intros,
    unfold e,
    split_ifs,
      exact false.elim (a h.symm),
    simp,
  have H2: finset.sum finset.univ (λ (K : fin n), v K * e R n K x) = v x * e R n x x,
    have Htemp := finset.sum_single (λ (K:fin n), v K * e R n K x ) H1,
    rw ←Htemp,
  rw H2,
  unfold e,
  split_ifs,
  simp,
  simp,
end

theorem equiv_one {R : Type} [ring R] {a b : nat} (f : (@linear_map R (finite_free_module R a) (finite_free_module R b) _ _ _)) :
    matrix_to_map (linear_map_to_matrix f ) = f :=
begin
funext,
unfold linear_map_to_matrix,
unfold matrix_to_map,
conv begin
to_rhs,
rw [span v],
end,
rw [← finset.sum_hom f _],
swap 3,
exact is_linear_map.zero f.2,
swap 2,
exact f.2.add,
rw[apply_function_to_sum (λ j,f (smul (v j) (e R a j)))],
simp,
show _ = finset.sum finset.univ (λ (K : fin a), f ((v K) • (e R a K)) i),
congr,
funext,
rw[( linear_map.is_linear_map_coe).smul],
refl,
end

theorem equiv_two {R : Type} [ring R] {p b : nat} (M : matrix R p b):
  linear_map_to_matrix ⟨ matrix_to_map M, module_hom M⟩  = M :=
  begin
   funext,
   unfold linear_map_to_matrix,
   show (matrix_to_map M) (e R p i) j =_,
   unfold matrix_to_map,
    have H1: ∀ (K : fin p), i ≠ K →  e R p i K * M K j = 0,
    intros,
    unfold e,
    split_ifs,
    exact false.elim (a h),
    simp,
    have H2: finset.sum finset.univ (λ (K : fin p), e R p i K * M K j) = e R p i i * M i j,
       have Htemp := finset.sum_single (λ (K : fin p), e R p i K * M K j) H1,
    rw ← Htemp,
    rw H2,
    unfold e,
    split_ifs,
    simp,
    simp,
  end

def matrix_transpose {R : Type} [ring R] {a b : nat} (M : matrix R a b) :
matrix R b a := λ I, λ J, M J I

definition matrix_to_map_right {R : Type} [ring R] {a b : nat} (M : matrix R a b) :
(finite_free_module R a) → (finite_free_module R b) :=
λ v, (λ I, finset.sum finset.univ (λ K, (matrix_transpose M) I K * (v K)))

-- end

def matrix_to_linear_map_equiv {R : Type} [ring R] {a b : nat} :
  equiv  (matrix R a b)  (@linear_map R (finite_free_module R a)  (finite_free_module R b) _ _ _):=
    {to_fun := matrix_to_linear_map,
    inv_fun := linear_map_to_matrix,
    right_inv:=
    begin
     unfold function.right_inverse,
     unfold function.left_inverse,
     intros,
    apply subtype.eq,
    dsimp,
    exact equiv_one x,
    end,

    left_inv:=
    begin
    unfold function.left_inverse,
    intros,
    exact equiv_two x,
    end
   }

instance {R : Type} [ring R] {a b : nat}:  is_add_group_hom (@matrix_to_linear_map R _ a b):=
  {
  add:=
  begin
  intros,
  unfold matrix_to_linear_map,
  apply linear_map.ext,
  intro x,
  show _ = matrix_to_map a_1 x + matrix_to_map b_1 x,
  show matrix_to_map (a_1 + b_1) x = _,
  unfold matrix_to_map,
  funext,
  show _ = (finset.sum finset.univ (λ (K : fin a), x K * a_1 K i)) +
       ( finset.sum finset.univ (λ (K : fin a), x K * b_1 K i)),

  rw[← finset.sum_add_distrib],
  congr,
  funext,
  have H1: x K * (a_1 + b_1) K i = x K * (a_1 K i + b_1 K i),
  refl,
  rw[H1],
  rw[mul_add],
  end
}

theorem comp_is_linear_map {R : Type} [ring R] {a b c : nat}
(f : (@linear_map R (finite_free_module R b)  (finite_free_module R a) _ _ _))
(g : (@linear_map R (finite_free_module R c)  (finite_free_module R b) _ _ _)):
  @is_linear_map R _ _ _ _ _ (f.1 ∘ g.1):=
{
  add:=
    begin
      intros,
      simp,
      have H1: f.val (g.val (x) + g.val(y)) = f.val (g.val (x + y)),
      rw[g.2.add],
      rw[← H1],
      rw[f.2.add],
    end,

  smul:=
    begin
      intros,
      simp,
      have H1: f.val (g.val (c_1 • x)) = f.val(c_1 • g.val(x)),
      rw[g.2.smul],
      rw[H1],
      rw[f.2.smul],
    end
}

theorem comp_equal_product_one {R : Type} [ring R] {a b c : nat}
(f : (@linear_map R (finite_free_module R b)  (finite_free_module R a) _ _ _))
(g : (@linear_map R (finite_free_module R c)  (finite_free_module R b) _ _ _)):
  (@linear_map_to_matrix R _ c a (⟨ f.1 ∘ g.1,  comp_is_linear_map f g⟩))
  = @matrix.mul _ _ b c a (@linear_map_to_matrix R _ c b g ) (@linear_map_to_matrix R _ b a f) :=
begin
  unfold linear_map_to_matrix,
  unfold matrix.mul,
  funext,
  simp,
  conv
  begin
    to_lhs,
    rw [span (g.1 (e R c i))],
  end,
  rw [is_linear_map.sum f.2],
  rw [apply_function_to_sum ],
  congr,
  funext,
  show  f.1 ((g.1 (e R c i) K) • (e R b K)) j = _,
  rw [is_linear_map.smul f.2],
  refl,
end

theorem comp_equal_product_two {R : Type} [ring R] {a b c : nat}
(M : matrix R b a) (N : matrix R c b):
@matrix_to_linear_map _ _ _ _ (@matrix.mul _ _ b c a N M) =
⟨(@matrix_to_linear_map _ _ _ _ M).1 ∘ (@matrix_to_linear_map _ _ _ _ N).1,
comp_is_linear_map (@matrix_to_linear_map _ _ _ _ M) (@matrix_to_linear_map _ _ _ _ N)⟩  :=
begin
  unfold matrix_to_linear_map,
  funext,
  apply subtype.eq,
  simp,
  unfold matrix_to_map,
  unfold matrix.mul,
  funext,
  simp,
  conv in (v _ * finset.sum _ _)
  begin
  rw [finset.mul_sum],
  end,
  simp only [ finset.sum_mul],
  conv
  begin
    to_lhs,
    rw [finset.sum_comm],
  end,
  congr,
  funext,
  congr,
  funext,
  rw [mul_assoc],
  end

-- R-module structure on Hom(R^b, R^a)

theorem left_inv {R : Type} [ring R] {a b : nat} : left_inverse (@linear_map_to_matrix R _ a b ) (matrix_to_linear_map) :=
    begin
    unfold function.left_inverse,
    intros,
    exact equiv_two x,
    end
theorem right_inv {R : Type} [ring R] {a b : nat} : right_inverse (@linear_map_to_matrix R _ a b ) (matrix_to_linear_map) :=
    begin
     unfold function.right_inverse,
     unfold function.left_inverse,
     intros,
    apply subtype.eq,
    dsimp,
    exact equiv_one x,
    end

instance keji  {R : Type} [ring R] {a b : nat}:  is_add_group_hom (@linear_map_to_matrix R _ a b):=
begin
 exact is_add_group_hom_right_inv  (injective_of_left_inverse left_inv ) right_inv,
end

def Hom {R : Type} [comm_ring R] {a b : nat} := {f: finite_free_module R a → finite_free_module R b // is_add_group_hom f}

def module_Hom {R: Type} [ring R] {a b : nat} (M : matrix R a b) :
  @is_linear_map R _ _ _ _ _ (matrix_to_map M) :=
 { add:=
 begin
exact is_add_group_hom.add _,
 end,
 smul:= smul_ _,
}

-- definition matrix_to_map {R : Type} [ring R] {a b : nat} (M : matrix R a b) :
-- (finite_free_module R a) → (finite_free_module R b) := λ v ,(λ i,finset.sum finset.univ (λ K, (v K) *M K i ) )

def vec_to_mat {R : Type} [ring R] {n : nat} (vc : vector R n) :
matrix R n 1 := λ I, λ J, vector.nth vc I

def mat_mul_vec {R : Type} [ring R] {n m : nat} (M : matrix R n m) (vc : vector R m) :
matrix R n 1 := @matrix.mul _ _ m n 1 M (vec_to_mat vc)

theorem mat_mat_vec_assoc {R : Type} [ring R] {a b c : nat} (M : matrix R a b)
(N : matrix R b c) (vc : vector R c) :
@matrix.mul _ _ b a 1 M (@mat_mul_vec _ _ b c N vc) =
@mat_mul_vec _ _ a c (@matrix.mul _ _ b a c M N) vc :=
begin
apply matrix.mul_assoc,
end

end map_matrix

namespace vector_space

variables {k : Type} {V : Type}
variable [field k]
variable (n : nat)

--  a basis v1,v2,...,vn of a fdvs V/k is just an isomorphism k^n -> V.

open map_matrix
-- helper function to get basis
def simp_fun (V : Type*) [vector_space k V] (n : ℕ) (lm : linear_map (finite_free_module k n) V) :
(fin n → V) :=
λ I, lm (e k n I)

def linear_map_to_vec (V : Type*) [vector_space k V] (n : ℕ) :
(linear_map (finite_free_module k n) V) → vector V n :=
λ lm, vector.of_fn (simp_fun V n lm)

def vec_to_map (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n):
(finite_free_module k n) → V :=
λ sp, finset.sum finset.univ (λ K : fin n, (sp K) • (vector.nth M K))

instance vc_to_map_add_group (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n) :
is_add_group_hom (@vec_to_map k _ _ _ n M) :=
⟨ begin
intros a b,
unfold vec_to_map,
show (finset.sum finset.univ (λ (K : fin n), (a K + b K) • vector.nth M K))=_,
conv in ((a _ + b _) • _)
  begin
    rw [add_smul],
  end,
rw [← finset.sum_add_distrib],
end
⟩

theorem smul' (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n) :
∀ (c : k) (x : finite_free_module k n), @vec_to_map k _ _ _ n M (smul c x) =
c • (@vec_to_map k _ _ _ n M x):=
begin
intros c x,
unfold vec_to_map,
funext,
unfold smul,
conv
  begin
  to_rhs,
  rw [finset.smul_sum],
  end,
congr,
funext,
rw [smul_smul],
end

def module_hom' (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n) :
  @is_linear_map _ _ _ _ _ _ (@vec_to_map k _ _ _ n M) :=
  {
      add:=
        begin
          exact is_add_group_hom.add _,
        end,
      smul:= @smul' _ _ _ _ _ _,
  }

def vec_to_linear_map (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n):
(linear_map (finite_free_module k n) V) :=
⟨ @vec_to_map k _ _ _ n M , @module_hom' _ _ _ _ n M⟩

lemma ext {α : Type*} {n : ℕ} : ∀ (v w : vector α n),
  (∀ m : fin n, vector.nth v m = vector.nth w m) → v = w :=
λ ⟨v, hv⟩ ⟨w, hw⟩ h, subtype.eq (list.ext_le (by simp [hv, hw])
(λ m hm hn, h ⟨m, hv ▸ hm⟩))

def left_inv_ (V : Type*) [vector_space k V] (n : ℕ) (M : vector V n):
  (@linear_map_to_vec k _ _ _ n) (@vec_to_linear_map k _ _ _ n M) = M :=
begin
unfold vec_to_linear_map,
unfold linear_map_to_vec,
unfold vec_to_map,
dsimp,
unfold simp_fun,
apply ext,
assume m,
rw vector.nth_of_fn,
unfold_coes, dsimp,
rw [← @finset.sum_single _ _ _ _ (λ (K : fin n), e k n m K • vector.nth M K) m ],
simp,
unfold e,
split_ifs,
exact one_smul,
contradiction,
intros,
simp,
unfold e,
split_ifs,
contradiction,
exact zero_smul,
end

def right_inv_ (V : Type*) [vector_space k V] (n : ℕ) (lm : linear_map (finite_free_module k n) V) :
@vec_to_linear_map k _ _ _ n (@linear_map_to_vec k _ _ _ n lm) = lm :=
begin
unfold vec_to_linear_map,
unfold linear_map_to_vec,
unfold vec_to_map,
apply subtype.eq,
dsimp,
funext,
simp,
unfold simp_fun,
unfold_coes,
conv
  begin
    to_rhs,
    rw[span sp],
  end,
rw [is_linear_map.sum lm.2],
congr,
funext,
rw[← is_linear_map.smul lm.2],
refl,
end

def n_tuples_eq_linear_maps (V : Type*) [vector_space k V] (n : ℕ) :
equiv (vector V n) (linear_map (finite_free_module k n) V) :=
{ to_fun := vec_to_linear_map V n,
  inv_fun := linear_map_to_vec V n,
  left_inv := left_inv_ V n,
  right_inv := right_inv_ V n,
}
end vector_space