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

! This file was ported from Lean 3 source module measure_theory.integral.circle_integral
! leanprover-community/mathlib commit 639b66d9330da2cd8dd02be1a51b6d10d058e5e4
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Integral.IntervalIntegral
import Mathbin.Analysis.NormedSpace.Pointwise
import Mathbin.Analysis.SpecialFunctions.NonIntegrable
import Mathbin.Analysis.Analytic.Basic

/-!
# Integral over a circle in `ℂ`

In this file we define `∮ z in C(c, R), f z` to be the integral $\oint_{|z-c|=|R|} f(z)\,dz$ and
prove some properties of this integral. We give definition and prove most lemmas for a function
`f : ℂ → E`, where `E` is a complex Banach space. For this reason,
some lemmas use, e.g., `(z - c)⁻¹ • f z` instead of `f z / (z - c)`.

## Main definitions

* `circle_map c R`: the exponential map $θ ↦ c + R e^{θi}$;

* `circle_integrable f c R`: a function `f : ℂ → E` is integrable on the circle with center `c` and
  radius `R` if `f ∘ circle_map c R` is integrable on `[0, 2π]`;

* `circle_integral f c R`: the integral $\oint_{|z-c|=|R|} f(z)\,dz$, defined as
  $\int_{0}^{2π}(c + Re^{θ i})' f(c+Re^{θ i})\,dθ$;

* `cauchy_power_series f c R`: the power series that is equal to
  $\sum_{n=0}^{\infty} \oint_{|z-c|=R} \left(\frac{w-c}{z - c}\right)^n \frac{1}{z-c}f(z)\,dz$ at
  `w - c`. The coefficients of this power series depend only on `f ∘ circle_map c R`, and the power
  series converges to `f w` if `f` is differentiable on the closed ball `metric.closed_ball c R`
  and `w` belongs to the corresponding open ball.

## Main statements

* `has_fpower_series_on_cauchy_integral`: for any circle integrable function `f`, the power series
  `cauchy_power_series f c R`, `R > 0`, converges to the Cauchy integral
  `(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z` on the open disc `metric.ball c R`;

