lean4-htt/src/Init/Data/List/Range.lean
2025-05-30 00:09:51 +00:00

399 lines
15 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) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
module
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Zip
/-!
# Lemmas about `List.range` and `List.zipIdx`
Most of the results are deferred to `Data.Init.List.Nat.Range`, where more results about
natural arithmetic are available.
-/
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 List
open Nat
/-! ## Ranges and enumeration -/
/-! ### range' -/
theorem range'_succ {s n step} : range' s (n + 1) step = s :: range' (s + step) n step := by
simp [range', Nat.add_succ, Nat.mul_succ]
@[simp] theorem length_range' {s step} : ∀ {n : Nat}, length (range' s n step) = n
| 0 => rfl
| _ + 1 => congrArg succ length_range'
@[simp] theorem range'_eq_nil_iff : range' s n step = [] ↔ n = 0 := by
rw [← length_eq_zero_iff, length_range']
@[deprecated range'_eq_nil_iff (since := "2025-01-29")] abbrev range'_eq_nil := @range'_eq_nil_iff
theorem range'_ne_nil_iff (s : Nat) {n step : Nat} : range' s n step ≠ [] ↔ n ≠ 0 := by
cases n <;> simp
@[deprecated range'_ne_nil_iff (since := "2025-01-29")] abbrev range'_ne_nil := @range'_ne_nil_iff
@[simp] theorem range'_zero : range' s 0 step = [] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
@[simp] theorem tail_range' : (range' s n step).tail = range' (s + step) (n - 1) step := by
cases n with
| zero => simp
| succ n => simp [range'_succ]
@[simp] theorem range'_inj : range' s n = range' s' n' ↔ n = n' ∧ (n = 0 s = s') := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
subst h'
cases n with
| zero => simp
| succ n =>
simp only [range'_succ] at h
simp_all
· rintro ⟨rfl, rfl | rfl⟩ <;> simp
theorem mem_range' : ∀ {n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step * i
| 0 => by simp [range', Nat.not_lt_zero]
| n + 1 => by
have h (i) : i ≤ n ↔ i = 0 ∃ j, i = succ j ∧ j < n := by
cases i <;> simp [Nat.succ_le, Nat.succ_inj]
simp [range', mem_range', Nat.lt_succ, h]; simp only [← exists_and_right, and_assoc]
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
theorem getElem?_range' {s step} :
∀ {i n : Nat}, i < n → (range' s n step)[i]? = some (s + step * i)
| 0, n + 1, _ => by simp [range'_succ]
| m + 1, n + 1, h => by
simp only [range'_succ, getElem?_cons_succ]
exact (getElem?_range' (s := s + step) (by exact succ_lt_succ_iff.mp h)).trans <| by
simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem getElem_range' {n m step} {i} (H : i < (range' n m step).length) :
(range' n m step)[i] = n + step * i :=
(getElem?_eq_some_iff.1 <| getElem?_range' (by simpa using H)).2
theorem head?_range' : (range' s n).head? = if n = 0 then none else some s := by
induction n <;> simp_all [range'_succ, head?_append]
@[simp] theorem head_range' (h) : (range' s n).head h = s := by
repeat simp_all [head?_range', head_eq_iff_head?_eq_some]
theorem map_add_range' {a} : ∀ s n step, map (a + ·) (range' s n step) = range' (a + s) n step
| _, 0, _ => rfl
| s, n + 1, step => by simp [range', map_add_range' (s + step), Nat.add_assoc]
theorem range'_succ_left : range' (s + 1) n step = (range' s n step).map (· + 1) := by
apply ext_getElem
· simp
· simp [Nat.add_right_comm]
theorem range'_append : ∀ {s m n step : Nat},
range' s m step ++ range' (s + step * m) n step = range' s (m + n) step
| _, 0, _, _ => by simp
| s, m + 1, n, step => by
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
using range'_append (s := s + step)
@[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 (step := 1)
theorem range'_sublist_right {s m n : Nat} : range' s m step <+ range' s n step ↔ m ≤ n :=
⟨fun h => by simpa only [length_range'] using h.length_le,
fun h => by rw [← add_sub_of_le h, ← range'_append]; apply sublist_append_left⟩
theorem range'_subset_right {s m n : Nat} (step0 : 0 < step) :
range' s m step ⊆ range' s n step ↔ m ≤ n := by
refine ⟨fun h => Nat.le_of_not_lt fun hn => ?_, fun h => (range'_sublist_right.2 h).subset⟩
have ⟨i, h', e⟩ := mem_range'.1 <| h <| mem_range'.2 ⟨_, hn, rfl⟩
exact Nat.ne_of_gt h' (Nat.eq_of_mul_eq_mul_left step0 (Nat.add_left_cancel e))
theorem range'_subset_right_1 {s m n : Nat} : range' s m ⊆ range' s n ↔ m ≤ n :=
range'_subset_right (by decide)
theorem range'_concat {s n : Nat} : range' s (n + 1) step = range' s n step ++ [s + step * n] := by
exact range'_append.symm
theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ [s + n] := by
simp [range'_concat]
theorem range'_eq_cons_iff : range' s n = a :: xs ↔ s = a ∧ 0 < n ∧ xs = range' (a + 1) (n - 1) := by
induction n generalizing s with
| zero => simp
| succ n ih =>
simp only [range'_succ]
simp only [cons.injEq, and_congr_right_iff]
rintro rfl
simp [eq_comm]
/-! ### range -/
@[simp, grind =] theorem range_one : range 1 = [0] := rfl
theorem range_loop_range' : ∀ s n, range.loop s (range' s n) = range' 0 (n + s)
| 0, _ => rfl
| s + 1, n => by rw [← Nat.add_assoc, Nat.add_right_comm n s 1]; exact range_loop_range' s (n + 1)
theorem range_eq_range' {n : Nat} : range n = range' 0 n :=
(range_loop_range' n 0).trans <| by rw [Nat.zero_add]
theorem getElem?_range {i n : Nat} (h : i < n) : (range n)[i]? = some i := by
simp [range_eq_range', getElem?_range' h]
@[simp] theorem getElem_range (h : j < (range n).length) : (range n)[j] = j := by
simp [range_eq_range']
theorem range_succ_eq_map {n : Nat} : range (n + 1) = 0 :: map succ (range n) := by
rw [range_eq_range', range_eq_range', range', Nat.add_comm, ← map_add_range']
congr; exact funext (Nat.add_comm 1)
theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
@[simp] theorem length_range {n : Nat} : (range n).length = n := by
simp only [range_eq_range', length_range']
@[simp] theorem range_eq_nil {n : Nat} : range n = [] ↔ n = 0 := by
rw [← length_eq_zero_iff, length_range]
theorem range_ne_nil {n : Nat} : range n ≠ [] ↔ n ≠ 0 := by
cases n <;> simp
@[simp] theorem tail_range : (range n).tail = range' 1 (n - 1) := by
rw [range_eq_range', tail_range']
@[simp]
theorem range_sublist {m n : Nat} : range m <+ range n ↔ m ≤ n := by
simp only [range_eq_range', range'_sublist_right]
@[simp]
theorem range_subset {m n : Nat} : range m ⊆ range n ↔ m ≤ n := by
simp only [range_eq_range', range'_subset_right, lt_succ_self]
theorem range_succ {n : Nat} : range (succ n) = range n ++ [n] := by
simp only [range_eq_range', range'_1_concat, Nat.zero_add]
theorem range_add {n m : Nat} : range (n + m) = range n ++ (range m).map (n + ·) := by
rw [← range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 (s := 0)).symm
theorem head?_range {n : Nat} : (range n).head? = if n = 0 then none else some 0 := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, head?_append, ih]
split <;> simp_all
@[simp] theorem head_range {n : Nat} (h) : (range n).head h = 0 := by
cases n with
| zero => simp at h
| succ n => simp [head?_range, head_eq_iff_head?_eq_some]
theorem getLast?_range {n : Nat} : (range n).getLast? = if n = 0 then none else some (n - 1) := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, getLast?_append, ih]
split <;> simp_all
@[simp] theorem getLast_range {n : Nat} (h) : (range n).getLast h = n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range, getLast_eq_iff_getLast?_eq_some]
/-! ### zipIdx -/
@[simp]
theorem zipIdx_eq_nil_iff {l : List α} {i : Nat} : List.zipIdx l i = [] ↔ l = [] := by
cases l <;> simp
@[simp] theorem length_zipIdx : ∀ {l : List α} {i}, (zipIdx l i).length = l.length
| [], _ => rfl
| _ :: _, _ => congrArg Nat.succ length_zipIdx
@[simp]
theorem getElem?_zipIdx :
∀ {l : List α} {i j}, (zipIdx l i)[j]? = l[j]?.map fun a => (a, i + j)
| [], _, _ => rfl
| _ :: _, _, 0 => by simp
| _ :: l, n, m + 1 => by
simp only [zipIdx_cons, getElem?_cons_succ]
exact getElem?_zipIdx.trans <| by rw [Nat.add_right_comm]; rfl
@[simp]
theorem getElem_zipIdx {l : List α} (h : i < (l.zipIdx j).length) :
(l.zipIdx j)[i] = (l[i]'(by simpa [length_zipIdx] using h), j + i) := by
simp only [length_zipIdx] at h
rw [getElem_eq_getElem?_get]
simp only [getElem?_zipIdx, getElem?_eq_getElem h]
simp
@[simp]
theorem tail_zipIdx {l : List α} {i : Nat} : (zipIdx l i).tail = zipIdx l.tail (i + 1) := by
induction l generalizing i with
| nil => simp
| cons _ l ih => simp [ih, zipIdx_cons]
theorem map_snd_add_zipIdx_eq_zipIdx {l : List α} {n k : Nat} :
map (Prod.map id (· + n)) (zipIdx l k) = zipIdx l (n + k) :=
ext_getElem? fun i ↦ by simp [(· ∘ ·), Nat.add_comm, Nat.add_left_comm]; rfl
theorem zipIdx_cons' {i : Nat} {x : α} {xs : List α} :
zipIdx (x :: xs) i = (x, i) :: (zipIdx xs i).map (Prod.map id (· + 1)) := by
rw [zipIdx_cons, Nat.add_comm, ← map_snd_add_zipIdx_eq_zipIdx]
-- Arguments are explicit for parity with `zipIdx_map_fst`.
@[simp]
theorem zipIdx_map_snd (i : Nat) :
∀ (l : List α), map Prod.snd (zipIdx l i) = range' i l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (zipIdx_map_snd ..)
-- Arguments are explicit so we can rewrite from right to left.
@[simp]
theorem zipIdx_map_fst : ∀ i (l : List α), map Prod.fst (zipIdx l i) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (zipIdx_map_fst ..)
theorem zipIdx_eq_zip_range' {l : List α} {i : Nat} : l.zipIdx i = l.zip (range' i l.length) :=
zip_of_prod (zipIdx_map_fst ..) (zipIdx_map_snd ..)
@[simp]
theorem unzip_zipIdx_eq_prod {l : List α} {i : Nat} :
(l.zipIdx i).unzip = (l, range' i l.length) := by
simp only [zipIdx_eq_zip_range', unzip_zip, length_range']
/-- Replace `zipIdx` with a starting index `n+1` with `zipIdx` starting from `n`,
followed by a `map` increasing the indices by one. -/
theorem zipIdx_succ {l : List α} {i : Nat} :
l.zipIdx (i + 1) = (l.zipIdx i).map (fun ⟨a, i⟩ => (a, i + 1)) := by
induction l generalizing i with
| nil => rfl
| cons _ _ ih => simp only [zipIdx_cons, ih, map_cons]
/-- Replace `zipIdx` with a starting index with `zipIdx` starting from 0,
followed by a `map` increasing the indices. -/
theorem zipIdx_eq_map_add {l : List α} {i : Nat} :
l.zipIdx i = l.zipIdx.map (fun ⟨a, j⟩ => (a, i + j)) := by
induction l generalizing i with
| nil => rfl
| cons _ _ ih => simp [ih (i := i + 1), zipIdx_succ, Nat.add_assoc, Nat.add_comm 1]
/-! ### enumFrom -/
section
set_option linter.deprecated false
@[deprecated zipIdx_eq_nil_iff (since := "2025-01-21"), simp]
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] ↔ l = [] := by
cases l <;> simp
@[deprecated length_zipIdx (since := "2025-01-21"), simp]
theorem enumFrom_length : ∀ {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enumFrom :
∀ i (l : List α) j, (enumFrom i l)[j]? = l[j]?.map fun a => (i + j, a)
| _, [], _ => rfl
| _, _ :: _, 0 => by simp
| n, _ :: l, m + 1 => by
simp only [enumFrom_cons, getElem?_cons_succ]
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
@[deprecated getElem_zipIdx (since := "2025-01-21"), simp]
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
simp only [enumFrom_length] at h
rw [getElem_eq_getElem?_get]
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
simp
@[deprecated tail_zipIdx (since := "2025-01-21"), simp]
theorem tail_enumFrom (l : List α) (n : Nat) : (enumFrom n l).tail = enumFrom (n + 1) l.tail := by
induction l generalizing n with
| nil => simp
| cons _ l ih => simp [ih, enumFrom_cons]
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
ext_getElem? fun i ↦ by simp [(· ∘ ·), Nat.add_comm, Nat.add_left_comm]; rfl
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
map_fst_add_enumFrom_eq_enumFrom l _ _
@[deprecated zipIdx_cons' (since := "2025-01-21"), simp]
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
rw [enumFrom_cons, Nat.add_comm, ← map_fst_add_enumFrom_eq_enumFrom]
@[deprecated zipIdx_map_snd (since := "2025-01-21"), simp]
theorem enumFrom_map_fst (n) :
∀ (l : List α), map Prod.fst (enumFrom n l) = range' n l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
@[deprecated zipIdx_map_fst (since := "2025-01-21"), simp]
theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
@[deprecated zipIdx_eq_zip_range' (since := "2025-01-21")]
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
@[deprecated unzip_zipIdx_eq_prod (since := "2025-01-21"), simp]
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
(l.enumFrom n).unzip = (range' n l.length, l) := by
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
end
/-! ### enum -/
section
set_option linter.deprecated false
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons' (x : α) (xs : List α) :
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
enumFrom_cons' _ _ _
@[deprecated "These are now both `l.zipIdx 0`" (since := "2025-01-21")]
theorem enum_eq_enumFrom {l : List α} : l.enum = l.enumFrom 0 := rfl
@[deprecated "Use the reverse direction of `map_snd_add_zipIdx_eq_zipIdx` instead" (since := "2025-01-21")]
theorem enumFrom_eq_map_enum (l : List α) (n : Nat) :
enumFrom n l = (enum l).map (Prod.map (· + n) id) := by
induction l generalizing n with
| nil => simp
| cons x xs ih =>
simp only [enumFrom_cons, ih, enum_cons, map_cons, Prod.map_apply, Nat.zero_add, id_eq, map_map,
cons.injEq, map_inj_left, Function.comp_apply, Prod.forall, Prod.mk.injEq, and_true, true_and]
intro a b _
exact (succ_add a n).symm
end
end List