/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.witt_vector.is_poly
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
## Multiplication by `n` in the ring of Witt vectors

In this file we show that multiplication by `n` in the ring of Witt vectors
is a polynomial function. We then use this fact to show that the composition of Frobenius
and Verschiebung is equal to multiplication by `p`.

### Main declarations

* `mul_n_is_poly`: multiplication by `n` is a polynomial function

-/

namespace witt_vector

/-- `witt_mul_n p n` is the family of polynomials that computes
the coefficients of `x * n` in terms of the coefficients of the Witt vector `x`. -/
def witt_mul_n (p : ℕ) [hp : fact (nat.prime p)] : ℕ → ℕ → mv_polynomial ℕ ℤ := sorry

theorem mul_n_coeff {p : ℕ} {R : Type u_1} [hp : fact (nat.prime p)] [comm_ring R] (n : ℕ)
    (x : witt_vector p R) (k : ℕ) :
    coeff (x * ↑n) k = coe_fn (mv_polynomial.aeval (coeff x)) (witt_mul_n p n k) :=
  sorry

/-- Multiplication by `n` is a polynomial function. -/
theorem mul_n_is_poly (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    is_poly p fun (R : Type u_1) (_Rcr : comm_ring R) (x : witt_vector p R) => x * ↑n :=
  Exists.intro (witt_mul_n p n)
    fun (R : Type u_1) (_Rcr : comm_ring R) (x : witt_vector p R) =>
      funext fun (k : ℕ) => mul_n_coeff n x k

@[simp] theorem bind₁_witt_mul_n_witt_polynomial (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) (k : ℕ) :
    coe_fn (mv_polynomial.bind₁ (witt_mul_n p n)) (witt_polynomial p ℤ k) =
        ↑n * witt_polynomial p ℤ k :=
  sorry

end Mathlib


/-
Copyright (c) 2020 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis

! This file was ported from Lean 3 source module tactic.linarith.lemmas
! leanprover-community/mathlib commit 9b2660e1b25419042c8da10bf411aa3c67f14383
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Ring.Defs

/-!
# Lemmas for `linarith`

This file contains auxiliary lemmas that `linarith` uses to construct proofs.
If you find yourself looking for a theorem here, you might be in the wrong place.
-/

namespace Linarith

theorem zero_lt_one {α} [OrderedSemiring α] [Nontrivial α] : (0 : α) < 1 :=
  zero_lt_one
#align linarith.zero_lt_one Linarith.zero_lt_one

/- warning: linarith.eq_of_eq_of_eq -> Linarith.eq_of_eq_of_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (Eq.{succ u1} α (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (Eq.{succ u1} α (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.eq_of_eq_of_eq Linarith.eq_of_eq_of_eqₓ'. -/
theorem eq_of_eq_of_eq {α} [OrderedSemiring α] {a b : α} (ha : a = 0) (hb : b = 0) : a + b = 0 := by
  simp [*]
#align linarith.eq_of_eq_of_eq Linarith.eq_of_eq_of_eq

/- warning: linarith.le_of_eq_of_le -> Linarith.le_of_eq_of_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.le_of_eq_of_le Linarith.le_of_eq_of_leₓ'. -/
theorem le_of_eq_of_le {α} [OrderedSemiring α] {a b : α} (ha : a = 0) (hb : b ≤ 0) : a + b ≤ 0 := by
  simp [*]
#align linarith.le_of_eq_of_le Linarith.le_of_eq_of_le

/- warning: linarith.lt_of_eq_of_lt -> Linarith.lt_of_eq_of_lt is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.lt_of_eq_of_lt Linarith.lt_of_eq_of_ltₓ'. -/
theorem lt_of_eq_of_lt {α} [OrderedSemiring α] {a b : α} (ha : a = 0) (hb : b < 0) : a + b < 0 := by
  simp [*]
#align linarith.lt_of_eq_of_lt Linarith.lt_of_eq_of_lt

/- warning: linarith.le_of_le_of_eq -> Linarith.le_of_le_of_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.le_of_le_of_eq Linarith.le_of_le_of_eqₓ'. -/
theorem le_of_le_of_eq {α} [OrderedSemiring α] {a b : α} (ha : a ≤ 0) (hb : b = 0) : a + b ≤ 0 := by
  simp [*]
#align linarith.le_of_le_of_eq Linarith.le_of_le_of_eq

/- warning: linarith.lt_of_lt_of_eq -> Linarith.lt_of_lt_of_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.lt_of_lt_of_eq Linarith.lt_of_lt_of_eqₓ'. -/
theorem lt_of_lt_of_eq {α} [OrderedSemiring α] {a b : α} (ha : a < 0) (hb : b = 0) : a + b < 0 := by
  simp [*]
#align linarith.lt_of_lt_of_eq Linarith.lt_of_lt_of_eq

/- warning: linarith.mul_neg -> Linarith.mul_neg is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : StrictOrderedRing.{u1} α] {a : α} {b : α}, (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (StrictOrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α _inst_1)))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (StrictOrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α _inst_1))))))))) b) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (StrictOrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (StrictOrderedRing.toRing.{u1} α _inst_1)))) b a) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α _inst_1))))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : StrictOrderedRing.{u1} α] {a : α} {b : α}, (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (StrictOrderedRing.toPartialOrder.{u1} α _inst_1))) a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (StrictOrderedRing.toStrictOrderedSemiring.{u1} α _inst_1))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (StrictOrderedRing.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (StrictOrderedRing.toStrictOrderedSemiring.{u1} α _inst_1)))))) b) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (StrictOrderedRing.toPartialOrder.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α _inst_1))))) b a) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (StrictOrderedRing.toStrictOrderedSemiring.{u1} α _inst_1)))))))
Case conversion may be inaccurate. Consider using '#align linarith.mul_neg Linarith.mul_negₓ'. -/
theorem mul_neg {α} [StrictOrderedRing α] {a b : α} (ha : a < 0) (hb : 0 < b) : b * a < 0 :=
  have : -b * a > 0 := mul_pos_of_neg_of_neg (neg_neg_of_pos hb) ha
  neg_of_neg_pos (by simpa)
#align linarith.mul_neg Linarith.mul_neg

/- warning: linarith.mul_nonpos -> Linarith.mul_nonpos is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedRing.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (OrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (OrderedRing.toRing.{u1} α _inst_1)))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (OrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (OrderedRing.toRing.{u1} α _inst_1))))))))) b) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (OrderedRing.toOrderedAddCommGroup.{u1} α _inst_1)))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (OrderedRing.toRing.{u1} α _inst_1)))) b a) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (OrderedRing.toRing.{u1} α _inst_1))))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedRing.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedRing.toPartialOrder.{u1} α _inst_1))) a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedRing.toOrderedSemiring.{u1} α _inst_1))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedRing.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedRing.toOrderedSemiring.{u1} α _inst_1)))))) b) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedRing.toPartialOrder.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (OrderedRing.toRing.{u1} α _inst_1))))) b a) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedRing.toOrderedSemiring.{u1} α _inst_1)))))))
Case conversion may be inaccurate. Consider using '#align linarith.mul_nonpos Linarith.mul_nonposₓ'. -/
theorem mul_nonpos {α} [OrderedRing α] {a b : α} (ha : a ≤ 0) (hb : 0 < b) : b * a ≤ 0 :=
  by
  have : -b * a ≥ 0 := mul_nonneg_of_nonpos_of_nonpos (le_of_lt (neg_neg_of_pos hb)) ha
  simpa
#align linarith.mul_nonpos Linarith.mul_nonpos

/- warning: linarith.mul_eq -> Linarith.mul_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) b) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) b a) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) -> (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))) b) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))) b a) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))))
Case conversion may be inaccurate. Consider using '#align linarith.mul_eq Linarith.mul_eqₓ'. -/
-- used alongside `mul_neg` and `mul_nonpos`, so has the same argument pattern for uniformity
@[nolint unused_arguments]
theorem mul_eq {α} [OrderedSemiring α] {a b : α} (ha : a = 0) (hb : 0 < b) : b * a = 0 := by
  simp [*]
#align linarith.mul_eq Linarith.mul_eq