* `circle_integral.integral_sub_zpow_of_undef`, `circle_integral.integral_sub_zpow_of_ne`, and
  `circle_integral.integral_sub_inv_of_mem_ball`: formulas for `∮ z in C(c, R), (z - w) ^ n`,
  `n : ℤ`. These lemmas cover the following cases:

  - `circle_integral.integral_sub_zpow_of_undef`, `n < 0` and `|w - c| = |R|`: in this case the
    function is not integrable, so the integral is equal to its default value (zero);

  - `circle_integral.integral_sub_zpow_of_ne`, `n ≠ -1`: in the cases not covered by the previous
    lemma, we have `(z - w) ^ n = ((z - w) ^ (n + 1) / (n + 1))'`, thus the integral equals zero;

  - `circle_integral.integral_sub_inv_of_mem_ball`, `n = -1`, `|w - c| < R`: in this case the
    integral is equal to `2πi`.

  The case `n = -1`, `|w -c| > R` is not covered by these lemmas. While it is possible to construct
  an explicit primitive, it is easier to apply Cauchy theorem, so we postpone the proof till we have
  this theorem (see #10000).

## Notation

- `∮ z in C(c, R), f z`: notation for the integral $\oint_{|z-c|=|R|} f(z)\,dz$, defined as
  $\int_{0}^{2π}(c + Re^{θ i})' f(c+Re^{θ i})\,dθ$.

## Tags

integral, circle, Cauchy integral
-/

variable {E : Type _} [NormedAddCommGroup E]

noncomputable section

open Real NNReal Interval Pointwise Topology

open Complex MeasureTheory TopologicalSpace Metric Function Set Filter Asymptotics

/-!
### `circle_map`, a parametrization of a circle
-/

/-- The exponential map $θ ↦ c + R e^{θi}$. The range of this map is the circle in `ℂ` with center
`c` and radius `|R|`. -/
def circleMap (c : ℂ) (R : ℝ) : ℝ → ℂ := fun θ => c + R * exp (θ * I)
#align circle_map circleMap

/-- `circle_map` is `2π`-periodic. -/
theorem periodic_circleMap (c : ℂ) (R : ℝ) : Periodic (circleMap c R) (2 * π) := fun θ => by
  simp [circleMap, add_mul, exp_periodic _]
#align periodic_circle_map periodic_circleMap

theorem Set.Countable.preimage_circleMap {s : Set ℂ} (hs : s.Countable) (c : ℂ) {R : ℝ}
    (hR : R ≠ 0) : (circleMap c R ⁻¹' s).Countable :=
  show (coe ⁻¹' ((· * I) ⁻¹' (exp ⁻¹' ((· * ·) R ⁻¹' ((· + ·) c ⁻¹' s))))).Countable from
    (((hs.Preimage (add_right_injective _)).Preimage <|
                mul_right_injective₀ <| ofReal_ne_zero.2 hR).preimage_cexp.Preimage <|
          mul_left_injective₀ I_ne_zero).Preimage
      ofReal_injective
#align set.countable.preimage_circle_map Set.Countable.preimage_circleMap

@[simp]
theorem circleMap_sub_center (c : ℂ) (R : ℝ) (θ : ℝ) : circleMap c R θ - c = circleMap 0 R θ := by
  simp [circleMap]
#align circle_map_sub_center circleMap_sub_center

theorem circleMap_zero (R θ : ℝ) : circleMap 0 R θ = R * exp (θ * I) :=
  zero_add _
#align circle_map_zero circleMap_zero

@[simp]
theorem abs_circleMap_zero (R : ℝ) (θ : ℝ) : abs (circleMap 0 R θ) = |R| := by simp [circleMap]
#align abs_circle_map_zero abs_circleMap_zero

theorem circleMap_mem_sphere' (c : ℂ) (R : ℝ) (θ : ℝ) : circleMap c R θ ∈ sphere c (|R|) := by simp
#align circle_map_mem_sphere' circleMap_mem_sphere'

theorem circleMap_mem_sphere (c : ℂ) {R : ℝ} (hR : 0 ≤ R) (θ : ℝ) : circleMap c R θ ∈ sphere c R :=
  by simpa only [_root_.abs_of_nonneg hR] using circleMap_mem_sphere' c R θ
#align circle_map_mem_sphere circleMap_mem_sphere

theorem circleMap_mem_closedBall (c : ℂ) {R : ℝ} (hR : 0 ≤ R) (θ : ℝ) :
    circleMap c R θ ∈ closedBall c R :=
  sphere_subset_closedBall (circleMap_mem_sphere c hR θ)
#align circle_map_mem_closed_ball circleMap_mem_closedBall

theorem circleMap_not_mem_ball (c : ℂ) (R : ℝ) (θ : ℝ) : circleMap c R θ ∉ ball c R := by
  simp [dist_eq, le_abs_self]
#align circle_map_not_mem_ball circleMap_not_mem_ball

theorem circleMap_ne_mem_ball {c : ℂ} {R : ℝ} {w : ℂ} (hw : w ∈ ball c R) (θ : ℝ) :
    circleMap c R θ ≠ w :=
  (ne_of_mem_of_not_mem hw (circleMap_not_mem_ball _ _ _)).symm
#align circle_map_ne_mem_ball circleMap_ne_mem_ball

/-- The range of `circle_map c R` is the circle with center `c` and radius `|R|`. -/
@[simp]
theorem range_circleMap (c : ℂ) (R : ℝ) : range (circleMap c R) = sphere c (|R|) :=
  calc
    range (circleMap c R) = c +ᵥ R • range fun θ : ℝ => exp (θ * I) := by
      simp only [← image_vadd, ← image_smul, ← range_comp, vadd_eq_add, circleMap, (· ∘ ·),
        real_smul]
    _ = sphere c (|R|) := by simp [smul_sphere R (0 : ℂ) zero_le_one]

#align range_circle_map range_circleMap

/-- The image of `(0, 2π]` under `circle_map c R` is the circle with center `c` and radius `|R|`. -/
@[simp]
theorem image_circleMap_Ioc (c : ℂ) (R : ℝ) : circleMap c R '' Ioc 0 (2 * π) = sphere c (|R|) := by
  rw [← range_circleMap, ← (periodic_circleMap c R).image_Ioc Real.two_pi_pos 0, zero_add]
#align image_circle_map_Ioc image_circleMap_Ioc

@[simp]
theorem circleMap_eq_center_iff {c : ℂ} {R : ℝ} {θ : ℝ} : circleMap c R θ = c ↔ R = 0 := by
  simp [circleMap, exp_ne_zero]
#align circle_map_eq_center_iff circleMap_eq_center_iff

@[simp]
theorem circleMap_zero_radius (c : ℂ) : circleMap c 0 = const ℝ c :=
  funext fun θ => circleMap_eq_center_iff.2 rfl
#align circle_map_zero_radius circleMap_zero_radius

theorem circleMap_ne_center {c : ℂ} {R : ℝ} (hR : R ≠ 0) {θ : ℝ} : circleMap c R θ ≠ c :=
  mt circleMap_eq_center_iff.1 hR
#align circle_map_ne_center circleMap_ne_center

theorem hasDerivAt_circleMap (c : ℂ) (R : ℝ) (θ : ℝ) :
    HasDerivAt (circleMap c R) (circleMap 0 R θ * I) θ := by
  simpa only [mul_assoc, one_mul, of_real_clm_apply, circleMap, of_real_one, zero_add] using
    ((of_real_clm.has_deriv_at.mul_const I).cexp.const_mul (R : ℂ)).const_add c
#align has_deriv_at_circle_map hasDerivAt_circleMap

/- TODO: prove `cont_diff ℝ (circle_map c R)`. This needs a version of `cont_diff.mul`
for multiplication in a normed algebra over the base field. -/
theorem differentiable_circleMap (c : ℂ) (R : ℝ) : Differentiable ℝ (circleMap c R) := fun θ =>
  (hasDerivAt_circleMap c R θ).DifferentiableAt
#align differentiable_circle_map differentiable_circleMap

@[continuity]
theorem continuous_circleMap (c : ℂ) (R : ℝ) : Continuous (circleMap c R) :=
  (differentiable_circleMap c R).Continuous
#align continuous_circle_map continuous_circleMap

@[measurability]
theorem measurable_circleMap (c : ℂ) (R : ℝ) : Measurable (circleMap c R) :=
  (continuous_circleMap c R).Measurable
#align measurable_circle_map measurable_circleMap

@[simp]
theorem deriv_circleMap (c : ℂ) (R : ℝ) (θ : ℝ) : deriv (circleMap c R) θ = circleMap 0 R θ * I :=
  (hasDerivAt_circleMap _ _ _).deriv
#align deriv_circle_map deriv_circleMap

theorem deriv_circleMap_eq_zero_iff {c : ℂ} {R : ℝ} {θ : ℝ} : deriv (circleMap c R) θ = 0 ↔ R = 0 :=
  by simp [I_ne_zero]
#align deriv_circle_map_eq_zero_iff deriv_circleMap_eq_zero_iff

theorem deriv_circleMap_ne_zero {c : ℂ} {R : ℝ} {θ : ℝ} (hR : R ≠ 0) :
    deriv (circleMap c R) θ ≠ 0 :=
  mt deriv_circleMap_eq_zero_iff.1 hR
#align deriv_circle_map_ne_zero deriv_circleMap_ne_zero

theorem lipschitzWith_circleMap (c : ℂ) (R : ℝ) : LipschitzWith R.nnabs (circleMap c R) :=
  lipschitzWith_of_nnnorm_deriv_le (differentiable_circleMap _ _) fun θ =>
    NNReal.coe_le_coe.1 <| by simp
#align lipschitz_with_circle_map lipschitzWith_circleMap

theorem continuous_circleMap_inv {R : ℝ} {z w : ℂ} (hw : w ∈ ball z R) :
    Continuous fun θ => (circleMap z R θ - w)⁻¹ :=
  by
  have : ∀ θ, circleMap z R θ - w ≠ 0 :=
    by
    simp_rw [sub_ne_zero]
    exact fun θ => circleMap_ne_mem_ball hw θ
  continuity
#align continuous_circle_map_inv continuous_circleMap_inv

/-!
### Integrability of a function on a circle
-/

/-- We say that a function `f : ℂ → E` is integrable on the circle with center `c` and radius `R` if
the function `f ∘ circle_map c R` is integrable on `[0, 2π]`.

Note that the actual function used in the definition of `circle_integral` is
`(deriv (circle_map c R) θ) • f (circle_map c R θ)`. Integrability of this function is equivalent
to integrability of `f ∘ circle_map c R` whenever `R ≠ 0`. -/
def CircleIntegrable (f : ℂ → E) (c : ℂ) (R : ℝ) : Prop :=
  IntervalIntegrable (fun θ : ℝ => f (circleMap c R θ)) volume 0 (2 * π)
#align circle_integrable CircleIntegrable

@[simp]
theorem circleIntegrable_const (a : E) (c : ℂ) (R : ℝ) : CircleIntegrable (fun _ => a) c R :=
  intervalIntegrableConst
#align circle_integrable_const circleIntegrable_const

namespace CircleIntegrable

variable {f g : ℂ → E} {c : ℂ} {R : ℝ}

theorem add (hf : CircleIntegrable f c R) (hg : CircleIntegrable g c R) :
    CircleIntegrable (f + g) c R :=
  hf.add hg
#align circle_integrable.add CircleIntegrable.add

theorem neg (hf : CircleIntegrable f c R) : CircleIntegrable (-f) c R :=
  hf.neg
#align circle_integrable.neg CircleIntegrable.neg

/-- The function we actually integrate over `[0, 2π]` in the definition of `circle_integral` is
integrable. -/
theorem out [NormedSpace ℂ E] (hf : CircleIntegrable f c R) :
    IntervalIntegrable (fun θ : ℝ => deriv (circleMap c R) θ • f (circleMap c R θ)) volume 0
      (2 * π) :=
  by
  simp only [CircleIntegrable, deriv_circleMap, intervalIntegrable_iff] at *
  refine' (hf.norm.const_mul (|R|)).mono' _ _
  ·
    exact
      ((continuous_circleMap _ _).AeStronglyMeasurable.mul_const I).smul hf.ae_strongly_measurable
  · simp [norm_smul]
#align circle_integrable.out CircleIntegrable.out

end CircleIntegrable

@[simp]
theorem circleIntegrable_zero_radius {f : ℂ → E} {c : ℂ} : CircleIntegrable f c 0 := by
  simp [CircleIntegrable]
#align circle_integrable_zero_radius circleIntegrable_zero_radius

theorem circleIntegrable_iff [NormedSpace ℂ E] {f : ℂ → E} {c : ℂ} (R : ℝ) :
    CircleIntegrable f c R ↔
      IntervalIntegrable (fun θ : ℝ => deriv (circleMap c R) θ • f (circleMap c R θ)) volume 0
        (2 * π) :=
  by
  by_cases h₀ : R = 0
  · simp [h₀]
  refine' ⟨fun h => h.out, fun h => _⟩
  simp only [CircleIntegrable, intervalIntegrable_iff, deriv_circleMap] at h⊢
  refine' (h.norm.const_mul (|R|)⁻¹).mono' _ _
  · have H : ∀ {θ}, circleMap 0 R θ * I ≠ 0 := fun θ => by simp [h₀, I_ne_zero]
    simpa only [inv_smul_smul₀ H] using
      ((continuous_circleMap 0 R).AeStronglyMeasurable.mul_const
                  I).AeMeasurable.inv.AeStronglyMeasurable.smul
        h.ae_strongly_measurable
  · simp [norm_smul, h₀]
#align circle_integrable_iff circleIntegrable_iff

theorem ContinuousOn.circle_integrable' {f : ℂ → E} {c : ℂ} {R : ℝ}
    (hf : ContinuousOn f (sphere c (|R|))) : CircleIntegrable f c R :=
  (hf.comp_continuous (continuous_circleMap _ _) (circleMap_mem_sphere' _ _)).IntervalIntegrable _ _
#align continuous_on.circle_integrable' ContinuousOn.circle_integrable'

theorem ContinuousOn.circleIntegrable {f : ℂ → E} {c : ℂ} {R : ℝ} (hR : 0 ≤ R)
    (hf : ContinuousOn f (sphere c R)) : CircleIntegrable f c R :=
  ContinuousOn.circle_integrable' <| (abs_of_nonneg hR).symm ▸ hf
#align continuous_on.circle_integrable ContinuousOn.circleIntegrable

/-- The function `λ z, (z - w) ^ n`, `n : ℤ`, is circle integrable on the circle with center `c` and
radius `|R|` if and only if `R = 0` or `0 ≤ n`, or `w` does not belong to this circle. -/
@[simp]
theorem circleIntegrable_sub_zpow_iff {c w : ℂ} {R : ℝ} {n : ℤ} :
    CircleIntegrable (fun z => (z - w) ^ n) c R ↔ R = 0 ∨ 0 ≤ n ∨ w ∉ sphere c (|R|) :=
  by
  constructor
  · intro h
    contrapose! h
    rcases h with ⟨hR, hn, hw⟩
    simp only [circleIntegrable_iff R, deriv_circleMap]
    rw [← image_circleMap_Ioc] at hw
    rcases hw with ⟨θ, hθ, rfl⟩
    replace hθ : θ ∈ [0, 2 * π]
    exact Icc_subset_uIcc (Ioc_subset_Icc_self hθ)
    refine' not_intervalIntegrable_of_sub_inv_isO_punctured _ real.two_pi_pos.ne hθ
    set f : ℝ → ℂ := fun θ' => circleMap c R θ' - circleMap c R θ
    have : ∀ᶠ θ' in 𝓝[≠] θ, f θ' ∈ ball (0 : ℂ) 1 \ {0} :=
      by
      suffices : ∀ᶠ z in 𝓝[≠] circleMap c R θ, z - circleMap c R θ ∈ ball (0 : ℂ) 1 \ {0}
      exact
        ((differentiable_circleMap c R θ).HasDerivAt.tendsto_punctured_nhds
              (deriv_circleMap_ne_zero hR)).Eventually
          this
      filter_upwards [self_mem_nhdsWithin, mem_nhdsWithin_of_mem_nhds (ball_mem_nhds _ zero_lt_one)]
      simp (config := { contextual := true }) only [dist_eq, sub_eq_zero, mem_compl_iff,
        mem_singleton_iff, mem_ball, mem_diff, mem_ball_zero_iff, norm_eq_abs, not_false_iff,
        and_self_iff, imp_true_iff]
    refine'
      (((hasDerivAt_circleMap c R θ).isO_sub.mono inf_le_left).inv_rev
            (this.mono fun θ' h₁ h₂ => absurd h₂ h₁.2)).trans
        _
    refine' is_O.of_bound (|R|)⁻¹ (this.mono fun θ' hθ' => _)
    set x := abs (f θ')
    suffices x⁻¹ ≤ x ^ n by
      simpa only [inv_mul_cancel_left₀, abs_eq_zero.not.2 hR, norm_eq_abs, map_inv₀,
        Algebra.id.smul_eq_mul, map_mul, abs_circleMap_zero, abs_I, mul_one, abs_zpow, Ne.def,
        not_false_iff] using this
    have : x ∈ Ioo (0 : ℝ) 1 := by simpa [and_comm, x] using hθ'
    rw [← zpow_neg_one]
    refine' (zpow_strictAnti this.1 this.2).le_iff_le.2 (Int.lt_add_one_iff.1 _)
    exact hn
  · rintro (rfl | H)
    exacts[circleIntegrable_zero_radius,
      ((continuous_on_id.sub continuousOn_const).zpow₀ _ fun z hz =>
          H.symm.imp_left fun hw => sub_ne_zero.2 <| ne_of_mem_of_not_mem hz hw).circle_integrable']
#align circle_integrable_sub_zpow_iff circleIntegrable_sub_zpow_iff

@[simp]
theorem circleIntegrable_sub_inv_iff {c w : ℂ} {R : ℝ} :
    CircleIntegrable (fun z => (z - w)⁻¹) c R ↔ R = 0 ∨ w ∉ sphere c (|R|) :=
  by
  simp only [← zpow_neg_one, circleIntegrable_sub_zpow_iff]
  norm_num
#align circle_integrable_sub_inv_iff circleIntegrable_sub_inv_iff

variable [NormedSpace ℂ E] [CompleteSpace E]

/-- Definition for $\oint_{|z-c|=R} f(z)\,dz$. -/
def circleIntegral (f : ℂ → E) (c : ℂ) (R : ℝ) : E :=
  ∫ θ : ℝ in 0 ..2 * π, deriv (circleMap c R) θ • f (circleMap c R θ)
#align circle_integral circleIntegral

-- mathport name: «expr∮ inC( , ), »
notation3"∮ "(...)" in ""C("c", "R")"", "r:(scoped f => circleIntegral f c R) => r

theorem circleIntegral_def_Icc (f : ℂ → E) (c : ℂ) (R : ℝ) :
    (∮ z in C(c, R), f z) = ∫ θ in Icc 0 (2 * π), deriv (circleMap c R) θ • f (circleMap c R θ) :=
  by
  simp only [circleIntegral, intervalIntegral.integral_of_le real.two_pi_pos.le,
    measure.restrict_congr_set Ioc_ae_eq_Icc]
#align circle_integral_def_Icc circleIntegral_def_Icc

namespace circleIntegral

@[simp]
theorem integral_radius_zero (f : ℂ → E) (c : ℂ) : (∮ z in C(c, 0), f z) = 0 := by
  simp [circleIntegral]
#align circle_integral.integral_radius_zero circleIntegral.integral_radius_zero

theorem integral_congr {f g : ℂ → E} {c : ℂ} {R : ℝ} (hR : 0 ≤ R) (h : EqOn f g (sphere c R)) :
    (∮ z in C(c, R), f z) = ∮ z in C(c, R), g z :=
  intervalIntegral.integral_congr fun θ hθ => by simp only [h (circleMap_mem_sphere _ hR _)]
#align circle_integral.integral_congr circleIntegral.integral_congr

theorem integral_sub_inv_smul_sub_smul (f : ℂ → E) (c w : ℂ) (R : ℝ) :
    (∮ z in C(c, R), (z - w)⁻¹ • (z - w) • f z) = ∮ z in C(c, R), f z :=
  by
  rcases eq_or_ne R 0 with (rfl | hR)
  · simp only [integral_radius_zero]
  have : (circleMap c R ⁻¹' {w}).Countable := (countable_singleton _).preimage_circleMap c hR
  refine' intervalIntegral.integral_congr_ae ((this.ae_not_mem _).mono fun θ hθ hθ' => _)
  change circleMap c R θ ≠ w at hθ
  simp only [inv_smul_smul₀ (sub_ne_zero.2 <| hθ)]
#align circle_integral.integral_sub_inv_smul_sub_smul circleIntegral.integral_sub_inv_smul_sub_smul

theorem integral_undef {f : ℂ → E} {c : ℂ} {R : ℝ} (hf : ¬CircleIntegrable f c R) :
    (∮ z in C(c, R), f z) = 0 :=
  intervalIntegral.integral_undef (mt (circleIntegrable_iff R).mpr hf)
#align circle_integral.integral_undef circleIntegral.integral_undef

theorem integral_sub {f g : ℂ → E} {c : ℂ} {R : ℝ} (hf : CircleIntegrable f c R)
    (hg : CircleIntegrable g c R) :
    (∮ z in C(c, R), f z - g z) = (∮ z in C(c, R), f z) - ∮ z in C(c, R), g z := by
  simp only [circleIntegral, smul_sub, intervalIntegral.integral_sub hf.out hg.out]
#align circle_integral.integral_sub circleIntegral.integral_sub

theorem norm_integral_le_of_norm_le_const' {f : ℂ → E} {c : ℂ} {R C : ℝ}
    (hf : ∀ z ∈ sphere c (|R|), ‖f z‖ ≤ C) : ‖∮ z in C(c, R), f z‖ ≤ 2 * π * |R| * C :=
  calc
    ‖∮ z in C(c, R), f z‖ ≤ |R| * C * |2 * π - 0| :=
      intervalIntegral.norm_integral_le_of_norm_le_const fun θ _ =>
        calc
          ‖deriv (circleMap c R) θ • f (circleMap c R θ)‖ = |R| * ‖f (circleMap c R θ)‖ := by
            simp [norm_smul]
          _ ≤ |R| * C :=
            mul_le_mul_of_nonneg_left (hf _ <| circleMap_mem_sphere' _ _ _) (abs_nonneg _)

    _ = 2 * π * |R| * C := by
      rw [sub_zero, _root_.abs_of_pos Real.two_pi_pos]
      ac_rfl

#align circle_integral.norm_integral_le_of_norm_le_const' circleIntegral.norm_integral_le_of_norm_le_const'

theorem norm_integral_le_of_norm_le_const {f : ℂ → E} {c : ℂ} {R C : ℝ} (hR : 0 ≤ R)
    (hf : ∀ z ∈ sphere c R, ‖f z‖ ≤ C) : ‖∮ z in C(c, R), f z‖ ≤ 2 * π * R * C :=
  have : |R| = R := abs_of_nonneg hR
  calc
    ‖∮ z in C(c, R), f z‖ ≤ 2 * π * |R| * C := norm_integral_le_of_norm_le_const' <| by rwa [this]
    _ = 2 * π * R * C := by rw [this]

#align circle_integral.norm_integral_le_of_norm_le_const circleIntegral.norm_integral_le_of_norm_le_const

theorem norm_two_pi_i_inv_smul_integral_le_of_norm_le_const {f : ℂ → E} {c : ℂ} {R C : ℝ}
    (hR : 0 ≤ R) (hf : ∀ z ∈ sphere c R, ‖f z‖ ≤ C) :
    ‖(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), f z‖ ≤ R * C :=
  by
  have : ‖(2 * π * I : ℂ)⁻¹‖ = (2 * π)⁻¹ := by simp [real.pi_pos.le]
  rw [norm_smul, this, ← div_eq_inv_mul, div_le_iff Real.two_pi_pos, mul_comm (R * C), ← mul_assoc]
  exact norm_integral_le_of_norm_le_const hR hf
#align circle_integral.norm_two_pi_I_inv_smul_integral_le_of_norm_le_const circleIntegral.norm_two_pi_i_inv_smul_integral_le_of_norm_le_const

/-- If `f` is continuous on the circle `|z - c| = R`, `R > 0`, the `‖f z‖` is less than or equal to
`C : ℝ` on this circle, and this norm is strictly less than `C` at some point `z` of the circle,
then `‖∮ z in C(c, R), f z‖ < 2 * π * R * C`. -/
theorem norm_integral_lt_of_norm_le_const_of_lt {f : ℂ → E} {c : ℂ} {R C : ℝ} (hR : 0 < R)
    (hc : ContinuousOn f (sphere c R)) (hf : ∀ z ∈ sphere c R, ‖f z‖ ≤ C)
    (hlt : ∃ z ∈ sphere c R, ‖f z‖ < C) : ‖∮ z in C(c, R), f z‖ < 2 * π * R * C :=
  by
  rw [← _root_.abs_of_pos hR, ← image_circleMap_Ioc] at hlt
  rcases hlt with ⟨_, ⟨θ₀, hmem, rfl⟩, hlt⟩
  calc
    ‖∮ z in C(c, R), f z‖ ≤ ∫ θ in 0 ..2 * π, ‖deriv (circleMap c R) θ • f (circleMap c R θ)‖ :=
      intervalIntegral.norm_integral_le_integral_norm real.two_pi_pos.le
    _ < ∫ θ in 0 ..2 * π, R * C :=
      by
      simp only [norm_smul, deriv_circleMap, norm_eq_abs, map_mul, abs_I, mul_one,
        abs_circleMap_zero, abs_of_pos hR]
      refine'
        intervalIntegral.integral_lt_integral_of_continuousOn_of_le_of_exists_lt Real.two_pi_pos _
          continuousOn_const (fun θ hθ => _) ⟨θ₀, Ioc_subset_Icc_self hmem, _⟩
      ·
        exact
          continuous_on_const.mul
            (hc.comp (continuous_circleMap _ _).ContinuousOn fun θ hθ =>
                circleMap_mem_sphere _ hR.le _).norm
      · exact mul_le_mul_of_nonneg_left (hf _ <| circleMap_mem_sphere _ hR.le _) hR.le
      · exact (mul_lt_mul_left hR).2 hlt
    _ = 2 * π * R * C := by simp [mul_assoc]

#align circle_integral.norm_integral_lt_of_norm_le_const_of_lt circleIntegral.norm_integral_lt_of_norm_le_const_of_lt

@[simp]
theorem integral_smul {𝕜 : Type _} [IsROrC 𝕜] [NormedSpace 𝕜 E] [SMulCommClass 𝕜 ℂ E] (a : 𝕜)
    (f : ℂ → E) (c : ℂ) (R : ℝ) : (∮ z in C(c, R), a • f z) = a • ∮ z in C(c, R), f z := by
  simp only [circleIntegral, ← smul_comm a, intervalIntegral.integral_smul]
#align circle_integral.integral_smul circleIntegral.integral_smul

@[simp]
theorem integral_smul_const (f : ℂ → ℂ) (a : E) (c : ℂ) (R : ℝ) :
    (∮ z in C(c, R), f z • a) = (∮ z in C(c, R), f z) • a := by
  simp only [circleIntegral, intervalIntegral.integral_smul_const, ← smul_assoc]
#align circle_integral.integral_smul_const circleIntegral.integral_smul_const

@[simp]
theorem integral_const_mul (a : ℂ) (f : ℂ → ℂ) (c : ℂ) (R : ℝ) :
    (∮ z in C(c, R), a * f z) = a * ∮ z in C(c, R), f z :=
  integral_smul a f c R
#align circle_integral.integral_const_mul circleIntegral.integral_const_mul

@[simp]
theorem integral_sub_center_inv (c : ℂ) {R : ℝ} (hR : R ≠ 0) :
    (∮ z in C(c, R), (z - c)⁻¹) = 2 * π * I := by
  simp [circleIntegral, ← div_eq_mul_inv, mul_div_cancel_left _ (circleMap_ne_center hR)]
#align circle_integral.integral_sub_center_inv circleIntegral.integral_sub_center_inv

/-- If `f' : ℂ → E` is a derivative of a complex differentiable function on the circle
`metric.sphere c |R|`, then `∮ z in C(c, R), f' z = 0`. -/
theorem integral_eq_zero_of_has_deriv_within_at' {f f' : ℂ → E} {c : ℂ} {R : ℝ}
    (h : ∀ z ∈ sphere c (|R|), HasDerivWithinAt f (f' z) (sphere c (|R|)) z) :
    (∮ z in C(c, R), f' z) = 0 :=
  by
  by_cases hi : CircleIntegrable f' c R
  · rw [← sub_eq_zero.2 ((periodic_circleMap c R).comp f).Eq]
    refine' intervalIntegral.integral_eq_sub_of_hasDerivAt (fun θ hθ => _) hi.out
    exact
      (h _ (circleMap_mem_sphere' _ _ _)).scomp_hasDerivAt θ
        (differentiable_circleMap _ _ _).HasDerivAt (circleMap_mem_sphere' _ _)
  · exact integral_undef hi
#align circle_integral.integral_eq_zero_of_has_deriv_within_at' circleIntegral.integral_eq_zero_of_has_deriv_within_at'

/-- If `f' : ℂ → E` is a derivative of a complex differentiable function on the circle
`metric.sphere c R`, then `∮ z in C(c, R), f' z = 0`. -/
theorem integral_eq_zero_of_hasDerivWithinAt {f f' : ℂ → E} {c : ℂ} {R : ℝ} (hR : 0 ≤ R)
    (h : ∀ z ∈ sphere c R, HasDerivWithinAt f (f' z) (sphere c R) z) : (∮ z in C(c, R), f' z) = 0 :=
  integral_eq_zero_of_has_deriv_within_at' <| (abs_of_nonneg hR).symm.subst h
#align circle_integral.integral_eq_zero_of_has_deriv_within_at circleIntegral.integral_eq_zero_of_hasDerivWithinAt

/-- If `n < 0` and `|w - c| = |R|`, then `(z - w) ^ n` is not circle integrable on the circle with
center `c` and radius `(|R|)`, so the integral `∮ z in C(c, R), (z - w) ^ n` is equal to zero. -/
theorem integral_sub_zpow_of_undef {n : ℤ} {c w : ℂ} {R : ℝ} (hn : n < 0)
    (hw : w ∈ sphere c (|R|)) : (∮ z in C(c, R), (z - w) ^ n) = 0 :=
  by
  rcases eq_or_ne R 0 with (rfl | h0); · apply integral_radius_zero
  apply integral_undef
  simp [circleIntegrable_sub_zpow_iff, *]
#align circle_integral.integral_sub_zpow_of_undef circleIntegral.integral_sub_zpow_of_undef

/-- If `n ≠ -1` is an integer number, then the integral of `(z - w) ^ n` over the circle equals
zero. -/
theorem integral_sub_zpow_of_ne {n : ℤ} (hn : n ≠ -1) (c w : ℂ) (R : ℝ) :
    (∮ z in C(c, R), (z - w) ^ n) = 0 :=
  by
  rcases em (w ∈ sphere c (|R|) ∧ n < -1) with (⟨hw, hn⟩ | H)
  · exact integral_sub_zpow_of_undef (hn.trans (by decide)) hw
  push_neg  at H
  have hd :
    ∀ z, z ≠ w ∨ -1 ≤ n → HasDerivAt (fun z => (z - w) ^ (n + 1) / (n + 1)) ((z - w) ^ n) z :=
    by
    intro z hne
    convert((hasDerivAt_zpow (n + 1) _ (hne.imp _ _)).comp z
            ((hasDerivAt_id z).sub_const w)).div_const
        _ using
      1
    · have hn' : (n + 1 : ℂ) ≠ 0 := by
        rwa [Ne, ← eq_neg_iff_add_eq_zero, ← Int.cast_one, ← Int.cast_neg, Int.cast_inj]
      simp [mul_assoc, mul_div_cancel_left _ hn']
    exacts[sub_ne_zero.2, neg_le_iff_add_nonneg.1]
  refine' integral_eq_zero_of_has_deriv_within_at' fun z hz => (hd z _).HasDerivWithinAt
  exact (ne_or_eq z w).imp_right fun h => H <| h ▸ hz
#align circle_integral.integral_sub_zpow_of_ne circleIntegral.integral_sub_zpow_of_ne

end circleIntegral

/-- The power series that is equal to
$\sum_{n=0}^{\infty} \oint_{|z-c|=R} \left(\frac{w-c}{z - c}\right)^n \frac{1}{z-c}f(z)\,dz$ at
`w - c`. The coefficients of this power series depend only on `f ∘ circle_map c R`, and the power
series converges to `f w` if `f` is differentiable on the closed ball `metric.closed_ball c R` and
`w` belongs to the corresponding open ball. For any circle integrable function `f`, this power
series converges to the Cauchy integral for `f`. -/
def cauchyPowerSeries (f : ℂ → E) (c : ℂ) (R : ℝ) : FormalMultilinearSeries ℂ ℂ E := fun n =>
  ContinuousMultilinearMap.mkPiField ℂ _ <|
    (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - c)⁻¹ ^ n • (z - c)⁻¹ • f z
#align cauchy_power_series cauchyPowerSeries

theorem cauchyPowerSeries_apply (f : ℂ → E) (c : ℂ) (R : ℝ) (n : ℕ) (w : ℂ) :
    (cauchyPowerSeries f c R n fun _ => w) =
      (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (w / (z - c)) ^ n • (z - c)⁻¹ • f z :=
  by
  simp only [cauchyPowerSeries, ContinuousMultilinearMap.mkPiField_apply, Fin.prod_const,
    div_eq_mul_inv, mul_pow, mul_smul, circleIntegral.integral_smul, ← smul_comm (w ^ n)]
#align cauchy_power_series_apply cauchyPowerSeries_apply

theorem norm_cauchyPowerSeries_le (f : ℂ → E) (c : ℂ) (R : ℝ) (n : ℕ) :
    ‖cauchyPowerSeries f c R n‖ ≤
      ((2 * π)⁻¹ * ∫ θ : ℝ in 0 ..2 * π, ‖f (circleMap c R θ)‖) * (|R|)⁻¹ ^ n :=
  calc
    ‖cauchyPowerSeries f c R n‖ = (2 * π)⁻¹ * ‖∮ z in C(c, R), (z - c)⁻¹ ^ n • (z - c)⁻¹ • f z‖ :=
      by simp [cauchyPowerSeries, norm_smul, real.pi_pos.le]
    _ ≤
        (2 * π)⁻¹ *
          ∫ θ in 0 ..2 * π,
            ‖deriv (circleMap c R) θ •
                (circleMap c R θ - c)⁻¹ ^ n • (circleMap c R θ - c)⁻¹ • f (circleMap c R θ)‖ :=
      (mul_le_mul_of_nonneg_left
        (intervalIntegral.norm_integral_le_integral_norm Real.two_pi_pos.le)
        (by simp [real.pi_pos.le]))
    _ =
        (2 * π)⁻¹ *
          ((|R|)⁻¹ ^ n * (|R| * ((|R|)⁻¹ * ∫ x : ℝ in 0 ..2 * π, ‖f (circleMap c R x)‖))) :=
      by simp [norm_smul, mul_left_comm (|R|)]
    _ ≤ ((2 * π)⁻¹ * ∫ θ : ℝ in 0 ..2 * π, ‖f (circleMap c R θ)‖) * (|R|)⁻¹ ^ n :=
      by
      rcases eq_or_ne R 0 with (rfl | hR)
      · cases n <;> simp [-mul_inv_rev, Real.two_pi_pos]
      · rw [mul_inv_cancel_left₀, mul_assoc, mul_comm ((|R|)⁻¹ ^ n)]
        rwa [Ne.def, _root_.abs_eq_zero]

#align norm_cauchy_power_series_le norm_cauchyPowerSeries_le

theorem le_radius_cauchyPowerSeries (f : ℂ → E) (c : ℂ) (R : ℝ≥0) :
    ↑R ≤ (cauchyPowerSeries f c R).radius :=
  by
  refine'
    (cauchyPowerSeries f c R).le_radius_of_bound
      ((2 * π)⁻¹ * ∫ θ : ℝ in 0 ..2 * π, ‖f (circleMap c R θ)‖) fun n => _
  refine'
    (mul_le_mul_of_nonneg_right (norm_cauchyPowerSeries_le _ _ _ _)
          (pow_nonneg R.coe_nonneg _)).trans
      _
  rw [_root_.abs_of_nonneg R.coe_nonneg]
  cases' eq_or_ne (R ^ n : ℝ) 0 with hR hR
  · rw [hR, MulZeroClass.mul_zero]
    exact
      mul_nonneg (inv_nonneg.2 real.two_pi_pos.le)
        (intervalIntegral.integral_nonneg real.two_pi_pos.le fun _ _ => norm_nonneg _)
  · rw [inv_pow, inv_mul_cancel_right₀ hR]
#align le_radius_cauchy_power_series le_radius_cauchyPowerSeries

/-- For any circle integrable function `f`, the power series `cauchy_power_series f c R` multiplied
by `2πI` converges to the integral `∮ z in C(c, R), (z - w)⁻¹ • f z` on the open disc
`metric.ball c R`. -/
theorem hasSum_two_pi_I_cauchy_power_series_integral {f : ℂ → E} {c : ℂ} {R : ℝ} {w : ℂ}
    (hf : CircleIntegrable f c R) (hw : abs w < R) :
    HasSum (fun n : ℕ => ∮ z in C(c, R), (w / (z - c)) ^ n • (z - c)⁻¹ • f z)
      (∮ z in C(c, R), (z - (c + w))⁻¹ • f z) :=
  by
  have hR : 0 < R := (complex.abs.nonneg w).trans_lt hw
  have hwR : abs w / R ∈ Ico (0 : ℝ) 1 :=
    ⟨div_nonneg (complex.abs.nonneg w) hR.le, (div_lt_one hR).2 hw⟩
  refine'
    intervalIntegral.hasSum_integral_of_dominated_convergence
      (fun n θ => ‖f (circleMap c R θ)‖ * (abs w / R) ^ n) (fun n => _) (fun n => _) _ _ _
  · simp only [deriv_circleMap]
    apply_rules [ae_strongly_measurable.smul, hf.def.1] <;>
      · apply Measurable.aeStronglyMeasurable
        measurability
  · simp [norm_smul, abs_of_pos hR, mul_left_comm R, mul_inv_cancel_left₀ hR.ne', mul_comm ‖_‖]
  · exact eventually_of_forall fun _ _ => (summable_geometric_of_lt_1 hwR.1 hwR.2).mul_left _
  ·
    simpa only [tsum_mul_left, tsum_geometric_of_lt_1 hwR.1 hwR.2] using
      hf.norm.mul_continuous_on continuousOn_const
  · refine' eventually_of_forall fun θ hθ => HasSum.const_smul _ _
    simp only [smul_smul]
    refine' HasSum.smul_const _ _
    have : ‖w / (circleMap c R θ - c)‖ < 1 := by simpa [abs_of_pos hR] using hwR.2
    convert(hasSum_geometric_of_norm_lt_1 this).mul_right _
    simp [← sub_sub, ← mul_inv, sub_mul, div_mul_cancel _ (circleMap_ne_center hR.ne')]
#align has_sum_two_pi_I_cauchy_power_series_integral hasSum_two_pi_I_cauchy_power_series_integral

/-- For any circle integrable function `f`, the power series `cauchy_power_series f c R`, `R > 0`,
converges to the Cauchy integral `(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z` on the open
disc `metric.ball c R`. -/
theorem hasSum_cauchyPowerSeries_integral {f : ℂ → E} {c : ℂ} {R : ℝ} {w : ℂ}
    (hf : CircleIntegrable f c R) (hw : abs w < R) :
    HasSum (fun n => cauchyPowerSeries f c R n fun _ => w)
      ((2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - (c + w))⁻¹ • f z) :=
  by
  simp only [cauchyPowerSeries_apply]
  exact (hasSum_two_pi_I_cauchy_power_series_integral hf hw).const_smul _
#align has_sum_cauchy_power_series_integral hasSum_cauchyPowerSeries_integral

/-- For any circle integrable function `f`, the power series `cauchy_power_series f c R`, `R > 0`,
converges to the Cauchy integral `(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z` on the open
disc `metric.ball c R`. -/
theorem sum_cauchyPowerSeries_eq_integral {f : ℂ → E} {c : ℂ} {R : ℝ} {w : ℂ}
    (hf : CircleIntegrable f c R) (hw : abs w < R) :
    (cauchyPowerSeries f c R).Sum w = (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - (c + w))⁻¹ • f z :=
  (hasSum_cauchyPowerSeries_integral hf hw).tsum_eq
#align sum_cauchy_power_series_eq_integral sum_cauchyPowerSeries_eq_integral

/-- For any circle integrable function `f`, the power series `cauchy_power_series f c R`, `R > 0`,
converges to the Cauchy integral `(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z` on the open
disc `metric.ball c R`. -/
theorem hasFpowerSeriesOnCauchyIntegral {f : ℂ → E} {c : ℂ} {R : ℝ≥0} (hf : CircleIntegrable f c R)
    (hR : 0 < R) :
    HasFpowerSeriesOnBall (fun w => (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z)
      (cauchyPowerSeries f c R) c R :=
  { r_le := le_radius_cauchyPowerSeries _ _ _
    r_pos := ENNReal.coe_pos.2 hR
    HasSum := fun y hy => by
      refine' hasSum_cauchyPowerSeries_integral hf _
      rw [← norm_eq_abs, ← coe_nnnorm, NNReal.coe_lt_coe, ← ENNReal.coe_lt_coe]
      exact mem_emetric_ball_zero_iff.1 hy }
#align has_fpower_series_on_cauchy_integral hasFpowerSeriesOnCauchyIntegral

namespace circleIntegral

/-- Integral $\oint_{|z-c|=R} \frac{dz}{z-w}=2πi$ whenever $|w-c|<R$. -/
theorem integral_sub_inv_of_mem_ball {c w : ℂ} {R : ℝ} (hw : w ∈ ball c R) :
    (∮ z in C(c, R), (z - w)⁻¹) = 2 * π * I :=
  by
  have hR : 0 < R := dist_nonneg.trans_lt hw
  suffices H : HasSum (fun n : ℕ => ∮ z in C(c, R), ((w - c) / (z - c)) ^ n * (z - c)⁻¹) (2 * π * I)
  · have A : CircleIntegrable (fun _ => (1 : ℂ)) c R := continuous_on_const.circle_integrable'
    refine' (H.unique _).symm
    simpa only [smul_eq_mul, mul_one, add_sub_cancel'_right] using
      hasSum_two_pi_I_cauchy_power_series_integral A hw
  have H : ∀ n : ℕ, n ≠ 0 → (∮ z in C(c, R), (z - c) ^ (-n - 1 : ℤ)) = 0 :=
    by
    refine' fun n hn => integral_sub_zpow_of_ne _ _ _ _
    simpa
  have : (∮ z in C(c, R), ((w - c) / (z - c)) ^ 0 * (z - c)⁻¹) = 2 * π * I := by simp [hR.ne']
  refine' this ▸ hasSum_single _ fun n hn => _
  simp only [div_eq_mul_inv, mul_pow, integral_const_mul, mul_assoc]
  rw [(integral_congr hR.le fun z hz => _).trans (H n hn), MulZeroClass.mul_zero]
  rw [← pow_succ', ← zpow_ofNat, inv_zpow, ← zpow_neg, Int.ofNat_succ, neg_add,
    sub_eq_add_neg _ (1 : ℤ)]
#align circle_integral.integral_sub_inv_of_mem_ball circleIntegral.integral_sub_inv_of_mem_ball

end circleIntegral


import Rings.Rings
import Rings.Fields
import Rings.ToMathlib.list
import Rings.ToMathlib.nat
import algebra.big_operators.finprod

namespace AxGroth

noncomputable theory

universe u

open fol
open Rings

def monom_of_bd_monom (n d : ℕ) :
  (fin n → fin d) ↪ fin n → ℕ :=
⟨
  λ f, coe ∘ f ,
  λ f g h,
  begin
    funext i,
    have h' := congr_fun h i,
    apply fin.coe_injective h',
  end
⟩

/-- {{ f : fin n → ℕ | finsum f ≤ d }}-/
def n_var_monom_of_deg_le_d (n d : ℕ) : finset (fin n → ℕ) :=
finset.map (monom_of_bd_monom n d.succ) finset.univ

-- @[simp] def n_var_monom_of_deg_d (n d : ℕ) : finset (fin n → ℕ) :=
-- finset.filter (λ f, finsum f = d) $ n_var_monom_of_deg_le_d n d

#exit

/-- lists all n-variable monomials of degree d -/
@[simp] def n_var_monomials_of_deg : Π (n d : ℕ), list (fin n → ℕ)
| 0 d := [fin_zero_elim]
| 1 d := [λ k, d]
| (n + 2) 0 := [λ k, 0]
| (n + 2) (d + 1) :=
  list.join $ list.of_fn $
  λ i : fin (d + 2), list_image_append (i : ℕ)
  (n_var_monomials_of_deg (n + 1) (d + 1 - i))

lemma list.lt_length_join_of_lt_length_mem
  {A : Type*} {ls : list (list A)} (n : ℕ) (l : list A) (hmem : l ∈ ls) :
  n < l.length → n < ls.join.length :=
begin
  intro hl,
  induction ls with _ _ hind,
  {simpa using hmem},
  cases hmem,
  {
    rw ← hmem,
    simp only [list.join, list.length_append, list.length_join],
    apply nat.lt_add_right _ _ _ hl,
  },
  {
    simp only [list.join, list.length_append, list.length_join],
    apply nat.lt_add_left,
    simpa using hind hmem,
  }
end

#check list.join

lemma mem_n_var_monomials_of_deg : Π (n d : ℕ),
  fin n → ℕ
| 0 d := fin_zero_elim
| 1 d := λ k, d
| (n + 2) 0 := λ k, 0
| (n + 2) (d + 1) :=
  λ i : fin (d + 2), list_image_append (i : ℕ)
  (n_var_monomials_of_deg (n + 1) (d + 1 - i))
 -- list.join $ list.of_fn $
 --  λ i : fin (d + 2), list_image_append i
 --  (n_var_monomials_of_deg (n + 1) (d + 1 - i))

-- lemma zero_lt_n_var_monomials_of_deg_length : Π (n d : ℕ),
--   0 < (n_var_monomials_of_deg n d).length
-- | 0 d := by simp
-- | 1 d := by simp
-- | (n + 2) 0 := by simp
-- | (n + 2) (d + 1) :=
-- begin
--   simp only [n_var_monomials_of_deg],
--   apply list.lt_length_join_of_lt_length_mem 0,

-- end

/-- lists all n-variable monomials of degree ≤ d -/
@[simp] def n_var_monomials_of_deg_le (n d : ℕ) : list (fin n → ℕ) :=
nat.natlist d.succ (λ d', n_var_monomials_of_deg n d')

-- counts all n-variable monomials of degree ≤ d
@[simp] def n_var_monomials_of_deg_le_length (n d : ℕ) : ℕ :=
list.length $ n_var_monomials_of_deg_le n d

def finsupp_to_nat_of_fin_to_nat {n : ℕ} (p : fin n → ℕ) :
  fin n →₀ ℕ :=
⟨ {k ∈ finset.fin_range n | p k ≠ 0 } , p ,
begin
  intro a,
  split,
  {intro ha, simpa using ha},
  {intro ha, simpa using ha}
end ⟩

/-- lists all n-variable monomials of degree ≤ d as finsupp maps-/
@[simp] def n_var_monomials_of_deg_le₀ (n d : ℕ) : list (fin n →₀ ℕ) :=
list.map (finsupp_to_nat_of_fin_to_nat) $ n_var_monomials_of_deg_le n d

-- no restriction on degree
/-- takes a polynomial in n.succ variables and gives a list of its coefficients-/
@[simp] def coeffs_list_of_mv_polynomial
  {K : Type} [comm_semiring K] {n : ℕ} (d : ℕ)
  (p : mv_polynomial (fin n) K) : list K :=
list.map (λ m, mv_polynomial.coeff m p) (n_var_monomials_of_deg_le₀ n d)

section prop_decidable

local attribute [instance] classical.prop_decidable

lemma zero_le_n_var_monomials_of_deg_le_length (n d : ℕ) :
  0 < n_var_monomials_of_deg_le_length n d :=
begin
  simp only [list.length_append, n_var_monomials_of_deg_le,
    nat.natlist, n_var_monomials_of_deg_le_length],
  apply nat.lt_add_right,
  sorry,
end

-- namespace list

-- def fin_index_of {α : Type u} (a : α) :
--   Π (l : list α), (a ∈ l) → fin (list.length l)
-- | [] h :=
-- begin
--   exfalso,
--   rw ← list.mem_nil_iff a,
--   apply h,
-- end
-- | (hd :: l) h := ⟨ list.index_of a (hd :: l) ,
-- begin rw list.index_of_lt_length, apply h end ⟩

-- end list

#exit

lemma poly_indexed_by_monos_aux (n d s c : ℕ)
  (hndc : (n_var_monomials_of_deg_le n d).length + s < c) (f : fin n → ℕ) :
  list.index_of f (n_var_monomials_of_deg_le n d) + s < c :=
begin
  apply nat.lt_of_le_of_lt _ hndc,
  apply nat.add_le_add_right list.index_of_le_length,
end

lemma fin_add_lt_of_add_le (n p c : ℕ) (hnpc : n + p ≤ c) (i : fin n) : (i : ℕ) + p < c :=
begin
  apply nat.lt_of_lt_of_le _ hnpc,
  apply nat.add_lt_add_right i.2,
end

-- ∑_{f ∈ n_var_monomials_of_deg n d} xⱼ₊ₛ ∏ {0 ≤ i < n} xᵢ₊ₚᶠ⁽ⁱ⁾ in "context c"
-- where j is the index of f in n_var_monomials_of_deg n d
-- the context should at least include the variables xⱼ₊ₛ -- this is hndc
-- the context should at least include the variables xᵢ₊ₚ -- this is hpc
@[simp] def poly_indexed_by_monos (n d s p c : ℕ)
  (hndc : (n_var_monomials_of_deg_le n d).length + s < c)
  (hnpc : n + p ≤ c) :
  bounded_ring_term c :=
-- sum indexed by the n-variable monomials of degree < d
list.sumr
(list.mapr
  (λ f : (fin n → ℕ),
    let
      x_js : bounded_ring_term c :=
      x_ ⟨(list.index_of f (n_var_monomials_of_deg_le n d) + s) ,
      poly_indexed_by_monos_aux n d s c hndc f ⟩,
      x_ip (i : fin n) : bounded_ring_term c :=
      x_ (⟨ (i : ℕ) + p , fin_add_lt_of_add_le n p c hnpc i ⟩)
    in
    x_js * (nat.prod n $ λ i, (x_ip i) ^ (f i) )
    )
  (n_var_monomials_of_deg_le n d)
)

#exit
-- -- ∑ {f ∈ n_var_monomials_of_deg n.succ d} x₍ⱼ₊ₛ₎ ∏ {0 ≤ i < n} x₍ᵢ₊ₚ₎ᶠ⁽ⁱ⁾ in "context c"
-- -- where j is the index of f in n_var_monomials_of_deg n.succ d
-- @[simp] def poly_indexed_by_monos (n d s p c : ℕ) (h : 0 < c) :
--   bounded_ring_term c :=
-- begin
--   rw ← nat.succ_pred_eq_of_pos h,
--   apply poly_indexed_by_monos' n d s p,
-- end

-- NOTE s = 2 * n.succ
-- NOTE c = n.succ * n_var_monomials_of_deg_lt n.succ d + 2 * n.succ

end prop_decidable

lemma inj_formula_aux {n d : ℕ} :
  0 < n.succ * n_var_monomials_of_deg_lt_length n d + n.succ + n.succ :=
begin
  apply nat.lt_add_left 0 (n.succ) _ ,
  simp,
end

-- in the context of having n.succ polynomials pⱼ indexed by
-- their monomial coefficients,
-- if for all xᵢ and all yᵢ, every polynomial satisfies pⱼ xᵢ = pⱼ yᵢ
-- then each xᵢ = yᵢ.
-- This says the polynomial map formed by the pⱼs is injective
def inj_formula (n d : ℕ) :
  bounded_ring_formula (n.succ * (n_var_monomials_of_deg_lt_length n d)) :=
let c := n.succ * (n_var_monomials_of_deg_lt_length n d) + 2 * n.succ,
    monom := n_var_monomials_of_deg_lt_length n d in
-- for all pairs in the domain x₋ ∈ Kⁿ⁺¹ and ...
bd_alls' n.succ _
$
-- ... y₋ ∈ Kⁿ⁺¹
bd_alls' n.succ _
$
-- if at each pⱼ
(bd_big_and n.succ
-- pⱼ xᵢ = pⱼ yᵢ
  (λ j,
    (poly_indexed_by_monos n.succ d (2 * n.succ + j * monom) 0 _)
    ≃
    (poly_indexed_by_monos n.succ d (2 * n.succ + j * monom) (n.succ) _)
  )
)
-- then
⟹
-- at each 0 ≤ i < n.succ,
(bd_big_and n.succ $ λ i,
-- xᵢ = yᵢ (where yᵢ is written as xᵢ₊ₙ₊₁)
  x_ i ≃ x_ (i + n.succ)
)

def surj_formula (n d : ℕ) :
  bounded_ring_formula (n.succ * n_var_monomials_of_deg_lt_length n d) :=
let monom := n_var_monomials_of_deg_lt_length n d in
-- for all z₋ ∈ Kⁿ⁺¹ in the codomain
bd_alls' n.succ _
$
-- there exists x₋ ∈ Kⁿ⁺¹ in the domain such that
bd_exs' n.succ _
$
-- at each 0 ≤ j < n.succ
bd_big_and n.succ
$
-- zⱼ = pⱼ x₋
λ j, x_ j ≃ poly_indexed_by_monos n.succ d (n.succ + n.succ + j * monom) 0 _

def Ax_Groth_formula (n d : ℕ) : sentence ring_signature :=
-- quantify over (n.succ) many (n.succ-variable polynomials) called ps;
-- i.e. the data of a polynomial map
-- by quantifying over (n.succ * monomials_of_bounded_degree) monomial coefficients
bd_alls (n.succ * (n_var_monomials_of_deg_lt_length n d))
-- if the polynomial function is injective then it is surjective
$ inj_formula n d ⟹ surj_formula n d

@[simp] def poly_map_data (K : Type) [comm_semiring K] (n : ℕ) : Type :=
fin n → mv_polynomial (fin n) K

def poly_map {K : Type} [comm_semiring K] {n : ℕ} :
  poly_map_data K n → (fin n → K) → (fin n → K) :=
λ ps as k, mv_polynomial.eval as (ps k)

lemma realize_Ax_Groth_formula {K : Type} [field K] [is_alg_closed K]
  (h0 : char_zero K) (n d : ℕ) : struc_to_ring_struc.Structure K ⊨ Ax_Groth_formula n d := sorry

------------
def dvector.of_list {A : Type} : Π (as : list A), dvector A (list.length as)
| list.nil := []
| (list.cons a as) := dvector.cons a (dvector.of_list as)

-- #check list.length_map

-- def coeffs_dvector_of_mv_polynomial {K : Type} [comm_semiring K] {n d : ℕ}
--   (p : mv_polynomial (fin n.succ) K) (hdeg : mv_polynomial.total_degree p < d) :
--   dvector K (list.length (coeffs_list_of_mv_polynomial d p)) :=
-- dvector.of_list (coeffs_list_of_mv_polynomial d p)
-- ------------

-- #check @list.of_fn
-- #eval list.join (list.cons (list.cons 0 list.nil) (list.cons (list.cons 1 list.nil) list.nil))

section semiring

variables {A : Type} [comm_semiring A] {n : ℕ}

-- takes a polynomial map (preferably of total deg < d) and gives list of coeffs of each polynomial
@[simp] def poly_map_data.coeffs_list
  {n : ℕ} (d : ℕ) (ps : poly_map_data A n.succ) : list A :=
list.join (list.of_fn (λ i : fin n.succ, coeffs_list_of_mv_polynomial d (ps i)))

/- Writes polynomial map into a dvector of its coefficients -/
def poly_map_data.coeffs_dvector {n : ℕ} (d : ℕ) (ps : poly_map_data A n.succ) :
  dvector A (poly_map_data.coeffs_list d ps).length :=
dvector.of_list (poly_map_data.coeffs_list d ps)

lemma list.map_length_of_fn_const {α} {m : ℕ} (f : fin n → list α)
  (h : ∀ i : fin n, (f i).length = m) :
  (list.map list.length (list.of_fn f)).sum = n * m :=
begin
 rw list.map_of_fn,
 rw list.sum_of_fn,
 have h' : (λ (i : fin n), (list.length ∘ f) i) = λ (i : fin n), m,
 {funext, exact h i},
 rw h',
 simp,
end

lemma coeffs_list_of_mv_polynomial_const {d : ℕ}
  (ps : poly_map_data A n.succ) (i : fin n.succ) :
  (coeffs_list_of_mv_polynomial d (ps i)).length = n_var_monomials_of_deg_lt_length n d :=
by simp

lemma variable_bound_equal {n : ℕ} (d : ℕ) (ps : poly_map_data A n.succ) :
  (n.succ * n_var_monomials_of_deg_lt_length n d) = (poly_map_data.coeffs_list d ps).length :=
begin
  simp only [poly_map_data.coeffs_list, list.length_join],
  rw list.map_length_of_fn_const _ (coeffs_list_of_mv_polynomial_const ps),
end

/- Writes polynomial map into a dvector of its coefficients (with a better variable context) -/
lemma poly_map_data.coeffs_dvector' {n : ℕ} (d : ℕ)
  (ps : poly_map_data A n.succ) :
  dvector A (n.succ * n_var_monomials_of_deg_lt_length n d) :=
begin
  have xs0 := poly_map_data.coeffs_dvector d ps,
  rw ← variable_bound_equal d ps at xs0,
  exact xs0,
end

open mv_polynomial

-- (X₀ + 1 , X₁ + 1)
-- def example_poly_map : poly_map_data ℤ 2 := λ k, X k + 1

end semiring

-- -- move to ToMathlib
-- def bd_pis {A : Type u} : Π (n : ℕ) (P : (fin n → A) → Prop), Prop
-- | nat.zero P := P fin_zero_elim
-- | (nat.succ n) P := Π (a : A), bd_pis n $ λ as, P $ fin.cons a as

-- lemma realize_bounded_formula_bd_big_and' {L} {S : Structure L} {n m : ℕ}
--   {v : dvector S n} (f : fin m.succ → bounded_formula L n) :
--   realize_bounded_formula v (bd_big_and m.succ f) dvector.nil
--   ↔
--   (realize_bounded_formula v (bd_big_and m (λ k, f k)) dvector.nil
--   ∧
--   realize_bounded_formula v (f m) dvector.nil) :=
-- begin
--   simp only [bd_big_and, realize_bounded_formula_and],
-- end

@[simp] lemma realize_bounded_formula_bd_big_and {L} {S : Structure L} {n : ℕ}
  {v : dvector S n} : Π {m : ℕ} (f : fin m → bounded_formula L n),
  realize_bounded_formula v (bd_big_and m f) dvector.nil
  ↔
  (Π k : fin m, realize_bounded_formula v (f k) dvector.nil)
| nat.zero f :=
begin
  simp only [bd_big_and, realize_bounded_formula_not,
    realize_bounded_formula, true_iff, not_false_iff],
  exact fin_zero_elim,
end
| (nat.succ m) f :=
begin
  simp only [bd_big_and, realize_bounded_formula_and],
  split,
  {
    intros hf k,
    rw realize_bounded_formula_bd_big_and at hf,
    cases fin.lt_or_eq_fin k with hk,
    -- by_cases hk : (k : ℕ) < m,
    {
      have h :=  hf.1 ⟨ k , _ ⟩,
      {simpa using h},
      {
        rw fin.lt_coe_iff_val_lt,
        exact hk,
        exact lt_add_one m,
      },
    },
    {rw h, exact hf.2}
  },
  {
    intro hf,
    split,
    {
      rw realize_bounded_formula_bd_big_and,
      intro k,
      apply hf k,
    },
    {exact hf m}
  },
end

-- #check list.sum

@[simp] lemma realize_bounded_term_list_sumr
  {A : Structure ring_signature} {c : ℕ} {xs : dvector A c} :
  Π {l : list (bounded_ring_term c)},
  realize_bounded_term xs (list.sumr l) dvector.nil
  =
  list.sumr (list.mapr (λ t, realize_bounded_term xs t dvector.nil) l)
| list.nil := by simp
| (list.cons t ts) :=
begin
  simp only [list.mapr, models_ring_theory_to_comm_ring.realize_add,
    ring_signature.add, list.sumr, realize_bounded_term],
  rw realize_bounded_term_list_sumr,
end

-- list.sumr
-- (list.mapr
--   (λ f : (fin n.succ → ℕ),
--     (x_ (list.index_of f (n_var_monomials_of_deg_lt n d) + s))
--     *
--     (nat.prod n.succ $ λ i, (x_ (i + p)) ^ (f i) )
--     )
--   (n_var_monomials_of_deg_lt n d)
-- )

def realize_add_zero_hom {A : Type*} [comm_ring A]
  {c : ℕ} (xs : dvector (struc_to_ring_struc.Structure A) c):
  add_zero_hom (bounded_ring_term c) A :=
⟨ λ t, realize_bounded_term xs t dvector.nil ,
  models_ring_theory_to_comm_ring.realize_zero ,
  λ t s, models_ring_theory_to_comm_ring.realize_add ⟩

lemma realize_sumr {A : Type*} [comm_ring A]
  {c : ℕ} (xs : dvector (struc_to_ring_struc.Structure A) c)
  {ts : list (bounded_ring_term c)} :
  realize_bounded_term xs (ts).sumr dvector.nil
  =
  (list.mapr (realize_add_zero_hom xs).to_fun ts).sumr :=
begin
  rw ← list.mapr_sumr (realize_add_zero_hom xs) ts,
  refl,
end

lemma realize_nat_prod {A : Type*} [comm_ring A]
  {c : ℕ} (xs : dvector (struc_to_ring_struc.Structure A) c)
  (ts : ℕ → bounded_ring_term c) : Π n,
  realize_bounded_term xs (nat.prod n ts) dvector.nil
  =
  nat.prod n (λ n, realize_bounded_term xs (ts n) dvector.nil)
| nat.zero :=
begin
  simp only [nat.prod],
  refl,
end
| (nat.succ n) :=
begin
  simp only [nat.prod, struc_to_ring_struc.func_map, dvector.last, struc_to_ring_struc.binaries_map, realize_bounded_term,
  ring_signature.mul, dvector.nth],
  rw realize_nat_prod n,
end

lemma realize_pow {A : Type*} [comm_ring A]
  {c : ℕ} (xs : dvector (struc_to_ring_struc.Structure A) c)
  (t : bounded_ring_term c) : Π (n : ℕ),
  realize_bounded_term xs (t ^ n) dvector.nil
  =
  (realize_bounded_term xs t dvector.nil) ^ n
| nat.zero := by
  simpa only [nat.nat_zero_eq_zero, ring_signature.pow_zero,
    realize_bounded_term, models_ring_theory_to_comm_ring.realize_one,
    ring_signature.one, models_ring_theory_to_comm_ring.realize_one]
| (nat.succ n) :=
begin
  simp only [struc_to_ring_struc.func_map, dvector.last, ring_signature.pow_succ, struc_to_ring_struc.binaries_map,
  realize_bounded_term, ring_signature.mul, dvector.nth],
  rw realize_pow n,
  refl,
end

lemma realize_poly_indexed_by_monos
  {A : Type*} [comm_ring A] {n d s p c : ℕ}
  (hcf : Π (f : fin n.succ → ℕ), list.index_of f (n_var_monomials_of_deg_lt n d) + s < c.succ)
  (hci : Π (i : ℕ), i + p < c.succ)
  {xs : dvector (struc_to_ring_struc.Structure A) c.succ}  :
  realize_bounded_term xs (poly_indexed_by_monos n d s p c) dvector.nil
  =
  list.sumr
  (list.mapr
    (λ f,
    (dvector.nth xs (list.index_of f (n_var_monomials_of_deg_lt n d) + s) (hcf f))
    *
    (nat.prod n.succ $ λ i, ((dvector.nth xs (i + p) (hci i)) ^ (f i) ))
    )
  (n_var_monomials_of_deg_lt n d)
  ) :=
begin
  simp only [poly_indexed_by_monos],
  rw realize_sumr,
  rw ← list.comp_mapr,
  congr,
  funext f,
  simp only [realize_add_zero_hom, function.comp_app],
  simp only [struc_to_ring_struc.func_map, fin.val_eq_coe, dvector.last,
    struc_to_ring_struc.binaries_map, realize_bounded_term,
    ring_signature.mul, dvector.nth],
  congr,
  {
    norm_cast,
    simp only [fin.val_eq_coe, fin.coe_of_nat_eq_mod],
    rw ← nat.mod_eq_of_lt (hcf f),
    congr,
  },
  {
    rw realize_nat_prod,
    congr,
    funext i,
    rw realize_pow,
    simp,
    norm_cast,
    congr,
    simp only [fin.val_eq_coe, fin.coe_of_nat_eq_mod],
    rw nat.mod_eq_of_lt (hci i),
  },
end

lemma realize_poly_map_data_coeffs_xs
  {A : Type*} [comm_ring A] {n d : ℕ}
  (ps : poly_map_data A n.succ)
  (hdeg : ∀ (i : fin n.succ), (ps i).total_degree < d)
  (xs ys : dvector ↥(struc_to_ring_struc.Structure A) n.succ)
  (i : fin n.succ)
  :
  realize_bounded_term (ys.append (xs.append (poly_map_data.coeffs_dvector' d ps)))
      (poly_indexed_by_monos n.succ d
        (2 * n.succ + ↑i * (d.natlist (n_var_monomials_of_deg n.succ)).length)
        0
        (n.succ * n_var_monomials_of_deg_lt_length n d + n.succ + n))
      dvector.nil
  =
  mv_polynomial.eval (λ (i : fin n.succ), xs.nth i i.2) (ps i) :=
begin
  rw realize_poly_indexed_by_monos,
  { sorry },
  {
    intro f, -- f is a monomial
    simp only [n_var_monomials_of_deg_lt],
    sorry,
  },
  {
    sorry
  },
end

lemma Ax_Groth_inj_aux {K : Type} [field K] [is_alg_closed K]
  (h0 : char_zero K)
  {n d : ℕ}
  (ps : poly_map_data K n.succ)
  (hdeg : ∀ (i : fin n.succ), (ps i).total_degree < d)
  (hinj : function.injective (poly_map ps))
  : @realize_bounded_formula _ (struc_to_ring_struc.Structure K)
    _ _ (@poly_map_data.coeffs_dvector' K _ n d ps)
    (inj_formula n d) dvector.nil :=
begin
  let xs0 := poly_map_data.coeffs_dvector' d ps,
  -- open up the definition of inj_formula
  simp only [inj_formula],
  rw realize_bounded_formula_bd_alls',
  intro xs, -- an n tuple in the domain
  rw realize_bounded_formula_bd_alls',
  intro ys, -- an n tuple in the domain
  simp only [realize_bounded_formula_imp],
  -- we are showing that ps xs = ps yx implies xs = ys
  -- assume the images are all equal (expressed model theoretically)
  intro hImage,
  rw realize_bounded_formula_bd_big_and,
  rw realize_bounded_formula_bd_big_and at hImage,
  -- translate this to the images are equal (expressed algebraically / in the ring)
  have himage : (poly_map ps (λ i, dvector.nth xs i i.2)) = poly_map ps (λ i, dvector.nth ys i i.2),
  {
    funext i, -- for each i < n (... the tuples at i are equal)
    simp only [poly_map],
    have hImagei := hImage i,
    simp only [n_var_monomials_of_deg_lt_length, realize_bounded_formula,
      n_var_monomials_of_deg_lt] at hImagei,
    convert hImagei,
    sorry,
    sorry,
  },
  intro k, -- for each input (... they are equal)
  -- ... they are equal
  {sorry},
end

-- realize_bounded_term (ys.append (xs.append xs0))
      -- (poly_indexed_by_monos n.succ d (2 * n.succ + ↑i * (nat.natlist d (n_var_monomials_of_deg n.succ)).length) 0
      --    (n.succ * n_var_monomials_of_deg_lt_length n d + n.succ + n.succ)
      --    inj_formula_aux)
      -- dvector.nil
      --
-- ⇑(mv_polynomial.eval (λ (i : fin n.succ), xs.nth ↑i _)) (ps i)

lemma Ax_Groth_aux {K : Type} [field K] [is_alg_closed K]
  (h0 : char_zero K) {n d : ℕ}
  (ps : poly_map_data K n.succ) (hdeg : ∀ (i : fin n.succ), mv_polynomial.total_degree (ps i) < d)
  (hinj : function.injective (poly_map ps)) :
  function.surjective (poly_map ps) :=
begin
  let xs0 := poly_map_data.coeffs_dvector' d ps,
  have hAG := realize_Ax_Groth_formula h0 n d,
  simp only [realize_sentence_bd_alls,
    Ax_Groth_formula, realize_bounded_formula] at hAG,
  -- injective -> realize inj_formula
  have hInj : @realize_bounded_formula _ (struc_to_ring_struc.Structure K) _ _
    (poly_map_data.coeffs_dvector' d ps) (inj_formula n d) dvector.nil,
  {exact Ax_Groth_inj_aux h0 ps hdeg hinj},
  -- apply realize_Ax_Groth to ps, i.e. apply hAG to its coefficients
  have hSurj := hAG xs0 hInj,
  sorry,
end

@[simp] def max_total_deg {K : Type} [comm_semiring K] :
  Π {n m : ℕ}, (fin n → mv_polynomial (fin m) K) → ℕ
| 0 _ ps := 0
| (n + 1) _ ps := max (max_total_deg (λ (i : fin n), ps i)) (mv_polynomial.total_degree (ps n))

def total_deg_le_max_total_deg {K : Type} [comm_semiring K] :
  Π {n m : ℕ} (ps : fin n → mv_polynomial (fin m) K) (i : fin n),
  mv_polynomial.total_degree (ps i) < max_total_deg ps + 1
| 0 _ ps i := fin_zero_elim i
| (n + 1) _ ps i :=
begin
  rw nat.lt_add_one_iff,
  simp only [max_total_deg],
  rw le_max_iff,
  cases fin.lt_or_eq_fin i with h,
  {
    left,
    have hlt : (i : ℕ) < n,
    {
      rw fin.lt_coe_iff_val_lt i (nat.lt_succ_self _),
      exact h,
    },
    have hind :=
    nat.le_of_lt_succ (@total_deg_le_max_total_deg n _ (λ j, ps j.val.cast) ⟨ i , hlt ⟩),
    simp only [fin.coe_eq_cast_succ, fin.cast_succ_mk, fin.eta] at hind,
    rw ← fin.coe_coe_eq_self i,
    exact hind,
  },
  {
    right,
    rw h,
  },
end

theorem Ax_Groth {K : Type} [field K] [is_alg_closed K]
  (h0 : char_zero K) {n : ℕ}
  (ps : poly_map_data K n.succ) (hinj : function.injective (poly_map ps)) :
  function.surjective (poly_map ps) :=
Ax_Groth_aux h0 ps (total_deg_le_max_total_deg ps) hinj

end AxGroth


/-
Copyright (c) 2022 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Patrick Massot
-/

import topology.separation

/-!
# The topological support of a function

In this file we define the topological support of a function `f`, `tsupport f`,
as the closure of the support of `f`.

Furthermore, we say that `f` has compact support if the topological support of `f` is compact.

## Main definitions

* `function.mul_tsupport` & `function.tsupport`
* `function.has_compact_mul_support` & `function.has_compact_support`

## Implementation Notes

* We write all lemmas for multiplicative functions, and use `@[to_additive]` to get the more common
  additive versions.
* We do not put the definitions in the `function` namespace, following many other topological
  definitions that are in the root namespace (compare `embedding` vs `function.embedding`).
-/

open function set filter
open_locale topological_space

variables {X α α' β γ δ M E R : Type*}

section one
variables [has_one α]
variables [topological_space X]

/-- The topological support of a function is the closure of its support, i.e. the closure of the
  set of all elements where the function is not equal to 1. -/
@[to_additive
/-" The topological support of a function is the closure of its support. i.e. the closure of the
  set of all elements where the function is nonzero. "-/]
def mul_tsupport (f : X → α) : set X := closure (mul_support f)

@[to_additive]
lemma subset_mul_tsupport (f : X → α) : mul_support f ⊆ mul_tsupport f :=
subset_closure

@[to_additive]
lemma is_closed_mul_tsupport (f : X → α) : is_closed (mul_tsupport f) :=
is_closed_closure

@[to_additive]
lemma mul_tsupport_eq_empty_iff {f : X → α} : mul_tsupport f = ∅ ↔ f = 1 :=
by rw [mul_tsupport, closure_empty_iff, mul_support_eq_empty_iff]

@[to_additive]
lemma image_eq_zero_of_nmem_mul_tsupport {f : X → α} {x : X} (hx : x ∉ mul_tsupport f) : f x = 1 :=
mul_support_subset_iff'.mp (subset_mul_tsupport f) x hx

end one

section

variables [topological_space α] [topological_space α']
variables [has_one β] [has_one γ] [has_one δ]
variables {g : β → γ} {f : α → β} {f₂ : α → γ} {m : β → γ → δ} {x : α}

@[to_additive]
lemma not_mem_closure_mul_support_iff_eventually_eq : x ∉ mul_tsupport f ↔ f =ᶠ[𝓝 x] 1 :=
by simp_rw [mul_tsupport, mem_closure_iff_nhds, not_forall, not_nonempty_iff_eq_empty,
    ← disjoint_iff_inter_eq_empty, disjoint_mul_support_iff, eventually_eq_iff_exists_mem]

/-- A function `f` *has compact multiplicative support* or is *compactly supported* if the closure
of the multiplicative support of `f` is compact. In a T₂ space this is equivalent to `f` being equal
to `1` outside a compact set. -/
@[to_additive
/-" A function `f` *has compact support* or is *compactly supported* if the closure of the support
of `f` is compact. In a T₂ space this is equivalent to `f` being equal to `0` outside a compact
set. "-/]
def has_compact_mul_support (f : α → β) : Prop :=
is_compact (mul_tsupport f)

@[to_additive]
lemma has_compact_mul_support_def :
  has_compact_mul_support f ↔ is_compact (closure (mul_support f)) :=
by refl

@[to_additive]
lemma exists_compact_iff_has_compact_mul_support [t2_space α] :
  (∃ K : set α, is_compact K ∧ ∀ x ∉ K, f x = 1) ↔ has_compact_mul_support f :=
by simp_rw [← nmem_mul_support, ← mem_compl_iff, ← subset_def, compl_subset_compl,
    has_compact_mul_support_def, exists_compact_superset_iff]

@[to_additive]
lemma has_compact_mul_support.intro [t2_space α] {K : set α}
  (hK : is_compact K) (hfK : ∀ x ∉ K, f x = 1) : has_compact_mul_support f :=
exists_compact_iff_has_compact_mul_support.mp ⟨K, hK, hfK⟩

@[to_additive]
lemma has_compact_mul_support.is_compact (hf : has_compact_mul_support f) :
  is_compact (mul_tsupport f) :=
hf

@[to_additive]
lemma has_compact_mul_support_iff_eventually_eq :
  has_compact_mul_support f ↔ f =ᶠ[coclosed_compact α] 1 :=
⟨ λ h, mem_coclosed_compact.mpr ⟨mul_tsupport f, is_closed_mul_tsupport _, h,
    λ x, not_imp_comm.mpr $ λ hx, subset_mul_tsupport f hx⟩,
  λ h, let ⟨C, hC⟩ := mem_coclosed_compact'.mp h in
    compact_of_is_closed_subset hC.2.1 (is_closed_mul_tsupport _) (closure_minimal hC.2.2 hC.1)⟩

@[to_additive]
lemma has_compact_mul_support.mono' {f' : α → γ} (hf : has_compact_mul_support f)
  (hff' : mul_support f' ⊆ mul_tsupport f) : has_compact_mul_support f' :=
compact_of_is_closed_subset hf is_closed_closure $ closure_minimal hff' is_closed_closure

@[to_additive]
lemma has_compact_mul_support.mono {f' : α → γ} (hf : has_compact_mul_support f)
  (hff' : mul_support f' ⊆ mul_support f) : has_compact_mul_support f' :=
hf.mono' $ hff'.trans subset_closure

@[to_additive]
lemma has_compact_mul_support.comp_left (hf : has_compact_mul_support f) (hg : g 1 = 1) :
  has_compact_mul_support (g ∘ f) :=
hf.mono $ mul_support_comp_subset hg f

@[to_additive]
lemma has_compact_mul_support_comp_left (hg : ∀ {x}, g x = 1 ↔ x = 1) :
  has_compact_mul_support (g ∘ f) ↔ has_compact_mul_support f :=
by simp_rw [has_compact_mul_support_def, mul_support_comp_eq g @hg f]

@[to_additive]
lemma has_compact_mul_support.comp_closed_embedding (hf : has_compact_mul_support f)
  {g : α' → α} (hg : closed_embedding g) : has_compact_mul_support (f ∘ g) :=
begin
  rw [has_compact_mul_support_def, function.mul_support_comp_eq_preimage],
  refine compact_of_is_closed_subset (hg.is_compact_preimage hf) is_closed_closure _,
  rw [hg.to_embedding.closure_eq_preimage_closure_image],
  exact preimage_mono (closure_mono $ image_preimage_subset _ _)
end

@[to_additive]
lemma has_compact_mul_support.comp₂_left (hf : has_compact_mul_support f)
  (hf₂ : has_compact_mul_support f₂) (hm : m 1 1 = 1) :
  has_compact_mul_support (λ x, m (f x) (f₂ x)) :=
begin
  rw [has_compact_mul_support_iff_eventually_eq] at hf hf₂ ⊢,
  filter_upwards [hf, hf₂] using λ x hx hx₂, by simp_rw [hx, hx₂, pi.one_apply, hm]
end

end

section monoid

variables [topological_space α] [monoid β]
variables {f f' : α → β} {x : α}

@[to_additive]
lemma has_compact_mul_support.mul (hf : has_compact_mul_support f)
  (hf' : has_compact_mul_support f') : has_compact_mul_support (f * f') :=
by apply hf.comp₂_left hf' (mul_one 1) -- `by apply` speeds up elaboration

end monoid

section distrib_mul_action

variables [topological_space α] [monoid_with_zero R] [add_monoid M] [distrib_mul_action R M]
variables {f : α → R} {f' : α → M} {x : α}

lemma has_compact_support.smul_left (hf : has_compact_support f') : has_compact_support (f • f') :=
begin
  rw [has_compact_support_iff_eventually_eq] at hf ⊢,
  refine hf.mono (λ x hx, by simp_rw [pi.smul_apply', hx, pi.zero_apply, smul_zero])
end

end distrib_mul_action

section smul_with_zero

variables [topological_space α] [has_zero R] [has_zero M] [smul_with_zero R M]
variables {f : α → R} {f' : α → M} {x : α}

lemma has_compact_support.smul_right (hf : has_compact_support f) : has_compact_support (f • f') :=
begin
  rw [has_compact_support_iff_eventually_eq] at hf ⊢,
  refine hf.mono (λ x hx, by simp_rw [pi.smul_apply', hx, pi.zero_apply, zero_smul])
end

lemma has_compact_support.smul_left' (hf : has_compact_support f') : has_compact_support (f • f') :=
begin
  rw [has_compact_support_iff_eventually_eq] at hf ⊢,
  refine hf.mono (λ x hx, by simp_rw [pi.smul_apply', hx, pi.zero_apply, smul_zero'])
end

end smul_with_zero

section mul_zero_class

variables [topological_space α] [mul_zero_class β]
variables {f f' : α → β} {x : α}

lemma has_compact_support.mul_right (hf : has_compact_support f) : has_compact_support (f * f') :=
begin
  rw [has_compact_support_iff_eventually_eq] at hf ⊢,
  refine hf.mono (λ x hx, by simp_rw [pi.mul_apply, hx, pi.zero_apply, zero_mul])
end

lemma has_compact_support.mul_left (hf : has_compact_support f') : has_compact_support (f * f') :=
begin
  rw [has_compact_support_iff_eventually_eq] at hf ⊢,
  refine hf.mono (λ x hx, by simp_rw [pi.mul_apply, hx, pi.zero_apply, mul_zero])
end

end mul_zero_class

namespace locally_finite

variables {ι : Type*} {U : ι → set X} [topological_space X] [has_one R]

/-- If a family of functions `f` has locally-finite multiplicative support, subordinate to a family
of open sets, then for any point we can find a neighbourhood on which only finitely-many members of
`f` are not equal to 1. -/
@[to_additive
/-" If a family of functions `f` has locally-finite support, subordinate to a family of open sets,
then for any point we can find a neighbourhood on which only finitely-many members of `f` are
non-zero. "-/]
lemma exists_finset_nhd_mul_support_subset
  {f : ι → X → R} (hlf : locally_finite (λ i, mul_support (f i)))
  (hso : ∀ i, mul_tsupport (f i) ⊆ U i) (ho : ∀ i, is_open (U i)) (x : X) :
  ∃ (is : finset ι) {n : set X} (hn₁ : n ∈ 𝓝 x) (hn₂ : n ⊆ ⋂ i ∈ is, U i), ∀ (z ∈ n),
    mul_support (λ i, f i z) ⊆ is :=
begin
  obtain ⟨n, hn, hnf⟩ := hlf x,
  classical,
  let is := hnf.to_finset.filter (λ i, x ∈ U i),
  let js := hnf.to_finset.filter (λ j, x ∉ U j),
  refine ⟨is, n ∩ (⋂ j ∈ js, (mul_tsupport (f j))ᶜ) ∩ (⋂ i ∈ is, U i),
    inter_mem (inter_mem hn _) _, inter_subset_right _ _, λ z hz, _⟩,
  { exact (bInter_finset_mem js).mpr (λ j hj, is_closed.compl_mem_nhds
      (is_closed_mul_tsupport _) (set.not_mem_subset (hso j) (finset.mem_filter.mp hj).2)), },
  { exact (bInter_finset_mem is).mpr (λ i hi, (ho i).mem_nhds (finset.mem_filter.mp hi).2) },
  { have hzn : z ∈ n,
    { rw inter_assoc at hz,
      exact mem_of_mem_inter_left hz, },
    replace hz := mem_of_mem_inter_right (mem_of_mem_inter_left hz),
    simp only [finset.mem_filter, finite.mem_to_finset, mem_set_of_eq, mem_Inter, and_imp] at hz,
    suffices : mul_support (λ i, f i z) ⊆ hnf.to_finset,
    { refine hnf.to_finset.subset_coe_filter_of_subset_forall _ this (λ i hi, _),
      specialize hz i ⟨z, ⟨hi, hzn⟩⟩,
      contrapose hz,
      simp [hz, subset_mul_tsupport (f i) hi], },
    intros i hi,
    simp only [finite.coe_to_finset, mem_set_of_eq],
    exact ⟨z, ⟨hi, hzn⟩⟩, },
end

end locally_finite