l3lab/ntp-mathlib huggingface dataset:
theorem IsUltrahomogeneous.amalgamation_age (h : L.IsUltrahomogeneous M) :
    Amalgamation (L.age M) := by
  rintro N P Q NP NQ ⟨Nfg, ⟨-⟩⟩ ⟨Pfg, ⟨PM⟩⟩ ⟨Qfg, ⟨QM⟩⟩
  obtain ⟨g, hg⟩ := h (PM.comp NP).toHom.range (Nfg.range _)
    ((QM.comp NQ).comp (PM.comp NP).equivRange.symm.toEmbedding)
  let s := (g.toHom.comp PM.toHom).range ⊔ QM.toHom.range
  refine' ⟨Bundled.of s,
    Embedding.comp (Substructure.inclusion le_sup_left)
      (g.toEmbedding.comp PM).equivRange.toEmbedding,
    Embedding.comp (Substructure.inclusion le_sup_right) QM.equivRange.toEmbedding,
    ⟨(fg_iff_structure_fg _).1 (FG.sup (Pfg.range _) (Qfg.range _)), ⟨Substructure.subtype _⟩⟩, _⟩
  ext n
  apply Subtype.ext
  have hgn := (Embedding.ext_iff.1 hg) ((PM.comp NP).equivRange n)
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Equiv.symm_apply_apply,
    Substructure.coeSubtype, Embedding.equivRange_apply] at hgn
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.h.a
L : Language
K : Set (Bundled (Structure L))
M : Type w
inst✝¹ : Structure L M
N✝ : Type w
inst✝ : Structure L N✝
h : IsUltrahomogeneous L M
N P Q : Bundled (Structure L)
NP : ↑N ↪[L] ↑P
NQ : ↑N ↪[L] ↑Q
Nfg : Structure.FG L ↑N
Pfg : Structure.FG L ↑P
PM : ↑P ↪[L] M
Qfg : Structure.FG L ↑Q
QM : ↑Q ↪[L] M
g : M ≃[L] M
hg :
  Embedding.comp (Embedding.comp QM NQ) (Equiv.toEmbedding (Equiv.symm (Embedding.equivRange (Embedding.comp PM NP)))) =
    Embedding.comp (Equiv.toEmbedding g) (subtype (Hom.range (Embedding.toHom (Embedding.comp PM NP))))
s : Substructure L M := Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)
n : ↑N
hgn : QM (NQ n) = g (PM (NP n))
⊢ ↑((Embedding.comp
          (Embedding.comp
            (Substructure.inclusion
              (_ :
                Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ≤
                  Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)))
            (Equiv.toEmbedding (Embedding.equivRange (Embedding.comp (Equiv.toEmbedding g) PM))))
          NP)
        n) =
    ↑((Embedding.comp
          (Embedding.comp
            (Substructure.inclusion
              (_ :
                Hom.range (Embedding.toHom QM) ≤
                  Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)))
            (Equiv.toEmbedding (Embedding.equivRange QM)))
          NQ)
        n)

