lean4-htt/src/Init/Data/Vector/Zip.lean
Sebastian Ullrich 01dbbeed99
feat: do not export def bodies by default (#8221)
This PR adjusts the experimental module system to not export the bodies
of `def`s unless opted out by the new attribute `@[expose]` on the `def`
or on a surrounding `section`.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-05-15 12:16:54 +00:00

306 lines
12 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Zip
import all Init.Data.Vector.Basic
import Init.Data.Vector.Lemmas
/-!
# Lemmas about `Vector.zip`, `Vector.zipWith`, `Vector.zipWithAll`, and `Vector.unzip`.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Vector
open Nat
/-! ## Zippers -/
/-! ### zipWith -/
theorem zipWith_comm {f : α → β → γ} {as : Vector α n} {bs : Vector β n} :
zipWith f as bs = zipWith (fun b a => f a b) bs as := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simpa using Array.zipWith_comm
theorem zipWith_comm_of_comm {f : αα → β} (comm : ∀ x y : α, f x y = f y x) {xs ys : Vector α n} :
zipWith f xs ys = zipWith f ys xs := by
rw [zipWith_comm]
simp only [comm]
@[simp]
theorem zipWith_self {f : αα → δ} {xs : Vector α n} : zipWith f xs xs = xs.map fun a => f a a := by
cases xs
simp
/--
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
cases as
cases bs
simp [Array.getElem?_zipWith]
rfl
/-- Variant of `getElem?_zipWith` using `Option.map` and `Option.bind` rather than a `match`. -/
theorem getElem?_zipWith' {f : α → β → γ} {i : Nat} :
(zipWith f as bs)[i]? = (as[i]?.map f).bind fun g => bs[i]?.map g := by
cases as
cases bs
simp [Array.getElem?_zipWith']
theorem getElem?_zipWith_eq_some {f : α → β → γ} {as : Vector α n} {bs : Vector β n} {z : γ} {i : Nat} :
(zipWith f as bs)[i]? = some z ↔
∃ x y, as[i]? = some x ∧ bs[i]? = some y ∧ f x y = z := by
cases as
cases bs
simp [Array.getElem?_zipWith_eq_some]
theorem getElem?_zip_eq_some {as : Vector α n} {bs : Vector β n} {z : α × β} {i : Nat} :
(zip as bs)[i]? = some z ↔ as[i]? = some z.1 ∧ bs[i]? = some z.2 := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.getElem?_zip_eq_some]
@[simp]
theorem zipWith_map {μ} {f : γ → δ → μ} {g : αγ} {h : β → δ} {as : Vector α n} {bs : Vector β n} :
zipWith f (as.map g) (bs.map h) = zipWith (fun a b => f (g a) (h b)) as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.zipWith_map]
theorem zipWith_map_left {as : Vector α n} {bs : Vector β n} {f : αα'} {g : α' → β → γ} :
zipWith g (as.map f) bs = zipWith (fun a b => g (f a) b) as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.zipWith_map_left]
theorem zipWith_map_right {as : Vector α n} {bs : Vector β n} {f : β → β'} {g : α → β' → γ} :
zipWith g as (bs.map f) = zipWith (fun a b => g a (f b)) as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.zipWith_map_right]
theorem zipWith_foldr_eq_zip_foldr {f : α → β → γ} {i : δ} :
(zipWith f as bs).foldr g i = (zip as bs).foldr (fun p r => g (f p.1 p.2) r) i := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simpa using Array.zipWith_foldr_eq_zip_foldr
theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
(zipWith f as bs).foldl g i = (zip as bs).foldl (fun r p => g r (f p.1 p.2)) i := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simpa using Array.zipWith_foldl_eq_zip_foldl
theorem map_zipWith {δ : Type _} {f : α → β} {g : γ → δ → α} {as : Vector γ n} {bs : Vector δ n} :
map f (zipWith g as bs) = zipWith (fun x y => f (g x y)) as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.map_zipWith]
theorem take_zipWith : (zipWith f as bs).take i = zipWith f (as.take i) (bs.take i) := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.take_zipWith]
theorem extract_zipWith : (zipWith f as bs).extract i j = zipWith f (as.extract i j) (bs.extract i j) := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.extract_zipWith]
theorem zipWith_append {f : α → β → γ}
{as : Vector α n} {as' : Vector α m} {bs : Vector β n} {bs' : Vector β m} :
zipWith f (as ++ as') (bs ++ bs') = zipWith f as bs ++ zipWith f as' bs' := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
rcases as' with ⟨as', rfl⟩
rcases bs' with ⟨bs', h'⟩
simp [Array.zipWith_append, *]
theorem zipWith_eq_append_iff {f : α → β → γ} {as : Vector α (n + m)} {bs : Vector β (n + m)} :
zipWith f as bs = xs ++ ys ↔
∃ as₁ as₂ bs₁ bs₂, as = as₁ ++ as₂ ∧ bs = bs₁ ++ bs₂ ∧ xs = zipWith f as₁ bs₁ ∧ ys = zipWith f as₂ bs₂ := by
rcases as with ⟨as, h₁⟩
rcases bs with ⟨bs, h₂⟩
rcases xs with ⟨xs, rfl⟩
rcases ys with ⟨ys, rfl⟩
simp only [mk_zipWith_mk, mk_append_mk, eq_mk, Array.zipWith_eq_append_iff,
mk_eq, toArray_append, toArray_zipWith]
constructor
· rintro ⟨as₁, as₂, bs₁, bs₂, h, rfl, rfl, rfl, rfl⟩
simp only [Array.size_append, Array.size_zipWith] at h₁ h₂
exact ⟨mk as₁ (by simp; omega), mk as₂ (by simp; omega), mk bs₁ (by simp; omega), mk bs₂ (by simp; omega), by simp⟩
· rintro ⟨⟨as₁, hw⟩, ⟨as₂, hx⟩, ⟨bs₁, hy⟩, ⟨bs₂, hz⟩, rfl, rfl, w₁, w₂⟩
simp only at w₁ w₂
exact ⟨as₁, as₂, bs₁, bs₂, by simpa [hw, hy] using ⟨w₁, w₂⟩⟩
@[simp] theorem zipWith_replicate {a : α} {b : β} {n : Nat} :
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
ext
simp
@[deprecated zipWith_replicate (since := "2025-03-18")]
abbrev zipWith_mkVector := @zipWith_replicate
theorem map_uncurry_zip_eq_zipWith {f : α → β → γ} {as : Vector α n} {bs : Vector β n} :
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.map_uncurry_zip_eq_zipWith]
theorem map_zip_eq_zipWith {f : α × β → γ} {as : Vector α n} {bs : Vector β n} :
map f (as.zip bs) = zipWith (Function.curry f) as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.map_zip_eq_zipWith]
theorem reverse_zipWith {f : α → β → γ} {as : Vector α n} {bs : Vector β n} :
(zipWith f as bs).reverse = zipWith f as.reverse bs.reverse := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.reverse_zipWith, h]
/-! ### zip -/
@[simp]
theorem getElem_zip {as : Vector α n} {bs : Vector β n} {i : Nat} {h : i < n} :
(zip as bs)[i] = (as[i], bs[i]) :=
getElem_zipWith ..
theorem zip_eq_zipWith {as : Vector α n} {bs : Vector β n} : zip as bs = zipWith Prod.mk as bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.zip_eq_zipWith, h]
theorem zip_map {f : αγ} {g : β → δ} {as : Vector α n} {bs : Vector β n} :
zip (as.map f) (bs.map g) = (zip as bs).map (Prod.map f g) := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.zip_map, h]
theorem zip_map_left {f : αγ} {as : Vector α n} {bs : Vector β n} :
zip (as.map f) bs = (zip as bs).map (Prod.map f id) := by rw [← zip_map, map_id]
theorem zip_map_right {f : β → γ} {as : Vector α n} {bs : Vector β n} :
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [← zip_map, map_id]
theorem zip_append {as : Vector α n} {bs : Vector β n} {as' : Vector α m} {bs' : Vector β m} :
zip (as ++ as') (bs ++ bs') = zip as bs ++ zip as' bs' := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
rcases as' with ⟨as', rfl⟩
rcases bs' with ⟨bs', h'⟩
simp [Array.zip_append, h, h']
theorem zip_map' {f : α → β} {g : αγ} {xs : Vector α n} :
zip (xs.map f) (xs.map g) = xs.map fun a => (f a, g a) := by
rcases xs with ⟨xs, rfl⟩
simp [Array.zip_map']
theorem of_mem_zip {a b} {as : Vector α n} {bs : Vector β n} : (a, b) ∈ zip as bs → a ∈ as ∧ b ∈ bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simpa using Array.of_mem_zip
-- The argument `as` is explicit so we can rewrite from right to left.
theorem map_fst_zip (as : Vector α n) {bs : Vector β n} :
map Prod.fst (zip as bs) = as := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.map_fst_zip, h]
-- The argument `bs` is explicit so we can rewrite from right to left.
theorem map_snd_zip {as : Vector α n} (bs : Vector β n) :
map Prod.snd (zip as bs) = bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.map_snd_zip, h]
theorem map_prod_left_eq_zip {xs : Vector α n} {f : α → β} :
(xs.map fun x => (x, f x)) = xs.zip (xs.map f) := by
rcases xs with ⟨xs, rfl⟩
rw [← zip_map']
congr
simp
theorem map_prod_right_eq_zip {xs : Vector α n} {f : α → β} :
(xs.map fun x => (f x, x)) = (xs.map f).zip xs := by
rcases xs with ⟨xs, rfl⟩
rw [← zip_map']
congr
simp
theorem zip_eq_append_iff {as : Vector α (n + m)} {bs : Vector β (n + m)} {xs : Vector (α × β) n} {ys : Vector (α × β) m} :
zip as bs = xs ++ ys ↔
∃ as₁ as₂ bs₁ bs₂, as = as₁ ++ as₂ ∧ bs = bs₁ ++ bs₂ ∧ xs = zip as₁ bs₁ ∧ ys = zip as₂ bs₂ := by
simp [zip_eq_zipWith, zipWith_eq_append_iff]
@[simp] theorem zip_replicate {a : α} {b : β} {n : Nat} :
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
ext <;> simp
@[deprecated zip_replicate (since := "2025-03-18")]
abbrev zip_mkVector := @zip_replicate
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip xs).fst = xs.map Prod.fst := by
cases xs
simp_all
@[simp] theorem unzip_snd : (unzip xs).snd = xs.map Prod.snd := by
cases xs
simp_all
theorem unzip_eq_map {xs : Vector (α × β) n} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs
simp [List.unzip_eq_map]
-- The argument `xs` is explicit so we can rewrite from right to left.
theorem zip_unzip (xs : Vector (α × β) n) : zip (unzip xs).1 (unzip xs).2 = xs := by
cases xs
simp only [unzip_mk, mk_zip_mk, Array.zip_unzip]
theorem unzip_zip_left {as : Vector α n} {bs : Vector β n} :
(unzip (zip as bs)).1 = as := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.unzip_zip_left, h, Array.map_fst_zip]
theorem unzip_zip_right {as : Vector α n} {bs : Vector β n} :
(unzip (zip as bs)).2 = bs := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.unzip_zip_right, h, Array.map_snd_zip]
theorem unzip_zip {as : Vector α n} {bs : Vector β n} :
unzip (zip as bs) = (as, bs) := by
rcases as with ⟨as, rfl⟩
rcases bs with ⟨bs, h⟩
simp [Array.unzip_zip, h, Array.map_fst_zip, Array.map_snd_zip]
theorem zip_of_prod {as : Vector α n} {bs : Vector β n} {xs : Vector (α × β) n} (hl : xs.map Prod.fst = as)
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
rw [← hl, ← hr, ← zip_unzip xs, ← unzip_fst, ← unzip_snd, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {a : α} {b : β} {n : Nat} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp
@[deprecated unzip_replicate (since := "2025-03-18")]
abbrev unzip_mkVector := @unzip_replicate
end Vector