| /- | |
| 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.Basic | |
| import Init.Data.Repr | |
| import Init.Data.ToString.Basic | |
| import Init.GetElem | |
| universe u v w | |
| /-! ### Array literal syntax -/ | |
| syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term | |
| macro_rules | |
| | `(#[ $elems,* ]) => `(List.toArray [ $elems,* ]) | |
| variable {α : Type u} | |
| namespace Array | |
| /-! ### Preliminary theorems -/ | |
| @[simp] theorem size_set (a : Array α) (i : Fin a.size) (v : α) : (set a i v).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] | |
| @[simp] theorem toList_toArray (as : List α) : as.toArray.toList = as := rfl | |
| @[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size] | |
| @[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, h⟩ v | |
| @[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 : @& Fin a.size) : Array α := | |
| let v₁ := a.get i | |
| let v₂ := a.get j | |
| let a' := a.set i v₂ | |
| a'.set (size_set a i v₂ ▸ j) v₁ | |
| @[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by | |
| show ((a.set i (a.get j)).set (size_set a i _ ▸ j) (a.get i)).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 swap! (a : Array α) (i j : @& Nat) : Array α := | |
| if h₁ : i < a.size then | |
| if h₂ : j < a.size then swap a ⟨i, h₁⟩ ⟨j, h₂⟩ | |
| else a | |
| else a | |
| /-! ### 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 | |
| @[simp] 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 := | |
| n.fold (flip Array.push) (mkEmpty n) | |
| def singleton (v : α) : Array α := | |
| mkArray 1 v | |
| def back [Inhabited α] (a : Array α) : α := | |
| a.get! (a.size - 1) | |
| def get? (a : Array α) (i : Nat) : Option α := | |
| if h : i < a.size then some a[i] else none | |
| def back? (a : Array α) : Option α := | |
| a.get? (a.size - 1) | |
| @[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α := | |
| let e := a.get 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, h⟩ v | |
| else | |
| have : Inhabited α := ⟨v⟩ | |
| panic! ("index " ++ toString i ++ " out of bounds") | |
| def shrink (a : Array α) (n : Nat) : Array α := | |
| let rec loop | |
| | 0, a => a | |
| | n+1, a => loop n a.pop | |
| loop (a.size - n) a | |
| @[inline] | |
| unsafe def modifyMUnsafe [Monad m] (a : Array α) (i : Nat) (f : α → m α) : m (Array α) := do | |
| if h : i < a.size then | |
| let idx : Fin a.size := ⟨i, h⟩ | |
| let v := a.get idx | |
| -- 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 idx (unsafeCast ()) | |
| let v ← f v | |
| pure <| a'.set (size_set a .. ▸ idx) v | |
| 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 idx := ⟨i, h⟩ | |
| let v := a.get idx | |
| let v ← f v | |
| pure <| a.set idx 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 forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (b : β) (f : α → β → 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 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.forInUnsafe] | |
| protected def forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (b : β) (f : α → β → 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] 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 α) α where | |
| forIn := Array.forIn | |
| /-- See comment at `forInUnsafe` -/ | |
| @[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 `forInUnsafe` -/ | |
| @[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 `forInUnsafe` -/ | |
| @[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) | |
| @[inline] | |
| def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : Fin 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 < as.size := by | |
| rw [← inv, Nat.add_assoc, Nat.add_comm 1 j, Nat.add_comm] | |
| apply Nat.le_add_right | |
| let idx : Fin as.size := ⟨j, this⟩ | |
| 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 idx (as.get idx))) | |
| map as.size 0 rfl (mkEmpty as.size) | |
| @[inline] | |
| def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : α → m (Option β)) : m (Option β) := do | |
| for a in as do | |
| match (← f a) with | |
| | some b => return b | |
| | _ => pure ⟨⟩ | |
| return none | |
| @[inline] | |
| def findM? {α : Type} {m : Type → Type} [Monad m] (as : Array α) (p : α → m Bool) : m (Option α) := do | |
| for a in as do | |
| if (← p a) then | |
| return a | |
| return none | |
| @[inline] | |
| def findIdxM? [Monad m] (as : Array α) (p : α → m Bool) : 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] (as : Array α) (f : α → m (Option β)) : 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] (as : Array α) (p : α → m Bool) : 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 | |
| @[inline] | |
| def map {α : Type u} {β : Type v} (f : α → β) (as : Array α) : Array β := | |
| Id.run <| as.mapM f | |
| @[inline] | |
| def mapIdx {α : Type u} {β : Type v} (as : Array α) (f : Fin as.size → α → β) : Array β := | |
| Id.run <| as.mapIdxM f | |
| /-- Turns `#[a, b]` into `#[(a, 0), (b, 1)]`. -/ | |
| def zipWithIndex (arr : Array α) : Array (α × Nat) := | |
| arr.mapIdx fun i a => (a, i) | |
| @[inline] | |
| def find? {α : Type} (as : Array α) (p : α → Bool) : Option α := | |
| Id.run <| as.findM? p | |
| @[inline] | |
| def findSome? {α : Type u} {β : Type v} (as : Array α) (f : α → Option β) : Option β := | |
| Id.run <| as.findSomeM? f | |
| @[inline] | |
| def findSome! {α : Type u} {β : Type v} [Inhabited β] (a : Array α) (f : α → Option β) : β := | |
| match findSome? a f with | |
| | some b => b | |
| | none => panic! "failed to find element" | |
| @[inline] | |
| def findSomeRev? {α : Type u} {β : Type v} (as : Array α) (f : α → Option β) : Option β := | |
| Id.run <| as.findSomeRevM? f | |
| @[inline] | |
| def findRev? {α : Type} (as : Array α) (p : α → Bool) : Option α := | |
| Id.run <| as.findRevM? p | |
| @[inline] | |
| def findIdx? {α : Type u} (as : Array α) (p : α → Bool) : 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 | |
| def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat := | |
| a.findIdx? fun a => a == v | |
| @[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 | |
| let idx : Fin a.size := ⟨i, h⟩; | |
| if a.get idx == v then some idx | |
| 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 | |
| @[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 | |
| def contains [BEq α] (as : Array α) (a : α) : Bool := | |
| as.any (· == a) | |
| 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 concatMapM [Monad m] (f : α → m (Array β)) (as : Array α) : m (Array β) := | |
| as.foldlM (init := empty) fun bs a => do return bs ++ (← f a) | |
| @[inline] | |
| def concatMap (f : α → Array β) (as : Array α) : Array β := | |
| as.foldl (init := empty) fun bs a => bs ++ f a | |
| /-- Joins array of array into a single array. | |
| `flatten #[#[a₁, a₂, ⋯], #[b₁, b₂, ⋯], ⋯]` = `#[a₁, a₂, ⋯, b₁, b₂, ⋯]` | |
| -/ | |
| 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, Nat.lt_trans h j.2⟩ j | |
| 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.get ⟨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.get ⟨i, h⟩ | |
| 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 bounds checks, using a `Fin` index. | |
| 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 feraseIdx (a : Array α) (i : Fin a.size) : Array α := | |
| if h : i.val + 1 < a.size then | |
| let a' := a.swap ⟨i.val + 1, h⟩ i | |
| let i' : Fin a'.size := ⟨i.val + 1, by simp [a', h]⟩ | |
| a'.feraseIdx i' | |
| else | |
| a.pop | |
| termination_by a.size - i.val | |
| decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ i.isLt | |
| -- This is required in `Lean.Data.PersistentHashMap`. | |
| theorem size_feraseIdx (a : Array α) (i : Fin a.size) : (a.feraseIdx i).size = a.size - 1 := by | |
| induction a, i using Array.feraseIdx.induct with | |
| | @case1 a i h a' _ ih => | |
| unfold feraseIdx | |
| simp [h, a', ih] | |
| | case2 a i h => | |
| unfold feraseIdx | |
| 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 eraseIdx (a : Array α) (i : Nat) : Array α := | |
| if h : i < a.size then a.feraseIdx ⟨i, h⟩ else a | |
| def erase [BEq α] (as : Array α) (a : α) : Array α := | |
| match as.indexOf? a with | |
| | none => as | |
| | some i => as.feraseIdx i | |
| /-- Insert element `a` at position `i`. -/ | |
| @[inline] def insertAt (as : Array α) (i : Fin (as.size + 1)) (a : α) : Array α := | |
| let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion. | |
| loop (as : Array α) (j : Fin as.size) := | |
| if i.1 < j then | |
| let j' := ⟨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⟩ | |
| /-- Insert element `a` at position `i`. Panics if `i` is not `i ≤ as.size`. -/ | |
| def insertAt! (as : Array α) (i : Nat) (a : α) : Array α := | |
| if h : i ≤ as.size then | |
| insertAt as ⟨i, Nat.lt_succ_of_le h⟩ a | |
| else panic! "invalid index" | |
| @[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 (f : α → β → γ) (as : Array α) (bs : Array β) (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 f as bs (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 f as bs 0 #[] | |
| def zip (as : Array α) (bs : Array β) : Array (α × β) := | |
| zipWith as bs Prod.mk | |
| def unzip (as : Array (α × β)) : Array α × Array β := | |
| as.foldl (init := (#[], #[])) fun (as, bs) (a, b) => (as.push a, bs.push b) | |
| 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) | |
| /-! ## Auxiliary functions used in metaprogramming. | |
| We do not intend to provide verification theorems for these functions. | |
| -/ | |
| /-! ### 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) |