#print Linarith.eq_of_not_lt_of_not_gt /-
theorem eq_of_not_lt_of_not_gt {α} [LinearOrder α] (a b : α) (h1 : ¬a < b) (h2 : ¬b < a) : a = b :=
  le_antisymm (le_of_not_gt h2) (le_of_not_gt h1)
#align linarith.eq_of_not_lt_of_not_gt Linarith.eq_of_not_lt_of_not_gt
-/

/- warning: linarith.mul_zero_eq -> Linarith.mul_zero_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : α -> α -> Prop} [_inst_1 : Semiring.{u1} α] {a : α} {b : α}, (R a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))))))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1))))))))
but is expected to have type
  forall {α : Type.{u1}} {R : α -> α -> Prop} [_inst_1 : Semiring.{u1} α] {a : α} {b : α}, (R a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1))))) -> (Eq.{succ u1} α b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1))))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1)))))
Case conversion may be inaccurate. Consider using '#align linarith.mul_zero_eq Linarith.mul_zero_eqₓ'. -/
-- used in the `nlinarith` normalization steps. The `_` argument is for uniformity.
@[nolint unused_arguments]
theorem mul_zero_eq {α} {R : α → α → Prop} [Semiring α] {a b : α} (_ : R a 0) (h : b = 0) :
    a * b = 0 := by simp [h]
#align linarith.mul_zero_eq Linarith.mul_zero_eq

/- warning: linarith.zero_mul_eq -> Linarith.zero_mul_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : α -> α -> Prop} [_inst_1 : Semiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))))))) -> (R b (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))))))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1))))) a b) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1))))))))
but is expected to have type
  forall {α : Type.{u1}} {R : α -> α -> Prop} [_inst_1 : Semiring.{u1} α] {a : α} {b : α}, (Eq.{succ u1} α a (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1))))) -> (R b (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1))))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α _inst_1)))) a b) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α _inst_1)))))
Case conversion may be inaccurate. Consider using '#align linarith.zero_mul_eq Linarith.zero_mul_eqₓ'. -/
-- used in the `nlinarith` normalization steps. The `_` argument is for uniformity.
@[nolint unused_arguments]
theorem zero_mul_eq {α} {R : α → α → Prop} [Semiring α] {a b : α} (h : a = 0) (_ : R b 0) :
    a * b = 0 := by simp [h]
#align linarith.zero_mul_eq Linarith.zero_mul_eq

end Linarith


-- BOTH:
import analysis.special_functions.log.basic

variables a b c d e : ℝ
open real

/- TEXT:
.. _using_theorems_and_lemmas:

Using Theorems and Lemmas
-------------------------

.. index:: inequalities

Rewriting is great for proving equations,
but what about other sorts of theorems?
For example, how can we prove an inequality,
like the fact that :math:`a + e^b \le a + e^c` holds whenever :math:`b \le c`?
We have already seen that theorems can be applied to arguments and hypotheses,
and that the ``apply`` and ``exact`` tactics can be used to solve goals.
In this section, we will make good use of these tools.

Consider the library theorems ``le_refl`` and ``le_trans``:
TEXT. -/
-- QUOTE:
#check (le_refl : ∀ a : ℝ, a ≤ a)
#check (le_trans : a ≤ b → b ≤ c → a ≤ c)
-- QUOTE.