Next tactic:
simp only [Embedding.comp_apply, Equiv.coe_toEmbedding]
theorem IsUltrahomogeneous.amalgamation_age (h : L.IsUltrahomogeneous M) :
    Amalgamation (L.age M) := by
  rintro N P Q NP NQ ⟨Nfg, ⟨-⟩⟩ ⟨Pfg, ⟨PM⟩⟩ ⟨Qfg, ⟨QM⟩⟩
  obtain ⟨g, hg⟩ := h (PM.comp NP).toHom.range (Nfg.range _)
    ((QM.comp NQ).comp (PM.comp NP).equivRange.symm.toEmbedding)
  let s := (g.toHom.comp PM.toHom).range ⊔ QM.toHom.range
  refine' ⟨Bundled.of s,
    Embedding.comp (Substructure.inclusion le_sup_left)
      (g.toEmbedding.comp PM).equivRange.toEmbedding,
    Embedding.comp (Substructure.inclusion le_sup_right) QM.equivRange.toEmbedding,
    ⟨(fg_iff_structure_fg _).1 (FG.sup (Pfg.range _) (Qfg.range _)), ⟨Substructure.subtype _⟩⟩, _⟩
  ext n
  apply Subtype.ext
  have hgn := (Embedding.ext_iff.1 hg) ((PM.comp NP).equivRange n)
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Equiv.symm_apply_apply,
    Substructure.coeSubtype, Embedding.equivRange_apply] at hgn
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding]
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.h.a
L : Language
K : Set (Bundled (Structure L))
M : Type w
inst✝¹ : Structure L M
N✝ : Type w
inst✝ : Structure L N✝
h : IsUltrahomogeneous L M
N P Q : Bundled (Structure L)
NP : ↑N ↪[L] ↑P
NQ : ↑N ↪[L] ↑Q
Nfg : Structure.FG L ↑N
Pfg : Structure.FG L ↑P
PM : ↑P ↪[L] M
Qfg : Structure.FG L ↑Q
QM : ↑Q ↪[L] M
g : M ≃[L] M
hg :
  Embedding.comp (Embedding.comp QM NQ) (Equiv.toEmbedding (Equiv.symm (Embedding.equivRange (Embedding.comp PM NP)))) =
    Embedding.comp (Equiv.toEmbedding g) (subtype (Hom.range (Embedding.toHom (Embedding.comp PM NP))))
s : Substructure L M := Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)
n : ↑N
hgn : QM (NQ n) = g (PM (NP n))
⊢ ↑((Substructure.inclusion
          (_ :
            Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ≤
              Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)))
        ((Equiv.toEmbedding (Embedding.equivRange (Embedding.comp (Equiv.toEmbedding g) PM))) (NP n))) =
    ↑((Substructure.inclusion
          (_ :
            Hom.range (Embedding.toHom QM) ≤
              Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)))
        ((Embedding.equivRange QM) (NQ n)))

Next tactic:
erw [Substructure.coe_inclusion, Substructure.coe_inclusion]
theorem IsUltrahomogeneous.amalgamation_age (h : L.IsUltrahomogeneous M) :
    Amalgamation (L.age M) := by
  rintro N P Q NP NQ ⟨Nfg, ⟨-⟩⟩ ⟨Pfg, ⟨PM⟩⟩ ⟨Qfg, ⟨QM⟩⟩
  obtain ⟨g, hg⟩ := h (PM.comp NP).toHom.range (Nfg.range _)
    ((QM.comp NQ).comp (PM.comp NP).equivRange.symm.toEmbedding)
  let s := (g.toHom.comp PM.toHom).range ⊔ QM.toHom.range
  refine' ⟨Bundled.of s,
    Embedding.comp (Substructure.inclusion le_sup_left)
      (g.toEmbedding.comp PM).equivRange.toEmbedding,
    Embedding.comp (Substructure.inclusion le_sup_right) QM.equivRange.toEmbedding,
    ⟨(fg_iff_structure_fg _).1 (FG.sup (Pfg.range _) (Qfg.range _)), ⟨Substructure.subtype _⟩⟩, _⟩
  ext n
  apply Subtype.ext
  have hgn := (Embedding.ext_iff.1 hg) ((PM.comp NP).equivRange n)
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Equiv.symm_apply_apply,
    Substructure.coeSubtype, Embedding.equivRange_apply] at hgn
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding]
  erw [Substructure.coe_inclusion, Substructure.coe_inclusion]
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.h.a
L : Language
K : Set (Bundled (Structure L))
M : Type w
inst✝¹ : Structure L M
N✝ : Type w
inst✝ : Structure L N✝
h : IsUltrahomogeneous L M
N P Q : Bundled (Structure L)
NP : ↑N ↪[L] ↑P
NQ : ↑N ↪[L] ↑Q
Nfg : Structure.FG L ↑N
Pfg : Structure.FG L ↑P
PM : ↑P ↪[L] M
Qfg : Structure.FG L ↑Q
QM : ↑Q ↪[L] M
g : M ≃[L] M
hg :
  Embedding.comp (Embedding.comp QM NQ) (Equiv.toEmbedding (Equiv.symm (Embedding.equivRange (Embedding.comp PM NP)))) =
    Embedding.comp (Equiv.toEmbedding g) (subtype (Hom.range (Embedding.toHom (Embedding.comp PM NP))))
