l3lab/ntp-mathlib huggingface dataset:
theorem mem_zip_inits_tails {l : List α} {init tail : List α} :
    (init, tail) ∈ zip l.inits l.tails ↔ init ++ tail = l := by
  induction' l with hd tl ih generalizing init tail <;> simp_rw [tails, inits, zip_cons_cons]
  · simp
  · constructor <;> rw [mem_cons, zip_map_left, mem_map, Prod.exists]
    · rintro (⟨rfl, rfl⟩ | ⟨_, _, h, rfl, rfl⟩)
      · simp
      · simp [ih.mp h]
    · cases' init with hd' tl'
      · rintro rfl
        simp
      · intro h
        right
        use tl', tail
        

Proof state:
case h
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
hd : α
tl : List α
ih : ∀ {init tail : List α}, (init, tail) ∈ zip (inits tl) (tails tl) ↔ init ++ tail = tl
tail : List α
hd' : α
tl' : List α
h : hd' :: tl' ++ tail = hd :: tl
⊢ (tl', tail) ∈ zip (inits tl) (tails tl) ∧ Prod.map (fun t => hd :: t) id (tl', tail) = (hd' :: tl', tail)

Next tactic:
simp_all
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
l : List α
l' : List β
⊢ map (Function.uncurry f) (zip l l') = zipWith f l l'

Next tactic:
rw [zip]
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  rw [zip]
  

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
l : List α
l' : List β
⊢ map (Function.uncurry f) (zipWith Prod.mk l l') = zipWith f l l'

Next tactic:
induction' l with hd tl hl generalizing l'
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  rw [zip]
  induction' l with hd tl hl generalizing l'
  · 

Proof state:
case nil
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
l' : List β
⊢ map (Function.uncurry f) (zipWith Prod.mk [] l') = zipWith f [] l'

Next tactic:
simp
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  rw [zip]
  induction' l with hd tl hl generalizing l'
  · simp
  · 

Proof state:
case cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
hd : α
tl : List α
hl : ∀ (l' : List β), map (Function.uncurry f) (zipWith Prod.mk tl l') = zipWith f tl l'
l' : List β
⊢ map (Function.uncurry f) (zipWith Prod.mk (hd :: tl) l') = zipWith f (hd :: tl) l'

Next tactic:
cases' l' with hd' tl'
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  rw [zip]
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · 

Proof state:
case cons.nil
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
hd : α
tl : List α
hl : ∀ (l' : List β), map (Function.uncurry f) (zipWith Prod.mk tl l') = zipWith f tl l'
⊢ map (Function.uncurry f) (zipWith Prod.mk (hd :: tl) []) = zipWith f (hd :: tl) []

Next tactic:
simp
theorem map_uncurry_zip_eq_zipWith (f : α → β → γ) (l : List α) (l' : List β) :
    map (Function.uncurry f) (l.zip l') = zipWith f l l' := by
  rw [zip]
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · 

Proof state:
case cons.cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
hd : α
tl : List α
hl : ∀ (l' : List β), map (Function.uncurry f) (zipWith Prod.mk tl l') = zipWith f tl l'
hd' : β
tl' : List β
⊢ map (Function.uncurry f) (zipWith Prod.mk (hd :: tl) (hd' :: tl')) = zipWith f (hd :: tl) (hd' :: tl')

Next tactic:
simp [hl]
@[simp]
theorem sum_zipWith_distrib_left {γ : Type*} [Semiring γ] (f : α → β → γ) (n : γ) (l : List α)
    (l' : List β) : (l.zipWith (fun x y => n * f x y) l').sum = n * (l.zipWith f l').sum := by
  

Proof state:
α : Type u
β : Type u_1
γ✝ : Type u_2
δ : Type u_3
ε : Type u_4
γ : Type u_5
inst✝ : Semiring γ
f : α → β → γ
n : γ
l : List α
l' : List β
⊢ sum (zipWith (fun x y => n * f x y) l l') = n * sum (zipWith f l l')

Next tactic:
induction' l with hd tl hl generalizing f n l'
@[simp]
theorem sum_zipWith_distrib_left {γ : Type*} [Semiring γ] (f : α → β → γ) (n : γ) (l : List α)
    (l' : List β) : (l.zipWith (fun x y => n * f x y) l').sum = n * (l.zipWith f l').sum := by
  induction' l with hd tl hl generalizing f n l'
  · 

Proof state:
case nil
α : Type u
β : Type u_1
γ✝ : Type u_2
δ : Type u_3
ε : Type u_4
γ : Type u_5
inst✝ : Semiring γ
f : α → β → γ
n : γ
l' : List β
⊢ sum (zipWith (fun x y => n * f x y) [] l') = n * sum (zipWith f [] l')

Next tactic:
simp
@[simp]
theorem sum_zipWith_distrib_left {γ : Type*} [Semiring γ] (f : α → β → γ) (n : γ) (l : List α)
    (l' : List β) : (l.zipWith (fun x y => n * f x y) l').sum = n * (l.zipWith f l').sum := by
  induction' l with hd tl hl generalizing f n l'
  · simp
  · 

Proof state:
case cons
α : Type u
β : Type u_1
γ✝ : Type u_2
δ : Type u_3
ε : Type u_4
γ : Type u_5
inst✝ : Semiring γ
hd : α
tl : List α
hl : ∀ (f : α → β → γ) (n : γ) (l' : List β), sum (zipWith (fun x y => n * f x y) tl l') = n * sum (zipWith f tl l')
f : α → β → γ
n : γ
l' : List β
⊢ sum (zipWith (fun x y => n * f x y) (hd :: tl) l') = n * sum (zipWith f (hd :: tl) l')

Next tactic:
cases' l' with hd' tl'
@[simp]
theorem sum_zipWith_distrib_left {γ : Type*} [Semiring γ] (f : α → β → γ) (n : γ) (l : List α)
    (l' : List β) : (l.zipWith (fun x y => n * f x y) l').sum = n * (l.zipWith f l').sum := by
  induction' l with hd tl hl generalizing f n l'
  · simp
  · cases' l' with hd' tl'
    · 

Proof state:
case cons.nil
α : Type u
β : Type u_1
γ✝ : Type u_2
δ : Type u_3
ε : Type u_4
γ : Type u_5
inst✝ : Semiring γ
hd : α
tl : List α
hl : ∀ (f : α → β → γ) (n : γ) (l' : List β), sum (zipWith (fun x y => n * f x y) tl l') = n * sum (zipWith f tl l')
f : α → β → γ
n : γ
⊢ sum (zipWith (fun x y => n * f x y) (hd :: tl) []) = n * sum (zipWith f (hd :: tl) [])

Next tactic:
simp
@[simp]
theorem sum_zipWith_distrib_left {γ : Type*} [Semiring γ] (f : α → β → γ) (n : γ) (l : List α)
    (l' : List β) : (l.zipWith (fun x y => n * f x y) l').sum = n * (l.zipWith f l').sum := by
  induction' l with hd tl hl generalizing f n l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · 

Proof state:
case cons.cons
α : Type u
β : Type u_1
γ✝ : Type u_2
δ : Type u_3
ε : Type u_4
γ : Type u_5
inst✝ : Semiring γ
hd : α
tl : List α
hl : ∀ (f : α → β → γ) (n : γ) (l' : List β), sum (zipWith (fun x y => n * f x y) tl l') = n * sum (zipWith f tl l')
f : α → β → γ
n : γ
hd' : β
tl' : List β
⊢ sum (zipWith (fun x y => n * f x y) (hd :: tl) (hd' :: tl')) = n * sum (zipWith f (hd :: tl) (hd' :: tl'))

Next tactic:
simp [hl, mul_add]
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
l : List α
l' : List β
n : ℕ
h : length l = length l'
⊢ reverse (zipWith f l l') = zipWith f (reverse l) (reverse l')

Next tactic:
induction' l with hd tl hl generalizing l'
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · 

Proof state:
case nil
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
l' : List β
h : length [] = length l'
⊢ reverse (zipWith f [] l') = zipWith f (reverse []) (reverse l')

Next tactic:
simp
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · 

Proof state:
case cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
l' : List β
h : length (hd :: tl) = length l'
⊢ reverse (zipWith f (hd :: tl) l') = zipWith f (reverse (hd :: tl)) (reverse l')

Next tactic:
cases' l' with hd' tl'
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · 

Proof state:
case cons.nil
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
h : length (hd :: tl) = length []
⊢ reverse (zipWith f (hd :: tl) []) = zipWith f (reverse (hd :: tl)) (reverse [])

Next tactic:
simp
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · 

Proof state:
case cons.cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
hd' : β
tl' : List β
h : length (hd :: tl) = length (hd' :: tl')
⊢ reverse (zipWith f (hd :: tl) (hd' :: tl')) = zipWith f (reverse (hd :: tl)) (reverse (hd' :: tl'))

Next tactic:
simp only [add_left_inj, length] at h
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · simp only [add_left_inj, length] at h
      

Proof state:
case cons.cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
hd' : β
tl' : List β
h : length tl = length tl'
⊢ reverse (zipWith f (hd :: tl) (hd' :: tl')) = zipWith f (reverse (hd :: tl)) (reverse (hd' :: tl'))

Next tactic:
have : tl.reverse.length = tl'.reverse.length := by simp [h]
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · simp only [add_left_inj, length] at h
      have : tl.reverse.length = tl'.reverse.length := by 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
hd' : β
tl' : List β
h : length tl = length tl'
⊢ length (reverse tl) = length (reverse tl')

Next tactic:
simp [h]
theorem zipWith_distrib_reverse (h : l.length = l'.length) :
    (zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
  induction' l with hd tl hl generalizing l'
  · simp
  · cases' l' with hd' tl'
    · simp
    · simp only [add_left_inj, length] at h
      have : tl.reverse.length = tl'.reverse.length := by simp [h]
      

Proof state:
case cons.cons
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
f : α → β → γ
n : ℕ
hd : α
tl : List α
hl : ∀ (l' : List β), length tl = length l' → reverse (zipWith f tl l') = zipWith f (reverse tl) (reverse l')
hd' : β
tl' : List β
h : length tl = length tl'
this : length (reverse tl) = length (reverse tl')
⊢ reverse (zipWith f (hd :: tl) (hd' :: tl')) = zipWith f (reverse (hd :: tl)) (reverse (hd' :: tl'))

Next tactic:
simp [hl _ h, zipWith_append _ _ _ _ _ this]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
ys : List α
⊢ prod [] * prod ys =
    prod (zipWith (fun x x_1 => x * x_1) [] ys) * prod (drop (length ys) []) * prod (drop (length []) ys)

Next tactic:
simp [Nat.zero_le]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
xs : List α
⊢ prod xs * prod [] =
    prod (zipWith (fun x x_1 => x * x_1) xs []) * prod (drop (length []) xs) * prod (drop (length xs) [])

Next tactic:
simp [Nat.zero_le]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
⊢ prod (x :: xs) * prod (y :: ys) =
    prod (zipWith (fun x x_1 => x * x_1) (x :: xs) (y :: ys)) * prod (drop (length (y :: ys)) (x :: xs)) *
      prod (drop (length (x :: xs)) (y :: ys))

Next tactic:
simp only [drop, length, zipWith_cons_cons, prod_cons]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
⊢ x * prod xs * (y * prod ys) =
    x * y * prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (Nat.add (length ys) 0) xs) *
      prod (drop (Nat.add (length xs) 0) ys)

Next tactic:
conv =>
      lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
      rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| x * prod xs * (y * prod ys) =
    x * y * prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (Nat.add (length ys) 0) xs) *
      prod (drop (Nat.add (length xs) 0) ys)

Next tactic:
lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
      rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| x * prod xs * (y * prod ys) =
    x * y * prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (Nat.add (length ys) 0) xs) *
      prod (drop (Nat.add (length xs) 0) ys)

Next tactic:
lhs
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| x * prod xs * (y * prod ys)

Next tactic:
rw [mul_assoc]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| x * (prod xs * (y * prod ys))

Next tactic:
right
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; right; 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| prod xs * (y * prod ys)

Next tactic:
rw [mul_comm, mul_assoc]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; 

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| y * (prod ys * prod xs)

Next tactic:
right
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
      

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
| prod ys * prod xs

Next tactic:
rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
      rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
    

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
⊢ x * (y * (prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (length ys) xs) * prod (drop (length xs) ys))) =
    x * y * prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (Nat.add (length ys) 0) xs) *
      prod (drop (Nat.add (length xs) 0) ys)

Next tactic:
simp only [add_eq, add_zero]
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
    ∀ L L' : List α,
      L.prod * L'.prod =
        (zipWith (· * ·) L L').prod * (L.drop L'.length).prod * (L'.drop L.length).prod
  | [], ys => by simp [Nat.zero_le]
  | xs, [] => by simp [Nat.zero_le]
  | x :: xs, y :: ys => by
    simp only [drop, length, zipWith_cons_cons, prod_cons]
    conv =>
      lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
      rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
    simp only [add_eq, add_zero]
    

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
x : α
xs : List α
y : α
ys : List α
⊢ x * (y * (prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (length ys) xs) * prod (drop (length xs) ys))) =
    x * y * prod (zipWith (fun x x_1 => x * x_1) xs ys) * prod (drop (length ys) xs) * prod (drop (length xs) ys)

Next tactic:
ac_rfl
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_of_length_eq (L L' : List α) (h : L.length = L'.length) :
    L.prod * L'.prod = (zipWith (· * ·) L L').prod := by
  

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
L L' : List α
h : length L = length L'
⊢ prod L * prod L' = prod (zipWith (fun x x_1 => x * x_1) L L')

Next tactic:
apply (prod_mul_prod_eq_prod_zipWith_mul_prod_drop L L').trans
@[to_additive]
theorem prod_mul_prod_eq_prod_zipWith_of_length_eq (L L' : List α) (h : L.length = L'.length) :
    L.prod * L'.prod = (zipWith (· * ·) L L').prod := by
  apply (prod_mul_prod_eq_prod_zipWith_mul_prod_drop L L').trans
  

Proof state:
α : Type u
β : Type u_1
γ : Type u_2
δ : Type u_3
ε : Type u_4
inst✝ : CommMonoid α
L L' : List α
h : length L = length L'
⊢ prod (zipWith (fun x x_1 => x * x_1) L L') * prod (drop (length L') L) * prod (drop (length L) L') =
    prod (zipWith (fun x x_1 => x * x_1) L L')

Next tactic:
rw [← h, drop_length, h, drop_length, prod_nil, mul_one, mul_one]
/-- Specialized destructor on `WPath` -/
def wPathCasesOn {α : TypeVec n} {a : P.A} {f : P.last.B a → P.last.W} (g' : P.drop.B a ⟹ α)
    (g : ∀ j : P.last.B a, P.WPath (f j) ⟹ α) : P.WPath ⟨a, f⟩ ⟹ α := by
  

Proof state:
n : ℕ
P : MvPFunctor.{u} (n + 1)
α : TypeVec.{?u.1798} n
a : P.A
f : PFunctor.B (last P) a → PFunctor.W (last P)
g' : B (drop P) a ⟹ α
g : (j : PFunctor.B (last P) a) → WPath P (f j) ⟹ α
⊢ WPath P (WType.mk a f) ⟹ α

Next tactic:
intro i x
/-- Specialized destructor on `WPath` -/
def wPathCasesOn {α : TypeVec n} {a : P.A} {f : P.last.B a → P.last.W} (g' : P.drop.B a ⟹ α)
    (g : ∀ j : P.last.B a, P.WPath (f j) ⟹ α) : P.WPath ⟨a, f⟩ ⟹ α := by
  intro i x;
  

Proof state:
n : ℕ
P : MvPFunctor.{u} (n + 1)
α : TypeVec.{?u.1798} n
a : P.A
f : PFunctor.B (last P) a → PFunctor.W (last P)
g' : B (drop P) a ⟹ α
g : (j : PFunctor.B (last P) a) → WPath P (f j) ⟹ α
i : Fin2 n
x : WPath P (WType.mk a f) i
⊢ α i

Next tactic:
match x with
  | WPath.root _ _ i c => exact g' i c
  | WPath.child _ _ i j c => exact g j i c
/-- Specialized destructor on `WPath` -/
def wPathCasesOn {α : TypeVec n} {a : P.A} {f : P.last.B a → P.last.W} (g' : P.drop.B a ⟹ α)
    (g : ∀ j : P.last.B a, P.WPath (f j) ⟹ α) : P.WPath ⟨a, f⟩ ⟹ α := by
  intro i x;
  match x with
  | WPath.root _ _ i c => 

Proof state:
n : ℕ
P : MvPFunctor.{u} (n + 1)
α : TypeVec.{?u.1798} n
a : P.A
f : PFunctor.B (last P) a → PFunctor.W (last P)
g' : B (drop P) a ⟹ α
g : (j : PFunctor.B (last P) a) → WPath P (f j) ⟹ α
i✝ : Fin2 n
x : WPath P (WType.mk a f) i✝
i : Fin2 n
c : B (drop P) a i
⊢ α i

Next tactic:
exact g' i c
/-- Specialized destructor on `WPath` -/
def wPathCasesOn {α : TypeVec n} {a : P.A} {f : P.last.B a → P.last.W} (g' : P.drop.B a ⟹ α)
    (g : ∀ j : P.last.B a, P.WPath (f j) ⟹ α) : P.WPath ⟨a, f⟩ ⟹ α := by
  intro i x;
  match x with
  | WPath.root _ _ i c => exact g' i c
  | WPath.child _ _ i j c => 

Proof state:
n : ℕ
P : MvPFunctor.{u} (n + 1)
α : TypeVec.{?u.1798} n
a : P.A
f : PFunctor.B (last P) a → PFunctor.W (last P)
g' : B (drop P) a ⟹ α
g : (j : PFunctor.B (last P) a) → WPath P (f j) ⟹ α
i✝ : Fin2 n
x : WPath P (WType.mk a f) i✝
i : Fin2 n
j : PFunctor.B (last P) a
c : WPath P (f j) i
⊢ α i

Next tactic:
exact g j i c