import data.list.sort
import data.string.basic
import data.multiset.sort
import .utils

/-

GitHub repository: https://github.com/meganfrisella/shell-types

This project is based on the ideas in this assignment
https://cs.brown.edu/courses/cs173/2022/shell.html
about retrofitting refinement types to shell commands.

I will continue this work in collaboration with Nikos Vasilakis
and Michael Greenberg over the Winter break.

-/

namespace shell

-- represents a line of data in a stream-of-strings where each column
-- is associated with a type
def record : Type := list string

-- aliases for per-line predicates (line_pred) and between-line predicates (doc_pred)
def line_pred : Type := record → Prop
def doc_pred : Type := list record → Prop

-- custom types
inductive type : Type
| str : type
| num : type

-- represents a stream-of-string datum that is refined by a set of per-line and
-- between-line predicates
structure stream_of_strings :=
  (records : list record)
  (column_types : list type)
  (per_line : list line_pred)
  (bet_line : list doc_pred)

instance : has_repr record := ⟨λ r, utils.list_to_string r⟩
instance : has_repr stream_of_strings := ⟨λ sos, list.repr sos.records⟩

/-                                  -/
/-               SORT               -/
/-                                  -/

-- ordering predicates on pairs of records (order ascdending or descending by the first column)
-- TODO: make more sophisticated (if first col is equal, move to second col and so on)
def record_le : record → record → Prop
| r1 r2 := (list.head r1) ≤ (list.head r2)
def record_gt : record → record → Prop
| r1 r2 := (list.head r1) > (list.head r2)

instance : has_le record := { le := λ r1 r2, record_le r1 r2, }

instance : decidable_rel record_le :=
begin
  rw decidable_rel,
  intros r1 r2,
  apply string.decidable_le,
end

instance : decidable_rel record_gt :=
begin
  rw decidable_rel,
  intros r1 r2,
  apply string.decidable_lt,
end

-- TODO: useful (necessary?) to have proofs that comparison on
-- strings are instances of these
instance : linear_order record :=
{ le := record_le,
  le_refl := begin intro a, simp, rw record_le, simp end,
  le_trans := begin intros a b c, simp, repeat {rw record_le}, intros hab hbc, sorry end,
  lt_iff_le_not_le := sorry,
  le_antisymm := sorry,
  le_total := sorry,
  decidable_le := record_le.decidable_rel, }
instance : is_trans record record_gt := sorry
instance : is_antisymm record record_gt := sorry
instance : is_total record record_gt := sorry

-- sort a list of records ascending
def sort_asc (rs : list record) : list record := multiset.sort record_le ⟦rs⟧
-- sort a list of records descending
def sort_des (rs : list record) : list record := multiset.sort record_gt ⟦rs⟧

-- sorted predicates on a list of records
def sorted_asc : list record → Prop
| r := list.sorted record_le r
def sorted_des : list record → Prop
| r := list.sorted record_gt r

-- sort
-- sort in ascending order
def sort : stream_of_strings → stream_of_strings
| r := { records := sort_asc r.records,
         column_types := r.column_types,
         per_line := r.per_line,
         bet_line := [sorted_asc] }

-- sort -r
-- sort in descending order
def sort_r : stream_of_strings → stream_of_strings
| r := { records := sort_des r.records,
         column_types := r.column_types,
         per_line := r.per_line,
         bet_line := [sorted_des] }

-- sort -n
-- sort in ascending order, first column has numeric type
def sort_n_hlp : stream_of_strings → list type → option stream_of_strings
| sos (type.num :: ts) := some {records := sort_asc sos.records,
                                column_types := sos.column_types,
                                per_line := sos.per_line,
                                bet_line := [sorted_asc]}
| _ _ := none
def sort_n : stream_of_strings → option stream_of_strings
| r := sort_n_hlp r r.column_types

-- sort -rn
-- sort in descending order, first column has numeric type
def sort_rn_hlp : stream_of_strings → list type → option stream_of_strings
| sos (type.num :: ts) := some {records := sort_des sos.records,
                                column_types := sos.column_types,
                                per_line := sos.per_line,
                                bet_line := [sorted_des]}
| _ _ := none
def sort_rn (input : stream_of_strings) : option stream_of_strings :=
  sort_rn_hlp input input.column_types

/-                                  -/
/-               UNIQ               -/
/-                                  -/

-- a predicate that says "no adjacent lines are the same"
def adj_lines_uniq : list record → Prop
| (r1 :: (r2 :: rs)) := r1 ≠ r2 ∧ adj_lines_uniq (r2 :: rs)
| _ := true

-- collapse adjacent duplicate records
def make_uniq : list record → list record
| (r1 :: (r2 :: rs)) := if r1 = r2
                        then make_uniq (r1 :: rs)
                        else (r1 :: make_uniq (r2 :: rs))
| e := e

-- collapse adjacent duplicate records and prefix records with a count
def prefix_init_count : list record → list record
| (r :: rs) := ((utils.nat_to_string 1 :: r) :: prefix_init_count rs)
| [] := []
def make_uniq_c_hlp : list record → list record
| ((cnt1 :: r1) :: ((cnt2 :: r2) :: rs)) := if r1 = r2
  then make_uniq_c_hlp ((utils.nat_to_string (utils.string_to_nat cnt1 + utils.string_to_nat cnt2) :: r2) :: rs)
  else ((cnt1 :: r1) :: (make_uniq_c_hlp ((cnt2 :: r2) :: rs)))