s : Substructure L M := Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)
n : ↑N
hgn : QM (NQ n) = g (PM (NP n))
⊢ ↑(Set.inclusion
        (_ :
          Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ≤
            Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM))
        ((Equiv.toEmbedding (Embedding.equivRange (Embedding.comp (Equiv.toEmbedding g) PM))) (NP n))) =
    ↑(Set.inclusion
        (_ :
          Hom.range (Embedding.toHom QM) ≤
            Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM))
        ((Embedding.equivRange QM) (NQ n)))

Next tactic:
simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Set.coe_inclusion,
    Embedding.equivRange_apply, hgn]
theorem IsUltrahomogeneous.amalgamation_age (h : L.IsUltrahomogeneous M) :
    Amalgamation (L.age M) := by
  rintro N P Q NP NQ ⟨Nfg, ⟨-⟩⟩ ⟨Pfg, ⟨PM⟩⟩ ⟨Qfg, ⟨QM⟩⟩
  obtain ⟨g, hg⟩ := h (PM.comp NP).toHom.range (Nfg.range _)
    ((QM.comp NQ).comp (PM.comp NP).equivRange.symm.toEmbedding)
  let s := (g.toHom.comp PM.toHom).range ⊔ QM.toHom.range
  refine' ⟨Bundled.of s,
    Embedding.comp (Substructure.inclusion le_sup_left)
      (g.toEmbedding.comp PM).equivRange.toEmbedding,
    Embedding.comp (Substructure.inclusion le_sup_right) QM.equivRange.toEmbedding,
    ⟨(fg_iff_structure_fg _).1 (FG.sup (Pfg.range _) (Qfg.range _)), ⟨Substructure.subtype _⟩⟩, _⟩
  ext n
  apply Subtype.ext
  have hgn := (Embedding.ext_iff.1 hg) ((PM.comp NP).equivRange n)
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Equiv.symm_apply_apply,
    Substructure.coeSubtype, Embedding.equivRange_apply] at hgn
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding]
  erw [Substructure.coe_inclusion, Substructure.coe_inclusion]
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Set.coe_inclusion,
    Embedding.equivRange_apply, hgn]
  -- This used to be `simp only [...]` before leanprover/lean4#2644
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.h.a
L : Language
K : Set (Bundled (Structure L))
M : Type w
inst✝¹ : Structure L M
N✝ : Type w
inst✝ : Structure L N✝
h : IsUltrahomogeneous L M
N P Q : Bundled (Structure L)
NP : ↑N ↪[L] ↑P
NQ : ↑N ↪[L] ↑Q
Nfg : Structure.FG L ↑N
Pfg : Structure.FG L ↑P
PM : ↑P ↪[L] M
Qfg : Structure.FG L ↑Q
QM : ↑Q ↪[L] M
g : M ≃[L] M
hg :
  Embedding.comp (Embedding.comp QM NQ) (Equiv.toEmbedding (Equiv.symm (Embedding.equivRange (Embedding.comp PM NP)))) =
    Embedding.comp (Equiv.toEmbedding g) (subtype (Hom.range (Embedding.toHom (Embedding.comp PM NP))))
s : Substructure L M := Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)
n : ↑N
hgn : QM (NQ n) = g (PM (NP n))
⊢ ↑((Equiv.toEmbedding (Embedding.equivRange (Embedding.comp (Equiv.toEmbedding g) PM))) (NP n)) = g (PM (NP n))