/- TEXT:
As we explain in more detail in  :numref:`implication_and_the_universal_quantifier`,
the implicit parentheses in the statement of ``le_trans``
associate to the right, so it should be interpreted as ``a ≤ b → (b ≤ c → a ≤ c)``.
The library designers have set the arguments to ``le_trans`` implicit,
so that Lean will *not* let you provide them explicitly (unless you
really insist, as we will discuss later).
Rather, it expects to infer them from the context in which they are used.
For example, when hypotheses ``h : a ≤ b`` and  ``h' : b ≤ c``
are in the context,
all the following work:
TEXT. -/
section
-- QUOTE:
variables (h : a ≤ b) (h' : b ≤ c)

#check (le_refl : ∀ a : real, a ≤ a)
#check (le_refl a : a ≤ a)
#check (le_trans : a ≤ b → b ≤ c → a ≤ c)
#check (le_trans h : b ≤ c → a ≤ c)
#check (le_trans h h' : a ≤ c)
-- QUOTE.
end

/- TEXT:
.. index:: apply, tactics ; apply

The ``apply`` tactic takes a proof of a general statement or implication,
tries to match the conclusion with the current goal,
and leaves the hypotheses, if any, as new goals.
If the given proof matches the goal exactly
(modulo *definitional* equality),
you can use the ``exact`` tactic instead of ``apply``.
So, all of these work:
TEXT. -/
-- QUOTE:
example (x y z : ℝ) (h₀ : x ≤ y) (h₁ : y ≤ z) : x ≤ z :=
begin
  apply le_trans,
  { apply h₀ },
  apply h₁
end

example (x y z : ℝ) (h₀ : x ≤ y) (h₁ : y ≤ z) : x ≤ z :=
begin
  apply le_trans h₀,
  apply h₁
end

example (x y z : ℝ) (h₀ : x ≤ y) (h₁ : y ≤ z) : x ≤ z :=
by exact le_trans h₀ h₁

example (x y z : ℝ) (h₀ : x ≤ y) (h₁ : y ≤ z) : x ≤ z :=
le_trans h₀ h₁

example (x : ℝ) : x ≤ x :=
by apply le_refl

example (x : ℝ) : x ≤ x :=
by exact le_refl x

example (x : ℝ) : x ≤ x :=
le_refl x
-- QUOTE.

/- TEXT:
In the first example, applying ``le_trans``
creates two goals,
and we use the curly braces to enclose the proof
of the first one.
In the fourth example and in the last example,
we avoid going into tactic mode entirely:
``le_trans h₀ h₁`` and ``le_refl x`` are the proof terms we need.

Here are a few more library theorems:
TEXT. -/
-- QUOTE:
#check (le_refl  : ∀ a, a ≤ a)
#check (le_trans : a ≤ b → b ≤ c → a ≤ c)
#check (lt_of_le_of_lt : a ≤ b → b < c → a < c)
#check (lt_of_lt_of_le : a < b → b ≤ c → a < c)
#check (lt_trans : a < b → b < c → a < c)
-- QUOTE.

/- TEXT:
Use them together with ``apply`` and ``exact`` to prove the following:
TEXT. -/
/- Try this. -/

-- QUOTE:
example (h₀ : a ≤ b) (h₁ : b < c) (h₂ : c ≤ d)
    (h₃ : d < e) :
  a < e :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (h₀ : a ≤ b) (h₁ : b < c) (h₂ : c ≤ d)
    (h₃ : d < e) :
  a < e :=
begin
  apply lt_of_le_of_lt h₀,
  apply lt_trans h₁,
  exact lt_of_le_of_lt h₂ h₃
end

/- TEXT:
.. index:: linarith, tactics ; linarith

In fact, Lean has a tactic that does this sort of thing automatically:
TEXT. -/
-- QUOTE:
example (h₀ : a ≤ b) (h₁ : b < c) (h₂ : c ≤ d)
    (h₃ : d < e) :
  a < e :=
by linarith
-- QUOTE.

/- TEXT:
The ``linarith`` tactic is designed to handle *linear arithmetic*.
TEXT. -/
section
-- QUOTE:
example (h : 2 * a ≤ 3 * b) (h' : 1 ≤ a) (h'' : d = 2) :
  d + a ≤ 5 * b :=
by linarith
-- QUOTE.
end

/- TEXT:
In addition to equations and inequalities in the context,
``linarith`` will use additional inequalities that you pass as arguments.
In the next example, ``exp_le_exp.mpr h'`` is a proof of
``exp b ≤ exp c``, as we will explain in a moment.
Notice that, in Lean, we write ``f x`` to denote the application
of a function ``f`` to the argument ``x``,
exactly the same way we write ``h x`` to denote the result of
applying a fact or theorem ``h`` to the argument ``x``.
Parentheses are only needed for compound arguments,
as in ``f (x + y)``. Without the parentheses, ``f x + y``
would be parsed as ``(f x) + y``.
TEXT. -/
-- QUOTE:
example (h : 1 ≤ a) (h' : b ≤ c) :
  2 + a + exp b ≤ 3 * a + exp c :=
by linarith [exp_le_exp.mpr h']
-- QUOTE.

/- TEXT:
.. index:: exponential, logarithm

Here are some more theorems in the library that can be used to establish
inequalities on the real numbers.
TEXT. -/
-- QUOTE:
#check (exp_le_exp : exp a ≤ exp b ↔ a ≤ b)
#check (exp_lt_exp : exp a < exp b ↔ a < b)
#check (log_le_log : 0 < a → 0 < b → (log a ≤ log b ↔ a ≤ b))
#check (log_lt_log : 0 < a → a < b → log a < log b)
#check (add_le_add : a ≤ b → c ≤ d → a + c ≤ b + d)
#check (add_le_add_left : a ≤ b → ∀ c, c + a ≤ c + b)
#check (add_le_add_right : a ≤ b → ∀ c, a + c ≤ b + c)
#check (add_lt_add_of_le_of_lt : a ≤ b → c < d → a + c < b + d)
#check (add_lt_add_of_lt_of_le : a < b → c ≤ d → a + c < b + d)
#check (add_lt_add_left : a < b → ∀ c, c + a < c + b)
#check (add_lt_add_right : a < b → ∀ c, a + c < b + c)
#check (add_nonneg : 0 ≤ a → 0 ≤ b → 0 ≤ a + b)
#check (add_pos : 0 < a → 0 < b → 0 < a + b)
#check (add_pos_of_pos_of_nonneg : 0 < a → 0 ≤ b → 0 < a + b)
#check (exp_pos : ∀ a, 0 < exp a)

#check @add_le_add_left
-- QUOTE.
/- TEXT:
Some of the theorems, ``exp_le_exp``, ``exp_lt_exp``, and ``log_le_log``
use a *bi-implication*, which represents the
phrase "if and only if."
(You can type it in VS Code with ``\lr`` of ``\iff``).
We will discuss this connective in greater detail in the next chapter.
Such a theorem can be used with ``rw`` to rewrite a goal to
an equivalent one:
TEXT. -/
-- QUOTE:
example (h : a ≤ b) : exp a ≤ exp b :=
begin
  rw exp_le_exp,
  exact h
end
-- QUOTE.

/- TEXT:
In this section, however, we will use the fact that if ``h : A ↔ B``
is such an equivalence,
then ``h.mp`` establishes the forward direction, ``A → B``,
and ``h.mpr`` establishes the reverse direction, ``B → A``.
Here, ``mp`` stands for "modus ponens" and
``mpr`` stands for "modus ponens reverse."
You can also use ``h.1`` and ``h.2`` for ``h.mp`` and ``h.mpr``,
respectively, if you prefer.
Thus the following proof works:
TEXT. -/
-- QUOTE:
example (h₀ : a ≤ b) (h₁ : c < d) : a + exp c + e < b + exp d + e :=
begin
  apply add_lt_add_of_lt_of_le,
  { apply add_lt_add_of_le_of_lt h₀,
    apply exp_lt_exp.mpr h₁ },
  apply le_refl
end
-- QUOTE.

/- TEXT:
The first line, ``apply add_lt_add_of_lt_of_le``,
creates two goals,
and once again we use the curly brackets to separate the
proof of the first from the proof of the second.

.. index:: norm_num, tactics ; norm_num

Try the following examples on your own.
The example in the middle shows you that the ``norm_num``
tactic can be used to solve concrete numeric goals.
TEXT. -/
-- QUOTE:
example (h₀ : d ≤ e) : c + exp (a + d) ≤ c + exp (a + e) :=
begin
  sorry
end

example : (0 : ℝ) < 1 :=
by norm_num

example (h : a ≤ b) : log (1 + exp a) ≤ log (1 + exp b) :=
begin
  have h₀ : 0 < 1 + exp a,
  { sorry },
  have h₁ : 0 < 1 + exp b,
  { sorry },
  apply (log_le_log h₀ h₁).mpr,
  sorry
end
-- QUOTE.

-- SOLUTIONS:
example (h₀ : d ≤ e) : c + exp (a + d) ≤ c + exp (a + e) :=
begin
  apply add_le_add_left,
  rw exp_le_exp,
  apply add_le_add_left h₀
end

-- an alterantive using `linarith`.
example (h₀ : d ≤ e) : c + exp (a + d) ≤ c + exp (a + e) :=
begin
  have : exp (a + d) ≤ exp (a + e),
  { rw exp_le_exp, linarith },
  linarith [this]
end

example (h : a ≤ b) : log (1 + exp a) ≤ log (1 + exp b) :=
begin
  have h₀ : 0 < 1 + exp a,
  { linarith [exp_pos a]},
  have h₁ : 0 < 1 + exp b,
  { linarith [exp_pos b] },
  apply (log_le_log h₀ h₁).mpr,
  apply add_le_add_left (exp_le_exp.mpr h),
end

-- SOLUTION.
/- TEXT:
From these examples, it should be clear that being able to
find the library theorems you need constitutes an important
part of formalization.
There are a number of strategies you can use:

* You can browse mathlib in its
  `GitHub repository <https://github.com/leanprover-community/mathlib>`_.

* You can use the API documentation on the mathlib
  `web pages <https://leanprover-community.github.io/mathlib_docs/>`_.

* You can rely on mathlib naming conventions and tab completion in
  the editor to guess a theorem name.
  In Lean, a theorem named ``A_of_B_of_C`` establishes
  something of the form ``A`` from hypotheses of the form ``B`` and ``C``,
  where ``A``, ``B``, and ``C``
  approximate the way we might read the goals out loud.
  So a theorem establishing something like ``x + y ≤ ...`` will probably
  start with ``add_le``.
  Typing ``add_le`` and hitting tab will give you some helpful choices.

* If you right-click on an existing theorem name in VS Code,
  the editor will show a menu with the option to
  jump to the file where the theorem is defined,
  and you can find similar theorems nearby.

* You can use the ``library_search`` tactic,
  which tries to find the relevant theorem in the library.
TEXT. -/
-- QUOTE:
    example : 0 ≤ a^2 :=
    begin
      -- library_search,
      exact pow_two_nonneg a
    end
-- QUOTE.

/- TEXT:
To try out ``library_search`` in this example,
delete the ``exact`` command and uncomment the previous line.
If you replace ``library_search`` with ``suggest``,
you'll see a long list of suggestions.
In this case, the suggestions are not helpful, but in other cases
it does better.
Using these tricks,
see if you can find what you need to do the
next example:
TEXT. -/
-- QUOTE:
example (h : a ≤ b) : c - exp b ≤ c - exp a :=
  sorry
-- QUOTE.

-- SOLUTIONS:
example (h : a ≤ b) : c - exp b ≤ c - exp a :=
begin
  apply sub_le_sub_left,
  exact exp_le_exp.mpr h
end

-- alternatively:
example (h : a ≤ b) : c - exp b ≤ c - exp a :=
by linarith [exp_le_exp.mpr h]

/- TEXT:
Using the same tricks, confirm that ``linarith`` instead of ``library_search``
can also finish the job.

Here is another example of an inequality:
TEXT. -/
-- QUOTE:
example : 2*a*b ≤ a^2 + b^2 :=
begin
  have h : 0 ≤ a^2 - 2*a*b + b^2,
  calc
    a^2 - 2*a*b + b^2 = (a - b)^2     : by ring
    ... ≥ 0                           : by apply pow_two_nonneg,
  calc
    2*a*b
        = 2*a*b + 0                   : by ring
    ... ≤ 2*a*b + (a^2 - 2*a*b + b^2) : add_le_add (le_refl _) h
    ... = a^2 + b^2                   : by ring
end
-- QUOTE.

/- TEXT:
Mathlib tends to put spaces around binary operations like ``*`` and ``^``,
but in this example, the more compressed format increases readability.
There are a number of things worth noticing.
First, an expression ``s ≥ t`` is definitionally equivalent to ``t ≤ s``.
In principle, this means one should be able to use them interchangeably.
But some of Lean's automation does not recognize the equivalence,
so mathlib tends to favor ``≤`` over ``≥``.
Second, we have used the ``ring`` tactic extensively.
It is a real timesaver!
Finally, notice that in the second line of the
second ``calc`` proof,
instead of writing ``by exact add_le_add (le_refl _) h``,
we can simply write the proof term ``add_le_add (le_refl _) h``.

In fact, the only cleverness in the proof above is figuring
out the hypothesis ``h``.
Once we have it, the second calculation involves only
linear arithmetic, and ``linarith`` can handle it:
TEXT. -/
-- QUOTE:
example : 2*a*b ≤ a^2 + b^2 :=
begin
  have h : 0 ≤ a^2 - 2*a*b + b^2,
  calc
    a^2 - 2*a*b + b^2 = (a - b)^2 : by ring
    ... ≥ 0                       : by apply pow_two_nonneg,
  linarith
end
-- QUOTE.

/- TEXT:
How nice! We challenge you to use these ideas to prove the
following theorem. You can use the theorem ``abs_le'.mpr``.
TEXT. -/
-- QUOTE:
example : abs (a*b) ≤ (a^2 + b^2) / 2 :=
sorry

#check abs_le'.mpr
-- QUOTE.

-- SOLUTIONS:
theorem fact1 : a*b*2 ≤ a^2 + b^2 :=
begin
  have h : 0 ≤ a^2 - 2*a*b + b^2,
  calc
    a^2 - 2*a*b + b^2 = (a - b)^2 : by ring
    ... ≥ 0                       : by apply pow_two_nonneg,
  linarith
end

theorem fact2 : -(a*b)*2 ≤ a^2 + b^2 :=
begin
  have h : 0 ≤ a^2 + 2*a*b + b^2,
  calc
    a^2 + 2*a*b + b^2 = (a + b)^2 : by ring
    ... ≥ 0                       : by apply pow_two_nonneg,
  linarith
end

example : abs (a*b) ≤ (a^2 + b^2) / 2 :=
begin
  have h : (0 : ℝ) < 2,
  { norm_num },
  apply abs_le'.mpr,
  split,
  { rw le_div_iff h,
    apply fact1 },
  rw le_div_iff h,
  apply fact2,
end
/- TEXT:
If you managed to solve this, congratulations!
You are well on your way to becoming a master formalizer.
TEXT. -/


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.zero
import Mathlib.category_theory.limits.shapes.kernels
import Mathlib.category_theory.abelian.basic
import Mathlib.PostPort

universes v u l

namespace Mathlib

namespace category_theory

/-- An object is simple if monomorphisms into it are (exclusively) either isomorphisms or zero. -/
-- This is a constructive definition, from which we can extract an inverse for `f` given `f ≠ 0`.

-- We show below that although it contains data, it is a subsingleton.

class simple {C : Type u} [category C] [limits.has_zero_morphisms C] (X : C)
where
  mono_is_iso_equiv_nonzero : {Y : C} → (f : Y ⟶ X) → [_inst_3 : mono f] → is_iso f ≃ f ≠ 0

theorem simple.ext {C : Type u} [category C] [limits.has_zero_morphisms C] {X : C} {a : simple X} {b : simple X} : a = b := sorry

protected instance subsingleton_simple {C : Type u} [category C] [limits.has_zero_morphisms C] (X : C) : subsingleton (simple X) :=
  subsingleton.intro simple.ext

/-- A nonzero monomorphism to a simple object is an isomorphism. -/
def is_iso_of_mono_of_nonzero {C : Type u} [category C] [limits.has_zero_morphisms C] {X : C} {Y : C} [simple Y] {f : X ⟶ Y} [mono f] (w : f ≠ 0) : is_iso f :=
  coe_fn (equiv.symm (simple.mono_is_iso_equiv_nonzero f)) w

theorem kernel_zero_of_nonzero_from_simple {C : Type u} [category C] [limits.has_zero_morphisms C] {X : C} {Y : C} [simple X] {f : X ⟶ Y} [limits.has_kernel f] (w : f ≠ 0) : limits.kernel.ι f = 0 :=
  decidable.by_contradiction fun (h : ¬limits.kernel.ι f = 0) => w (limits.eq_zero_of_epi_kernel f)

theorem mono_to_simple_zero_of_not_iso {C : Type u} [category C] [limits.has_zero_morphisms C] {X : C} {Y : C} [simple Y] {f : X ⟶ Y} [mono f] (w : is_iso f → False) : f = 0 :=
  decidable.by_contradiction fun (h : ¬f = 0) => w (is_iso_of_mono_of_nonzero h)

theorem id_nonzero {C : Type u} [category C] [limits.has_zero_morphisms C] (X : C) [simple X] : 𝟙 ≠ 0 :=
  coe_fn (simple.mono_is_iso_equiv_nonzero 𝟙) (is_iso.id X)

/-- We don't want the definition of 'simple' to include the zero object, so we check that here. -/
theorem zero_not_simple {C : Type u} [category C] [limits.has_zero_morphisms C] [limits.has_zero_object C] [simple 0] : False :=
  coe_fn (simple.mono_is_iso_equiv_nonzero 0) (is_iso.mk 0) rfl

-- We next make the dual arguments, but for this we must be in an abelian category.

/-- In an abelian category, an object satisfying the dual of the definition of a simple object is
    simple. -/
def simple_of_cosimple {C : Type u} [category C] [abelian C] (X : C) (h : {Z : C} → (f : X ⟶ Z) → [_inst_3 : epi f] → is_iso f ≃ f ≠ 0) : simple X :=
  simple.mk
    fun (Y : C) (f : Y ⟶ X) (I : mono f) =>
      equiv_of_subsingleton_of_subsingleton sorry fun (hf : f ≠ 0) => abelian.is_iso_of_mono_of_epi f

/-- A nonzero epimorphism from a simple object is an isomorphism. -/
def is_iso_of_epi_of_nonzero {C : Type u} [category C] [abelian C] {X : C} {Y : C} [simple X] {f : X ⟶ Y} [epi f] (w : f ≠ 0) : is_iso f :=
  abelian.is_iso_of_mono_of_epi f

theorem cokernel_zero_of_nonzero_to_simple {C : Type u} [category C] [abelian C] {X : C} {Y : C} [simple Y] {f : X ⟶ Y} [limits.has_cokernel f] (w : f ≠ 0) : limits.cokernel.π f = 0 :=
  decidable.by_contradiction fun (h : ¬limits.cokernel.π f = 0) => w (limits.eq_zero_of_mono_cokernel f)

theorem epi_from_simple_zero_of_not_iso {C : Type u} [category C] [abelian C] {X : C} {Y : C} [simple X] {f : X ⟶ Y} [epi f] (w : is_iso f → False) : f = 0 :=
  decidable.by_contradiction fun (h : ¬f = 0) => w (is_iso_of_epi_of_nonzero h)


theorem foo (x : Nat) (h : x > 0) : x ≠ 0 :=
  match x with
  | 0   => sorry
  | h+1 => sorry

inductive Mem : α → List α → Prop where
 | head (a : α) (as : List α)   : Mem a (a::as)
 | tail (a b : α) (bs : List α) : Mem a bs → Mem a (b::bs)
infix:50 (priority := high) "∈" => Mem

theorem mem_split {a : α} {as : List α} (h : a ∈ as) : ∃ s t, as = s ++ a :: t := by
  induction as with
  | nil          => cases h
  | cons b bs ih => cases h with
    | head bs => exact ⟨[], ⟨bs, rfl⟩⟩
    | tail b bs h =>
      match bs, ih h with
      | _, ⟨s, t, rfl⟩ =>
        exists b::s, t


-- Bottcher map for Julia sets

import data.complex.basic
import data.complex.exponential
import analysis.complex.removable_singularity
import analysis.special_functions.complex.log
import tactic.monotonicity
import topology.basic

import analytic
import hartogs
import if_p
import osgood
import products
import zeros

open complex (exp log abs cpow)
open function (curry uncurry)
open metric (ball closed_ball is_open_ball)
open nat (iterate)
open set (univ)
open_locale nnreal
noncomputable theory

section bottcher

-- Information about a fixpoint.  Notes:
-- 1. Fixpoints are superattracting if d ≥ 2
-- 2. Monic fixpoints are those where a = 1
structure fixpoint (f : ℂ → ℂ) (c : ℂ) : Type :=
  (d : ℕ)  -- Order of the fixpoint
  (g : ℂ → ℂ)
  (a : ℂ)
  (a0 : a ≠ 0)
  (gc : g c = 1)
  (fc : f c = c)
  (fg : ∀ z, f z = c + a * (z - c)^d * g z)
  (fa : analytic_at ℂ f c)
  (ga : analytic_at ℂ g c)

-- All information for a superattracting fixed point
variables {f g : ℂ → ℂ}
variables {d : ℕ}
variables {c a z : ℂ}
variables {r : ℝ}

structure super (f g : ℂ → ℂ) (d : ℕ) (c a : ℂ) (r : ℝ) : Prop :=
  (d2 : d ≥ 2)
  (a0 : a ≠ 0)
  (gc : g c = 1)
  (fc : f c = c)
  (fg : ∀ z, f z = c + a * (z - c)^d * g z)
  (rp : r > 0)
  (r2 : r ≤ 1/2)
  (fa : analytic_on ℂ f (ball 0 r))
  (ga : analytic_on ℂ g (ball 0 r))
  (gs : ∀ {z : ℂ}, abs z ≤ r → abs (g z - 1) ≤ 1/4)

-- First, we prove Bottcher's theorem for a monic, superattracting fixed point at 0.  We have
--   f z = z^d * g z
--   g 0 = 1
-- Ignoring multiple values, we want
--   (E n z)^(d^n) = f^[n] z
--   E n z = (f^[n] z)^(1/d^n)
--   E n z = (f (f^[n-1] z))^(1/d^n)
--   E n z = (f ((E (n-1) z)^(d^(n-1))))^(1/d^n)
--   E n z = ((E (n-1) z)^(d^n) * g ((E (n-1) z)^(d^(n-1))))^(1/d^n)
--   E n z = E (n-1) z * (g ((E (n-1) z)^(d^(n-1))))^(1/d^n)
--   E n z = E (n-1) z * (g (f^[n-1] z))^(1/d^n)
--   E n z = z * prod_{1 < k ≤ n} (g (f^[k-1] z))^(1/d^k)

-- Terms in our infinite product
def term (s : super f g d 0 a r) (n : ℕ) (z : ℂ) := g (f^[n] z) ^ (1/d^(n+1) : ℂ)

-- With term in hand, we can define Böttcher coordinates
def bottcher (s : super f g d 0 1 r) (z : ℂ) := z * tprod (λ n, term s n z)

-- ^d shifts term (n+1) to term n:
--   (term n z)^d = (g (f^[n] z) ^ 1/d^(n+1))^d
--                = (g (f^[n-1] (f z)) ^ 1/d^n)
--                = term (n-1) (f z)
lemma term_eqn (s : super f g d 0 1 r) : ∀ n, abs z ≤ r → term s n (f z) = (term s (n+1) z)^d := begin
  intros n zr, rw term, rw term,
  rw ←function.iterate_succ_apply,
  rw [simple.pow_mul_nat, div_mul, simple.pow_div _],
  simp, exact ne_of_gt (gt_of_ge_of_gt s.d2 (by norm_num))
end

-- The analogue of term_eqn (-1):
--   (z * term 0 z)^d = (z * g z ^ 1/d)^d
--                    = z^d * g z
--                    = f z
lemma term_base (s : super f g d 0 1 r) : abs z ≤ r → f z = (z * term s 0 z)^d := begin
  intro zr, rw term, simp,
  rw [mul_pow, simple.pow_mul_nat, inv_mul_cancel _],
  rw s.fg, simp,
  simp, exact ne_of_gt (gt_of_ge_of_gt s.d2 (by norm_num))
end

-- How fast do we converge?  Within r, we (very loosely) have
--   abs (f z) = abs (z^d * g z) ≤ 5/4 * (abs z)^d ≤ 5/8 * abs z
--   abs (f^(n) z) ≤ (5/8)^n * abs z ≤ 1/2 * (5/8)^n
--   abs (term s n z - 1) ≤ 4 * 1/d^(n+1) * 1/4 ≤ 1/2 * (1/d)^n

-- We use this a few times, so make it a lemma
lemma super.dr2 (s : super f g d 0 1 r) : (d : ℝ) ≥ 2 := begin
  flip_ineq, transitivity ((2 : ℕ) : ℝ), norm_num,
  exact simple.nat_real_coe_le_coe s.d2
end

-- abs (f z) ≤ 5/8 * abs z within r
lemma f_converges (s : super f g d 0 1 r) : abs z ≤ r → abs (f z) ≤ 5/8 * abs z := begin
  intro zr,
  rw s.fg, simp,
  have gs : abs (g z) ≤ 5/4, {
    calc abs (g z) = abs (g z - 1 + 1) : by ring_nf
    ... ≤ abs (g z - 1) + abs (1 : ℂ) : complex.abs_add _ _
    ... ≤ 1/4 + abs (1 : ℂ) : by bound [s.gs zr]
    ... ≤ 5/4 : by norm_num
  },
  have az1 : abs z ≤ 1 := trans zr (trans s.r2 (by norm_num)),
  calc abs z ^ d * abs (g z) ≤ abs z ^ 2 * (5/4)
      : by bound [pow_le_pow_of_le_one (by bound) az1 s.d2]
  ... = abs z * abs z * (5/4) : by ring_nf
  ... ≤ 1/2 * abs z * (5/4) : by bound [trans zr s.r2]
  ... = 5/8 * abs z : by ring
end

-- abs (f z) ≤ abs z within r
lemma f_remains (s : super f g d 0 1 r) : abs z ≤ r → abs (f z) ≤ abs z := begin
  intro zs,
  transitivity 5/8 * abs z,
  exact f_converges s zs,
  transitivity 1 * abs z, bound, simp
end

lemma five_eights_pow_le {n : ℕ} {r : ℝ} : r > 0 → (5/8)^n * r ≤ r := begin
  intro rp, transitivity 1^n * r, bound, simp
end

lemma five_eights_pow_lt {n : ℕ} {r : ℝ} : r > 0 → n ≠ 0 → (5/8)^n * r < r := begin
  intros rp np,
  have h : (5/8 : ℝ)^n < 1 := pow_lt_one (by norm_num) (by norm_num) np,
  exact lt_of_lt_of_le (mul_lt_mul_of_pos_right h rp) (by simp)
end

-- abs (f^[n] z) ≤ (5/8)^n * abs z
lemma iterates_converge (s : super f g d 0 1 r) : ∀ n, abs z ≤ r → abs (f^[n] z) ≤ (5/8)^n * r := begin
  intros n zr,
  induction n with n nh, { simpa },
  rw function.iterate_succ',
  transitivity 5/8 * abs (f^[n] z),
  apply f_converges s (trans nh (five_eights_pow_le s.rp)),
  calc 5/8 * abs (f^[n] z) ≤ 5/8 * ((5/8)^n * r) : by bound [s.rp]
  ... = 5/8 * (5/8)^n * r : by ring
  ... = (5/8)^(n+1) * r : by rw ←pow_succ
  ... = (5/8)^(n.succ) * r : rfl
end

-- Often, all we need is abs (f^[n] z) ≤ r
lemma iterates_remain (s : super f g d 0 1 r) : ∀ n, abs z ≤ r → abs (f^[n] z) ≤ r := begin
  intros n zr,
  exact trans (iterates_converge s n zr) (five_eights_pow_le s.rp)
end

-- iterates_remain as set.maps_to
lemma iterates_maps_to (s : super f g d 0 1 r) : ∀ n, set.maps_to (f^[n]) (ball 0 r) (ball 0 r) := begin
  intros n z zs, simp at ⊢ zs,
  by_cases np : n = 0, { rw np, simpa },
  exact lt_of_le_of_lt (iterates_converge s n (le_of_lt zs)) (five_eights_pow_lt s.rp np)
end

-- Iterates are analytic
lemma iterates_analytic (s : super f g d 0 1 r) : ∀ n, analytic_on ℂ (f^[n]) (ball 0 r) := begin
  intro n,
  rw ←differentiable_iff_analytic is_open_ball,
  induction n with n, {
    simp, exact λ _ _, differentiable_within_at_id
  }, {
    rw function.iterate_succ',
    refine differentiable_on.comp _ (by assumption) (iterates_maps_to s n),
    exact (differentiable_iff_analytic is_open_ball).mpr s.fa,
  },
  exact complete_of_proper
end

-- term is analytic close to 0
lemma term_analytic (s : super f g d 0 1 r) : ∀ n, analytic_on ℂ (term s n) (ball 0 r) := begin
  intro n,
  rw ←differentiable_iff_analytic is_open_ball,
  apply differentiable_on.cpow, {
    refine differentiable_on.comp _ _ (iterates_maps_to s n),
    exact (differentiable_iff_analytic is_open_ball).mpr s.ga,
    exact (differentiable_iff_analytic is_open_ball).mpr (iterates_analytic s n)
  }, {
    exact differentiable_on_const _
  }, {
    intros z zr, simp at zr,
    refine near_one_avoids_negative_reals _,
    exact lt_of_le_of_lt (s.gs (iterates_remain s n (le_of_lt zr))) (by norm_num),
  },
  exact complete_of_proper
end

-- term converges to 1 exponentially, sufficiently close to 0
lemma term_converges (s : super f g d 0 1 r) : ∀ n, abs z ≤ r → abs (term s n z - 1) ≤ 1/2 * (1/2)^n := begin
  intros n zr,
  rw term,
  transitivity 4 * abs (g (f^[n] z) - 1) * abs (1/d^(n+1) : ℂ), {
    apply pow_small, {
      transitivity (1/4 : ℝ),
      exact s.gs (iterates_remain s n zr),
      norm_num
    }, {
      simp, apply inv_le_one,
      have hd : 1 ≤ (d : ℝ) := trans (by norm_num) s.dr2,
      exact one_le_pow_of_one_le hd _
    }
  }, {
    have fns : abs (f^[n] z) ≤ r := iterates_remain s n zr,
    have gs : abs (g (f^[n] z) - 1) ≤ 1/4 := s.gs fns,
    have ps : abs (1/↑d^(n+1) : ℂ) ≤ 1/2 * (1/2)^n, {
      have nn : 1/2 * (1/2 : ℝ)^n = (1/2)^(n+1) := (pow_succ _ _).symm,
      rw nn, simp, apply inv_le_inv_of_le, bound, bound [s.dr2]
    },
    calc 4 * abs (g (f^[n] z) - 1) * abs (1/↑d^(n+1) : ℂ) ≤ 4 * (1/4) * (1/2 * (1/2)^n) : by bound
    ... = 1/2 * (1/2)^n : by ring
  }
end

-- term is nonzero, sufficiently close to 0
lemma term_nonzero (s : super f g d 0 1 r) : ∀ n, abs z ≤ r → term s n z ≠ 0 := begin
  intros n zr,
  have h := term_converges s n zr,
  have o : 1/2 * (1/2 : ℝ)^n < 1, {
    have p : (1/2 : ℝ)^n ≤ 1 := pow_le_one n (by norm_num) (by bound),
    calc 1/2 * (1/2 : ℝ)^n ≤ 1/2 * 1 : by bound
    ... < 1 : by norm_num
  },
  exact near_one_avoids_zero (lt_of_le_of_lt h o)
end

-- The term product exists and is analytic
lemma term_prod (s : super f g d 0 1 r)
    : prod_exists_on (term s) (ball 0 r) ∧ analytic_on ℂ (tprod_on (term s)) (ball 0 r) := begin
  have c12 : (1/2 : ℝ) ≤ 1/2 := by norm_num,
  have a0 : 0 ≤ (1/2 : ℝ) := by norm_num,
  have a1 : (1/2 : ℝ) < 1 := by norm_num,
  apply fast_products_converge' is_open_ball c12 a0 a1, {
    exact term_analytic s
  }, {
    intros n z zr, simp at zr, exact term_converges s n (le_of_lt zr)
  }
end

-- bottcher satisfies b (f z) = (b z)^d near 0
theorem bottcher_eqn (s : super f g d 0 1 r) : abs z < r → bottcher s (f z) = (bottcher s z)^d := begin
  intro zr,
  rw bottcher, rw bottcher,
  have zs : z ∈ ball (0 : ℂ) r := by simpa,
  have zr' := le_of_lt zr,
  have pe := (term_prod s).left z zs,
  rw mul_pow,
  rw product_pow' pe, simp,
  have pe : prod_exists (λ n, term s n z ^ d), { rcases pe with ⟨g,hg⟩, exact ⟨_,product_pow d hg⟩ },
  rw product_split pe, simp,
  simp_rw ←term_eqn s _ zr',
  rw ←mul_assoc, rw ←mul_pow, rw ←term_base s zr'
end

-- f^[n] 0 = 0
lemma iterates_at_zero (s : super f g d 0 1 r) : ∀ n, f^[n] 0 = 0 := begin
  intro n, induction n with n h, simp,
  rw function.iterate_succ', simp, rw [h, s.fc]
end

-- term s n 0 = 1
lemma term_at_zero (s : super f g d 0 1 r) : ∀ n, term s n 0 = 1 := begin
  intro n, rw [term, iterates_at_zero s, s.gc], simp
end

-- prod (term s _ 0) = 1
lemma term_prod_at_zero (s : super f g d 0 1 r) : tprod_on (term s) 0 = 1 := begin
  simp_rw [tprod_on, term_at_zero s], exact prod_ones'
end

-- b' 0 = 1, and in particular b is a local isomorphism
theorem bottcher_monic (s : super f g d 0 1 r) : has_deriv_at (bottcher s) 1 0 := begin
  have p := term_prod s,
  have dp := (p.right 0 (metric.mem_ball_self s.rp)).has_deriv_at,
  have dz : has_deriv_at (λ z : ℂ, z) 1 0 := has_deriv_at_id 0,
  have db := has_deriv_at.mul dz dp, simp at db,
  rw term_prod_at_zero at db, exact db
end

-- bottcher is analytic in z
theorem bottcher_analytic_z (s : super f g d 0 1 r) : analytic_on ℂ (bottcher s) (ball 0 r) := begin
  rw ←differentiable_iff_analytic is_open_ball,
  exact differentiable_on.mul differentiable_on_id (term_prod s).right.differentiable_on,
  exact complete_of_proper
end

end bottcher

-- Next we prove that everything is analytic in an additional function parameter
section bottcher_c

variables {f g : ℂ → ℂ → ℂ}
variables {cs : set ℂ}
variables {d : ℕ}
variables {r : ℝ}

-- super everywhere on a parameter set
structure super_c (f g : ℂ → ℂ → ℂ) (cs : set ℂ) (d : ℕ) (r : ℝ) :=
(rp : r > 0)
(o : is_open cs)
(s : Π c, c ∈ cs → super (f c) (g c) d 0 1 r)
(fa : analytic_on ℂ (uncurry f) (cs ×ˢ (ball (0 : ℂ) r)))
(ga : analytic_on ℂ (uncurry g) (cs ×ˢ (ball (0 : ℂ) r)))

-- The set on which everything is defined is open
lemma super_c.o2 (s : super_c f g cs d r) : is_open (cs ×ˢ (ball (0 : ℂ) r)) := is_open.prod s.o is_open_ball

-- Differentiability of f and g
lemma super_c.fd (s : super_c f g cs d r) : differentiable_on ℂ (uncurry f) (cs ×ˢ (ball (0 : ℂ) r)) :=
  (differentiable_iff_analytic2 s.o2).mpr s.fa
lemma super_c.gd (s : super_c f g cs d r) : differentiable_on ℂ (uncurry g) (cs ×ˢ (ball (0 : ℂ) r)) :=
  (differentiable_iff_analytic2 s.o2).mpr s.ga

-- Parameterized version of bottcher
def bottcher_c (s : super_c f g cs d r) (c z : ℂ) : ℂ :=
  if_p (c ∈ cs) (λ m, bottcher (s.s c m) z)

-- bottcher_c factors as expected
lemma bottcher_c_def (s : super_c f g cs d r) (c z : ℂ)
    : bottcher_c s c z = z * tprod (λ n, if_p (c ∈ cs) (λ m, term (s.s c m) n z)) := begin
  simp_rw [bottcher_c, bottcher],
  by_cases m : c ∈ cs, {
    simp_rw if_p_true m
  }, {
    simp_rw [if_p_false m, has_prod.zero.tprod_eq], simp
  }
end

--lemma term_prod_fast_c
-- tprod_on (λ n c, if_p (c ∈ cs) (λ m, term (s.s c m) n z))

    --(o : is_open s) (c12 : c ≤ 1/2) (a0 : 0 ≤ a) (a1 : a < 1)
    --(h : ∀ n, analytic_on ℂ (f n) s) (hf : ∀ n z, z ∈ s → abs (f n z - 1) ≤ c * a^n)

lemma iterates_maps_to_c (s : super_c f g cs d r) {z : ℂ}
    (n : ℕ) (zm : z ∈ ball (0 : ℂ) r)
    : set.maps_to (λ (c : ℂ), (c, f c^[n] z)) cs (cs ×ˢ ball 0 r) := begin
  intros c cm, simp at ⊢ zm, refine ⟨cm,_⟩,
  by_cases n0 : n = 0, { rw n0, simpa },
  exact lt_of_le_of_lt (iterates_converge (s.s c cm) n (le_of_lt zm)) (five_eights_pow_lt s.rp n0)
end

lemma iterates_differentiable_c (s : super_c f g cs d r) {z : ℂ}
    (n : ℕ) (zm : z ∈ ball (0 : ℂ) r)
    : differentiable_on ℂ (λ c, f c^[n] z) cs := begin
  induction n with n nh, {
    simp, exact differentiable_on_const _
  }, {
    simp_rw function.iterate_succ', simp,
    have e : (λ c, f c (f c^[n] z)) = (uncurry f) ∘ (λ c, (c, f c^[n] z)) := rfl,
    rw e, clear e, apply differentiable_on.comp s.fd,
    exact differentiable_on.prod differentiable_on_id nh,
    exact iterates_maps_to_c s n zm
  }
end

lemma term_analytic_c (s : super_c f g cs d r) {z : ℂ}
    (n : ℕ) (zm : z ∈ ball (0 : ℂ) r)
    : analytic_on ℂ (λ c, if_p (c ∈ cs) (λ m, term (s.s c m) n z)) cs := begin
  simp_rw term,
  rw analytic_on_congr s.o (if_p_const cs),
  rw ←differentiable_iff_analytic s.o,
  apply differentiable_on.cpow, {
    have e : (λ c, g c (f c^[n] z)) = (uncurry g) ∘ (λ c, (c, f c^[n] z)) := rfl,
    rw e, apply differentiable_on.comp s.gd,
    apply differentiable_on.prod differentiable_on_id,
    exact iterates_differentiable_c s n zm,
    exact iterates_maps_to_c s n zm
  }, {
    exact differentiable_on_const _
  }, {
    intros c cm, simp at zm,
    refine near_one_avoids_negative_reals _,
    exact lt_of_le_of_lt ((s.s c cm).gs (iterates_remain (s.s c cm) n (le_of_lt zm))) (by norm_num)
  },
  repeat { exact complete_of_proper },
end

-- bottcher is analytic in c
theorem bottcher_analytic_c (s : super_c f g cs d r) {z : ℂ} (zm : z ∈ ball (0 : ℂ) r)
    : analytic_on ℂ (λ c, bottcher_c s c z) cs := begin
  simp_rw bottcher_c_def s,
  rw ←differentiable_iff_analytic s.o,
  apply differentiable_on.mul (differentiable_on_const _),
  rw differentiable_iff_analytic s.o,
  rw ←tprod_on,
  have c12 : (1/2 : ℝ) ≤ 1/2 := by norm_num,
  have a0 : 0 ≤ (1/2 : ℝ) := by norm_num,
  have a1 : (1/2 : ℝ) < 1 := by norm_num,
  refine (fast_products_converge' s.o c12 a0 a1 _ _).right, {
    exact λ n, term_analytic_c s n zm
  }, {
    intros n c cm, rw if_p_true cm, simp at zm,
    exact term_converges (s.s c cm) n (le_of_lt zm)
  },
  repeat { exact complete_of_proper }
end

-- bottcher is jointly analytic (using Hartogs's theorem for simplicity)
theorem bottcher_analytic2 (s : super_c f g cs d r)
    : analytic_on ℂ (uncurry (bottcher_c s)) (cs ×ˢ (ball (0 : ℂ) r)) := begin
  apply pair.hartogs (s.o.prod metric.is_open_ball), {
    simp, intros c0 c1 c0s c1s, exact bottcher_analytic_c s (by simpa) _ c0s,
  }, {
    simp, intros c0 c1 c0s c1s,
    have e : bottcher_c s c0 = bottcher (s.s c0 c0s), { funext, rw [bottcher_c, if_p_true c0s] },
    rw e, apply bottcher_analytic_z, simpa,
  },
  apply_instance, apply_instance,
end

end bottcher_c


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

! This file was ported from Lean 3 source module algebraic_geometry.function_field
! leanprover-community/mathlib commit d39590fc8728fbf6743249802486f8c91ffe07bc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.AlgebraicGeometry.Properties

/-!
# Function field of integral schemes

We define the function field of an irreducible scheme as the stalk of the generic point.
This is a field when the scheme is integral.

## Main definition
* `algebraic_geometry.Scheme.function_field`: The function field of an integral scheme.
* `algebraic_geometry.germ_to_function_field`: The canonical map from a component into the function
  field. This map is injective.
-/

universe u v

open TopologicalSpace Opposite CategoryTheory CategoryTheory.Limits TopCat

namespace AlgebraicGeometry

variable (X : Scheme)

/-- The function field of an irreducible scheme is the local ring at its generic point.
Despite the name, this is a field only when the scheme is integral. -/
noncomputable abbrev Scheme.functionField [IrreducibleSpace X.carrier] : CommRingCat :=
  X.Presheaf.stalk (genericPoint X.carrier)
#align algebraic_geometry.Scheme.function_field AlgebraicGeometry.Scheme.functionField

/-- The restriction map from a component to the function field. -/
noncomputable abbrev Scheme.germToFunctionField [IrreducibleSpace X.carrier] (U : Opens X.carrier)
    [h : Nonempty U] : X.Presheaf.obj (op U) ⟶ X.functionField :=
  X.Presheaf.germ
    ⟨genericPoint X.carrier,
      ((genericPoint_spec X.carrier).mem_open_set_iff U.IsOpen).mpr (by simpa using h)⟩
#align algebraic_geometry.Scheme.germ_to_function_field AlgebraicGeometry.Scheme.germToFunctionField

noncomputable instance [IrreducibleSpace X.carrier] (U : Opens X.carrier) [Nonempty U] :
    Algebra (X.Presheaf.obj (op U)) X.functionField :=
  (X.germToFunctionField U).toAlgebra

noncomputable instance [IsIntegral X] : Field X.functionField :=
  by
  apply fieldOfIsUnitOrEqZero
  intro a
  obtain ⟨U, m, s, rfl⟩ := TopCat.Presheaf.germ_exist _ _ a
  rw [or_iff_not_imp_right, ← (X.presheaf.germ ⟨_, m⟩).map_zero]
  intro ha
  replace ha := ne_of_apply_ne _ ha
  have hs : genericPoint X.carrier ∈ RingedSpace.basic_open _ s :=
    by
    rw [← SetLike.mem_coe, (genericPoint_spec X.carrier).mem_open_set_iff, Set.top_eq_univ,
      Set.univ_inter, Set.nonempty_iff_ne_empty, Ne.def, ← opens.coe_bot, ← SetLike.ext'_iff]
    erw [basic_open_eq_bot_iff]
    exacts[ha, (RingedSpace.basic_open _ _).IsOpen]
  have := (X.presheaf.germ ⟨_, hs⟩).isUnit_map (RingedSpace.is_unit_res_basic_open _ s)
  rwa [TopCat.Presheaf.germ_res_apply] at this

theorem germ_injective_of_isIntegral [IsIntegral X] {U : Opens X.carrier} (x : U) :
    Function.Injective (X.Presheaf.germ x) :=
  by
  rw [injective_iff_map_eq_zero]
  intro y hy
  rw [← (X.presheaf.germ x).map_zero] at hy
  obtain ⟨W, hW, iU, iV, e⟩ := X.presheaf.germ_eq _ x.prop x.prop _ _ hy
  cases show iU = iV from Subsingleton.elim _ _
  haveI : Nonempty W := ⟨⟨_, hW⟩⟩
  exact map_injective_of_is_integral X iU e
#align algebraic_geometry.germ_injective_of_is_integral AlgebraicGeometry.germ_injective_of_isIntegral

theorem Scheme.germToFunctionField_injective [IsIntegral X] (U : Opens X.carrier) [Nonempty U] :
    Function.Injective (X.germToFunctionField U) :=
  germ_injective_of_isIntegral _ _
#align algebraic_geometry.Scheme.germ_to_function_field_injective AlgebraicGeometry.Scheme.germToFunctionField_injective

theorem genericPoint_eq_of_isOpenImmersion {X Y : Scheme} (f : X ⟶ Y) [H : IsOpenImmersion f]
    [hX : IrreducibleSpace X.carrier] [IrreducibleSpace Y.carrier] :
    f.1.base (genericPoint X.carrier : _) = (genericPoint Y.carrier : _) :=
  by
  apply ((genericPoint_spec _).Eq _).symm
  show T0Space Y.carrier; · infer_instance
  convert(genericPoint_spec X.carrier).image (show Continuous f.1.base by continuity)
  symm
  rw [eq_top_iff, Set.top_eq_univ, Set.top_eq_univ]
  convert subset_closure_inter_of_isPreirreducible_of_isOpen _ H.base_open.open_range _
  rw [Set.univ_inter, Set.image_univ]
  apply (config := { instances := false }) PreirreducibleSpace.isPreirreducible_univ
  show PreirreducibleSpace Y.carrier; · infer_instance
  exact ⟨_, trivial, Set.mem_range_self hX.2.some⟩
#align algebraic_geometry.generic_point_eq_of_is_open_immersion AlgebraicGeometry.genericPoint_eq_of_isOpenImmersion

noncomputable instance stalkFunctionFieldAlgebra [IrreducibleSpace X.carrier] (x : X.carrier) :
    Algebra (X.Presheaf.stalk x) X.functionField :=
  by
  apply RingHom.toAlgebra
  exact X.presheaf.stalk_specializes ((genericPoint_spec X.carrier).Specializes trivial)
#align algebraic_geometry.stalk_function_field_algebra AlgebraicGeometry.stalkFunctionFieldAlgebra

instance functionField_isScalarTower [IrreducibleSpace X.carrier] (U : Opens X.carrier) (x : U)
    [Nonempty U] : IsScalarTower (X.Presheaf.obj <| op U) (X.Presheaf.stalk x) X.functionField :=
  by
  apply IsScalarTower.of_algebraMap_eq'
  simp_rw [RingHom.algebraMap_toAlgebra]
  change _ = X.presheaf.germ x ≫ _
  rw [X.presheaf.germ_stalk_specializes]
  rfl
#align algebraic_geometry.function_field_is_scalar_tower AlgebraicGeometry.functionField_isScalarTower

noncomputable instance (R : CommRingCat) [IsDomain R] :
    Algebra R (Scheme.spec.obj <| op R).functionField :=
  RingHom.toAlgebra <| by
    change CommRingCat.of R ⟶ _
    apply structure_sheaf.to_stalk

@[simp]
theorem genericPoint_eq_bot_of_affine (R : CommRingCat) [IsDomain R] :
    genericPoint (Scheme.spec.obj <| op R).carrier = (⟨0, Ideal.bot_prime⟩ : PrimeSpectrum R) :=
  by
  apply (genericPoint_spec (Scheme.Spec.obj <| op R).carrier).Eq
  simp [isGenericPoint_def, ← PrimeSpectrum.zeroLocus_vanishingIdeal_eq_closure]
#align algebraic_geometry.generic_point_eq_bot_of_affine AlgebraicGeometry.genericPoint_eq_bot_of_affine

instance functionField_isFractionRing_of_affine (R : CommRingCat.{u}) [IsDomain R] :
    IsFractionRing R (Scheme.spec.obj <| op R).functionField :=
  by
  convert structure_sheaf.is_localization.to_stalk R _
  delta IsFractionRing IsLocalization.AtPrime
  congr 1
  rw [generic_point_eq_bot_of_affine]
  ext
  exact mem_nonZeroDivisors_iff_ne_zero
#align algebraic_geometry.function_field_is_fraction_ring_of_affine AlgebraicGeometry.functionField_isFractionRing_of_affine

instance {X : Scheme} [IsIntegral X] {U : Opens X.carrier} [hU : Nonempty U] :
    IsIntegral (X.restrict U.OpenEmbedding) :=
  haveI : Nonempty (X.restrict U.open_embedding).carrier := hU
  is_integral_of_open_immersion (X.of_restrict U.open_embedding)

theorem IsAffineOpen.primeIdealOf_genericPoint {X : Scheme} [IsIntegral X] {U : Opens X.carrier}
    (hU : IsAffineOpen U) [h : Nonempty U] :
    hU.primeIdealOf
        ⟨genericPoint X.carrier,
          ((genericPoint_spec X.carrier).mem_open_set_iff U.IsOpen).mpr (by simpa using h)⟩ =
      genericPoint (Scheme.spec.obj <| op <| X.Presheaf.obj <| op U).carrier :=
  by
  haveI : is_affine _ := hU
  have e : U.open_embedding.is_open_map.functor.obj ⊤ = U :=
    by
    ext1
    exact set.image_univ.trans Subtype.range_coe
  delta is_affine_open.prime_ideal_of
  rw [← Scheme.comp_val_base_apply]
  convert generic_point_eq_of_is_open_immersion
      ((X.restrict U.open_embedding).isoSpec.Hom ≫
        Scheme.Spec.map (X.presheaf.map (eq_to_hom e).op).op)
  ext1
  exact (generic_point_eq_of_is_open_immersion (X.of_restrict U.open_embedding)).symm
#align algebraic_geometry.is_affine_open.prime_ideal_of_generic_point AlgebraicGeometry.IsAffineOpen.primeIdealOf_genericPoint

theorem functionField_isFractionRing_of_isAffineOpen [IsIntegral X] (U : Opens X.carrier)
    (hU : IsAffineOpen U) [hU' : Nonempty U] :
    IsFractionRing (X.Presheaf.obj <| op U) X.functionField :=
  by
  haveI : is_affine _ := hU
  haveI : Nonempty (X.restrict U.open_embedding).carrier := hU'
  haveI : IsIntegral (X.restrict U.open_embedding) :=
    @is_integral_of_is_affine_is_domain _ _ _
      (by
        dsimp
        rw [opens.open_embedding_obj_top]
        infer_instance)
  have e : U.open_embedding.is_open_map.functor.obj ⊤ = U :=
    by
    ext1
    exact set.image_univ.trans Subtype.range_coe
  delta IsFractionRing Scheme.function_field
  convert hU.is_localization_stalk ⟨genericPoint X.carrier, _⟩ using 1
  rw [hU.prime_ideal_of_generic_point, generic_point_eq_bot_of_affine]
  ext
  exact mem_nonZeroDivisors_iff_ne_zero
#align algebraic_geometry.function_field_is_fraction_ring_of_is_affine_open AlgebraicGeometry.functionField_isFractionRing_of_isAffineOpen

instance (x : X.carrier) : IsAffine (X.affineCover.obj x) :=
  AlgebraicGeometry.specIsAffine _

instance [h : IsIntegral X] (x : X.carrier) : IsFractionRing (X.Presheaf.stalk x) X.functionField :=
  by
  let U : opens X.carrier :=
    ⟨Set.range (X.affine_cover.map x).1.base,
      PresheafedSpace.is_open_immersion.base_open.open_range⟩
  haveI : Nonempty U := ⟨⟨_, X.affine_cover.covers x⟩⟩
  have hU : is_affine_open U := range_is_affine_open_of_open_immersion (X.affine_cover.map x)
  exact
    @IsFractionRing.isFractionRing_of_isDomain_of_isLocalization _ _ _ _ _ _ _ _ _ _ _
      (hU.is_localization_stalk ⟨x, X.affine_cover.covers x⟩)
      (function_field_is_fraction_ring_of_is_affine_open X U hU)

end AlgebraicGeometry