l3lab/ntp-mathlib huggingface dataset:
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
⊢ f ∈ ⊥

Next tactic:
have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    exact zero_ne_one hX.symm
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
hX : X = 0
⊢ False

Next tactic:
replace hX := congr_arg (fun f => coeff f 1) hX
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
hX : (fun f => coeff f 1) X = (fun f => coeff f 1) 0
⊢ False

Next tactic:
simp only [coeff_X_one, coeff_zero] at hX
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
hX : 1 = 0
⊢ False

Next tactic:
exact zero_ne_one hX.symm
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    exact zero_ne_one hX.symm
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
r1 : X ≠ 0
⊢ f ∈ ⊥

Next tactic:
have r2 := eq_C_of_degree_eq_zero (degree_eq_zero_of_isUnit ((mem_jacobson_bot.1 hf) X))
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    exact zero_ne_one hX.symm
  have r2 := eq_C_of_degree_eq_zero (degree_eq_zero_of_isUnit ((mem_jacobson_bot.1 hf) X))
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
r1 : X ≠ 0
r2 : f * X + 1 = C (coeff (f * X + 1) 0)
⊢ f ∈ ⊥

Next tactic:
simp only [coeff_add, mul_coeff_zero, coeff_X_zero, mul_zero, coeff_one_zero, zero_add] at r2
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    exact zero_ne_one hX.symm
  have r2 := eq_C_of_degree_eq_zero (degree_eq_zero_of_isUnit ((mem_jacobson_bot.1 hf) X))
  simp only [coeff_add, mul_coeff_zero, coeff_X_zero, mul_zero, coeff_one_zero, zero_add] at r2
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
r1 : X ≠ 0
r2 : f * X + 1 = C 1
⊢ f ∈ ⊥

Next tactic:
erw [add_left_eq_self] at r2
theorem jacobson_bot_polynomial_le_sInf_map_maximal :
    jacobson (⊥ : Ideal R[X]) ≤ sInf (map (C : R →+* R[X]) '' { J : Ideal R | J.IsMaximal }) := by
  refine' le_sInf fun J => exists_imp.2 fun j hj => _
  haveI : j.IsMaximal := hj.1
  refine' Trans.trans (jacobson_mono bot_le) (le_of_eq _ : J.jacobson ≤ J)
  suffices t : (⊥ : Ideal (Polynomial (R ⧸ j))).jacobson = ⊥ by
    rw [← hj.2, jacobson_eq_iff_jacobson_quotient_eq_bot]
    replace t := congr_arg (map (polynomialQuotientEquivQuotientPolynomial j).toRingHom) t
    rwa [map_jacobson_of_bijective _, map_bot] at t
    exact RingEquiv.bijective (polynomialQuotientEquivQuotientPolynomial j)
  refine' eq_bot_iff.2 fun f hf => _
  have r1 : (X : (R ⧸ j)[X]) ≠ 0 := fun hX => by
    replace hX := congr_arg (fun f => coeff f 1) hX
    simp only [coeff_X_one, coeff_zero] at hX
    exact zero_ne_one hX.symm
  have r2 := eq_C_of_degree_eq_zero (degree_eq_zero_of_isUnit ((mem_jacobson_bot.1 hf) X))
  simp only [coeff_add, mul_coeff_zero, coeff_X_zero, mul_zero, coeff_one_zero, zero_add] at r2
  erw [add_left_eq_self] at r2
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
J : Ideal R[X]
j : Ideal R
hj : j ∈ {J | IsMaximal J} ∧ map C j = J
this : IsMaximal j
f : (R ⧸ j)[X]
hf : f ∈ jacobson ⊥
r1 : X ≠ 0
r2 : f * X = 0
⊢ f ∈ ⊥

Next tactic:
simpa using (mul_eq_zero.mp r2).resolve_right r1
theorem jacobson_bot_polynomial_of_jacobson_bot (h : jacobson (⊥ : Ideal R) = ⊥) :
    jacobson (⊥ : Ideal R[X]) = ⊥ := by
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
h : jacobson ⊥ = ⊥
⊢ jacobson ⊥ = ⊥

Next tactic:
refine' eq_bot_iff.2 (le_trans jacobson_bot_polynomial_le_sInf_map_maximal _)
theorem jacobson_bot_polynomial_of_jacobson_bot (h : jacobson (⊥ : Ideal R) = ⊥) :
    jacobson (⊥ : Ideal R[X]) = ⊥ := by
  refine' eq_bot_iff.2 (le_trans jacobson_bot_polynomial_le_sInf_map_maximal _)
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
h : jacobson ⊥ = ⊥
⊢ sInf (map C '' {J | IsMaximal J}) ≤ ⊥