Next tactic:
erw [Embedding.comp_apply, Equiv.coe_toEmbedding,
    Embedding.equivRange_apply]
theorem IsUltrahomogeneous.amalgamation_age (h : L.IsUltrahomogeneous M) :
    Amalgamation (L.age M) := by
  rintro N P Q NP NQ ⟨Nfg, ⟨-⟩⟩ ⟨Pfg, ⟨PM⟩⟩ ⟨Qfg, ⟨QM⟩⟩
  obtain ⟨g, hg⟩ := h (PM.comp NP).toHom.range (Nfg.range _)
    ((QM.comp NQ).comp (PM.comp NP).equivRange.symm.toEmbedding)
  let s := (g.toHom.comp PM.toHom).range ⊔ QM.toHom.range
  refine' ⟨Bundled.of s,
    Embedding.comp (Substructure.inclusion le_sup_left)
      (g.toEmbedding.comp PM).equivRange.toEmbedding,
    Embedding.comp (Substructure.inclusion le_sup_right) QM.equivRange.toEmbedding,
    ⟨(fg_iff_structure_fg _).1 (FG.sup (Pfg.range _) (Qfg.range _)), ⟨Substructure.subtype _⟩⟩, _⟩
  ext n
  apply Subtype.ext
  have hgn := (Embedding.ext_iff.1 hg) ((PM.comp NP).equivRange n)
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Equiv.symm_apply_apply,
    Substructure.coeSubtype, Embedding.equivRange_apply] at hgn
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding]
  erw [Substructure.coe_inclusion, Substructure.coe_inclusion]
  simp only [Embedding.comp_apply, Equiv.coe_toEmbedding, Set.coe_inclusion,
    Embedding.equivRange_apply, hgn]
  -- This used to be `simp only [...]` before leanprover/lean4#2644
  erw [Embedding.comp_apply, Equiv.coe_toEmbedding,
    Embedding.equivRange_apply]
  

Proof state:
case intro.intro.intro.intro.intro.intro.intro.h.a
L : Language
K : Set (Bundled (Structure L))
M : Type w
inst✝¹ : Structure L M
N✝ : Type w
inst✝ : Structure L N✝
h : IsUltrahomogeneous L M
N P Q : Bundled (Structure L)
NP : ↑N ↪[L] ↑P
NQ : ↑N ↪[L] ↑Q
Nfg : Structure.FG L ↑N
Pfg : Structure.FG L ↑P
PM : ↑P ↪[L] M
Qfg : Structure.FG L ↑Q
QM : ↑Q ↪[L] M
g : M ≃[L] M
hg :
  Embedding.comp (Embedding.comp QM NQ) (Equiv.toEmbedding (Equiv.symm (Embedding.equivRange (Embedding.comp PM NP)))) =
    Embedding.comp (Equiv.toEmbedding g) (subtype (Hom.range (Embedding.toHom (Embedding.comp PM NP))))
s : Substructure L M := Hom.range (Hom.comp (Equiv.toHom g) (Embedding.toHom PM)) ⊔ Hom.range (Embedding.toHom QM)
n : ↑N
hgn : QM (NQ n) = g (PM (NP n))
⊢ (Embedding.comp (Equiv.toEmbedding g) PM) (NP n) = g (PM (NP n))

Next tactic:
simp
theorem join_singleton (l : List α) : [l].join = l := by 

Proof state:
α : Type u_1
β : Type u_2
l : List α
⊢ join [l] = l

Next tactic:
rw [join, join, append_nil]
@[simp]
theorem join_eq_nil : ∀ {L : List (List α)}, join L = [] ↔ ∀ l ∈ L, l = []
  | [] => iff_of_true rfl (forall_mem_nil _)
  | l :: L => by 

Proof state:
α : Type u_1
β : Type u_2
l : List α
L : List (List α)
⊢ join (l :: L) = [] ↔ ∀ (l_1 : List α), l_1 ∈ l :: L → l_1 = []

