Module docstring
{"# Lemmas about List.zip, List.zipWith, List.zipWithAll, and List.unzip.
","## Zippers ","### zipWith ","### zip ","### zipWithAll ","### unzip "}
{"# Lemmas about List.zip, List.zipWith, List.zipWithAll, and List.unzip.
","## Zippers ","### zipWith ","### zip ","### zipWithAll ","### unzip "}
List.zipWith_comm
      theorem
     {f : α → β → γ} : ∀ {as : List α} {bs : List β}, zipWith f as bs = zipWith (fun b a => f a b) bs as
        
      List.zipWith_comm_of_comm
      theorem
     {f : α → α → β} (comm : ∀ x y : α, f x y = f y x) {l l' : List α} : zipWith f l l' = zipWith f l' l
        theorem zipWith_comm_of_comm {f : α → α → β} (comm : ∀ x y : α, f x y = f y x) {l l' : List α} :
    zipWith f l l' = zipWith f l' l := by
  rw [zipWith_comm]
  simp only [comm]
        List.zipWith_self
      theorem
     {f : α → α → δ} : ∀ {l : List α}, zipWith f l l = l.map fun a => f a a
        
      List.zipWith_same
      abbrev
    
        @[deprecated zipWith_self (since := "2025-01-29")] abbrev zipWith_same := @zipWith_self
        List.getElem?_zipWith
      theorem
     {f : α → β → γ} {i : Nat} :
  (zipWith f as bs)[i]? =
    match as[i]?, bs[i]? with
    | some a, some b => some (f a b)
    | _, _ => none
        /--
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
theorem getElem?_zipWith {f : α → β → γ} {i : Nat} :
    (zipWith f as bs)[i]? = match as[i]?, bs[i]? with
      | some a, some b => some (f a b) | _, _ => none := by
  induction as generalizing bs i with
  | nil => cases bs with
    | nil => simp
    | cons b bs => simp
  | cons a as aih => cases bs with
    | nil => simp
    | cons b bs => cases i <;> simp_all
        List.getElem?_zipWith'
      theorem
     {f : α → β → γ} {i : Nat} : (zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g
        /-- Variant of `getElem?_zipWith` using `Option.map` and `Option.bind` rather than a `match`. -/
theorem getElem?_zipWith' {f : α → β → γ} {i : Nat} :
    (zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g := by
  induction l₁ generalizing l₂ i with
  | nil => rw [zipWith] <;> simp
  | cons _ _ =>
    cases l₂
    · simp
    · cases i <;> simp_all
        List.getElem?_zipWith_eq_some
      theorem
     {f : α → β → γ} {l₁ : List α} {l₂ : List β} {z : γ} {i : Nat} :
  (zipWith f l₁ l₂)[i]? = some z ↔ ∃ x y, l₁[i]? = some x ∧ l₂[i]? = some y ∧ f x y = z
        theorem getElem?_zipWith_eq_some {f : α → β → γ} {l₁ : List α} {l₂ : List β} {z : γ} {i : Nat} :
    (zipWith f l₁ l₂)[i]?(zipWith f l₁ l₂)[i]? = some z ↔
      ∃ x y, l₁[i]? = some x ∧ l₂[i]? = some y ∧ f x y = z := by
  induction l₁ generalizing l₂ i
  · simp
  · cases l₂ <;> cases i <;> simp_all
        List.getElem?_zip_eq_some
      theorem
     {l₁ : List α} {l₂ : List β} {z : α × β} {i : Nat} : (zip l₁ l₂)[i]? = some z ↔ l₁[i]? = some z.1 ∧ l₂[i]? = some z.2
        theorem getElem?_zip_eq_some {l₁ : List α} {l₂ : List β} {z : α × β} {i : Nat} :
    (zip l₁ l₂)[i]?(zip l₁ l₂)[i]? = some z ↔ l₁[i]? = some z.1 ∧ l₂[i]? = some z.2 := by
  cases z
  rw [zip, getElem?_zipWith_eq_some]; constructor
  · rintro ⟨x, y, h₀, h₁, h₂⟩
    simpa [h₀, h₁] using h₂
  · rintro ⟨h₀, h₁⟩
    exact ⟨_, _, h₀, h₁, rfl⟩
        List.head?_zipWith
      theorem
     {f : α → β → γ} :
  (List.zipWith f as bs).head? =
    match as.head?, bs.head? with
    | some a, some b => some (f a b)
    | _, _ => none
        theorem head?_zipWith {f : α → β → γ} :
    (List.zipWith f as bs).head? = match as.head?, bs.head? with
      | some a, some b => some (f a b) | _, _ => none := by
  simp [head?_eq_getElem?, getElem?_zipWith]
        List.head_zipWith
      theorem
     {f : α → β → γ} (h) :
  (List.zipWith f as bs).head h = f (as.head (by rintro rfl; simp_all)) (bs.head (by rintro rfl; simp_all))
        theorem head_zipWith {f : α → β → γ} (h):
    (List.zipWith f as bs).head h = f (as.head (by rintro rfl; simp_all)) (bs.head (by rintro rfl; simp_all)) := by
  apply Option.some.inj
  rw [← head?_eq_head, head?_zipWith, head?_eq_head, head?_eq_head]
        List.zipWith_map
      theorem
     {μ} {f : γ → δ → μ} {g : α → γ} {h : β → δ} {l₁ : List α} {l₂ : List β} :
  zipWith f (l₁.map g) (l₂.map h) = zipWith (fun a b => f (g a) (h b)) l₁ l₂
        
      List.zipWith_map_left
      theorem
     {l₁ : List α} {l₂ : List β} {f : α → α'} {g : α' → β → γ} :
  zipWith g (l₁.map f) l₂ = zipWith (fun a b => g (f a) b) l₁ l₂
        
      List.zipWith_map_right
      theorem
     {l₁ : List α} {l₂ : List β} {f : β → β'} {g : α → β' → γ} :
  zipWith g l₁ (l₂.map f) = zipWith (fun a b => g a (f b)) l₁ l₂
        
      List.zipWith_foldr_eq_zip_foldr
      theorem
     {f : α → β → γ} {i : δ} {g : γ → δ → δ} :
  (zipWith f l₁ l₂).foldr g i = (zip l₁ l₂).foldr (fun p r => g (f p.1 p.2) r) i
        
      List.zipWith_foldl_eq_zip_foldl
      theorem
     {f : α → β → γ} {i : δ} {g : δ → γ → δ} :
  (zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i
        
      List.zipWith_eq_nil_iff
      theorem
     {f : α → β → γ} {l l'} : zipWith f l l' = [] ↔ l = [] ∨ l' = []
        @[simp]
theorem zipWith_eq_nil_iff {f : α → β → γ} {l l'} : zipWithzipWith f l l' = [] ↔ l = [] ∨ l' = [] := by
  cases l <;> cases l' <;> simp
        List.map_zipWith
      theorem
     {δ : Type _} {f : α → β} {g : γ → δ → α} {l : List γ} {l' : List δ} :
  map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l'
        
      List.take_zipWith
      theorem
     : (zipWith f l l').take i = zipWith f (l.take i) (l'.take i)
        
      List.drop_zipWith
      theorem
     : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i)
        
      List.tail_zipWith
      theorem
     : (zipWith f l l').tail = zipWith f l.tail l'.tail
        @[simp]
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
  rw [← drop_one]; simp [drop_zipWith]
        List.zipWith_append
      theorem
     {f : α → β → γ} {l₁ l₁' : List α} {l₂ l₂' : List β} (h : l₁.length = l₂.length) :
  zipWith f (l₁ ++ l₁') (l₂ ++ l₂') = zipWith f l₁ l₂ ++ zipWith f l₁' l₂'
        theorem zipWith_append {f : α → β → γ} {l₁ l₁' : List α} {l₂ l₂' : List β}
    (h : l₁.length = l₂.length) :
    zipWith f (l₁ ++ l₁') (l₂ ++ l₂') = zipWith f l₁ l₂ ++ zipWith f l₁' l₂' := by
  induction l₁ generalizing l₂ with
  | nil =>
    have : l₂ = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
    simp [this]
  | cons hl tl ih =>
    cases l₂ with
    | nil => simp at h
    | cons _ _ =>
      simp only [length_cons, Nat.succ.injEq] at h
      simp [ih h]
        List.zipWith_eq_cons_iff
      theorem
     {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
  zipWith f l₁ l₂ = g :: l ↔ ∃ a l₁' b l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ g = f a b ∧ l = zipWith f l₁' l₂'
        theorem zipWith_eq_cons_iff {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
    zipWithzipWith f l₁ l₂ = g :: l ↔
      ∃ a l₁' b l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ g = f a b ∧ l = zipWith f l₁' l₂' := by
  match l₁, l₂ with
  | [], [] => simp
  | [], b :: l₂ => simp
  | a :: l₁, [] => simp
  | a' :: l₁, b' :: l₂ =>
    simp only [zip_cons_cons, cons.injEq, Prod.mk.injEq]
    constructor
    · rintro ⟨⟨rfl, rfl⟩, rfl⟩
      refine ⟨a', l₁, b', l₂, by simp⟩
    · rintro ⟨a, l₁, b, l₂, ⟨rfl, rfl⟩, ⟨rfl, rfl⟩, rfl, rfl⟩
      simp
        List.zipWith_eq_append_iff
      theorem
     {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
  zipWith f l₁ l₂ = l₁' ++ l₂' ↔
    ∃ ws xs ys zs, ws.length = ys.length ∧ l₁ = ws ++ xs ∧ l₂ = ys ++ zs ∧ l₁' = zipWith f ws ys ∧ l₂' = zipWith f xs zs
        theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
    zipWithzipWith f l₁ l₂ = l₁' ++ l₂' ↔
      ∃ ws xs ys zs, ws.length = ys.length ∧ l₁ = ws ++ xs ∧ l₂ = ys ++ zs ∧ l₁' = zipWith f ws ys ∧ l₂' = zipWith f xs zs := by
  induction l₁ generalizing l₂ l₁' with
  | nil =>
    simp
    constructor
    · rintro ⟨rfl, rfl⟩
      exact ⟨[], [], [], by simp⟩
    · rintro ⟨_, _, _, -, ⟨rfl, rfl⟩, _, rfl, rfl, rfl⟩
      simp
  | cons x₁ l₁ ih₁ =>
    cases l₂ with
    | nil =>
      constructor
      · simp only [zipWith_nil_right, nil_eq, append_eq_nil_iff, exists_and_left, and_imp]
        rintro rfl  rfl
        exact ⟨[], x₁ :: l₁, [], by simp⟩
      · rintro ⟨_, _, _, _, h₁, _, h₃, rfl, rfl⟩
        simp only [nil_eq, append_eq_nil_iff] at h₃
        obtain ⟨rfl, rfl⟩ := h₃
        simp
    | cons x₂ l₂ =>
      simp only [zipWith_cons_cons]
      rw [cons_eq_append_iff]
      constructor
      · rintro (⟨rfl, rfl⟩ | ⟨_, rfl, h⟩)
        · exact ⟨[], x₁ :: l₁, [], x₂ :: l₂, by simp⟩
        · rw [ih₁] at h
          obtain ⟨ws, xs, ys, zs, h, rfl, rfl, h', rfl⟩ := h
          refine ⟨x₁ :: ws, xs, x₂ :: ys, zs, by simp [h, h']⟩
      · rintro ⟨_, _, _, _, h₁, h₂, h₃, rfl, rfl⟩
        rw [cons_eq_append_iff] at h₂
        rw [cons_eq_append_iff] at h₃
        obtain (⟨rfl, rfl⟩ | ⟨_, rfl, rfl⟩) := h₂
        · simp only [zipWith_nil_left, true_and, nil_eq, reduceCtorEq, false_and, exists_const,
          or_false]
          obtain (⟨rfl, rfl⟩ | ⟨_, rfl, rfl⟩) := h₃
          · simp
          · simp_all
        · obtain (⟨rfl, rfl⟩ | ⟨_, rfl, rfl⟩) := h₃
          · simp_all
          · simp_all [zipWith_append, Nat.succ_inj']
        List.zipWith_replicate'
      theorem
     {a : α} {b : β} {n : Nat} : zipWith f (replicate n a) (replicate n b) = replicate n (f a b)
        /-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :
    zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
  induction n with
  | zero => rfl
  | succ n ih => simp [replicate_succ, ih]
        List.map_uncurry_zip_eq_zipWith
      theorem
     {f : α → β → γ} {l : List α} {l' : List β} : map (Function.uncurry f) (l.zip l') = zipWith f l 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 generalizing l' with
  | nil => simp
  | cons hl tl ih =>
    cases l' <;> simp [ih]
        List.map_zip_eq_zipWith
      theorem
     {f : α × β → γ} {l : List α} {l' : List β} : map f (l.zip l') = zipWith (Function.curry f) l l'
        
      List.zip_eq_zipWith
      theorem
     : ∀ {l₁ : List α} {l₂ : List β}, zip l₁ l₂ = zipWith Prod.mk l₁ l₂
        
      List.zip_map
      theorem
     {f : α → γ} {g : β → δ} : ∀ {l₁ : List α} {l₂ : List β}, zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
        
      List.zip_map_left
      theorem
     {f : α → γ} {l₁ : List α} {l₂ : List β} : zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id)
        
      List.zip_map_right
      theorem
     {f : β → γ} {l₁ : List α} {l₂ : List β} : zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f)
        
      List.tail_zip
      theorem
     {l₁ : List α} {l₂ : List β} : (zip l₁ l₂).tail = zip l₁.tail l₂.tail
        
      List.zip_append
      theorem
     : ∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂), zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
        theorem zip_append :
    ∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
      zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
  | [], _, _, _, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
  | _, _, [], _, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
  | _ :: _, _, _ :: _, _, h => by
    simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
        List.zip_map'
      theorem
     {f : α → β} {g : α → γ} : ∀ {l : List α}, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
        
      List.of_mem_zip
      theorem
     {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip l₁ l₂ → a ∈ l₁ ∧ b ∈ l₂
        theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b)(a, b) ∈ zip l₁ l₂ → a ∈ l₁a ∈ l₁ ∧ b ∈ l₂
  | _ :: l₁, _ :: l₂, h => by
    cases h
    case head => simp
    case tail h =>
    · have := of_mem_zip h
      exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
        List.map_fst_zip
      theorem
     : ∀ {l₁ : List α} {l₂ : List β}, l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
        theorem map_fst_zip :
    ∀ {l₁ : List α} {l₂ : List β}, l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
  | [], _, _ => rfl
  | _ :: as, _ :: bs, h => by
    simp [Nat.succ_le_succ_iff] at h
    show _ :: map Prod.fst (zip as bs) = _ :: as
    rw [map_fst_zip (l₁ := as) h]
  | _ :: _, [], h => by simp at h
        List.map_snd_zip
      theorem
     : ∀ {l₁ : List α} {l₂ : List β}, l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
        theorem map_snd_zip :
    ∀ {l₁ : List α} {l₂ : List β}, l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
  | _, [], _ => by
    rw [zip_nil_right]
    rfl
  | [], b :: bs, h => by simp at h
  | a :: as, b :: bs, h => by
    simp [Nat.succ_le_succ_iff] at h
    show _ :: map Prod.snd (zip as bs) = _ :: bs
    rw [map_snd_zip (l₂ := bs) h]
        List.map_prod_left_eq_zip
      theorem
     {l : List α} {f : α → β} : (l.map fun x => (x, f x)) = l.zip (l.map f)
        
      List.map_prod_right_eq_zip
      theorem
     {l : List α} {f : α → β} : (l.map fun x => (f x, x)) = (l.map f).zip l
        
      List.zip_eq_nil_iff
      theorem
     {l₁ : List α} {l₂ : List β} : zip l₁ l₂ = [] ↔ l₁ = [] ∨ l₂ = []
        @[simp] theorem zip_eq_nil_iff {l₁ : List α} {l₂ : List β} :
    zipzip l₁ l₂ = [] ↔ l₁ = [] ∨ l₂ = [] := by
  simp [zip_eq_zipWith]
        List.zip_eq_cons_iff
      theorem
     {l₁ : List α} {l₂ : List β} : zip l₁ l₂ = (a, b) :: l ↔ ∃ l₁' l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ l = zip l₁' l₂'
        theorem zip_eq_cons_iff {l₁ : List α} {l₂ : List β} :
    zipzip l₁ l₂ = (a, b) :: l ↔
      ∃ l₁' l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ l = zip l₁' l₂' := by
  simp only [zip_eq_zipWith, zipWith_eq_cons_iff]
  constructor
  · rintro ⟨a, l₁, b, l₂, rfl, rfl, h, rfl, rfl⟩
    simp only [Prod.mk.injEq] at h
    obtain ⟨rfl, rfl⟩ := h
    simp
  · rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
    refine ⟨a, l₁', b, l₂', by simp⟩
        List.zip_eq_append_iff
      theorem
     {l₁ : List α} {l₂ : List β} :
  zip l₁ l₂ = l₁' ++ l₂' ↔
    ∃ ws xs ys zs, ws.length = ys.length ∧ l₁ = ws ++ xs ∧ l₂ = ys ++ zs ∧ l₁' = zip ws ys ∧ l₂' = zip xs zs
        theorem zip_eq_append_iff {l₁ : List α} {l₂ : List β} :
    zipzip l₁ l₂ = l₁' ++ l₂' ↔
      ∃ ws xs ys zs, ws.length = ys.length ∧ l₁ = ws ++ xs ∧ l₂ = ys ++ zs ∧ l₁' = zip ws ys ∧ l₂' = zip xs zs := by
  simp [zip_eq_zipWith, zipWith_eq_append_iff]
        List.zip_replicate'
      theorem
     {a : α} {b : β} {n : Nat} : zip (replicate n a) (replicate n b) = replicate n (a, b)
        /-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
    zip (replicate n a) (replicate n b) = replicate n (a, b) := by
  induction n with
  | zero => rfl
  | succ n ih => simp [replicate_succ, ih]
        List.getElem?_zipWithAll
      theorem
     {f : Option α → Option β → γ} {i : Nat} :
  (zipWithAll f as bs)[i]? =
    match as[i]?, bs[i]? with
    | none, none => .none
    | a?, b? => some (f a? b?)
        theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
    (zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
      | none, none => .none | a?, b? => some (f a? b?) := by
  induction as generalizing bs i with
  | nil => induction bs generalizing i with
    | nil => simp
    | cons b bs bih => cases i <;> simp_all
  | cons a as aih => cases bs with
    | nil =>
      specialize @aih []
      cases i <;> simp_all
    | cons b bs => cases i <;> simp_all
        List.head?_zipWithAll
      theorem
     {f : Option α → Option β → γ} :
  (zipWithAll f as bs).head? =
    match as.head?, bs.head? with
    | none, none => .none
    | a?, b? => some (f a? b?)
        theorem head?_zipWithAll {f : Option α → Option β → γ} :
    (zipWithAll f as bs).head? = match as.head?, bs.head? with
      | none, none => .none | a?, b? => some (f a? b?) := by
  simp [head?_eq_getElem?, getElem?_zipWithAll]
        List.head_zipWithAll
      theorem
     {f : Option α → Option β → γ} (h) : (zipWithAll f as bs).head h = f as.head? bs.head?
        @[simp] theorem head_zipWithAll {f : Option α → Option β → γ} (h) :
    (zipWithAll f as bs).head h = f as.head? bs.head? := by
  apply Option.some.inj
  rw [← head?_eq_head, head?_zipWithAll]
  split <;> simp_all
        List.tail_zipWithAll
      theorem
     {f : Option α → Option β → γ} : (zipWithAll f as bs).tail = zipWithAll f as.tail bs.tail
        @[simp] theorem tail_zipWithAll {f : Option α → Option β → γ} :
    (zipWithAll f as bs).tail = zipWithAll f as.tail bs.tail := by
  cases as <;> cases bs <;> simp
        List.zipWithAll_map
      theorem
     {μ} {f : Option γ → Option δ → μ} {g : α → γ} {h : β → δ} {l₁ : List α} {l₂ : List β} :
  zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂
        theorem zipWithAll_map {μ} {f : Option γ → Option δ → μ} {g : α → γ} {h : β → δ} {l₁ : List α} {l₂ : List β} :
    zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂ := by
  induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
        List.zipWithAll_map_left
      theorem
     {l₁ : List α} {l₂ : List β} {f : α → α'} {g : Option α' → Option β → γ} :
  zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂
        theorem zipWithAll_map_left {l₁ : List α} {l₂ : List β} {f : α → α'} {g : Option α' → Option β → γ} :
    zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂ := by
  induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
        List.zipWithAll_map_right
      theorem
     {l₁ : List α} {l₂ : List β} {f : β → β'} {g : Option α → Option β' → γ} :
  zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂
        theorem zipWithAll_map_right {l₁ : List α} {l₂ : List β} {f : β → β'} {g : Option α → Option β' → γ} :
    zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂ := by
  induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
        List.map_zipWithAll
      theorem
     {δ : Type _} {f : α → β} {g : Option γ → Option δ → α} {l : List γ} {l' : List δ} :
  map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l'
        theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option δ → α} {l : List γ} {l' : List δ} :
    map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l' := by
  induction l generalizing l' with
  | nil => simp
  | cons hd tl hl =>
    cases l' <;> simp_all
        List.zipWithAll_replicate
      theorem
     {a : α} {b : β} {n : Nat} : zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b)
        @[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
    zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
  induction n with
  | zero => rfl
  | succ n ih => simp [replicate_succ, ih]
        List.unzip_fst
      theorem
     : (unzip l).fst = l.map Prod.fst
        
      List.unzip_snd
      theorem
     : (unzip l).snd = l.map Prod.snd
        
      List.unzip_eq_map
      theorem
     : ∀ {l : List (α × β)}, unzip l = (l.map Prod.fst, l.map Prod.snd)
        theorem unzip_eq_map : ∀ {l : List (α × β)}, unzip l = (l.map Prod.fst, l.map Prod.snd)
  | [] => rfl
  | (a, b)(a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map (l := l)]
        List.zip_unzip
      theorem
     : ∀ l : List (α × β), zip (unzip l).1 (unzip l).2 = l
        theorem zip_unzip : ∀ l : List (α × β), zip (unzip l).1 (unzip l).2 = l
  | [] => rfl
  | (a, b)(a, b) :: l => by simp only [unzip_cons, zip_cons_cons, zip_unzip l]
        List.unzip_zip_left
      theorem
     : ∀ {l₁ : List α} {l₂ : List β}, length l₁ ≤ length l₂ → (unzip (zip l₁ l₂)).1 = l₁
        theorem unzip_zip_left :
    ∀ {l₁ : List α} {l₂ : List β}, length l₁ ≤ length l₂ → (unzip (zip l₁ l₂)).1 = l₁
  | [], _, _ => rfl
  | _, [], h => by rw [eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero h)]; rfl
  | _ :: _, _ :: _, h => by
    simp only [zip_cons_cons, unzip_cons, unzip_zip_left (le_of_succ_le_succ h)]
        List.unzip_zip_right
      theorem
     : ∀ {l₁ : List α} {l₂ : List β}, length l₂ ≤ length l₁ → (unzip (zip l₁ l₂)).2 = l₂
        theorem unzip_zip_right :
    ∀ {l₁ : List α} {l₂ : List β}, length l₂ ≤ length l₁ → (unzip (zip l₁ l₂)).2 = l₂
  | [], l₂, _ => by simp_all
  | l₁, [], _ => by simp
  | a :: l₁, b :: l₂, h => by
    simp only [zip_cons_cons, unzip_cons, unzip_zip_right (le_of_succ_le_succ h)]
        List.unzip_zip
      theorem
     {l₁ : List α} {l₂ : List β} (h : length l₁ = length l₂) : unzip (zip l₁ l₂) = (l₁, l₂)
        theorem unzip_zip {l₁ : List α} {l₂ : List β} (h : length l₁ = length l₂) :
    unzip (zip l₁ l₂) = (l₁, l₂) := by
  ext
  · rw [unzip_zip_left (Nat.le_of_eq h)]
  · rw [unzip_zip_right (Nat.le_of_eq h.symm)]
        List.zip_of_prod
      theorem
     {l : List α} {l' : List β} {xs : List (α × β)} (hl : xs.map Prod.fst = l) (hr : xs.map Prod.snd = l') : xs = l.zip l'
        
      List.tail_zip_fst
      theorem
     {l : List (α × β)} : l.unzip.1.tail = l.tail.unzip.1
        
      List.tail_zip_snd
      theorem
     {l : List (α × β)} : l.unzip.2.tail = l.tail.unzip.2
        
      List.unzip_replicate
      theorem
     {n : Nat} {a : α} {b : β} : unzip (replicate n (a, b)) = (replicate n a, replicate n b)
        @[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
    unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
  ext1 <;> simp