Next tactic:
refine' fun f hf => (Submodule.mem_bot R[X]).2 <| Polynomial.ext fun n =>
    Trans.trans (?_ : coeff f n = 0) (coeff_zero n).symm
theorem jacobson_bot_polynomial_of_jacobson_bot (h : jacobson (⊥ : Ideal R) = ⊥) :
    jacobson (⊥ : Ideal R[X]) = ⊥ := by
  refine' eq_bot_iff.2 (le_trans jacobson_bot_polynomial_le_sInf_map_maximal _)
  refine' fun f hf => (Submodule.mem_bot R[X]).2 <| Polynomial.ext fun n =>
    Trans.trans (?_ : coeff f n = 0) (coeff_zero n).symm
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
h : jacobson ⊥ = ⊥
f : R[X]
hf : f ∈ sInf (map C '' {J | IsMaximal J})
n : ℕ
⊢ coeff f n = 0

Next tactic:
suffices f.coeff n ∈ Ideal.jacobson ⊥ by rwa [h, Submodule.mem_bot] at this
theorem jacobson_bot_polynomial_of_jacobson_bot (h : jacobson (⊥ : Ideal R) = ⊥) :
    jacobson (⊥ : Ideal R[X]) = ⊥ := by
  refine' eq_bot_iff.2 (le_trans jacobson_bot_polynomial_le_sInf_map_maximal _)
  refine' fun f hf => (Submodule.mem_bot R[X]).2 <| Polynomial.ext fun n =>
    Trans.trans (?_ : coeff f n = 0) (coeff_zero n).symm
  suffices f.coeff n ∈ Ideal.jacobson ⊥ by 

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
h : jacobson ⊥ = ⊥
f : R[X]
hf : f ∈ sInf (map C '' {J | IsMaximal J})
n : ℕ
this : coeff f n ∈ jacobson ⊥
⊢ coeff f n = 0

Next tactic:
rwa [h, Submodule.mem_bot] at this
theorem jacobson_bot_polynomial_of_jacobson_bot (h : jacobson (⊥ : Ideal R) = ⊥) :
    jacobson (⊥ : Ideal R[X]) = ⊥ := by
  refine' eq_bot_iff.2 (le_trans jacobson_bot_polynomial_le_sInf_map_maximal _)
  refine' fun f hf => (Submodule.mem_bot R[X]).2 <| Polynomial.ext fun n =>
    Trans.trans (?_ : coeff f n = 0) (coeff_zero n).symm
  suffices f.coeff n ∈ Ideal.jacobson ⊥ by rwa [h, Submodule.mem_bot] at this
  

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
h : jacobson ⊥ = ⊥
f : R[X]
hf : f ∈ sInf (map C '' {J | IsMaximal J})
n : ℕ
⊢ coeff f n ∈ jacobson ⊥

