| /- | |
| Copyright (c) 2018 Microsoft Corporation. All rights reserved. | |
| Released under Apache 2.0 license as described in the file LICENSE. | |
| Authors: Leonardo de Moura | |
| -/ | |
| prelude | |
| import Init.WFTactics | |
| import Init.Data.Nat.Basic | |
| import Init.Data.Fin.Basic | |
| import Init.Data.UInt.BasicAux | |
| import Init.Data.Repr | |
| import Init.Data.ToString.Basic | |
| import Init.GetElem | |
| import Init.Data.List.ToArrayImpl | |
| import Init.Data.Array.Set | |
| universe u v w | |
| /-! ### Array literal syntax -/ | |
| syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term | |
| macro_rules | |
| | `(#[ $elems,* ]) => `(List.toArray [ $elems,* ]) | |
| variable {α : Type u} | |
| namespace Array | |
| @[deprecated toList (since := "2024-10-13")] abbrev data := @toList | |
| /-! ### Preliminary theorems -/ | |
| @[simp] theorem size_set (a : Array α) (i : Nat) (v : α) (h : i < a.size) : | |
| (set a i v h).size = a.size := | |
| List.length_set .. | |
| @[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 := | |
| List.length_concat .. | |
| theorem ext (a b : Array α) | |
| (h₁ : a.size = b.size) | |
| (h₂ : (i : Nat) → (hi₁ : i < a.size) → (hi₂ : i < b.size) → a[i] = b[i]) | |
| : a = b := by | |
| let rec extAux (a b : List α) | |
| (h₁ : a.length = b.length) | |
| (h₂ : (i : Nat) → (hi₁ : i < a.length) → (hi₂ : i < b.length) → a.get ⟨i, hi₁⟩ = b.get ⟨i, hi₂⟩) | |
| : a = b := by | |
| induction a generalizing b with | |
| | nil => | |
| cases b with | |
| | nil => rfl | |
| | cons b bs => rw [List.length_cons] at h₁; injection h₁ | |
| | cons a as ih => | |
| cases b with | |
| | nil => rw [List.length_cons] at h₁; injection h₁ | |
| | cons b bs => | |
| have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ | |
| have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ | |
| have headEq : a = b := h₂ 0 hz₁ hz₂ | |
| have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁ | |
| have h₂' : (i : Nat) → (hi₁ : i < as.length) → (hi₂ : i < bs.length) → as.get ⟨i, hi₁⟩ = bs.get ⟨i, hi₂⟩ := by | |
| intro i hi₁ hi₂ | |
| have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption | |
| have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption | |
| have : (a::as).get ⟨i+1, hi₁'⟩ = (b::bs).get ⟨i+1, hi₂'⟩ := h₂ (i+1) hi₁' hi₂' | |
| apply this | |
| have tailEq : as = bs := ih bs h₁' h₂' | |
| rw [headEq, tailEq] | |
| cases a; cases b | |
| apply congrArg | |
| apply extAux | |
| assumption | |
| assumption | |
| theorem ext' {as bs : Array α} (h : as.toList = bs.toList) : as = bs := by | |
| cases as; cases bs; simp at h; rw [h] | |
| @[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).toList = acc.toList ++ as := by | |
| induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append] | |
| -- This does not need to be a simp lemma, as already after the `whnfR` the right hand side is `as`. | |
| theorem toList_toArray (as : List α) : as.toArray.toList = as := rfl | |
| @[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size] | |
| @[simp] theorem getElem_toList {a : Array α} {i : Nat} (h : i < a.size) : a.toList[i] = a[i] := rfl | |
| @[simp] theorem getElem?_toList {a : Array α} {i : Nat} : a.toList[i]? = a[i]? := rfl | |
| /-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/ | |
| -- NB: This is defined as a structure rather than a plain def so that a lemma | |
| -- like `sizeOf_lt_of_mem` will not apply with no actual arrays around. | |
| structure Mem (as : Array α) (a : α) : Prop where | |
| val : a ∈ as.toList | |
| instance : Membership α (Array α) where | |
| mem := Mem | |
| theorem mem_def {a : α} {as : Array α} : a ∈ as ↔ a ∈ as.toList := | |
| ⟨fun | .mk h => h, Array.Mem.mk⟩ | |
| @[simp] theorem mem_toArray {a : α} {l : List α} : a ∈ l.toArray ↔ a ∈ l := by | |
| simp [mem_def] | |
| @[simp] theorem getElem_mem {l : Array α} {i : Nat} (h : i < l.size) : l[i] ∈ l := by | |
| rw [Array.mem_def, ← getElem_toList] | |
| apply List.getElem_mem | |
| end Array | |
| namespace List | |
| @[simp] theorem toArray_toList (a : Array α) : a.toList.toArray = a := rfl | |
| @[simp] theorem getElem_toArray {a : List α} {i : Nat} (h : i < a.toArray.size) : | |
| a.toArray[i] = a[i]'(by simpa using h) := rfl | |
| @[simp] theorem getElem?_toArray {a : List α} {i : Nat} : a.toArray[i]? = a[i]? := rfl | |
| @[simp] theorem getElem!_toArray [Inhabited α] {a : List α} {i : Nat} : | |
| a.toArray[i]! = a[i]! := rfl | |
| end List | |
| namespace Array | |
| @[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @toList_toArray | |
| @[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList | |
| /-! ### Externs -/ | |
| /-- Low-level version of `size` that directly queries the C array object cached size. | |
| While this is not provable, `usize` always returns the exact size of the array since | |
| the implementation only supports arrays of size less than `USize.size`. | |
| -/ | |
| @[extern "lean_array_size", simp] | |
| def usize (a : @& Array α) : USize := a.size.toUSize | |
| /-- Low-level version of `fget` which is as fast as a C array read. | |
| `Fin` values are represented as tag pointers in the Lean runtime. Thus, | |
| `fget` may be slightly slower than `uget`. -/ | |
| @[extern "lean_array_uget", simp] | |
| def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α := | |
| a[i.toNat] | |
| /-- Low-level version of `fset` which is as fast as a C array fset. | |
| `Fin` values are represented as tag pointers in the Lean runtime. Thus, | |
| `fset` may be slightly slower than `uset`. -/ | |
| @[extern "lean_array_uset"] | |
| def uset (a : Array α) (i : USize) (v : α) (h : i.toNat < a.size) : Array α := | |
| a.set i.toNat v h | |
| @[extern "lean_array_pop"] | |
| def pop (a : Array α) : Array α where | |
| toList := a.toList.dropLast | |
| @[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by | |
| match a with | |
| | ⟨[]⟩ => rfl | |
| | ⟨a::as⟩ => simp [pop, Nat.succ_sub_succ_eq_sub, size] | |
| @[extern "lean_mk_array"] | |
| def mkArray {α : Type u} (n : Nat) (v : α) : Array α where | |
| toList := List.replicate n v | |
| /-- | |
| Swaps two entries in an array. | |
| This will perform the update destructively provided that `a` has a reference | |
| count of 1 when called. | |
| -/ | |
| @[extern "lean_array_fswap"] | |
| def swap (a : Array α) (i j : @& Nat) (hi : i < a.size := by get_elem_tactic) (hj : j < a.size := by get_elem_tactic) : Array α := | |
| let v₁ := a[i] | |
| let v₂ := a[j] | |
| let a' := a.set i v₂ | |
| a'.set j v₁ (Nat.lt_of_lt_of_eq hj (size_set a i v₂ _).symm) | |
| @[simp] theorem size_swap (a : Array α) (i j : Nat) {hi hj} : (a.swap i j hi hj).size = a.size := by | |
| show ((a.set i a[j]).set j a[i] | |
| (Nat.lt_of_lt_of_eq hj (size_set a i a[j] _).symm)).size = a.size | |
| rw [size_set, size_set] | |
| /-- | |
| Swaps two entries in an array, or returns the array unchanged if either index is out of bounds. | |
| This will perform the update destructively provided that `a` has a reference | |
| count of 1 when called. | |
| -/ | |
| @[extern "lean_array_swap"] | |
| def swapIfInBounds (a : Array α) (i j : @& Nat) : Array α := | |
| if h₁ : i < a.size then | |
| if h₂ : j < a.size then swap a i j | |
| else a | |
| else a | |
| @[deprecated swapIfInBounds (since := "2024-11-24")] abbrev swap! := @swapIfInBounds | |
| /-! ### GetElem instance for `USize`, backed by `uget` -/ | |
| instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where | |
| getElem xs i h := xs.uget i h | |
| /-! ### Definitions -/ | |
| instance : EmptyCollection (Array α) := ⟨Array.empty⟩ | |
| instance : Inhabited (Array α) where | |
| default := Array.empty | |
| def isEmpty (a : Array α) : Bool := | |
| a.size = 0 | |
| @[specialize] | |
| def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) : | |
| ∀ (i : Nat) (_ : i ≤ a.size), Bool | |
| | 0, _ => true | |
| | i+1, h => | |
| p a[i] (b[i]'(hsz ▸ h)) && isEqvAux a b hsz p i (Nat.le_trans (Nat.le_add_right i 1) h) | |
| @[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool := | |
| if h : a.size = b.size then | |
| isEqvAux a b h p a.size (Nat.le_refl a.size) | |
| else | |
| false | |
| instance [BEq α] : BEq (Array α) := | |
| ⟨fun a b => isEqv a b BEq.beq⟩ | |
| /-- | |
| `ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`. | |
| ``` | |
| ofFn f = #[f 0, f 1, ... , f(n - 1)] | |
| ``` -/ | |
| def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where | |
| /-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/ | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| go (i : Nat) (acc : Array α) : Array α := | |
| if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| /-- The array `#[0, 1, ..., n - 1]`. -/ | |
| def range (n : Nat) : Array Nat := | |
| ofFn fun (i : Fin n) => i | |
| @[inline] protected def singleton (v : α) : Array α := #[v] | |
| def back! [Inhabited α] (a : Array α) : α := | |
| a[a.size - 1]! | |
| @[deprecated back! (since := "2024-10-31")] abbrev back := @back! | |
| def get? (a : Array α) (i : Nat) : Option α := | |
| if h : i < a.size then some a[i] else none | |
| def back? (a : Array α) : Option α := | |
| a[a.size - 1]? | |
| @[inline] def swapAt (a : Array α) (i : Nat) (v : α) (hi : i < a.size := by get_elem_tactic) : α × Array α := | |
| let e := a[i] | |
| let a := a.set i v | |
| (e, a) | |
| @[inline] | |
| def swapAt! (a : Array α) (i : Nat) (v : α) : α × Array α := | |
| if h : i < a.size then | |
| swapAt a i v | |
| else | |
| have : Inhabited (α × Array α) := ⟨(v, a)⟩ | |
| panic! ("index " ++ toString i ++ " out of bounds") | |
| /-- `take a n` returns the first `n` elements of `a`. -/ | |
| def take (a : Array α) (n : Nat) : Array α := | |
| let rec loop | |
| | 0, a => a | |
| | n+1, a => loop n a.pop | |
| loop (a.size - n) a | |
| @[deprecated take (since := "2024-10-22")] abbrev shrink := @take | |
| @[inline] | |
| unsafe def modifyMUnsafe [Monad m] (a : Array α) (i : Nat) (f : α → m α) : m (Array α) := do | |
| if h : i < a.size then | |
| let v := a[i] | |
| -- Replace a[i] by `box(0)`. This ensures that `v` remains unshared if possible. | |
| -- Note: we assume that arrays have a uniform representation irrespective | |
| -- of the element type, and that it is valid to store `box(0)` in any array. | |
| let a' := a.set i (unsafeCast ()) | |
| let v ← f v | |
| pure <| a'.set i v (Nat.lt_of_lt_of_eq h (size_set a ..).symm) | |
| else | |
| pure a | |
| @[implemented_by modifyMUnsafe] | |
| def modifyM [Monad m] (a : Array α) (i : Nat) (f : α → m α) : m (Array α) := do | |
| if h : i < a.size then | |
| let v := a[i] | |
| let v ← f v | |
| pure <| a.set i v | |
| else | |
| pure a | |
| @[inline] | |
| def modify (a : Array α) (i : Nat) (f : α → α) : Array α := | |
| Id.run <| modifyM a i f | |
| @[inline] | |
| def modifyOp (self : Array α) (idx : Nat) (f : α → α) : Array α := | |
| self.modify idx f | |
| /-- | |
| We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime. | |
| This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/ | |
| @[inline] unsafe def forIn'Unsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) → a ∈ as → β → m (ForInStep β)) : m β := | |
| let sz := as.usize | |
| let rec @[specialize] loop (i : USize) (b : β) : m β := do | |
| if i < sz then | |
| let a := as.uget i lcProof | |
| match (← f a lcProof b) with | |
| | ForInStep.done b => pure b | |
| | ForInStep.yield b => loop (i+1) b | |
| else | |
| pure b | |
| loop 0 b | |
| /-- Reference implementation for `forIn'` -/ | |
| @[implemented_by Array.forIn'Unsafe] | |
| protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) → a ∈ as → β → m (ForInStep β)) : m β := | |
| let rec loop (i : Nat) (h : i ≤ as.size) (b : β) : m β := do | |
| match i, h with | |
| | 0, _ => pure b | |
| | i+1, h => | |
| have h' : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self i) h | |
| have : as.size - 1 < as.size := Nat.sub_lt (Nat.zero_lt_of_lt h') (by decide) | |
| have : as.size - 1 - i < as.size := Nat.lt_of_le_of_lt (Nat.sub_le (as.size - 1) i) this | |
| match (← f as[as.size - 1 - i] (getElem_mem this) b) with | |
| | ForInStep.done b => pure b | |
| | ForInStep.yield b => loop i (Nat.le_of_lt h') b | |
| loop as.size (Nat.le_refl _) b | |
| instance : ForIn' m (Array α) α inferInstance where | |
| forIn' := Array.forIn' | |
| -- No separate `ForIn` instance is required because it can be derived from `ForIn'`. | |
| /-- See comment at `forIn'Unsafe` -/ | |
| @[inline] | |
| unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β := | |
| let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do | |
| if i == stop then | |
| pure b | |
| else | |
| fold (i+1) stop (← f b (as.uget i lcProof)) | |
| if start < stop then | |
| if stop ≤ as.size then | |
| fold (USize.ofNat start) (USize.ofNat stop) init | |
| else | |
| pure init | |
| else | |
| pure init | |
| /-- Reference implementation for `foldlM` -/ | |
| @[implemented_by foldlMUnsafe] | |
| def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β := | |
| let fold (stop : Nat) (h : stop ≤ as.size) := | |
| let rec loop (i : Nat) (j : Nat) (b : β) : m β := do | |
| if hlt : j < stop then | |
| match i with | |
| | 0 => pure b | |
| | i'+1 => | |
| have : j < as.size := Nat.lt_of_lt_of_le hlt h | |
| loop i' (j+1) (← f b as[j]) | |
| else | |
| pure b | |
| loop (stop - start) start init | |
| if h : stop ≤ as.size then | |
| fold stop h | |
| else | |
| fold as.size (Nat.le_refl _) | |
| /-- See comment at `forIn'Unsafe` -/ | |
| @[inline] | |
| unsafe def foldrMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β := | |
| let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do | |
| if i == stop then | |
| pure b | |
| else | |
| fold (i-1) stop (← f (as.uget (i-1) lcProof) b) | |
| if start ≤ as.size then | |
| if stop < start then | |
| fold (USize.ofNat start) (USize.ofNat stop) init | |
| else | |
| pure init | |
| else if stop < as.size then | |
| fold (USize.ofNat as.size) (USize.ofNat stop) init | |
| else | |
| pure init | |
| /-- Reference implementation for `foldrM` -/ | |
| @[implemented_by foldrMUnsafe] | |
| def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β := | |
| let rec fold (i : Nat) (h : i ≤ as.size) (b : β) : m β := do | |
| if i == stop then | |
| pure b | |
| else match i, h with | |
| | 0, _ => pure b | |
| | i+1, h => | |
| have : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self _) h | |
| fold i (Nat.le_of_lt this) (← f as[i] b) | |
| if h : start ≤ as.size then | |
| if stop < start then | |
| fold start h init | |
| else | |
| pure init | |
| else if stop < as.size then | |
| fold as.size (Nat.le_refl _) init | |
| else | |
| pure init | |
| /-- See comment at `forIn'Unsafe` -/ | |
| @[inline] | |
| unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) := | |
| let sz := as.usize | |
| let rec @[specialize] map (i : USize) (r : Array NonScalar) : m (Array PNonScalar.{v}) := do | |
| if i < sz then | |
| let v := r.uget i lcProof | |
| -- Replace r[i] by `box(0)`. This ensures that `v` remains unshared if possible. | |
| -- Note: we assume that arrays have a uniform representation irrespective | |
| -- of the element type, and that it is valid to store `box(0)` in any array. | |
| let r := r.uset i default lcProof | |
| let vNew ← f (unsafeCast v) | |
| map (i+1) (r.uset i (unsafeCast vNew) lcProof) | |
| else | |
| pure (unsafeCast r) | |
| unsafeCast <| map 0 (unsafeCast as) | |
| /-- Reference implementation for `mapM` -/ | |
| @[implemented_by mapMUnsafe] | |
| def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) := | |
| -- Note: we cannot use `foldlM` here for the reference implementation because this calls | |
| -- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`) | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| map (i : Nat) (r : Array β) : m (Array β) := do | |
| if hlt : i < as.size then | |
| map (i+1) (r.push (← f as[i])) | |
| else | |
| pure r | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| map 0 (mkEmpty as.size) | |
| @[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM | |
| /-- Variant of `mapIdxM` which receives the index as a `Fin as.size`. -/ | |
| @[inline] | |
| def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] | |
| (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → m β) : m (Array β) := | |
| let rec @[specialize] map (i : Nat) (j : Nat) (inv : i + j = as.size) (bs : Array β) : m (Array β) := do | |
| match i, inv with | |
| | 0, _ => pure bs | |
| | i+1, inv => | |
| have j_lt : j < as.size := by | |
| rw [← inv, Nat.add_assoc, Nat.add_comm 1 j, Nat.add_comm] | |
| apply Nat.le_add_right | |
| have : i + (j + 1) = as.size := by rw [← inv, Nat.add_comm j 1, Nat.add_assoc] | |
| map i (j+1) this (bs.push (← f j (as.get j j_lt) j_lt)) | |
| map as.size 0 rfl (mkEmpty as.size) | |
| @[inline] | |
| def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : Nat → α → m β) (as : Array α) : m (Array β) := | |
| as.mapFinIdxM fun i a _ => f i a | |
| @[inline] | |
| def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m (Option β)) (as : Array α) : m (Option β) := do | |
| for a in as do | |
| match (← f a) with | |
| | some b => return b | |
| | _ => pure ⟨⟩ | |
| return none | |
| /-- | |
| Note that the universe level is contrained to `Type` here, | |
| to avoid having to have the predicate live in `p : α → m (ULift Bool)`. | |
| -/ | |
| @[inline] | |
| def findM? {α : Type} {m : Type → Type} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := do | |
| for a in as do | |
| if (← p a) then | |
| return a | |
| return none | |
| @[inline] | |
| def findIdxM? [Monad m] (p : α → m Bool) (as : Array α) : m (Option Nat) := do | |
| let mut i := 0 | |
| for a in as do | |
| if (← p a) then | |
| return some i | |
| i := i + 1 | |
| return none | |
| @[inline] | |
| unsafe def anyMUnsafe {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool := | |
| let rec @[specialize] any (i : USize) (stop : USize) : m Bool := do | |
| if i == stop then | |
| pure false | |
| else | |
| if (← p (as.uget i lcProof)) then | |
| pure true | |
| else | |
| any (i+1) stop | |
| if start < stop then | |
| let stop' := min stop as.size | |
| if start < stop' then | |
| any (USize.ofNat start) (USize.ofNat stop') | |
| else | |
| pure false | |
| else | |
| pure false | |
| @[implemented_by anyMUnsafe] | |
| def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool := | |
| let any (stop : Nat) (h : stop ≤ as.size) := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| loop (j : Nat) : m Bool := do | |
| if hlt : j < stop then | |
| have : j < as.size := Nat.lt_of_lt_of_le hlt h | |
| if (← p as[j]) then | |
| pure true | |
| else | |
| loop (j+1) | |
| else | |
| pure false | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| loop start | |
| if h : stop ≤ as.size then | |
| any stop h | |
| else | |
| any as.size (Nat.le_refl _) | |
| @[inline] | |
| def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool := | |
| return !(← as.anyM (start := start) (stop := stop) fun v => return !(← p v)) | |
| @[inline] | |
| def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m (Option β)) (as : Array α) : m (Option β) := | |
| let rec @[specialize] find : (i : Nat) → i ≤ as.size → m (Option β) | |
| | 0, _ => pure none | |
| | i+1, h => do | |
| have : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self _) h | |
| let r ← f as[i] | |
| match r with | |
| | some _ => pure r | |
| | none => | |
| have : i ≤ as.size := Nat.le_of_lt this | |
| find i this | |
| find as.size (Nat.le_refl _) | |
| @[inline] | |
| def findRevM? {α : Type} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := | |
| as.findSomeRevM? fun a => return if (← p a) then some a else none | |
| @[inline] | |
| def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Array α) (start := 0) (stop := as.size) : m PUnit := | |
| as.foldlM (fun _ => f) ⟨⟩ start stop | |
| @[inline] | |
| def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Array α) (start := as.size) (stop := 0) : m PUnit := | |
| as.foldrM (fun a _ => f a) ⟨⟩ start stop | |
| @[inline] | |
| def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Array α) (start := 0) (stop := as.size) : β := | |
| Id.run <| as.foldlM f init start stop | |
| @[inline] | |
| def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Array α) (start := as.size) (stop := 0) : β := | |
| Id.run <| as.foldrM f init start stop | |
| /-- Sum of an array. | |
| `Array.sum #[a, b, c] = a + (b + (c + 0))` -/ | |
| @[inline] | |
| def sum {α} [Add α] [Zero α] : Array α → α := | |
| foldr (· + ·) 0 | |
| @[inline] | |
| def countP {α : Type u} (p : α → Bool) (as : Array α) : Nat := | |
| as.foldr (init := 0) fun a acc => bif p a then acc + 1 else acc | |
| @[inline] | |
| def count {α : Type u} [BEq α] (a : α) (as : Array α) : Nat := | |
| countP (· == a) as | |
| @[inline] | |
| def map {α : Type u} {β : Type v} (f : α → β) (as : Array α) : Array β := | |
| Id.run <| as.mapM f | |
| /-- Variant of `mapIdx` which receives the index as a `Fin as.size`. -/ | |
| @[inline] | |
| def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → β) : Array β := | |
| Id.run <| as.mapFinIdxM f | |
| @[inline] | |
| def mapIdx {α : Type u} {β : Type v} (f : Nat → α → β) (as : Array α) : Array β := | |
| Id.run <| as.mapIdxM f | |
| /-- Turns `#[a, b]` into `#[(a, 0), (b, 1)]`. -/ | |
| def zipIdx (arr : Array α) (start := 0) : Array (α × Nat) := | |
| arr.mapIdx fun i a => (a, i + start) | |
| @[deprecated zipIdx (since := "2025-01-21")] abbrev zipWithIndex := @zipIdx | |
| @[inline] | |
| def find? {α : Type u} (p : α → Bool) (as : Array α) : Option α := | |
| Id.run do | |
| for a in as do | |
| if p a then | |
| return a | |
| return none | |
| @[inline] | |
| def findSome? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β := | |
| Id.run <| as.findSomeM? f | |
| @[inline] | |
| def findSome! {α : Type u} {β : Type v} [Inhabited β] (f : α → Option β) (a : Array α) : β := | |
| match a.findSome? f with | |
| | some b => b | |
| | none => panic! "failed to find element" | |
| @[inline] | |
| def findSomeRev? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β := | |
| Id.run <| as.findSomeRevM? f | |
| @[inline] | |
| def findRev? {α : Type} (p : α → Bool) (as : Array α) : Option α := | |
| Id.run <| as.findRevM? p | |
| @[inline] | |
| def findIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option Nat := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| loop (j : Nat) := | |
| if h : j < as.size then | |
| if p as[j] then some j else loop (j + 1) | |
| else none | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| loop 0 | |
| @[inline] | |
| def findFinIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option (Fin as.size) := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| loop (j : Nat) := | |
| if h : j < as.size then | |
| if p as[j] then some ⟨j, h⟩ else loop (j + 1) | |
| else none | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| loop 0 | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) := | |
| if h : i < a.size then | |
| if a[i] == v then some ⟨i, h⟩ | |
| else indexOfAux a v (i+1) | |
| else none | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) := | |
| indexOfAux a v 0 | |
| @[deprecated indexOf? (since := "2024-11-20")] | |
| def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat := | |
| a.findIdx? fun a => a == v | |
| @[inline] | |
| def any (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool := | |
| Id.run <| as.anyM p start stop | |
| @[inline] | |
| def all (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool := | |
| Id.run <| as.allM p start stop | |
| /-- `as.contains a` is true if there is some element `b` in `as` such that `a == b`. -/ | |
| def contains [BEq α] (as : Array α) (a : α) : Bool := | |
| as.any (a == ·) | |
| /-- | |
| Variant of `Array.contains` with arguments reversed. | |
| For verification purposes, we simplify this to `contains`. | |
| -/ | |
| def elem [BEq α] (a : α) (as : Array α) : Bool := | |
| as.contains a | |
| /-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/ | |
| -- This function is exported to C, where it is called by `Array.toList` | |
| -- (the projection) to implement this functionality. | |
| @[export lean_array_to_list_impl] | |
| def toListImpl (as : Array α) : List α := | |
| as.foldr List.cons [] | |
| /-- Prepends an `Array α` onto the front of a list. Equivalent to `as.toList ++ l`. -/ | |
| @[inline] | |
| def toListAppend (as : Array α) (l : List α) : List α := | |
| as.foldr List.cons l | |
| protected def append (as : Array α) (bs : Array α) : Array α := | |
| bs.foldl (init := as) fun r v => r.push v | |
| instance : Append (Array α) := ⟨Array.append⟩ | |
| protected def appendList (as : Array α) (bs : List α) : Array α := | |
| bs.foldl (init := as) fun r v => r.push v | |
| instance : HAppend (Array α) (List α) (Array α) := ⟨Array.appendList⟩ | |
| @[inline] | |
| def flatMapM [Monad m] (f : α → m (Array β)) (as : Array α) : m (Array β) := | |
| as.foldlM (init := empty) fun bs a => do return bs ++ (← f a) | |
| @[deprecated flatMapM (since := "2024-10-16")] abbrev concatMapM := @flatMapM | |
| @[inline] | |
| def flatMap (f : α → Array β) (as : Array α) : Array β := | |
| as.foldl (init := empty) fun bs a => bs ++ f a | |
| @[deprecated flatMap (since := "2024-10-16")] abbrev concatMap := @flatMap | |
| /-- Joins array of array into a single array. | |
| `flatten #[#[a₁, a₂, ⋯], #[b₁, b₂, ⋯], ⋯]` = `#[a₁, a₂, ⋯, b₁, b₂, ⋯]` | |
| -/ | |
| @[inline] def flatten (as : Array (Array α)) : Array α := | |
| as.foldl (init := empty) fun r a => r ++ a | |
| @[inline] | |
| def filter (p : α → Bool) (as : Array α) (start := 0) (stop := as.size) : Array α := | |
| as.foldl (init := #[]) (start := start) (stop := stop) fun r a => | |
| if p a then r.push a else r | |
| @[inline] | |
| def filterM {α : Type} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m (Array α) := | |
| as.foldlM (init := #[]) (start := start) (stop := stop) fun r a => do | |
| if (← p a) then return r.push a else return r | |
| @[specialize] | |
| def filterMapM [Monad m] (f : α → m (Option β)) (as : Array α) (start := 0) (stop := as.size) : m (Array β) := | |
| as.foldlM (init := #[]) (start := start) (stop := stop) fun bs a => do | |
| match (← f a) with | |
| | some b => pure (bs.push b) | |
| | none => pure bs | |
| @[inline] | |
| def filterMap (f : α → Option β) (as : Array α) (start := 0) (stop := as.size) : Array β := | |
| Id.run <| as.filterMapM f (start := start) (stop := stop) | |
| @[specialize] | |
| def getMax? (as : Array α) (lt : α → α → Bool) : Option α := | |
| if h : 0 < as.size then | |
| let a0 := as[0] | |
| some <| as.foldl (init := a0) (start := 1) fun best a => | |
| if lt best a then a else best | |
| else | |
| none | |
| @[inline] | |
| def partition (p : α → Bool) (as : Array α) : Array α × Array α := Id.run do | |
| let mut bs := #[] | |
| let mut cs := #[] | |
| for a in as do | |
| if p a then | |
| bs := bs.push a | |
| else | |
| cs := cs.push a | |
| return (bs, cs) | |
| def reverse (as : Array α) : Array α := | |
| if h : as.size ≤ 1 then | |
| as | |
| else | |
| loop as 0 ⟨as.size - 1, Nat.pred_lt (mt (fun h : as.size = 0 => h ▸ by decide) h)⟩ | |
| where | |
| termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by | |
| rw [Nat.sub_sub, Nat.add_comm] | |
| exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h) | |
| loop (as : Array α) (i : Nat) (j : Fin as.size) := | |
| if h : i < j then | |
| have := termination h | |
| let as := as.swap i j (Nat.lt_trans h j.2) | |
| have : j-1 < as.size := by rw [size_swap]; exact Nat.lt_of_le_of_lt (Nat.pred_le _) j.2 | |
| loop as (i+1) ⟨j-1, this⟩ | |
| else | |
| as | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| def popWhile (p : α → Bool) (as : Array α) : Array α := | |
| if h : as.size > 0 then | |
| if p (as[as.size - 1]'(Nat.sub_lt h (by decide))) then | |
| popWhile p as.pop | |
| else | |
| as | |
| else | |
| as | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| def takeWhile (p : α → Bool) (as : Array α) : Array α := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| go (i : Nat) (r : Array α) : Array α := | |
| if h : i < as.size then | |
| let a := as[i] | |
| if p a then | |
| go (i+1) (r.push a) | |
| else | |
| r | |
| else | |
| r | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| go 0 #[] | |
| /-- | |
| Remove the element at a given index from an array without a runtime bounds checks, | |
| using a `Nat` index and a tactic-provided bound. | |
| This function takes worst case O(n) time because | |
| it has to backshift all elements at positions greater than `i`.-/ | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| def eraseIdx (a : Array α) (i : Nat) (h : i < a.size := by get_elem_tactic) : Array α := | |
| if h' : i + 1 < a.size then | |
| let a' := a.swap (i + 1) i | |
| a'.eraseIdx (i + 1) (by simp [a', h']) | |
| else | |
| a.pop | |
| termination_by a.size - i | |
| decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ h | |
| -- This is required in `Lean.Data.PersistentHashMap`. | |
| @[simp] theorem size_eraseIdx (a : Array α) (i : Nat) (h) : (a.eraseIdx i h).size = a.size - 1 := by | |
| induction a, i, h using Array.eraseIdx.induct with | |
| | @case1 a i h h' a' ih => | |
| unfold eraseIdx | |
| simp +zetaDelta [h', a', ih] | |
| | case2 a i h h' => | |
| unfold eraseIdx | |
| simp [h'] | |
| /-- Remove the element at a given index from an array, or do nothing if the index is out of bounds. | |
| This function takes worst case O(n) time because | |
| it has to backshift all elements at positions greater than `i`.-/ | |
| def eraseIdxIfInBounds (a : Array α) (i : Nat) : Array α := | |
| if h : i < a.size then a.eraseIdx i h else a | |
| /-- Remove the element at a given index from an array, or panic if the index is out of bounds. | |
| This function takes worst case O(n) time because | |
| it has to backshift all elements at positions greater than `i`. -/ | |
| def eraseIdx! (a : Array α) (i : Nat) : Array α := | |
| if h : i < a.size then a.eraseIdx i h else panic! "invalid index" | |
| /-- Remove a specified element from an array, or do nothing if it is not present. | |
| This function takes worst case O(n) time because | |
| it has to backshift all later elements. -/ | |
| def erase [BEq α] (as : Array α) (a : α) : Array α := | |
| match as.indexOf? a with | |
| | none => as | |
| | some i => as.eraseIdx i | |
| /-- Erase the first element that satisfies the predicate `p`. | |
| This function takes worst case O(n) time because | |
| it has to backshift all later elements. -/ | |
| def eraseP (as : Array α) (p : α → Bool) : Array α := | |
| match as.findIdx? p with | |
| | none => as | |
| | some i => as.eraseIdxIfInBounds i | |
| /-- Insert element `a` at position `i`. -/ | |
| @[inline] def insertIdx (as : Array α) (i : Nat) (a : α) (_ : i ≤ as.size := by get_elem_tactic) : Array α := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| loop (as : Array α) (j : Fin as.size) := | |
| if i < j then | |
| let j' : Fin as.size := ⟨j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2⟩ | |
| let as := as.swap j' j | |
| loop as ⟨j', by rw [size_swap]; exact j'.2⟩ | |
| else | |
| as | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| let j := as.size | |
| let as := as.push a | |
| loop as ⟨j, size_push .. ▸ j.lt_succ_self⟩ | |
| @[deprecated insertIdx (since := "2024-11-20")] abbrev insertAt := @insertIdx | |
| /-- Insert element `a` at position `i`. Panics if `i` is not `i ≤ as.size`. -/ | |
| def insertIdx! (as : Array α) (i : Nat) (a : α) : Array α := | |
| if h : i ≤ as.size then | |
| insertIdx as i a | |
| else panic! "invalid index" | |
| @[deprecated insertIdx! (since := "2024-11-20")] abbrev insertAt! := @insertIdx! | |
| /-- Insert element `a` at position `i`, or do nothing if `as.size < i`. -/ | |
| def insertIdxIfInBounds (as : Array α) (i : Nat) (a : α) : Array α := | |
| if h : i ≤ as.size then | |
| insertIdx as i a | |
| else | |
| as | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : Nat) : Bool := | |
| if h : i < as.size then | |
| let a := as[i] | |
| have : i < bs.size := Nat.lt_of_lt_of_le h hle | |
| let b := bs[i] | |
| if a == b then | |
| isPrefixOfAux as bs hle (i+1) | |
| else | |
| false | |
| else | |
| true | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| /-- Return true iff `as` is a prefix of `bs`. | |
| That is, `bs = as ++ t` for some `t : List α`.-/ | |
| def isPrefixOf [BEq α] (as bs : Array α) : Bool := | |
| if h : as.size ≤ bs.size then | |
| isPrefixOfAux as bs h 0 | |
| else | |
| false | |
| @[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion. | |
| def zipWithAux (as : Array α) (bs : Array β) (f : α → β → γ) (i : Nat) (cs : Array γ) : Array γ := | |
| if h : i < as.size then | |
| let a := as[i] | |
| if h : i < bs.size then | |
| let b := bs[i] | |
| zipWithAux as bs f (i+1) <| cs.push <| f a b | |
| else | |
| cs | |
| else | |
| cs | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| @[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ := | |
| zipWithAux as bs f 0 #[] | |
| def zip (as : Array α) (bs : Array β) : Array (α × β) := | |
| zipWith as bs Prod.mk | |
| def zipWithAll (as : Array α) (bs : Array β) (f : Option α → Option β → γ) : Array γ := | |
| go as bs 0 #[] | |
| where go (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) := | |
| if i < max as.size bs.size then | |
| let a := as[i]? | |
| let b := bs[i]? | |
| go as bs (i+1) (cs.push (f a b)) | |
| else | |
| cs | |
| termination_by max as.size bs.size - i | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| def unzip (as : Array (α × β)) : Array α × Array β := | |
| as.foldl (init := (#[], #[])) fun (as, bs) (a, b) => (as.push a, bs.push b) | |
| @[deprecated partition (since := "2024-11-06")] | |
| def split (as : Array α) (p : α → Bool) : Array α × Array α := | |
| as.foldl (init := (#[], #[])) fun (as, bs) a => | |
| if p a then (as.push a, bs) else (as, bs.push a) | |
| /-! ### Lexicographic ordering -/ | |
| instance instLT [LT α] : LT (Array α) := ⟨fun as bs => as.toList < bs.toList⟩ | |
| instance instLE [LT α] : LE (Array α) := ⟨fun as bs => as.toList ≤ bs.toList⟩ | |
| -- See `Init.Data.Array.Lex.Basic` for the boolean valued lexicographic comparator. | |
| /-! ## Auxiliary functions used in metaprogramming. | |
| We do not currently intend to provide verification theorems for these functions. | |
| -/ | |
| /- ### reduceOption -/ | |
| /-- Drop `none`s from a Array, and replace each remaining `some a` with `a`. -/ | |
| @[inline] def reduceOption (as : Array (Option α)) : Array α := | |
| as.filterMap id | |
| /-! ### eraseReps -/ | |
| /-- | |
| `O(|l|)`. Erase repeated adjacent elements. Keeps the first occurrence of each run. | |
| * `eraseReps #[1, 3, 2, 2, 2, 3, 5] = #[1, 3, 2, 3, 5]` | |
| -/ | |
| def eraseReps {α} [BEq α] (as : Array α) : Array α := | |
| if h : 0 < as.size then | |
| let ⟨last, r⟩ := as.foldl (init := (as[0], #[])) fun ⟨last, r⟩ a => | |
| if a == last then ⟨last, r⟩ else ⟨a, r.push last⟩ | |
| r.push last | |
| else | |
| #[] | |
| /-! ### allDiff -/ | |
| private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat), i < as.size → Bool | |
| | 0, _ => true | |
| | i+1, h => | |
| have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h; | |
| a != as[i] && allDiffAuxAux as a i this | |
| @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool := | |
| if h : i < as.size then | |
| allDiffAuxAux as as[i] i h && allDiffAux as (i+1) | |
| else | |
| true | |
| decreasing_by simp_wf; decreasing_trivial_pre_omega | |
| def allDiff [BEq α] (as : Array α) : Bool := | |
| allDiffAux as 0 | |
| /-! ### getEvenElems -/ | |
| @[inline] def getEvenElems (as : Array α) : Array α := | |
| (·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a => | |
| if even then | |
| (false, r.push a) | |
| else | |
| (true, r) | |
| /-! ### Repr and ToString -/ | |
| instance {α : Type u} [Repr α] : Repr (Array α) where | |
| reprPrec a _ := | |
| let _ : Std.ToFormat α := ⟨repr⟩ | |
| if a.size == 0 then | |
| "#[]" | |
| else | |
| Std.Format.bracketFill "#[" (Std.Format.joinSep (toList a) ("," ++ Std.Format.line)) "]" | |
| instance [ToString α] : ToString (Array α) where | |
| toString a := "#" ++ toString a.toList | |
| end Array | |
| export Array (mkArray) |