| e := e
def make_uniq_c (rs : list record) : list record := make_uniq_c_hlp (prefix_init_count rs)

-- uniq
-- filter out adjacent repeated lines
def uniq (input : stream_of_strings) : stream_of_strings :=
  { records := make_uniq input.records,
    column_types := input.column_types,
    per_line := input.per_line,
    bet_line := adj_lines_uniq :: input.bet_line }

-- uniq -c
-- filter out adjacent repeated lines and prefix the lines with
-- a count column
def uniq_c (input : stream_of_strings) : stream_of_strings :=
  { records := make_uniq_c input.records,
    column_types := type.num :: input.column_types,
    per_line := input.per_line,
    bet_line := adj_lines_uniq :: input.bet_line }

/-                                  -/
/-           VERIFICATION           -/
/-                                  -/

def my_sos : stream_of_strings :=
{ records := [["Hello", " ", "world!"],
              ["Hello", " ", "world!"],
              ["Why,", " ", "hello."],
              ["Hello", " ", "world!"],
              ["ABC", " "],
              ["ABC"],
              ["ABC"],
              ["ABC"]],
  column_types := [type.str, type.str, type.str],
  per_line := [],
  bet_line := [] }

def my_sos_uniq : stream_of_strings := uniq my_sos
def my_sos_uniq_c : stream_of_strings := uniq_c my_sos
def my_sos_sort : stream_of_strings := sort my_sos
def my_sos_sort_r : stream_of_strings := sort_r my_sos
def my_sos_uniq_c_sort_n : option stream_of_strings := sort_n (uniq_c my_sos)
def my_sos_uniq_c_sort_rn : option stream_of_strings := sort_rn (uniq_c my_sos)

#eval my_sos.records
#eval my_sos_uniq.records
#eval my_sos_uniq_c.records
#eval my_sos_sort.records
#eval my_sos_sort_r.records
#eval my_sos_uniq_c_sort_n
#eval my_sos_uniq_c_sort_rn

----------- Part 1: composing sort commands -----------

-- the lists have the same exact items (including arity)
-- a.k.a. they are the same multiset
def same_items {α : Type} (l1 l2 : list α ) : Prop := ⟦l1⟧ = ⟦l2⟧