Next tactic:
exact mem_sInf.2 fun j hj => (mem_map_C_iff.1 ((mem_sInf.1 hf) ⟨j, ⟨hj.2, rfl⟩⟩)) n
theorem IsLocal.mem_jacobson_or_exists_inv {I : Ideal R} (hi : IsLocal I) (x : R) :
    x ∈ jacobson I ∨ ∃ y, y * x - 1 ∈ I :=
  by_cases
    (fun h : I ⊔ span {x} = ⊤ =>
      let ⟨p, hpi, q, hq, hpq⟩ := Submodule.mem_sup.1 ((eq_top_iff_one _).1 h)
      let ⟨r, hr⟩ := mem_span_singleton.1 hq
      Or.inr ⟨r, by 

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
I : Ideal R
hi : IsLocal I
x : R
h : I ⊔ span {x} = ⊤
p : R
hpi : p ∈ I
q : R
hq : q ∈ span {x}
hpq : p + q = 1
r : R
hr : q = x * r
⊢ r * x - 1 ∈ I

Next tactic:
rw [← hpq, mul_comm, ← hr, ← neg_sub, add_sub_cancel]
theorem IsLocal.mem_jacobson_or_exists_inv {I : Ideal R} (hi : IsLocal I) (x : R) :
    x ∈ jacobson I ∨ ∃ y, y * x - 1 ∈ I :=
  by_cases
    (fun h : I ⊔ span {x} = ⊤ =>
      let ⟨p, hpi, q, hq, hpq⟩ := Submodule.mem_sup.1 ((eq_top_iff_one _).1 h)
      let ⟨r, hr⟩ := mem_span_singleton.1 hq
      Or.inr ⟨r, by rw [← hpq, mul_comm, ← hr, ← neg_sub, add_sub_cancel]; 

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
I : Ideal R
hi : IsLocal I
x : R
h : I ⊔ span {x} = ⊤
p : R
hpi : p ∈ I
q : R
hq : q ∈ span {x}
hpq : p + q = 1
r : R
hr : q = x * r
⊢ -p ∈ I

Next tactic:
exact I.neg_mem hpi
theorem isPrimary_of_isMaximal_radical [CommRing R] {I : Ideal R} (hi : IsMaximal (radical I)) :
    IsPrimary I :=
  have : radical I = jacobson I :=
    le_antisymm (le_sInf fun M ⟨him, hm⟩ => hm.isPrime.radical_le_iff.2 him)
      (sInf_le ⟨le_radical, hi⟩)
  ⟨ne_top_of_lt <| lt_of_le_of_lt le_radical (lt_top_iff_ne_top.2 hi.1.1), fun {x y} hxy =>
    ((isLocal_of_isMaximal_radical hi).mem_jacobson_or_exists_inv y).symm.imp
      (fun ⟨z, hz⟩ => by
        

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
I : Ideal R
hi : IsMaximal (radical I)
this : radical I = jacobson I
x y : R
hxy : x * y ∈ I
x✝ : ∃ y_1, y_1 * y - 1 ∈ I
z : R
hz : z * y - 1 ∈ I
⊢ x ∈ I

Next tactic:
rw [← mul_one x, ← sub_sub_cancel (z * y) 1, mul_sub, mul_left_comm]
theorem isPrimary_of_isMaximal_radical [CommRing R] {I : Ideal R} (hi : IsMaximal (radical I)) :
    IsPrimary I :=
  have : radical I = jacobson I :=
    le_antisymm (le_sInf fun M ⟨him, hm⟩ => hm.isPrime.radical_le_iff.2 him)
      (sInf_le ⟨le_radical, hi⟩)
  ⟨ne_top_of_lt <| lt_of_le_of_lt le_radical (lt_top_iff_ne_top.2 hi.1.1), fun {x y} hxy =>
    ((isLocal_of_isMaximal_radical hi).mem_jacobson_or_exists_inv y).symm.imp
      (fun ⟨z, hz⟩ => by
        rw [← mul_one x, ← sub_sub_cancel (z * y) 1, mul_sub, mul_left_comm]
        

Proof state:
R : Type u
S : Type v
inst✝ : CommRing R
I : Ideal R
hi : IsMaximal (radical I)
this : radical I = jacobson I
x y : R
hxy : x * y ∈ I
x✝ : ∃ y_1, y_1 * y - 1 ∈ I
z : R
hz : z * y - 1 ∈ I
⊢ z * (x * y) - x * (z * y - 1) ∈ I

Next tactic:
exact I.sub_mem (I.mul_mem_left _ hxy) (I.mul_mem_left _ hz)
lemma iInf_coe_ne_top : ⨅ i, (f i : ℕ∞) ≠ ⊤ ↔ Nonempty ι := by
  

Proof state:
ι : Sort u_1
f : ι → ℕ
s : Set ℕ
⊢ ⨅ i, ↑(f i) ≠ ⊤ ↔ Nonempty ι

Next tactic:
rw [Ne.def, iInf_coe_eq_top, not_isEmpty_iff]
theorem fg_def {N : L.Substructure M} : N.FG ↔ ∃ S : Set M, S.Finite ∧ closure L S = N :=
  ⟨fun ⟨t, h⟩ => ⟨_, Finset.finite_toSet t, h⟩, by
    

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
⊢ (∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = N) → FG N

Next tactic:
rintro ⟨t', h, rfl⟩
theorem fg_def {N : L.Substructure M} : N.FG ↔ ∃ S : Set M, S.Finite ∧ closure L S = N :=
  ⟨fun ⟨t, h⟩ => ⟨_, Finset.finite_toSet t, h⟩, by
    rintro ⟨t', h, rfl⟩
    

Proof state:
case intro.intro
L : Language
M : Type u_1
inst✝ : Structure L M
t' : Set M
h : Set.Finite t'
⊢ FG (LowerAdjoint.toFun (closure L) t')

Next tactic:
rcases Finite.exists_finset_coe h with ⟨t, rfl⟩
theorem fg_def {N : L.Substructure M} : N.FG ↔ ∃ S : Set M, S.Finite ∧ closure L S = N :=
  ⟨fun ⟨t, h⟩ => ⟨_, Finset.finite_toSet t, h⟩, by
    rintro ⟨t', h, rfl⟩
    rcases Finite.exists_finset_coe h with ⟨t, rfl⟩
    

Proof state:
case intro.intro.intro
L : Language
M : Type u_1
inst✝ : Structure L M
t : Finset M
h : Set.Finite ↑t
⊢ FG (LowerAdjoint.toFun (closure L) ↑t)

Next tactic:
exact ⟨t, rfl⟩
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
⊢ FG N ↔ ∃ n s, LowerAdjoint.toFun (closure L) (range s) = N

Next tactic:
rw [fg_def]
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
⊢ (∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = N) ↔ ∃ n s, LowerAdjoint.toFun (closure L) (range s) = N

Next tactic:
constructor
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  constructor
  · 

Proof state:
case mp
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
⊢ (∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = N) → ∃ n s, LowerAdjoint.toFun (closure L) (range s) = N

Next tactic:
rintro ⟨S, Sfin, hS⟩
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  constructor
  · rintro ⟨S, Sfin, hS⟩
    

Proof state:
case mp.intro.intro
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
S : Set M
Sfin : Set.Finite S
hS : LowerAdjoint.toFun (closure L) S = N
⊢ ∃ n s, LowerAdjoint.toFun (closure L) (range s) = N

Next tactic:
obtain ⟨n, f, rfl⟩ := Sfin.fin_embedding
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  constructor
  · rintro ⟨S, Sfin, hS⟩
    obtain ⟨n, f, rfl⟩ := Sfin.fin_embedding
    

Proof state:
case mp.intro.intro.intro.intro
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
n : ℕ
f : Fin n ↪ M
Sfin : Set.Finite (range ⇑f)
hS : LowerAdjoint.toFun (closure L) (range ⇑f) = N
⊢ ∃ n s, LowerAdjoint.toFun (closure L) (range s) = N

Next tactic:
exact ⟨n, f, hS⟩
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  constructor
  · rintro ⟨S, Sfin, hS⟩
    obtain ⟨n, f, rfl⟩ := Sfin.fin_embedding
    exact ⟨n, f, hS⟩
  · 

Proof state:
case mpr
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
⊢ (∃ n s, LowerAdjoint.toFun (closure L) (range s) = N) → ∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = N

Next tactic:
rintro ⟨n, s, hs⟩
theorem fg_iff_exists_fin_generating_family {N : L.Substructure M} :
    N.FG ↔ ∃ (n : ℕ) (s : Fin n → M), closure L (range s) = N := by
  rw [fg_def]
  constructor
  · rintro ⟨S, Sfin, hS⟩
    obtain ⟨n, f, rfl⟩ := Sfin.fin_embedding
    exact ⟨n, f, hS⟩
  · rintro ⟨n, s, hs⟩
    

Proof state:
case mpr.intro.intro
L : Language
M : Type u_1
inst✝ : Structure L M
N : Substructure L M
n : ℕ
s : Fin n → M
hs : LowerAdjoint.toFun (closure L) (range s) = N
⊢ ∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = N

Next tactic:
refine' ⟨range s, finite_range s, hs⟩
theorem fg_bot : (⊥ : L.Substructure M).FG :=
  ⟨∅, by 

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
⊢ LowerAdjoint.toFun (closure L) ↑∅ = ⊥

Next tactic:
rw [Finset.coe_empty, closure_empty]
theorem fg_closure {s : Set M} (hs : s.Finite) : FG (closure L s) :=
  ⟨hs.toFinset, by 

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
s : Set M
hs : Set.Finite s
⊢ LowerAdjoint.toFun (closure L) ↑(Finite.toFinset hs) = LowerAdjoint.toFun (closure L) s

Next tactic:
rw [hs.coe_toFinset]
theorem FG.sup {N₁ N₂ : L.Substructure M} (hN₁ : N₁.FG) (hN₂ : N₂.FG) : (N₁ ⊔ N₂).FG :=
  let ⟨t₁, ht₁⟩ := fg_def.1 hN₁
  let ⟨t₂, ht₂⟩ := fg_def.1 hN₂
  fg_def.2 ⟨t₁ ∪ t₂, ht₁.1.union ht₂.1, by 

Proof state:
L : Language
M : Type u_1
inst✝ : Structure L M
N₁ N₂ : Substructure L M
hN₁ : FG N₁
hN₂ : FG N₂
t₁ : Set M
ht₁ : Set.Finite t₁ ∧ LowerAdjoint.toFun (closure L) t₁ = N₁
t₂ : Set M
ht₂ : Set.Finite t₂ ∧ LowerAdjoint.toFun (closure L) t₂ = N₂
⊢ LowerAdjoint.toFun (closure L) (t₁ ∪ t₂) = N₁ ⊔ N₂

Next tactic:
rw [closure_union, ht₁.2, ht₂.2]
theorem FG.map {N : Type*} [L.Structure N] (f : M →[L] N) {s : L.Substructure M} (hs : s.FG) :
    (s.map f).FG :=
  let ⟨t, ht⟩ := fg_def.1 hs
  fg_def.2 ⟨f '' t, ht.1.image _, by 

Proof state:
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M →[L] N
s : Substructure L M
hs : FG s
t : Set M
ht : Set.Finite t ∧ LowerAdjoint.toFun (closure L) t = s
⊢ LowerAdjoint.toFun (closure L) (⇑f '' t) = Substructure.map f s

Next tactic:
rw [closure_image, ht.2]
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  

Proof state:
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
hs : FG (Substructure.map (Embedding.toHom f) s)
⊢ FG s

Next tactic:
rcases hs with ⟨t, h⟩
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  rcases hs with ⟨t, h⟩
  

Proof state:
case intro
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
t : Finset N
h : LowerAdjoint.toFun (closure L) ↑t = Substructure.map (Embedding.toHom f) s
⊢ FG s

Next tactic:
rw [fg_def]
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  rcases hs with ⟨t, h⟩
  rw [fg_def]
  

Proof state:
case intro
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
t : Finset N
h : LowerAdjoint.toFun (closure L) ↑t = Substructure.map (Embedding.toHom f) s
⊢ ∃ S, Set.Finite S ∧ LowerAdjoint.toFun (closure L) S = s

Next tactic:
refine' ⟨f ⁻¹' t, t.finite_toSet.preimage (f.injective.injOn _), _⟩
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  rcases hs with ⟨t, h⟩
  rw [fg_def]
  refine' ⟨f ⁻¹' t, t.finite_toSet.preimage (f.injective.injOn _), _⟩
  

Proof state:
case intro
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
t : Finset N
h : LowerAdjoint.toFun (closure L) ↑t = Substructure.map (Embedding.toHom f) s
⊢ LowerAdjoint.toFun (closure L) (⇑f ⁻¹' ↑t) = s

Next tactic:
have hf : Function.Injective f.toHom := f.injective
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  rcases hs with ⟨t, h⟩
  rw [fg_def]
  refine' ⟨f ⁻¹' t, t.finite_toSet.preimage (f.injective.injOn _), _⟩
  have hf : Function.Injective f.toHom := f.injective
  

Proof state:
case intro
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
t : Finset N
h : LowerAdjoint.toFun (closure L) ↑t = Substructure.map (Embedding.toHom f) s
hf : Function.Injective ⇑(Embedding.toHom f)
⊢ LowerAdjoint.toFun (closure L) (⇑f ⁻¹' ↑t) = s

Next tactic:
refine' map_injective_of_injective hf _
theorem FG.of_map_embedding {N : Type*} [L.Structure N] (f : M ↪[L] N) {s : L.Substructure M}
    (hs : (s.map f.toHom).FG) : s.FG := by
  rcases hs with ⟨t, h⟩
  rw [fg_def]
  refine' ⟨f ⁻¹' t, t.finite_toSet.preimage (f.injective.injOn _), _⟩
  have hf : Function.Injective f.toHom := f.injective
  refine' map_injective_of_injective hf _
  

Proof state:
case intro
L : Language
M : Type u_1
inst✝¹ : Structure L M
N : Type u_2
inst✝ : Structure L N
f : M ↪[L] N
s : Substructure L M
t : Finset N
h : LowerAdjoint.toFun (closure L) ↑t = Substructure.map (Embedding.toHom f) s
hf : Function.Injective ⇑(Embedding.toHom f)
⊢ Substructure.map (Embedding.toHom f) (LowerAdjoint.toFun (closure L) (⇑f ⁻¹' ↑t)) =
    Substructure.map (Embedding.toHom f) s

Next tactic:
rw [← h, map_closure, Embedding.coe_toHom, image_preimage_eq_of_subset]