Next tactic:
simp only [join, append_eq_nil, join_eq_nil, forall_mem_cons]
@[simp]
theorem join_append (L₁ L₂ : List (List α)) : join (L₁ ++ L₂) = join L₁ ++ join L₂ := by
  

Proof state:
α : Type u_1
β : Type u_2
L₁ L₂ : List (List α)
⊢ join (L₁ ++ L₂) = join L₁ ++ join L₂

Next tactic:
induction L₁
@[simp]
theorem join_append (L₁ L₂ : List (List α)) : join (L₁ ++ L₂) = join L₁ ++ join L₂ := by
  induction L₁
  · 

Proof state:
case nil
α : Type u_1
β : Type u_2
L₂ : List (List α)
⊢ join ([] ++ L₂) = join [] ++ join L₂

Next tactic:
rfl
@[simp]
theorem join_append (L₁ L₂ : List (List α)) : join (L₁ ++ L₂) = join L₁ ++ join L₂ := by
  induction L₁
  · rfl
  · 

Proof state:
case cons
α : Type u_1
β : Type u_2
L₂ : List (List α)
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : join (tail✝ ++ L₂) = join tail✝ ++ join L₂
⊢ join (head✝ :: tail✝ ++ L₂) = join (head✝ :: tail✝) ++ join L₂

Next tactic:
simp [*]
theorem join_concat (L : List (List α)) (l : List α) : join (L.concat l) = join L ++ l := by 

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
l : List α
⊢ join (concat L l) = join L ++ l

Next tactic:
simp
@[simp]
theorem join_filter_isEmpty_eq_false [DecidablePred fun l : List α => l.isEmpty = false] :
    ∀ {L : List (List α)}, join (L.filter fun l => l.isEmpty = false) = L.join
  | [] => rfl
  | [] :: L => by
      

Proof state:
α : Type u_1
β : Type u_2
inst✝ : DecidablePred fun l => isEmpty l = false
L : List (List α)
⊢ join (filter (fun l => decide (isEmpty l = false)) ([] :: L)) = join ([] :: L)

Next tactic:
simp [join_filter_isEmpty_eq_false (L := L), isEmpty_iff_eq_nil]
@[simp]
theorem join_filter_isEmpty_eq_false [DecidablePred fun l : List α => l.isEmpty = false] :
    ∀ {L : List (List α)}, join (L.filter fun l => l.isEmpty = false) = L.join
  | [] => rfl
  | [] :: L => by
      simp [join_filter_isEmpty_eq_false (L := L), isEmpty_iff_eq_nil]
  | (a :: l) :: L => by
      

Proof state:
α : Type u_1
β : Type u_2
inst✝ : DecidablePred fun l => isEmpty l = false
a : α
l : List α
L : List (List α)
⊢ join (filter (fun l => decide (isEmpty l = false)) ((a :: l) :: L)) = join ((a :: l) :: L)

Next tactic:
have cons_not_empty : isEmpty (a :: l) = false := rfl
@[simp]
theorem join_filter_isEmpty_eq_false [DecidablePred fun l : List α => l.isEmpty = false] :
    ∀ {L : List (List α)}, join (L.filter fun l => l.isEmpty = false) = L.join
  | [] => rfl
  | [] :: L => by
      simp [join_filter_isEmpty_eq_false (L := L), isEmpty_iff_eq_nil]
  | (a :: l) :: L => by
      have cons_not_empty : isEmpty (a :: l) = false := rfl
      

Proof state:
α : Type u_1
β : Type u_2
inst✝ : DecidablePred fun l => isEmpty l = false
a : α
l : List α
L : List (List α)
cons_not_empty : isEmpty (a :: l) = false
⊢ join (filter (fun l => decide (isEmpty l = false)) ((a :: l) :: L)) = join ((a :: l) :: L)

Next tactic:
simp [join_filter_isEmpty_eq_false (L := L), cons_not_empty]
@[simp]
theorem join_filter_ne_nil [DecidablePred fun l : List α => l ≠ []] {L : List (List α)} :
    join (L.filter fun l => l ≠ []) = L.join := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : DecidablePred fun l => l ≠ []
