lean4-htt/src/Init/Data/Vector/Range.lean
Kim Morrison 40eefb1df6
feat: alignment of List/Array/Vector lemmas about range, range', zipIdx (#6878)
This PR completes alignments of `List/Array/Vector` lemmas about
`range`, `range'`, and `zipIdx`.
2025-01-31 00:06:51 +00:00

271 lines
10 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
-/
prelude
import Init.Data.Vector.Lemmas
import Init.Data.Vector.Zip
import Init.Data.Vector.MapIdx
import Init.Data.Array.Range
/-!
# Lemmas about `Vector.range'`, `Vector.range`, and `Vector.zipIdx`
-/
namespace Vector
open Nat
/-! ## Ranges and enumeration -/
/-! ### range' -/
@[simp] theorem toArray_range' (start size step) :
(range' start size step).toArray = Array.range' start size step := by
rfl
theorem range'_eq_mk_range' (start size step) :
range' start size step = Vector.mk (Array.range' start size step) (by simp) := by
rfl
@[simp] theorem getElem_range' (start size step i) (h : i < size) :
(range' start size step)[i] = start + step * i := by
simp [range', h]
@[simp] theorem getElem?_range' (start size step i) :
(range' start size step)[i]? = if i < size then some (start + step * i) else none := by
simp [getElem?_def, range']
theorem range'_succ (s n step) :
range' s (n + 1) step = (#v[s] ++ range' (s + step) n step).cast (by omega) := by
rw [← toArray_inj]
simp [Array.range'_succ]
theorem range'_zero : range' s 0 step = #v[] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #v[s] := rfl
@[simp] theorem range'_inj : range' s n = range' s' n ↔ (n = 0 s = s') := by
rw [← toArray_inj]
simp [List.range'_inj]
theorem mem_range' {n} : m ∈ range' s n step ↔ ∃ i < n, m = s + step * i := by
simp [range', Array.mem_range']
theorem pop_range' : (range' s n step).pop = range' s (n - 1) step := by
ext <;> simp
theorem map_add_range' (a) (s n step) : map (a + ·) (range' s n step) = range' (a + s) n step := by
ext <;> simp <;> omega
theorem range'_succ_left : range' (s + 1) n step = (range' s n step).map (· + 1) := by
ext <;> simp <;> omega
theorem range'_append (s m n step : Nat) :
range' s m step ++ range' (s + step * m) n step = range' s (m + n) step := by
rw [← toArray_inj]
simp [Array.range'_append]
@[simp] theorem range'_append_1 (s m n : Nat) :
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append s m n 1
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ #v[s + step * n] := by
exact (range'_append s n 1 step).symm
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ #v[s + n] := by
simp [range'_concat]
@[simp] theorem mem_range'_1 : m ∈ range' s n ↔ s ≤ m ∧ m < s + n := by
simp [mem_range']; exact ⟨
fun ⟨i, h, e⟩ => e ▸ ⟨Nat.le_add_right .., Nat.add_lt_add_left h _⟩,
fun ⟨h₁, h₂⟩ => ⟨m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm⟩⟩
theorem map_sub_range' (a s n : Nat) (h : a ≤ s) :
map (· - a) (range' s n step) = range' (s - a) n step := by
conv => lhs; rw [← Nat.add_sub_cancel' h]
rw [← map_add_range', map_map, (?_ : _∘_ = _), map_id]
funext x; apply Nat.add_sub_cancel_left
theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n ∧ ys = range' (s + n) m := by
simp only [← toArray_inj, toArray_range', toArray_append, Array.range'_eq_append_iff]
constructor
· rintro ⟨k, hk, h₁, h₂⟩
have w : k = n := by
replace h₁ := congrArg Array.size h₁
simp_all
subst w
simp_all
omega
· rintro ⟨h₁, h₂⟩
exact ⟨n, by omega, by simp_all; omega⟩
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat → Bool} :
(range' s n).find? p = some i ↔ p i ∧ i ∈ range' s n ∧ ∀ j, s ≤ j → j < i → !p j := by
simp [range'_eq_mk_range']
@[simp] theorem find?_range'_eq_none {s n : Nat} {p : Nat → Bool} :
(range' s n).find? p = none ↔ ∀ i, s ≤ i → i < s + n → !p i := by
simp [range'_eq_mk_range']
/-! ### range -/
theorem range_eq_range' (n : Nat) : range n = range' 0 n := by
simp [range, range', Array.range_eq_range']
theorem range_succ_eq_map (n : Nat) : range (n + 1) =
(#v[0] ++ map succ (range n)).cast (by omega) := by
rw [← toArray_inj]
simp [Array.range_succ_eq_map]
theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
theorem range_succ (n : Nat) : range (succ n) = range n ++ #v[n] := by
rw [← toArray_inj]
simp [Array.range_succ]
theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·) := by
rw [← range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
theorem reverse_range' (s n : Nat) : reverse (range' s n) = map (s + n - 1 - ·) (range n) := by
simp [← toList_inj, List.reverse_range']
@[simp]
theorem mem_range {m n : Nat} : m ∈ range n ↔ m < n := by
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
theorem not_mem_range_self {n : Nat} : n ∉ range n := by simp
theorem self_mem_range_succ (n : Nat) : n ∈ range (n + 1) := by simp
@[simp] theorem take_range (m n : Nat) : take (range n) m = range (min m n) := by
ext <;> simp
erw [getElem_extract] -- Why is an `erw` needed here? This should be by simp!
simp
@[simp] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat → Bool} :
(range n).find? p = some i ↔ p i ∧ i ∈ range n ∧ ∀ j, j < i → !p j := by
simp [range_eq_range']
@[simp] theorem find?_range_eq_none {n : Nat} {p : Nat → Bool} :
(range n).find? p = none ↔ ∀ i, i < n → !p i := by
simp [range_eq_range']
/-! ### zipIdx -/
@[simp]
theorem getElem?_zipIdx (l : Vector α n) (n m) : (zipIdx l n)[m]? = l[m]?.map fun a => (a, n + m) := by
simp [getElem?_def]
theorem map_snd_add_zipIdx_eq_zipIdx (l : Vector α n) (m k : Nat) :
map (Prod.map id (· + m)) (zipIdx l k) = zipIdx l (m + k) := by
ext <;> simp <;> omega
@[simp]
theorem zipIdx_map_snd (m) (l : Vector α n) : map Prod.snd (zipIdx l m) = range' m n := by
rcases l with ⟨l, rfl⟩
simp [Array.zipIdx_map_snd]
@[simp]
theorem zipIdx_map_fst (m) (l : Vector α n) : map Prod.fst (zipIdx l m) = l := by
rcases l with ⟨l, rfl⟩
simp [Array.zipIdx_map_fst]
theorem zipIdx_eq_zip_range' (l : Vector α n) : l.zipIdx m = l.zip (range' m n) := by
simp [zip_of_prod (zipIdx_map_fst _ _) (zipIdx_map_snd _ _)]
@[simp]
theorem unzip_zipIdx_eq_prod (l : Vector α n) {m : Nat} :
(l.zipIdx m).unzip = (l, range' m n) := by
simp only [zipIdx_eq_zip_range', unzip_zip]
/-- Replace `zipIdx` with a starting index `m+1` with `zipIdx` starting from `m`,
followed by a `map` increasing the indices by one. -/
theorem zipIdx_succ (l : Vector α n) (m : Nat) :
l.zipIdx (m + 1) = (l.zipIdx m).map (fun ⟨a, i⟩ => (a, i + 1)) := by
rcases l with ⟨l, rfl⟩
simp [Array.zipIdx_succ]
/-- Replace `zipIdx` with a starting index with `zipIdx` starting from 0,
followed by a `map` increasing the indices. -/
theorem zipIdx_eq_map_add (l : Vector α n) (m : Nat) :
l.zipIdx m = l.zipIdx.map (fun ⟨a, i⟩ => (a, m + i)) := by
rcases l with ⟨l, rfl⟩
simp only [zipIdx_mk, map_mk, eq_mk]
rw [Array.zipIdx_eq_map_add]
@[simp]
theorem zipIdx_singleton (x : α) (k : Nat) : zipIdx #v[x] k = #v[(x, k)] :=
rfl
theorem mk_add_mem_zipIdx_iff_getElem? {k i : Nat} {x : α} {l : Vector α n} :
(x, k + i) ∈ zipIdx l k ↔ l[i]? = some x := by
simp [mem_iff_getElem?, and_left_comm]
theorem le_snd_of_mem_zipIdx {x : α × Nat} {k : Nat} {l : Vector α n} (h : x ∈ zipIdx l k) :
k ≤ x.2 :=
(mk_mem_zipIdx_iff_le_and_getElem?_sub.1 h).1
theorem snd_lt_add_of_mem_zipIdx {x : α × Nat} {l : Vector α n} {k : Nat} (h : x ∈ zipIdx l k) :
x.2 < k + n := by
rcases mem_iff_getElem.1 h with ⟨i, h', rfl⟩
simpa using h'
theorem snd_lt_of_mem_zipIdx {x : α × Nat} {l : Vector α n} {k : Nat} (h : x ∈ l.zipIdx k) :
x.2 < n + k := by
simpa [Nat.add_comm] using snd_lt_add_of_mem_zipIdx h
theorem map_zipIdx (f : α → β) (l : Vector α n) (k : Nat) :
map (Prod.map f id) (zipIdx l k) = zipIdx (l.map f) k := by
cases l
simp [Array.map_zipIdx]
theorem fst_mem_of_mem_zipIdx {x : α × Nat} {l : Vector α n} {k : Nat} (h : x ∈ zipIdx l k) : x.1 ∈ l :=
zipIdx_map_fst k l ▸ mem_map_of_mem _ h
theorem fst_eq_of_mem_zipIdx {x : α × Nat} {l : Vector α n} {k : Nat} (h : x ∈ zipIdx l k) :
x.1 = l[x.2 - k]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) := by
cases l
exact Array.fst_eq_of_mem_zipIdx (by simpa using h)
theorem mem_zipIdx {x : α} {i : Nat} {xs : Vector α n} {k : Nat} (h : (x, i) ∈ xs.zipIdx k) :
k ≤ i ∧ i < k + n ∧
x = xs[i - k]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) :=
⟨le_snd_of_mem_zipIdx h, snd_lt_add_of_mem_zipIdx h, fst_eq_of_mem_zipIdx h⟩
/-- Variant of `mem_zipIdx` specialized at `k = 0`. -/
theorem mem_zipIdx' {x : α} {i : Nat} {xs : Vector α n} (h : (x, i) ∈ xs.zipIdx) :
i < n ∧ x = xs[i]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) :=
⟨by simpa using snd_lt_add_of_mem_zipIdx h, fst_eq_of_mem_zipIdx h⟩
theorem zipIdx_map (l : Vector α n) (k : Nat) (f : α → β) :
zipIdx (l.map f) k = (zipIdx l k).map (Prod.map f id) := by
cases l
simp [Array.zipIdx_map]
theorem zipIdx_append (xs : Vector α n) (ys : Vector α m) (k : Nat) :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + n) := by
rcases xs with ⟨xs, rfl⟩
rcases ys with ⟨ys, rfl⟩
simp [Array.zipIdx_append]
theorem zipIdx_eq_append_iff {l : Vector α (n + m)} {k : Nat} :
zipIdx l k = l₁ ++ l₂ ↔
∃ (l₁' : Vector α n) (l₂' : Vector α m),
l = l₁' ++ l₂' ∧ l₁ = zipIdx l₁' k ∧ l₂ = zipIdx l₂' (k + n) := by
rcases l with ⟨l, h⟩
rcases l₁ with ⟨l₁, rfl⟩
rcases l₂ with ⟨l₂, rfl⟩
simp only [zipIdx_mk, mk_append_mk, eq_mk, Array.zipIdx_eq_append_iff, mk_eq, toArray_append,
toArray_zipIdx]
constructor
· rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
exact ⟨⟨l₁', by simp⟩, ⟨l₂', by simp⟩, by simp⟩
· rintro ⟨⟨l₁', h₁⟩, ⟨l₂', h₂⟩, rfl, w₁, w₂⟩
exact ⟨l₁', l₂', by simp, w₁, by simp [h₁, w₂]⟩
end Vector