-- modified from mathlib/src/data/multiset/sort.lean [https://github.com/leanprover-community/mathlib/blob/11bb0c9152e5d14278fb0ac5e0be6d50e2c8fa05/src/data/multiset/sort.lean#L36]
-- to explicitly use ⟦⟧ notation instead of ↑
@[simp] theorem sort_eq {α : Type} (r : α → α → Prop) (s : multiset α) [decidable_rel r] [is_trans α r] [is_antisymm α r] [is_total α r] :
  ⟦(multiset.sort r s)⟧ = s := quot.induction_on s $ λ l, quot.sound $ list.perm_merge_sort _ _

-- applying a sorting rule to lists that contain the same
-- exact items produces the same lists
axiom sort_same_items_produces_same_lists {α : Type} (R : α → α → Prop) (l1 l2 : list α) [decidable_rel R] [is_trans α R] [is_antisymm α R] [is_total α R] :
  same_items l2 l1 → multiset.sort R ⟦l1⟧ = multiset.sort R ⟦l2⟧

-- a sorted list has the exact same items as the unsorted list
lemma sorted_list_has_same_items {α : Type} (R : α → α → Prop) (as : list α) [decidable_rel R] [is_trans α R] [is_antisymm α R] [is_total α R] :
  same_items (multiset.sort R ⟦as⟧) as :=
begin
  rw same_items,
  apply sort_eq R ⟦as⟧,
end

-- a composition of sorts flattens to the outermost sort,
-- in this case composing sort_r with sort
lemma sort_composition (sos : stream_of_strings) : sort sos = sort (sort_r sos) :=
begin
  rw [sort, sort, sort_r, sort_asc, sort_asc, sort_des],
  simp,
  apply sort_same_items_produces_same_lists record_le,
  exact sorted_list_has_same_items record_gt sos.records,
end

----------- Part 2: composing sort with uniq -----------

-- sorting then doing uniq produces a stream of strings without duplicate lines
lemma sort_then_uniq_nodup (sos : stream_of_strings) :
  list.nodup (uniq (sort sos)).records := sorry

end shell


import data.nat.basic
import algebra.group.pi
import tactic.ring

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

namespace complexity
namespace cost

@[simp]
def raw_cost_independent (f : ℕ → (ℕ × α)) :=
  ∀ c₀ c₁ : ℕ, (f c₀).fst + c₁ = (f c₁).fst + c₀

@[simp]
def raw_value_independent (f : ℕ → (ℕ × α)) :=
  ∀ c₀ c₁ : ℕ, (f c₀).snd = (f c₁).snd

@[simp]
def invariant (f : ℕ → (ℕ × α)) :=
  raw_cost_independent f ∧ raw_value_independent f

def costed (α : Type u) :=
  { val : ℕ → (ℕ × α) // invariant val }

theorem cost_independent {ca : costed α} : ∀ c₀, (ca.val c₀).fst = (ca.val 0).fst + c₀ :=
begin
  intro c₀,
  exact ca.2.left c₀ 0,
end

theorem value_independent {ca : costed α} : ∀ c₀, (ca.val c₀).snd = (ca.val 0).snd :=
begin
  intro c₀,
  exact ca.2.right c₀ 0,
end

@[simp]
def raw_pure (a : α) := λ c : ℕ, (c, a)

theorem raw_pure_invariant (a : α) : invariant (raw_pure a) :=
begin
  split,
  intros c₀ c₁,
  simp [nat.add_comm],
  intros c₀ c₁,
  simp,
end

def pure (a : α) : costed α := ⟨ raw_pure a, raw_pure_invariant a ⟩

@[simp]
def raw_succ (a : α) := λ c : ℕ, (nat.succ c, a)

theorem raw_succ_invariant (a : α) : invariant (raw_succ a) :=
begin
  split,
  intros c₀ c₁,
  unfold raw_succ,
  simp [nat.succ_add],
  rw  [nat.add_comm],
  intros c₀ c₁,
  simp,
end

def succ (a : α) : costed α := ⟨ raw_succ a, raw_succ_invariant a ⟩

@[simp]
def raw_bind (ca : costed α) (cba : α → costed β) : (ℕ → (ℕ × β)) :=
  λ c : ℕ, (cba (ca.1 c).snd).1 (ca.1 c).fst

theorem raw_bind_invariant (ca : costed α) (cba : α → costed β) : invariant (raw_bind ca cba) :=
begin
  split,
  intros c₀ c₁,
  unfold raw_bind,
  rw [@cost_independent α],
  rw [@cost_independent β],
  rw [value_independent],
  conv begin
    to_rhs,
    rw [@cost_independent α],
    rw [@cost_independent β],
    rw [value_independent],
  end,
  ring,
  intros c₀ c₁,
  unfold raw_bind,
  rw [@value_independent α],
  rw [@value_independent β],
  conv begin
    to_rhs,
    rw [@value_independent α],
    rw [@value_independent β],
  end,
end

def bind (ca : costed α) (cba : α → costed β) : costed β := ⟨ raw_bind ca cba, raw_bind_invariant ca cba ⟩

instance : monad costed := {pure := @complexity.cost.pure, bind := @complexity.cost.bind}

@[simp]
def cross (ca : costed α) (cb : costed β) : costed (α × β) :=
  do
    a ← ca,
    b ← cb,
    return (a, b)

def cost_of (ca : costed α) : ℕ := (ca.val 0).fst

def value_of (ca : costed α) : α := (ca.val 0).snd

@[simp]
theorem cost_of_pure {a : α} : cost_of (pure a) = 0 := by simp [cost_of, pure]
@[simp]
theorem value_of_pure {a : α} : value_of (pure a) = a := by simp[value_of, pure]

@[simp]
theorem cost_of_return {a : α} : cost_of (return a) = 0 := by simp [return, has_pure.pure]

@[simp]
theorem value_of_return {a : α} : value_of (return a) = a := by simp [return, has_pure.pure]

@[simp]
theorem cost_of_succ {a : α} : cost_of (succ a) = 1 := by simp[cost_of, succ]
@[simp]
theorem value_of_succ {a : α} : value_of (succ a) = a := by simp[value_of, succ]

@[simp]
theorem cost_of_bind {ca : costed α} {cab : α → costed β} :
    cost_of (bind ca cab) =  cost_of (cab (value_of ca)) + (cost_of ca) :=
begin
  simp [bind, cost_of, value_of],
  have g := @cost_independent β (cab (ca.val 0).snd) (ca.val 0).fst,
  simp at g,
  rw [← g],
end

@[simp]
theorem cost_of_bind_infix {α β : Type u} {ca : costed α} {cab : α → costed β} :
  cost_of (ca >>= cab) = cost_of (cab (value_of ca))+ cost_of ca := cost_of_bind

@[simp]
theorem value_of_bind {ca : costed α} {cab : α → costed β} :
    value_of (bind ca cab) = value_of (cab (value_of ca)) :=
begin
  simp [bind, value_of],
  have g := value_independent,
  simp at g,
  rw [g],
end

@[simp]
theorem value_of_bind_infix {α β : Type u} {ca : costed α} {cab : α → costed β} :
  value_of (ca >>= cab) = value_of (cab (value_of ca))  := value_of_bind

end cost
end complexity


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import algebra.group.pi

/-!
Test if `simp` can use a lemma about `pi.has_one` to simplify `1` coming from `pi.group`
-/

variables {I : Type*} {f : Π i : I, Type*}

namespace test

def eval_default [inhabited I] (F : Π i, f i) : f default := F default

@[simp] lemma eval_default_one [inhabited I] [Π i, has_one (f i)] :
  eval_default (1 : Π i, f i) = 1 := rfl

example [inhabited I] [Π i, group (f i)] (F : Π i, f i) : eval_default (F⁻¹ * F) = 1 := by simp

end test


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.char_p.default
import Mathlib.algebra.ring.pi
import Mathlib.analysis.special_functions.pow
import Mathlib.field_theory.perfect_closure
import Mathlib.ring_theory.localization
import Mathlib.ring_theory.subring
import Mathlib.ring_theory.valuation.integers
import Mathlib.PostPort

universes u₁ u₂ l u₃ u₄

namespace Mathlib

/-!
# Ring Perfection and Tilt

In this file we define the perfection of a ring of characteristic p, and the tilt of a field
given a valuation to `ℝ≥0`.

## TODO

Define the valuation on the tilt, and define a characteristic predicate for the tilt.

-/

/-- The perfection of a monoid `M`, defined to be the projective limit of `M`
using the `p`-th power maps `M → M` indexed by the natural numbers, implemented as
`{ f : ℕ → M | ∀ n, f (n + 1) ^ p = f n }`. -/
def monoid.perfection (M : Type u₁) [comm_monoid M] (p : ℕ) : submonoid (ℕ → M) :=
  submonoid.mk (set_of fun (f : ℕ → M) => ∀ (n : ℕ), f (n + 1) ^ p = f n) sorry sorry

/-- The perfection of a ring `R` with characteristic `p`,
defined to be the projective limit of `R` using the Frobenius maps `R → R`
indexed by the natural numbers, implemented as `{ f : ℕ → R | ∀ n, f (n + 1) ^ p = f n }`. -/
def ring.perfection (R : Type u₁) [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] : subsemiring (ℕ → R) :=
  subsemiring.mk (submonoid.carrier (monoid.perfection R p)) sorry sorry sorry sorry

namespace perfection

/-- The `n`-th coefficient of an element of the perfection. -/
def coeff (R : Type u₁) [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] (n : ℕ) : ↥(ring.perfection R p) →+* R :=
  ring_hom.mk (fun (f : ↥(ring.perfection R p)) => subtype.val f n) sorry sorry sorry sorry

theorem ext {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] {f : ↥(ring.perfection R p)} {g : ↥(ring.perfection R p)} (h : ∀ (n : ℕ), coe_fn (coeff R p n) f = coe_fn (coeff R p n) g) : f = g :=
  subtype.eq (funext h)

/-- The `p`-th root of an element of the perfection. -/
def pth_root (R : Type u₁) [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] : ↥(ring.perfection R p) →+* ↥(ring.perfection R p) :=
  ring_hom.mk
    (fun (f : ↥(ring.perfection R p)) => { val := fun (n : ℕ) => coe_fn (coeff R p (n + 1)) f, property := sorry }) sorry
    sorry sorry sorry

@[simp] theorem coeff_mk {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ℕ → R) (hf : f ∈ ring.perfection R p) (n : ℕ) : coe_fn (coeff R p n) { val := f, property := hf } = f n :=
  rfl

theorem coeff_pth_root {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) : coe_fn (coeff R p n) (coe_fn (pth_root R p) f) = coe_fn (coeff R p (n + 1)) f :=
  rfl

theorem coeff_pow_p {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) : coe_fn (coeff R p (n + 1)) (f ^ p) = coe_fn (coeff R p n) f := sorry

theorem coeff_pow_p' {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) : coe_fn (coeff R p (n + 1)) f ^ p = coe_fn (coeff R p n) f :=
  subtype.property f n

theorem coeff_frobenius {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) : coe_fn (coeff R p (n + 1)) (coe_fn (frobenius (↥(ring.perfection R p)) p) f) = coe_fn (coeff R p n) f := sorry

theorem coeff_iterate_frobenius {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) (m : ℕ) : coe_fn (coeff R p (n + m)) (nat.iterate (⇑(frobenius (↥(ring.perfection R p)) p)) m f) = coe_fn (coeff R p n) f := sorry

theorem coeff_iterate_frobenius' {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] (f : ↥(ring.perfection R p)) (n : ℕ) (m : ℕ) (hmn : m ≤ n) : coe_fn (coeff R p n) (nat.iterate (⇑(frobenius (↥(ring.perfection R p)) p)) m f) = coe_fn (coeff R p (n - m)) f :=
  Eq.symm (Eq.trans (Eq.symm (coeff_iterate_frobenius f (n - m) m)) (Eq.symm (nat.sub_add_cancel hmn) ▸ rfl))

theorem pth_root_frobenius {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] : ring_hom.comp (pth_root R p) (frobenius (↥(ring.perfection R p)) p) = ring_hom.id ↥(ring.perfection R p) := sorry

theorem frobenius_pth_root {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] : ring_hom.comp (frobenius (↥(ring.perfection R p)) p) (pth_root R p) = ring_hom.id ↥(ring.perfection R p) := sorry

theorem coeff_add_ne_zero {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] {f : ↥(ring.perfection R p)} {n : ℕ} (hfn : coe_fn (coeff R p n) f ≠ 0) (k : ℕ) : coe_fn (coeff R p (n + k)) f ≠ 0 := sorry

theorem coeff_ne_zero_of_le {R : Type u₁} [comm_semiring R] {p : ℕ} [hp : fact (nat.prime p)] [char_p R p] {f : ↥(ring.perfection R p)} {m : ℕ} {n : ℕ} (hfm : coe_fn (coeff R p m) f ≠ 0) (hmn : m ≤ n) : coe_fn (coeff R p n) f ≠ 0 := sorry

protected instance perfect_ring (R : Type u₁) [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] : perfect_ring (↥(ring.perfection R p)) p :=
  perfect_ring.mk ⇑(pth_root R p) sorry sorry

protected instance ring (p : ℕ) [hp : fact (nat.prime p)] (R : Type u₁) [comm_ring R] [char_p R p] : ring ↥(ring.perfection R p) :=
  subring.to_ring (subsemiring.to_subring (ring.perfection R p) sorry)

protected instance comm_ring (p : ℕ) [hp : fact (nat.prime p)] (R : Type u₁) [comm_ring R] [char_p R p] : comm_ring ↥(ring.perfection R p) :=
  subring.to_comm_ring (subsemiring.to_subring (ring.perfection R p) sorry)

/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* perfection S p`. -/
@[simp] theorem lift_symm_apply (p : ℕ) [hp : fact (nat.prime p)] (R : Type u₁) [comm_semiring R] [char_p R p] [perfect_ring R p] (S : Type u₂) [comm_semiring S] [char_p S p] (hmn : R →+* ↥(ring.perfection S p)) : coe_fn (equiv.symm (lift p R S)) hmn = ring_hom.comp (coeff S p 0) hmn :=
  Eq.refl (coe_fn (equiv.symm (lift p R S)) hmn)

theorem hom_ext (p : ℕ) [hp : fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] [perfect_ring R p] {S : Type u₂} [comm_semiring S] [char_p S p] {f : R →+* ↥(ring.perfection S p)} {g : R →+* ↥(ring.perfection S p)} (hfg : ∀ (x : R), coe_fn (coeff S p 0) (coe_fn f x) = coe_fn (coeff S p 0) (coe_fn g x)) : f = g :=
  equiv.injective (equiv.symm (lift p R S)) (ring_hom.ext hfg)

/-- A ring homomorphism `R →+* S` induces `perfection R p →+* perfection S p` -/
@[simp] theorem map_apply_coe {R : Type u₁} [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] {S : Type u₂} [comm_semiring S] [char_p S p] (φ : R →+* S) (f : ↥(ring.perfection R p)) (n : ℕ) : coe (coe_fn (map p φ) f) n = coe_fn φ (coe_fn (coeff R p n) f) :=
  Eq.refl (coe (coe_fn (map p φ) f) n)

theorem coeff_map {R : Type u₁} [comm_semiring R] (p : ℕ) [hp : fact (nat.prime p)] [char_p R p] {S : Type u₂} [comm_semiring S] [char_p S p] (φ : R →+* S) (f : ↥(ring.perfection R p)) (n : ℕ) : coe_fn (coeff S p n) (coe_fn (map p φ) f) = coe_fn φ (coe_fn (coeff R p n) f) :=
  rfl

end perfection

/-- A perfection map to a ring of characteristic `p` is a map that is isomorphic
to its perfection. -/
structure perfection_map (p : ℕ) [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₂} [comm_semiring P] [char_p P p] [perfect_ring P p] (π : P →+* R)
where
  injective : ∀ {x y : P},
  (∀ (n : ℕ), coe_fn π (nat.iterate (⇑(pth_root P p)) n x) = coe_fn π (nat.iterate (⇑(pth_root P p)) n y)) → x = y
  surjective : ∀ (f : ℕ → R),
  (∀ (n : ℕ), f (n + 1) ^ p = f n) → ∃ (x : P), ∀ (n : ℕ), coe_fn π (nat.iterate (⇑(pth_root P p)) n x) = f n

namespace perfection_map

/-- Create a `perfection_map` from an isomorphism to the perfection. -/
theorem mk' {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {f : P →+* R} (g : P ≃+* ↥(ring.perfection R p)) (hfg : coe_fn (perfection.lift p P R) f = ↑g) : perfection_map p f := sorry

/-- The canonical perfection map from the perfection of a ring. -/
theorem of (p : ℕ) [fact (nat.prime p)] (R : Type u₁) [comm_semiring R] [char_p R p] : perfection_map p (perfection.coeff R p 0) :=
  mk' (ring_equiv.refl ↥(ring.perfection R p))
    (iff.mpr (equiv.apply_eq_iff_eq_symm_apply (perfection.lift p (↥(ring.perfection R p)) R)) rfl)

/-- For a perfect ring, it itself is the perfection. -/
theorem id (p : ℕ) [fact (nat.prime p)] (R : Type u₁) [comm_semiring R] [char_p R p] [perfect_ring R p] : perfection_map p (ring_hom.id R) := sorry

/-- A perfection map induces an isomorphism to the prefection. -/
def equiv {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) : P ≃+* ↥(ring.perfection R p) :=
  ring_equiv.of_bijective (coe_fn (perfection.lift p P R) π) sorry

theorem equiv_apply {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) (x : P) : coe_fn (equiv m) x = coe_fn (coe_fn (perfection.lift p P R) π) x :=
  rfl

theorem comp_equiv {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) (x : P) : coe_fn (perfection.coeff R p 0) (coe_fn (equiv m) x) = coe_fn π x :=
  rfl

theorem comp_equiv' {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) : ring_hom.comp (perfection.coeff R p 0) ↑(equiv m) = π :=
  ring_hom.ext fun (x : P) => rfl

theorem comp_symm_equiv {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) (f : ↥(ring.perfection R p)) : coe_fn π (coe_fn (ring_equiv.symm (equiv m)) f) = coe_fn (perfection.coeff R p 0) f :=
  Eq.trans (Eq.symm (comp_equiv m (coe_fn (ring_equiv.symm (equiv m)) f)))
    (congr_arg (⇑(perfection.coeff R p 0)) (ring_equiv.apply_symm_apply (equiv m) f))

theorem comp_symm_equiv' {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {π : P →+* R} (m : perfection_map p π) : ring_hom.comp π ↑(ring_equiv.symm (equiv m)) = perfection.coeff R p 0 :=
  ring_hom.ext (comp_symm_equiv m)

/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* P`,
where `P` is any perfection of `S`. -/
@[simp] theorem lift_apply (p : ℕ) [fact (nat.prime p)] (R : Type u₁) [comm_semiring R] [char_p R p] [perfect_ring R p] (S : Type u₂) [comm_semiring S] [char_p S p] (P : Type u₃) [comm_semiring P] [char_p P p] [perfect_ring P p] (π : P →+* S) (m : perfection_map p π) (f : R →+* S) : coe_fn (lift p R S P π m) f = ring_hom.comp (↑(ring_equiv.symm (equiv m))) (coe_fn (perfection.lift p R S) f) :=
  Eq.refl (coe_fn (lift p R S P π m) f)

theorem hom_ext {p : ℕ} [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] [perfect_ring R p] {S : Type u₂} [comm_semiring S] [char_p S p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] (π : P →+* S) (m : perfection_map p π) {f : R →+* P} {g : R →+* P} (hfg : ∀ (x : R), coe_fn π (coe_fn f x) = coe_fn π (coe_fn g x)) : f = g :=
  equiv.injective (equiv.symm (lift p R S P π m)) (ring_hom.ext hfg)

/-- A ring homomorphism `R →+* S` induces `P →+* Q`, a map of the respective perfections -/
def map (p : ℕ) [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {S : Type u₂} [comm_semiring S] [char_p S p] {Q : Type u₄} [comm_semiring Q] [char_p Q p] [perfect_ring Q p] {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ) (φ : R →+* S) : P →+* Q :=
  coe_fn (lift p P S Q σ n) (ring_hom.comp φ π)

theorem comp_map (p : ℕ) [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {S : Type u₂} [comm_semiring S] [char_p S p] {Q : Type u₄} [comm_semiring Q] [char_p Q p] [perfect_ring Q p] {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ) (φ : R →+* S) : ring_hom.comp σ (map p m n φ) = ring_hom.comp φ π :=
  equiv.symm_apply_apply (lift p P S Q σ n) (ring_hom.comp φ π)

theorem map_map (p : ℕ) [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p] {S : Type u₂} [comm_semiring S] [char_p S p] {Q : Type u₄} [comm_semiring Q] [char_p Q p] [perfect_ring Q p] {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ) (φ : R →+* S) (x : P) : coe_fn σ (coe_fn (map p m n φ) x) = coe_fn φ (coe_fn π x) :=
  iff.mp ring_hom.ext_iff (comp_map p m n φ) x

-- Why is this slow?

theorem map_eq_map (p : ℕ) [fact (nat.prime p)] {R : Type u₁} [comm_semiring R] [char_p R p] {S : Type u₂} [comm_semiring S] [char_p S p] (φ : R →+* S) : map p (of p R) (of p S) φ = perfection.map p φ := sorry

end perfection_map

/-- `O/(p)` for `O`, ring of integers of `K`. -/
def mod_p (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) :=
  ideal.quotient (ideal.span (singleton ↑p))

namespace mod_p

protected instance comm_ring (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) : comm_ring (mod_p K v O hv p) :=
  ideal.quotient.comm_ring (ideal.span (singleton ↑p))

protected instance char_p (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : char_p (mod_p K v O hv p) p :=
  char_p.quotient O p
    (mt (valuation.integers.one_of_is_unit hv) (Eq.symm (ring_hom.map_nat_cast (algebra_map O K) p) ▸ hvp))

protected instance nontrivial (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : nontrivial (mod_p K v O hv p) :=
  char_p.nontrivial_of_char_ne_one (nat.prime.ne_one hp)

/-- For a field `K` with valuation `v : K → ℝ≥0` and ring of integers `O`,
a function `O/(p) → ℝ≥0` that sends `0` to `0` and `x + (p)` to `v(x)` as long as `x ∉ (p)`. -/
def pre_val (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) (x : mod_p K v O hv p) : nnreal :=
  ite (x = 0) 0 (coe_fn v (coe_fn (algebra_map O K) (quotient.out' x)))

theorem pre_val_mk {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} {x : O} (hx : coe_fn (ideal.quotient.mk (ideal.span (singleton ↑p))) x ≠ 0) : pre_val K v O hv p (coe_fn (ideal.quotient.mk (ideal.span (singleton ↑p))) x) = coe_fn v (coe_fn (algebra_map O K) x) := sorry

theorem pre_val_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} : pre_val K v O hv p 0 = 0 :=
  if_pos rfl

theorem pre_val_mul {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} {x : mod_p K v O hv p} {y : mod_p K v O hv p} (hxy0 : x * y ≠ 0) : pre_val K v O hv p (x * y) = pre_val K v O hv p x * pre_val K v O hv p y := sorry

theorem pre_val_add {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} (x : mod_p K v O hv p) (y : mod_p K v O hv p) : pre_val K v O hv p (x + y) ≤ max (pre_val K v O hv p x) (pre_val K v O hv p y) := sorry

theorem v_p_lt_pre_val {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} {x : mod_p K v O hv p} : coe_fn v ↑p < pre_val K v O hv p x ↔ x ≠ 0 := sorry

theorem pre_val_eq_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} {x : mod_p K v O hv p} : pre_val K v O hv p x = 0 ↔ x = 0 := sorry

theorem v_p_lt_val {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] (hv : valuation.integers v O) {p : ℕ} {x : O} : coe_fn v ↑p < coe_fn v (coe_fn (algebra_map O K) x) ↔ coe_fn (ideal.quotient.mk (ideal.span (singleton ↑p))) x ≠ 0 := sorry

theorem mul_ne_zero_of_pow_p_ne_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] {x : mod_p K v O hv p} {y : mod_p K v O hv p} (hx : x ^ p ≠ 0) (hy : y ^ p ≠ 0) : x * y ≠ 0 := sorry

end mod_p

/-- Perfection of `O/(p)` where `O` is the ring of integers of `K`. -/
def pre_tilt (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : subsemiring (ℕ → mod_p K v O hv p) :=
  ring.perfection (mod_p K v O hv p) p

namespace pre_tilt

protected instance comm_ring (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : comm_ring ↥(pre_tilt K v O hv p) :=
  perfection.comm_ring p (mod_p K v O hv p)

/-- The valuation `Perfection(O/(p)) → ℝ≥0` as a function.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
def val_aux (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] (f : ↥(pre_tilt K v O hv p)) : nnreal :=
  dite (∃ (n : ℕ), coe_fn (perfection.coeff (mod_p K v O hv p) p n) f ≠ 0)
    (fun (h : ∃ (n : ℕ), coe_fn (perfection.coeff (mod_p K v O hv p) p n) f ≠ 0) =>
      mod_p.pre_val K v O hv p (coe_fn (perfection.coeff (mod_p K v O hv p) p (nat.find h)) f) ^ p ^ nat.find h)
    fun (h : ¬∃ (n : ℕ), coe_fn (perfection.coeff (mod_p K v O hv p) p n) f ≠ 0) => 0

theorem coeff_nat_find_add_ne_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] {f : ↥(pre_tilt K v O hv p)} {h : ∃ (n : ℕ), coe_fn (perfection.coeff (mod_p K v O hv p) p n) f ≠ 0} (k : ℕ) : coe_fn (perfection.coeff (mod_p K v O hv p) p (nat.find h + k)) f ≠ 0 :=
  perfection.coeff_add_ne_zero (nat.find_spec h) k

theorem val_aux_eq {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] {f : ↥(pre_tilt K v O hv p)} {n : ℕ} (hfn : coe_fn (perfection.coeff (mod_p K v O hv p) p n) f ≠ 0) : val_aux K v O hv p f = mod_p.pre_val K v O hv p (coe_fn (perfection.coeff (mod_p K v O hv p) p n) f) ^ p ^ n := sorry

theorem val_aux_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : val_aux K v O hv p 0 = 0 := sorry

theorem val_aux_one {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : val_aux K v O hv p 1 = 1 := sorry

theorem val_aux_mul {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] (f : ↥(pre_tilt K v O hv p)) (g : ↥(pre_tilt K v O hv p)) : val_aux K v O hv p (f * g) = val_aux K v O hv p f * val_aux K v O hv p g := sorry

theorem val_aux_add {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] (f : ↥(pre_tilt K v O hv p)) (g : ↥(pre_tilt K v O hv p)) : val_aux K v O hv p (f + g) ≤ max (val_aux K v O hv p f) (val_aux K v O hv p g) := sorry

/-- The valuation `Perfection(O/(p)) → ℝ≥0`.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
def val (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : valuation (↥(pre_tilt K v O hv p)) nnreal :=
  valuation.mk (val_aux K v O hv p) val_aux_zero val_aux_one val_aux_mul val_aux_add

theorem map_eq_zero {K : Type u₁} [field K] {v : valuation K nnreal} {O : Type u₂} [comm_ring O] [algebra O K] {hv : valuation.integers v O} {p : ℕ} [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] {f : ↥(pre_tilt K v O hv p)} : coe_fn (val K v O hv p) f = 0 ↔ f = 0 := sorry

protected instance integral_domain (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : integral_domain ↥(pre_tilt K v O hv p) :=
  integral_domain.mk comm_ring.add sorry comm_ring.zero sorry sorry comm_ring.neg comm_ring.sub sorry sorry comm_ring.mul
    sorry comm_ring.one sorry sorry sorry sorry sorry sorry sorry

end pre_tilt

/-- The tilt of a field, as defined in Perfectoid Spaces by Peter Scholze, as in
[scholze2011perfectoid]. Given a field `K` with valuation `K → ℝ≥0` and ring of integers `O`,
this is implemented as the fraction field of the perfection of `O/(p)`. -/
def tilt (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] :=
  fraction_ring ↥(pre_tilt K v O hv p)

namespace tilt

protected instance field (K : Type u₁) [field K] (v : valuation K nnreal) (O : Type u₂) [comm_ring O] [algebra O K] (hv : valuation.integers v O) (p : ℕ) [hp : fact (nat.prime p)] [hvp : fact (coe_fn v ↑p ≠ 1)] : field (tilt K v O hv p) :=
  fraction_ring.field


--

axiom p {α β} : α → β → Prop
axiom foo {α β} (a : α) (b : β) : p a b

example : p 0 0 := by simp [foo]
example (a : Nat) : p a a := by simp [foo a]
example : p 0 0 := by simp [foo 0]
example : p 0 0 := by simp [foo 0 0]
example : p 0 0 := by
  simp [foo 1] -- will not simplify
  simp [foo 0]
example : p 0 0 ∧ p 1 1 := by
  simp [foo 1]
  trace_state
  simp [foo 0]

namespace Foo

axiom p {α} : α → Prop
axiom foo {α} [ToString α] (n : Nat) (a : α) : p a

example : p 0 := by simp [foo 0]
example : p 0 ∧ True := by simp [foo 0]

end Foo


import field_theory.is_alg_closed.algebraic_closure
import data.zmod.basic
import data.equiv.transfer_instance
import Rings.ToMathlib.char_p
import algebra.char_p.algebra
import field_theory.separable

universes u v

namespace is_alg_closed

open polynomial

lemma of_exists_root_nat_degree {k : Type*} [field k] (H : ∀ p : polynomial k, p.monic → irreducible p → nat_degree p ≠ 0 → ∃ x, p.eval x = 0) :
  is_alg_closed k :=
begin
  apply of_exists_root,
  intros p hmonic hirr,
  by_cases hdeg : nat_degree p = 0,
  {
    rw monic.nat_degree_eq_zero_iff_eq_one hmonic at hdeg,
    rw hdeg at hirr,
    exfalso,
    apply hirr.1,
    exact ⟨ 1 , rfl ⟩,
  },
  apply H p hmonic hirr hdeg,
end

lemma of_nat_degree_ne_zero_exists_root {k : Type*} [field k]
  (H : ∀ p : polynomial k, nat_degree p ≠ 0 → ∃ x, p.eval x = 0) :
  is_alg_closed k :=
of_exists_root_nat_degree $ λ _ _ hdeg, H _

lemma infinite {K : Type*} [field K] [is_alg_closed K] : infinite K :=
begin
  split,
  intro hfin, haveI := hfin,
  set n := fintype.card K with hn,
  set f : polynomial K := polynomial.monomial n.succ 1 - 1 with hf,
  have hfsep : f.separable,
  { rw polynomial.separable_def',
    refine ⟨ -1, (polynomial.monomial 1 (n.succ : K)⁻¹), _⟩,
    simp only [hf, polynomial.derivative_sub, polynomial.derivative_monomial,
      neg_one_mul, neg_sub, one_mul, polynomial.derivative_one, sub_zero,
      polynomial.monomial_mul_monomial],
    have hrw0 : (1 + (n.succ - 1)) = n.succ, ring,
    rw hrw0,
    have hrw1 : (n.succ : K)⁻¹ * n.succ = 1, simp,
    -- char_p.cast_card_eq_zero, nice simp lemma!
    rw hrw1,
    ring },
  apply nat.not_succ_le_self (fintype.card K),
  have hroot : fintype.card (f.root_set K) = n.succ,
  { rw [polynomial.card_root_set_eq_nat_degree hfsep (is_alg_closed.splits_domain _),
      hf, polynomial.nat_degree, polynomial.degree_sub_eq_left_of_degree_lt],
    { simp },
    { simpa [← cmp_eq_lt_iff] } },
  rw ← hroot,
  apply fintype.card_le_of_injective coe subtype.coe_injective,
end

end is_alg_closed

section ulift

variables {K : Type} [field K]

instance : field (ulift K) := equiv.ulift.field

lemma nat_down_eq : Π {n : ℕ}, (n : ulift K).down = (n : K)
| nat.zero := rfl
| (nat.succ n) := by { simp only [nat.cast_succ], rw ← nat_down_eq, refl }

lemma nat_up_eq : Π {n : ℕ}, ulift.up (n : K) = (n : ulift K)
| nat.zero := rfl
| (nat.succ n) := by { simp only [nat.cast_succ], rw ← nat_up_eq, refl }

lemma ulift_char_p (p : ℕ) [hp : fact (nat.prime p)] (hp : char_p K p) :
  char_p (ulift K) p :=
begin
  split,
  intro n,
  rw ← hp.cast_eq_zero_iff,
  split,
  {
    intro hn,
    convert congr_arg ulift.down hn,
    rw nat_down_eq,
  },
  {
    intro hn,
    rw ← ulift.up_down ↑n,
    rw ← ulift.up_down 0,
    apply congr_arg ulift.up,
    convert hn,
    rw nat_down_eq,
  },
end

lemma ulift_char_zero (h0 : char_zero K) : char_zero (ulift K) :=
begin
  split,
  intros n m hnm,
  apply h0.1,
  apply equiv.injective equiv.ulift.symm,
  convert hnm,
  repeat { simp [nat_up_eq] },
end

end ulift

namespace algebraic_closure

section instances

variables (p : ℕ) [hp : fact (nat.prime p)]

/-- algebraic closure of finite fields with char p lifted to any universe -/
@[reducible] def of_ulift_zmod (p : ℕ) [hp : fact (nat.prime p)] :=
algebraic_closure (ulift (zmod p))

/-- algebraic closure of zmod is still characteristic p -/
lemma of_ulift_zmod.char_p (p : ℕ) [hp : fact (nat.prime p)] : char_p (of_ulift_zmod.{u} p) p :=
(ring_hom.char_p_iff_char_p (algebra_map (ulift.{u} (zmod p))
  (of_ulift_zmod.{u} p)) p).1 $ ulift_char_p p (zmod.char_p p)

/-- algebraic closure of finite fields with char p lifted to any universe -/
@[reducible] def of_ulift_rat := algebraic_closure (ulift rat)

/-- algebraic closure of zmod is still characteristic p -/
lemma of_ulift_rat.char_zero : char_zero of_ulift_rat.{u} :=
(ring_hom.char_zero_iff (ring_hom.injective (algebra_map (ulift.{u} rat) _))).1
  (ulift_char_zero linear_ordered_semiring.to_char_zero)

end instances

end algebraic_closure


import tutorial_world.level10_cases1_and --hide
open set IncidencePlane --hide

variables {Ω : Type} [IncidencePlane Ω] --hide

/-
Suppose now that your hypothesis says that `P`
**or** `Q` holds. That is, you have `h : P ∨ Q`. Then `cases h` will
create two new goals, and in each of them it will
replace `h` with `h : P` in the first case, and with `h : Q` in the second.

-/

/- Lemma : no-side-bar
If X is any set in Ω and either P or Q is in X, then X is not empty.
-/
lemma nonempty_example (P Q : Ω) (X : set Ω) (h : P ∈ X ∨ Q ∈ X) : ∃ R, R ∈ X :=
begin
  cases h,
  {
    use P,
    exact h
  },
  {
    use Q,
    exact h
  }

end