L : List (List α)
⊢ join (filter (fun l => decide (l ≠ [])) L) = join L

Next tactic:
simp [join_filter_isEmpty_eq_false, ← isEmpty_iff_eq_nil]
theorem join_join (l : List (List (List α))) : l.join.join = (l.map join).join := by
  

Proof state:
α : Type u_1
β : Type u_2
l : List (List (List α))
⊢ join (join l) = join (map join l)

Next tactic:
induction l
theorem join_join (l : List (List (List α))) : l.join.join = (l.map join).join := by
  induction l <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
⊢ join (join []) = join (map join [])

Next tactic:
simp [*]
theorem join_join (l : List (List (List α))) : l.join.join = (l.map join).join := by
  induction l <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
head✝ : List (List α)
tail✝ : List (List (List α))
tail_ih✝ : join (join tail✝) = join (map join tail✝)
⊢ join (join (head✝ :: tail✝)) = join (map join (head✝ :: tail✝))

Next tactic:
simp [*]
@[simp]
theorem length_join (L : List (List α)) : length (join L) = sum (map length L) := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
⊢ length (join L) = sum (map length L)

Next tactic:
induction L <;> [rfl; simp only [*, join, map, sum_cons, length_append]]
@[simp]
theorem length_join (L : List (List α)) : length (join L) = sum (map length L) := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
⊢ length (join L) = sum (map length L)

