l3lab/ntp-mathlib huggingface dataset:
/-- The preimage of a substructure along a homomorphism is a substructure. -/
@[simps]
def comap (φ : M →[L] N) (S : L.Substructure N) : L.Substructure M where
  carrier := φ ⁻¹' S
  fun_mem {n} f x hx := by
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
φ : M →[L] N
S : Substructure L N
n : ℕ
f : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ ⇑φ ⁻¹' ↑S
⊢ funMap f x ∈ ⇑φ ⁻¹' ↑S

Next tactic:
rw [mem_preimage, φ.map_fun]
/-- The preimage of a substructure along a homomorphism is a substructure. -/
@[simps]
def comap (φ : M →[L] N) (S : L.Substructure N) : L.Substructure M where
  carrier := φ ⁻¹' S
  fun_mem {n} f x hx := by
    rw [mem_preimage, φ.map_fun]
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
φ : M →[L] N
S : Substructure L N
n : ℕ
f : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ ⇑φ ⁻¹' ↑S
⊢ funMap f (⇑φ ∘ x) ∈ ↑S

Next tactic:
exact S.fun_mem f (φ ∘ x) hx
@[simp]
theorem comap_id (S : L.Substructure P) : S.comap (Hom.id _ _) = S :=
  ext (by 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
S : Substructure L P
⊢ ∀ (x : P), x ∈ comap (Hom.id L P) S ↔ x ∈ S

Next tactic:
simp
/-- The image of a substructure along a homomorphism is a substructure. -/
@[simps]
def map (φ : M →[L] N) (S : L.Substructure M) : L.Substructure N where
  carrier := φ '' S
  fun_mem {n} f x hx :=
    (mem_image _ _ _).1
      ⟨funMap f fun i => Classical.choose (hx i),
        S.fun_mem f _ fun i => (Classical.choose_spec (hx i)).1, by
        

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
φ : M →[L] N
S : Substructure L M
n : ℕ
f : Functions L n
x : Fin n → N
hx : ∀ (i : Fin n), x i ∈ ⇑φ '' ↑S
⊢ (fun a => φ a) (funMap f fun i => Classical.choose (_ : x i ∈ ⇑φ '' ↑S)) = funMap f x

Next tactic:
simp only [Hom.map_fun, SetLike.mem_coe]
/-- The image of a substructure along a homomorphism is a substructure. -/
@[simps]
def map (φ : M →[L] N) (S : L.Substructure M) : L.Substructure N where
  carrier := φ '' S
  fun_mem {n} f x hx :=
    (mem_image _ _ _).1
      ⟨funMap f fun i => Classical.choose (hx i),
        S.fun_mem f _ fun i => (Classical.choose_spec (hx i)).1, by
        simp only [Hom.map_fun, SetLike.mem_coe]
        

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
φ : M →[L] N
S : Substructure L M
n : ℕ
f : Functions L n
x : Fin n → N
hx : ∀ (i : Fin n), x i ∈ ⇑φ '' ↑S
⊢ funMap f (⇑φ ∘ fun i => Classical.choose (_ : ∃ x_1, (fun x_2 => x_2 ∈ S ∧ φ x_2 = x i) x_1)) = funMap f x

Next tactic:
exact congr rfl (funext fun i => (Classical.choose_spec (hx i)).2)
/-- `map f` and `comap f` form a `GaloisCoinsertion` when `f` is injective. -/
def gciMapComap : GaloisCoinsertion (map f) (comap f) :=
  (gc_map_comap f).toGaloisCoinsertion fun S x => by 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
ι : Type u_3
f : M →[L] N
hf : Function.Injective ⇑f
S : Substructure L M
x : M
⊢ x ∈ comap f (map f S) → x ∈ S

Next tactic:
simp [mem_comap, mem_map, hf.eq_iff]
/-- `map f` and `comap f` form a `GaloisInsertion` when `f` is surjective. -/
def giMapComap : GaloisInsertion (map f) (comap f) :=
  (gc_map_comap f).toGaloisInsertion fun S x h =>
    let ⟨y, hy⟩ := hf x
    mem_map.2 ⟨y, by 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ : Substructure L M
s : Set M
ι : Type u_3
f : M →[L] N
hf : Function.Surjective ⇑f
S : Substructure L N
x : N
h : x ∈ S
y : M
hy : f y = x
⊢ y ∈ comap f S ∧ f y = x

Next tactic:
simp [hy, h]
/-- The equivalence between the maximal substructure of a structure and the structure itself. -/
def topEquiv : (⊤ : L.Substructure M) ≃[L] M where
  toFun := subtype ⊤
  invFun m := ⟨m, mem_top m⟩
  left_inv m := by 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s : Set M
m : ↥⊤
⊢ (fun m => { val := m, property := (_ : m ∈ ⊤) }) ((subtype ⊤) m) = m

Next tactic:
simp
@[simp]
theorem realize_boundedFormula_top {α : Type*} {n : ℕ} {φ : L.BoundedFormula α n}
    {v : α → (⊤ : L.Substructure M)} {xs : Fin n → (⊤ : L.Substructure M)} :
    φ.Realize v xs ↔ φ.Realize (((↑) : _ → M) ∘ v) ((↑) ∘ xs) := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s : Set M
α : Type u_3
n : ℕ
φ : BoundedFormula L α n
v : α → ↥⊤
xs : Fin n → ↥⊤
⊢ BoundedFormula.Realize φ v xs ↔ BoundedFormula.Realize φ (Subtype.val ∘ v) (Subtype.val ∘ xs)

Next tactic:
rw [← Substructure.topEquiv.realize_boundedFormula φ]
@[simp]
theorem realize_boundedFormula_top {α : Type*} {n : ℕ} {φ : L.BoundedFormula α n}
    {v : α → (⊤ : L.Substructure M)} {xs : Fin n → (⊤ : L.Substructure M)} :
    φ.Realize v xs ↔ φ.Realize (((↑) : _ → M) ∘ v) ((↑) ∘ xs) := by
  rw [← Substructure.topEquiv.realize_boundedFormula φ]
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s : Set M
α : Type u_3
n : ℕ
φ : BoundedFormula L α n
v : α → ↥⊤
xs : Fin n → ↥⊤
⊢ BoundedFormula.Realize φ (⇑topEquiv ∘ v) (⇑topEquiv ∘ xs) ↔
    BoundedFormula.Realize φ (Subtype.val ∘ v) (Subtype.val ∘ xs)

Next tactic:
simp
@[simp]
theorem realize_formula_top {α : Type*} {φ : L.Formula α} {v : α → (⊤ : L.Substructure M)} :
    φ.Realize v ↔ φ.Realize (((↑) : (⊤ : L.Substructure M) → M) ∘ v) := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s : Set M
α : Type u_3
φ : Formula L α
v : α → ↥⊤
⊢ Formula.Realize φ v ↔ Formula.Realize φ (Subtype.val ∘ v)

Next tactic:
rw [← Substructure.topEquiv.realize_formula φ]
@[simp]
theorem realize_formula_top {α : Type*} {φ : L.Formula α} {v : α → (⊤ : L.Substructure M)} :
    φ.Realize v ↔ φ.Realize (((↑) : (⊤ : L.Substructure M) → M) ∘ v) := by
  rw [← Substructure.topEquiv.realize_formula φ]
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s : Set M
α : Type u_3
φ : Formula L α
v : α → ↥⊤
⊢ Formula.Realize φ (⇑topEquiv ∘ v) ↔ Formula.Realize φ (Subtype.val ∘ v)

Next tactic:
simp
/-- A dependent version of `Substructure.closure_induction`. -/
@[elab_as_elim]
theorem closure_induction' (s : Set M) {p : ∀ x, x ∈ closure L s → Prop}
    (Hs : ∀ (x) (h : x ∈ s), p x (subset_closure h))
    (Hfun : ∀ {n : ℕ} (f : L.Functions n), ClosedUnder f { x | ∃ hx, p x hx }) {x}
    (hx : x ∈ closure L s) : p x hx := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s✝ s : Set M
p : (x : M) → x ∈ LowerAdjoint.toFun (closure L) s → Prop
Hs : ∀ (x : M) (h : x ∈ s), p x (_ : x ∈ ↑(LowerAdjoint.toFun (closure L) s))
Hfun : ∀ {n : ℕ} (f : Functions L n), ClosedUnder f {x | ∃ (hx : x ∈ LowerAdjoint.toFun (closure L) s), p x hx}
x : M
hx : x ∈ LowerAdjoint.toFun (closure L) s
⊢ p x hx

Next tactic:
refine' Exists.elim _ fun (hx : x ∈ closure L s) (hc : p x hx) => hc
/-- A dependent version of `Substructure.closure_induction`. -/
@[elab_as_elim]
theorem closure_induction' (s : Set M) {p : ∀ x, x ∈ closure L s → Prop}
    (Hs : ∀ (x) (h : x ∈ s), p x (subset_closure h))
    (Hfun : ∀ {n : ℕ} (f : L.Functions n), ClosedUnder f { x | ∃ hx, p x hx }) {x}
    (hx : x ∈ closure L s) : p x hx := by
  refine' Exists.elim _ fun (hx : x ∈ closure L s) (hc : p x hx) => hc
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
s✝ s : Set M
p : (x : M) → x ∈ LowerAdjoint.toFun (closure L) s → Prop
Hs : ∀ (x : M) (h : x ∈ s), p x (_ : x ∈ ↑(LowerAdjoint.toFun (closure L) s))
Hfun : ∀ {n : ℕ} (f : Functions L n), ClosedUnder f {x | ∃ (hx : x ∈ LowerAdjoint.toFun (closure L) s), p x hx}
x : M
hx : x ∈ LowerAdjoint.toFun (closure L) s
⊢ ∃ (x_1 : x ∈ LowerAdjoint.toFun (closure L) s), p x x_1

Next tactic:
exact closure_induction hx (fun x hx => ⟨subset_closure hx, Hs x hx⟩) @Hfun
/-- Reduces the language of a substructure along a language hom. -/
def substructureReduct : L'.Substructure M ↪o L.Substructure M where
  toFun S :=
    { carrier := S
      fun_mem := fun {n} f x hx => by
        

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝⁴ : Structure L M
inst✝³ : Structure L N
inst✝² : Structure L P
S✝ : Substructure L M
L' : Language
inst✝¹ : Structure L' M
φ : L →ᴸ L'
inst✝ : IsExpansionOn φ M
S : Substructure L' M
n : ℕ
f : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ ↑S
⊢ funMap f x ∈ ↑S

Next tactic:
have h := S.fun_mem (φ.onFunction f) x hx
/-- Reduces the language of a substructure along a language hom. -/
def substructureReduct : L'.Substructure M ↪o L.Substructure M where
  toFun S :=
    { carrier := S
      fun_mem := fun {n} f x hx => by
        have h := S.fun_mem (φ.onFunction f) x hx
        

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝⁴ : Structure L M
inst✝³ : Structure L N
inst✝² : Structure L P
S✝ : Substructure L M
L' : Language
inst✝¹ : Structure L' M
φ : L →ᴸ L'
inst✝ : IsExpansionOn φ M
S : Substructure L' M
n : ℕ
f : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ ↑S
h : funMap (onFunction φ f) x ∈ ↑S
⊢ funMap f x ∈ ↑S

Next tactic:
simp only [LHom.map_onFunction, Substructure.mem_carrier] at h
/-- Reduces the language of a substructure along a language hom. -/
def substructureReduct : L'.Substructure M ↪o L.Substructure M where
  toFun S :=
    { carrier := S
      fun_mem := fun {n} f x hx => by
        have h := S.fun_mem (φ.onFunction f) x hx
        simp only [LHom.map_onFunction, Substructure.mem_carrier] at h
        

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝⁴ : Structure L M
inst✝³ : Structure L N
inst✝² : Structure L P
S✝ : Substructure L M
L' : Language
inst✝¹ : Structure L' M
φ : L →ᴸ L'
inst✝ : IsExpansionOn φ M
S : Substructure L' M
n : ℕ
f : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ ↑S
h : funMap f x ∈ S
⊢ funMap f x ∈ ↑S

Next tactic:
exact h
/-- Reduces the language of a substructure along a language hom. -/
def substructureReduct : L'.Substructure M ↪o L.Substructure M where
  toFun S :=
    { carrier := S
      fun_mem := fun {n} f x hx => by
        have h := S.fun_mem (φ.onFunction f) x hx
        simp only [LHom.map_onFunction, Substructure.mem_carrier] at h
        exact h }
  inj' S T h := by
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝⁴ : Structure L M
inst✝³ : Structure L N
inst✝² : Structure L P
S✝ : Substructure L M
L' : Language
inst✝¹ : Structure L' M
φ : L →ᴸ L'
inst✝ : IsExpansionOn φ M
S T : Substructure L' M
h :
  (fun S =>
        { carrier := ↑S,
          fun_mem := (_ : ∀ {n : ℕ} (f : Functions L n) (x : Fin n → M), (∀ (i : Fin n), x i ∈ ↑S) → funMap f x ∈ S) })
      S =
    (fun S =>
        { carrier := ↑S,
          fun_mem := (_ : ∀ {n : ℕ} (f : Functions L n) (x : Fin n → M), (∀ (i : Fin n), x i ∈ ↑S) → funMap f x ∈ S) })
      T
⊢ S = T

Next tactic:
simp only [SetLike.coe_set_eq, Substructure.mk.injEq] at h
/-- Reduces the language of a substructure along a language hom. -/
def substructureReduct : L'.Substructure M ↪o L.Substructure M where
  toFun S :=
    { carrier := S
      fun_mem := fun {n} f x hx => by
        have h := S.fun_mem (φ.onFunction f) x hx
        simp only [LHom.map_onFunction, Substructure.mem_carrier] at h
        exact h }
  inj' S T h := by
    simp only [SetLike.coe_set_eq, Substructure.mk.injEq] at h
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝⁴ : Structure L M
inst✝³ : Structure L N
inst✝² : Structure L P
S✝ : Substructure L M
L' : Language
inst✝¹ : Structure L' M
φ : L →ᴸ L'
inst✝ : IsExpansionOn φ M
S T : Substructure L' M
h : S = T
⊢ S = T

Next tactic:
exact h
/-- Turns any substructure containing a constant set `A` into a `L[[A]]`-substructure. -/
def withConstants (S : L.Substructure M) {A : Set M} (h : A ⊆ S) : L[[A]].Substructure M where
  carrier := S
  fun_mem {n} f := by
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ S : Substructure L M
A : Set M
h : A ⊆ ↑S
n : ℕ
f : Functions (L[[↑A]]) n
⊢ ClosedUnder f ↑S

Next tactic:
cases' f with f f
/-- Turns any substructure containing a constant set `A` into a `L[[A]]`-substructure. -/
def withConstants (S : L.Substructure M) {A : Set M} (h : A ⊆ S) : L[[A]].Substructure M where
  carrier := S
  fun_mem {n} f := by
    cases' f with f f
    · 

Proof state:
case inl
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ S : Substructure L M
A : Set M
h : A ⊆ ↑S
n : ℕ
f : Functions L n
⊢ ClosedUnder (Sum.inl f) ↑S

Next tactic:
exact S.fun_mem f
/-- Turns any substructure containing a constant set `A` into a `L[[A]]`-substructure. -/
def withConstants (S : L.Substructure M) {A : Set M} (h : A ⊆ S) : L[[A]].Substructure M where
  carrier := S
  fun_mem {n} f := by
    cases' f with f f
    · exact S.fun_mem f
    · 

Proof state:
case inr
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ S : Substructure L M
A : Set M
h : A ⊆ ↑S
n : ℕ
f : Functions (constantsOn ↑A) n
⊢ ClosedUnder (Sum.inr f) ↑S

Next tactic:
cases n
/-- Turns any substructure containing a constant set `A` into a `L[[A]]`-substructure. -/
def withConstants (S : L.Substructure M) {A : Set M} (h : A ⊆ S) : L[[A]].Substructure M where
  carrier := S
  fun_mem {n} f := by
    cases' f with f f
    · exact S.fun_mem f
    · cases n
      · 

Proof state:
case inr.zero
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ S : Substructure L M
A : Set M
h : A ⊆ ↑S
f : Functions (constantsOn ↑A) Nat.zero
⊢ ClosedUnder (Sum.inr f) ↑S

Next tactic:
exact fun _ _ => h f.2
/-- Turns any substructure containing a constant set `A` into a `L[[A]]`-substructure. -/
def withConstants (S : L.Substructure M) {A : Set M} (h : A ⊆ S) : L[[A]].Substructure M where
  carrier := S
  fun_mem {n} f := by
    cases' f with f f
    · exact S.fun_mem f
    · cases n
      · exact fun _ _ => h f.2
      · 

Proof state:
case inr.succ
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S✝ S : Substructure L M
A : Set M
h : A ⊆ ↑S
n✝ : ℕ
f : Functions (constantsOn ↑A) (Nat.succ n✝)
⊢ ClosedUnder (Sum.inr f) ↑S

Next tactic:
exact isEmptyElim f
@[simp]
theorem reduct_withConstants :
    (L.lhomWithConstants A).substructureReduct (S.withConstants h) = S := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ (LHom.substructureReduct (lhomWithConstants L ↑A)) (withConstants S h) = S

Next tactic:
ext
@[simp]
theorem reduct_withConstants :
    (L.lhomWithConstants A).substructureReduct (S.withConstants h) = S := by
  ext
  

Proof state:
case h
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
x✝ : M
⊢ x✝ ∈ (LHom.substructureReduct (lhomWithConstants L ↑A)) (withConstants S h) ↔ x✝ ∈ S

Next tactic:
simp
theorem subset_closure_withConstants : A ⊆ closure (L[[A]]) s := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ A ⊆ ↑(LowerAdjoint.toFun (closure (L[[↑A]])) s)

Next tactic:
intro a ha
theorem subset_closure_withConstants : A ⊆ closure (L[[A]]) s := by
  intro a ha
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
a : M
ha : a ∈ A
⊢ a ∈ ↑(LowerAdjoint.toFun (closure (L[[↑A]])) s)

Next tactic:
simp only [SetLike.mem_coe]
theorem subset_closure_withConstants : A ⊆ closure (L[[A]]) s := by
  intro a ha
  simp only [SetLike.mem_coe]
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
a : M
ha : a ∈ A
⊢ a ∈ LowerAdjoint.toFun (closure (L[[↑A]])) s

Next tactic:
let a' : L[[A]].Constants := Sum.inr ⟨a, ha⟩
theorem subset_closure_withConstants : A ⊆ closure (L[[A]]) s := by
  intro a ha
  simp only [SetLike.mem_coe]
  let a' : L[[A]].Constants := Sum.inr ⟨a, ha⟩
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
a : M
ha : a ∈ A
a' : Language.Constants (L[[↑A]]) := Sum.inr { val := a, property := ha }
⊢ a ∈ LowerAdjoint.toFun (closure (L[[↑A]])) s

Next tactic:
exact constants_mem a'
theorem closure_withConstants_eq :
    closure (L[[A]]) s =
      (closure L (A ∪ s)).withConstants ((A.subset_union_left s).trans subset_closure) := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ LowerAdjoint.toFun (closure (L[[↑A]])) s =
    withConstants (LowerAdjoint.toFun (closure L) (A ∪ s)) (_ : A ⊆ ↑(LowerAdjoint.toFun (closure L) (A ∪ s)))

Next tactic:
refine' closure_eq_of_le ((A.subset_union_right s).trans subset_closure) _
theorem closure_withConstants_eq :
    closure (L[[A]]) s =
      (closure L (A ∪ s)).withConstants ((A.subset_union_left s).trans subset_closure) := by
  refine' closure_eq_of_le ((A.subset_union_right s).trans subset_closure) _
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ withConstants (LowerAdjoint.toFun (closure L) (A ∪ s)) (_ : A ⊆ ↑(LowerAdjoint.toFun (closure L) (A ∪ s))) ≤
    LowerAdjoint.toFun (closure (L[[↑A]])) s

Next tactic:
rw [← (L.lhomWithConstants A).substructureReduct.le_iff_le]
theorem closure_withConstants_eq :
    closure (L[[A]]) s =
      (closure L (A ∪ s)).withConstants ((A.subset_union_left s).trans subset_closure) := by
  refine' closure_eq_of_le ((A.subset_union_right s).trans subset_closure) _
  rw [← (L.lhomWithConstants A).substructureReduct.le_iff_le]
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ (LHom.substructureReduct (lhomWithConstants L ↑A))
      (withConstants (LowerAdjoint.toFun (closure L) (A ∪ s)) (_ : A ⊆ ↑(LowerAdjoint.toFun (closure L) (A ∪ s)))) ≤
    (LHom.substructureReduct (lhomWithConstants L ↑A)) (LowerAdjoint.toFun (closure (L[[↑A]])) s)

Next tactic:
simp only [subset_closure, reduct_withConstants, closure_le, LHom.coe_substructureReduct,
    Set.union_subset_iff, and_true_iff]
theorem closure_withConstants_eq :
    closure (L[[A]]) s =
      (closure L (A ∪ s)).withConstants ((A.subset_union_left s).trans subset_closure) := by
  refine' closure_eq_of_le ((A.subset_union_right s).trans subset_closure) _
  rw [← (L.lhomWithConstants A).substructureReduct.le_iff_le]
  simp only [subset_closure, reduct_withConstants, closure_le, LHom.coe_substructureReduct,
    Set.union_subset_iff, and_true_iff]
  · 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
A s : Set M
h : A ⊆ ↑S
⊢ A ⊆ ↑(LowerAdjoint.toFun (closure (L[[↑A]])) s)

Next tactic:
exact subset_closure_withConstants
/-- A first-order hom `f : M → N` whose values lie in a substructure `p ⊆ N` can be restricted to a
hom `M → p`. -/
@[simps]
def codRestrict (p : L.Substructure N) (f : M →[L] N) (h : ∀ c, f c ∈ p) : M →[L] p where
  toFun c := ⟨f c, h c⟩
  map_fun' {n} f x := by 

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
p : Substructure L N
f✝ : M →[L] N
h : ∀ (c : M), f✝ c ∈ p
n : ℕ
f : Functions L n
x : Fin n → M
⊢ (fun c => { val := f✝ c, property := (_ : f✝ c ∈ p) }) (funMap f x) =
    funMap f ((fun c => { val := f✝ c, property := (_ : f✝ c ∈ p) }) ∘ x)

Next tactic:
aesop
theorem range_eq_map (f : M →[L] N) : f.range = map f ⊤ := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f : M →[L] N
⊢ range f = Substructure.map f ⊤

Next tactic:
ext
theorem range_eq_map (f : M →[L] N) : f.range = map f ⊤ := by
  ext
  

Proof state:
case h
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f : M →[L] N
x✝ : N
⊢ x✝ ∈ range f ↔ x✝ ∈ Substructure.map f ⊤

Next tactic:
simp
theorem range_eq_top {f : M →[L] N} : range f = ⊤ ↔ Function.Surjective f := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f : M →[L] N
⊢ range f = ⊤ ↔ Function.Surjective ⇑f

Next tactic:
rw [SetLike.ext'_iff, range_coe, coe_top, Set.range_iff_surjective]
theorem range_le_iff_comap {f : M →[L] N} {p : L.Substructure N} : range f ≤ p ↔ comap f p = ⊤ := by
  

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f : M →[L] N
p : Substructure L N
⊢ range f ≤ p ↔ comap f p = ⊤

Next tactic:
rw [range_eq_map, map_le_iff_le_comap, eq_top_iff]
/-- The substructure of elements `x : M` such that `f x = g x` -/
def eqLocus (f g : M →[L] N) : Substructure L M where
  carrier := { x : M | f x = g x }
  fun_mem {n} fn x hx := by
    

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f g : M →[L] N
n : ℕ
fn : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ {x | f x = g x}
⊢ funMap fn x ∈ {x | f x = g x}

Next tactic:
have h : f ∘ x = g ∘ x := by
      ext
      repeat' rw [Function.comp_apply]
      apply hx
/-- The substructure of elements `x : M` such that `f x = g x` -/
def eqLocus (f g : M →[L] N) : Substructure L M where
  carrier := { x : M | f x = g x }
  fun_mem {n} fn x hx := by
    have h : f ∘ x = g ∘ x := by
      

Proof state:
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f g : M →[L] N
n : ℕ
fn : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ {x | f x = g x}
⊢ ⇑f ∘ x = ⇑g ∘ x

Next tactic:
ext
/-- The substructure of elements `x : M` such that `f x = g x` -/
def eqLocus (f g : M →[L] N) : Substructure L M where
  carrier := { x : M | f x = g x }
  fun_mem {n} fn x hx := by
    have h : f ∘ x = g ∘ x := by
      ext
      

Proof state:
case h
L : Language
M : Type w
N : Type u_1
P : Type u_2
inst✝² : Structure L M
inst✝¹ : Structure L N
inst✝ : Structure L P
S : Substructure L M
f g : M →[L] N
n : ℕ
fn : Functions L n
x : Fin n → M
hx : ∀ (i : Fin n), x i ∈ {x | f x = g x}
x✝ : Fin n
⊢ (⇑f ∘ x) x✝ = (⇑g ∘ x) x✝

Next tactic:
repeat' rw [Function.comp_apply]