Next tactic:
induction L
@[simp]
theorem length_join (L : List (List α)) : length (join L) = sum (map length L) := by
  induction L <;> [

Proof state:
case nil
α : Type u_1
β : Type u_2
⊢ length (join []) = sum (map length [])

Next tactic:
rfl
@[simp]
theorem length_join (L : List (List α)) : length (join L) = sum (map length L) := by
  induction L <;> [rfl; 

Proof state:
case cons
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : length (join tail✝) = sum (map length tail✝)
⊢ length (join (head✝ :: tail✝)) = sum (map length (head✝ :: tail✝))

Next tactic:
simp only [*, join, map, sum_cons, length_append]
@[simp]
theorem length_bind (l : List α) (f : α → List β) :
    length (List.bind l f) = sum (map (length ∘ f) l) := by 

Proof state:
α : Type u_1
β : Type u_2
l : List α
f : α → List β
⊢ length (List.bind l f) = sum (map (length ∘ f) l)

Next tactic:
rw [List.bind, length_join, map_map]
@[simp]
theorem bind_eq_nil {l : List α} {f : α → List β} : List.bind l f = [] ↔ ∀ x ∈ l, f x = [] :=
  join_eq_nil.trans <| by
    

Proof state:
α : Type u_1
β : Type u_2
l : List α
f : α → List β
⊢ (∀ (l_1 : List β), l_1 ∈ map f l → l_1 = []) ↔ ∀ (x : α), x ∈ l → f x = []

Next tactic:
simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
/-- In a join, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join of the first `i` sublists. -/
theorem take_sum_join (L : List (List α)) (i : ℕ) :
    L.join.take ((L.map length).take i).sum = (L.take i).join := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
i : ℕ
⊢ take (sum (take i (map length L))) (join L) = join (take i L)

Next tactic:
induction L generalizing i
/-- In a join, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join of the first `i` sublists. -/
theorem take_sum_join (L : List (List α)) (i : ℕ) :
    L.join.take ((L.map length).take i).sum = (L.take i).join := by
  induction L generalizing i
  · 

Proof state:
case nil
α : Type u_1
β : Type u_2
i : ℕ
⊢ take (sum (take i (map length []))) (join []) = join (take i [])

Next tactic:
simp
/-- In a join, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join of the first `i` sublists. -/
theorem take_sum_join (L : List (List α)) (i : ℕ) :
    L.join.take ((L.map length).take i).sum = (L.take i).join := by
  induction L generalizing i
  · simp
  · 

Proof state:
case cons
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), take (sum (take i (map length tail✝))) (join tail✝) = join (take i tail✝)
i : ℕ
⊢ take (sum (take i (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) = join (take i (head✝ :: tail✝))

Next tactic:
cases i
/-- In a join, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join of the first `i` sublists. -/
theorem take_sum_join (L : List (List α)) (i : ℕ) :
    L.join.take ((L.map length).take i).sum = (L.take i).join := by
  induction L generalizing i
  · simp
  · cases i <;> 

Proof state:
case cons.zero
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), take (sum (take i (map length tail✝))) (join tail✝) = join (take i tail✝)
⊢ take (sum (take Nat.zero (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) =
    join (take Nat.zero (head✝ :: tail✝))

Next tactic:
simp [take_append, *]
/-- In a join, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join of the first `i` sublists. -/
theorem take_sum_join (L : List (List α)) (i : ℕ) :
    L.join.take ((L.map length).take i).sum = (L.take i).join := by
  induction L generalizing i
  · simp
  · cases i <;> 

Proof state:
case cons.succ
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), take (sum (take i (map length tail✝))) (join tail✝) = join (take i tail✝)
n✝ : ℕ
⊢ take (sum (take (Nat.succ n✝) (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) =
    join (take (Nat.succ n✝) (head✝ :: tail✝))

Next tactic:
simp [take_append, *]
/-- In a join, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
theorem drop_sum_join (L : List (List α)) (i : ℕ) :
    L.join.drop ((L.map length).take i).sum = (L.drop i).join := by
  

Proof state:
α : Type u_1
β : Type u_2
L : List (List α)
i : ℕ
⊢ drop (sum (take i (map length L))) (join L) = join (drop i L)

Next tactic:
induction L generalizing i
/-- In a join, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
theorem drop_sum_join (L : List (List α)) (i : ℕ) :
    L.join.drop ((L.map length).take i).sum = (L.drop i).join := by
  induction L generalizing i
  · 

Proof state:
case nil
α : Type u_1
β : Type u_2
i : ℕ
⊢ drop (sum (take i (map length []))) (join []) = join (drop i [])

Next tactic:
simp
/-- In a join, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
theorem drop_sum_join (L : List (List α)) (i : ℕ) :
    L.join.drop ((L.map length).take i).sum = (L.drop i).join := by
  induction L generalizing i
  · simp
  · 

Proof state:
case cons
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), drop (sum (take i (map length tail✝))) (join tail✝) = join (drop i tail✝)
i : ℕ
⊢ drop (sum (take i (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) = join (drop i (head✝ :: tail✝))

Next tactic:
cases i
/-- In a join, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
theorem drop_sum_join (L : List (List α)) (i : ℕ) :
    L.join.drop ((L.map length).take i).sum = (L.drop i).join := by
  induction L generalizing i
  · simp
  · cases i <;> 

Proof state:
case cons.zero
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), drop (sum (take i (map length tail✝))) (join tail✝) = join (drop i tail✝)
⊢ drop (sum (take Nat.zero (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) =
    join (drop Nat.zero (head✝ :: tail✝))

Next tactic:
simp [drop_append, *]
/-- In a join, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
theorem drop_sum_join (L : List (List α)) (i : ℕ) :
    L.join.drop ((L.map length).take i).sum = (L.drop i).join := by
  induction L generalizing i
  · simp
  · cases i <;> 

Proof state:
case cons.succ
α : Type u_1
β : Type u_2
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : ∀ (i : ℕ), drop (sum (take i (map length tail✝))) (join tail✝) = join (drop i tail✝)
n✝ : ℕ
⊢ drop (sum (take (Nat.succ n✝) (map length (head✝ :: tail✝)))) (join (head✝ :: tail✝)) =
    join (drop (Nat.succ n✝) (head✝ :: tail✝))

Next tactic:
simp [drop_append, *]