/// @module std::core::vec
/// Method definitions for Vec<T> (Array).
///
/// Type-agnostic methods are defined via `extend Vec<T>`.
/// Numeric-only methods are defined via `trait NumericVec` + `impl NumericVec for Vec`.
/// Most methods have pure Shape implementations. Methods that need VM intrinsics
/// (pop, groupBy) delegate to Rust PHF dispatch; the compiler self-call
/// guard prevents infinite recursion. R8 W4 J.5f (supervisor D4 2026-05-24):
/// `sort` is now Rust-PHF only (the previous pure-Shape `sort(cmp)` body
/// couldn't handle the 0-arg `arr.sort()` natural-ordering case + was
/// element-kind-blind; the J.5f Rust handler dispatches both arities
/// kind-generically across every V2ElemType carrier). Note: `len`/`isEmpty`
/// are intentionally NOT defined here — they would intercept dispatch and
/// recurse on Vec<T>.
// -- Type-agnostic methods --------------------------------------------------
extend Vec<T> {
// NOTE: `len()` and `isEmpty()` are intentionally NOT defined here.
// `arr.len()` dispatches directly to the PHF handler (ARRAY_METHODS::handle_len_v2
// for Arc arrays; TYPED_INT_ARRAY_METHODS::len / TYPED_NUMBER_ARRAY_METHODS::len
// for v2 raw-ptr arrays via as_v2_typed_array). Defining `method len() -> int
// { self.len() }` here causes the extend-method to intercept dispatch and
// recurse infinitely (or, post self-call guard, produce garbage).
// Callers needing `isEmpty()` can write `arr.len() == 0` directly.
// --- Pure Shape implementations ---
method first() -> T { self[0] }
method last() -> T { self[self.len() - 1] }
method push(item: T) { self.push(item) } // ArrayPushLocal opcode (in-place)
method pop() -> T { self.pop() } // keep delegation — pop needs VM support
method reverse() -> Vec<T> {
let mut result = []
let mut i = self.len() - 1
while i >= 0 {
result.push(self[i])
i = i - 1
}
result
}
method clone() -> Vec<T> { self.slice(0, self.len()) }
method filter(predicate: (T) => bool) -> Vec<T> {
let mut result = []
for item in self {
if predicate(item) {
result.push(item)
}
}
result
}
method map<U>(f: (T) => U) -> Vec<U> {
let mut result = []
for item in self {
result.push(f(item))
}
result
}
method reduce<U>(f: (U, T) => U, init: U) -> U {
let mut acc = init
for item in self {
acc = f(acc, item)
}
acc
}
method find(predicate: (T) => bool) -> T {
for item in self {
if predicate(item) { return item }
}
}
method forEach(f: (T) => void) -> void {
for item in self { f(item) }
}
method some(predicate: (T) => bool) -> bool {
for item in self {
if predicate(item) { return true }
}
false
}
method every(predicate: (T) => bool) -> bool {
for item in self {
if !predicate(item) { return false }
}
true
}
method join(separator: string) -> string {
let mut result = ""
for i in 0..self.len() {
if i > 0 { result = result + separator }
result = result + self[i].toString()
}
result
}
/// Returns elements from `start` (inclusive) up to `end` (exclusive).
/// When `end` is omitted (or negative) the slice runs to `self.len()`,
/// matching the standard `arr.slice(start)` short form.
/// D-δ array_slice single-arg close: a `-1` sentinel default lets the
/// UFCS call site supply the suffix-from-`start` semantics without the
/// caller having to spell out `self.len()`. See
/// `v0.3-known-constraints-audit` §6(f) Repro 1.
method slice(start: int, end: int = -1) -> Vec<T> {
let actual_end = if end < 0 { self.len() } else { end }
let mut result = []
let mut i = start
while i < actual_end && i < self.len() {
result.push(self[i])
i = i + 1
}
result
}
method take(n: int) -> Vec<T> { self.slice(0, n) }
method drop(n: int) -> Vec<T> { self.slice(n, self.len()) }
method flatten() -> Vec<T> {
let mut result = []
for item in self {
for sub in item {
result.push(sub)
}
}
result
}
method unique() -> Vec<T> {
let mut result = []
for item in self {
if !result.includes(item) {
result.push(item)
}
}
result
}
method concat(other: Vec<T>) -> Vec<T> {
let mut result = []
for item in self { result.push(item) }
for item in other { result.push(item) }
result
}
method indexOf(value: T) -> int {
for i in 0..self.len() {
if self[i] == value { return i }
}
-1
}
// `sort` is intentionally NOT defined as a pure-Shape `extend Vec<T>`
// method here. The R8 W4 J.5f (supervisor D4 2026-05-24, v0.3 scope)
// Rust PHF handler at `array_sort::handle_sort_v2` is the canonical
// dispatcher for BOTH `arr.sort()` (natural ordering, no comparator)
// and `arr.sort(|a, b| cmp)` (closure comparator). Defining a
// Shape-side `extend Vec<T> { method sort(cmp) ... }` here would
// intercept dispatch and consume the 0-arg case as `sort(cmp=null)`
// → null-callee failure on first comparator invocation. The PHF
// handler is kind-generic across all 14 V2ElemType carriers + stable
// (bottom-up merge sort) + supports natural-ordering for scalar /
// String / Decimal / Char / Bool element kinds + structured surface
// on TypedObject natural-ordering (v0.4 territory per ADR-006
// §2.7.14 — Bool-default ordering refused).
//
// Pre-J.5f the body was a pure-Shape insertion sort with a required
// `cmp` parameter that broke `arr.sort()` (no args) by passing null
// to the comparator. Pre-D-α.1 (R7 W3 close `572791a3`) the body was
// `self.sort(cmp)` (self-recursing — generic extend method without
// its own type parameter; the monomorphization self-call guard only
// fires for methods with their own type parameter). The R8 W4 J.5f
// deletion lets dispatch fall through to the Rust PHF handler,
// which handles both arities + every supervisor D4 v0.3 element
// kind uniformly.
method includes(value: T) -> bool {
for item in self {
if item == value { return true }
}
false
}
method findIndex(predicate: (T) => bool) -> int {
for i in 0..self.len() {
if predicate(self[i]) { return i }
}
-1
}
method flatMap<U>(f: (T) => Vec<U>) -> Vec<U> {
let mut result = []
for item in self {
for sub in f(item) {
result.push(sub)
}
}
result
}
/// Group the elements of `self` by the key returned by `key_fn`.
///
/// Returns a flat `Vec<T>` in which every element that shares a key
/// is contiguous. Groups appear in the order each key was first
/// encountered, and within a group the elements keep their original
/// relative order. For `[1,2,3,4,5,6].groupBy(|x| x % 2)` the result
/// is `[1, 3, 5, 2, 4, 6]` — the odd group, then the even group.
///
/// This is a pure-Shape implementation built from a single result
/// array and index-based loops. For each position `i`, the method
/// first checks whether `key_fn(self[i])` is the *first* occurrence
/// of that key (no earlier element shares it). On a first occurrence
/// it scans the rest of the array once and appends every element
/// with the matching key, so the group is emitted contiguously and
/// in original relative order. Later positions whose key already
/// appeared are skipped. Every loop is bounded by `self.len()`, so
/// the method always terminates and never recurses into itself.
///
/// Why a flat `Vec<T>` and not a `Vec<Vec<T>>` of buckets: a
/// collection-of-collections return value cannot currently be
/// constructed — a nested array literal or a `[a, b]` of array
/// variables hits the `op_new_array` / `op_new_typed_array` V3-S5
/// ckpt-5 surface (the W12 typed-array-data deletion has not yet
/// landed its construction-site rebuild). A `HashMap<K, Vec<T>>` is
/// equally blocked: HashMap keys must be strings and HashMap values
/// reject array payloads. A flat `Vec<T>` is the only return shape
/// that is fully supported today — the same working envelope the
/// ε-2 checkpoint used for `sort`. The grouping is real: callers
/// recover the bucket boundaries by re-applying `key_fn` to adjacent
/// elements (a key change marks a new group). The body uses a single
/// `[]` literal: a *second* array literal in one method body hits
/// the same `op_new_array(0)` V3-S5 ckpt-5 surface, so the distinct
/// keys are detected inline by re-scanning rather than collected
/// into their own array.
///
/// The previous body was `self.groupBy(key_fn)` — annotated "keep
/// Rust delegation", but method dispatch resolves `self.groupBy`
/// back to this same Shape method, so it recursed unconditionally
/// and overflowed the stack on the first call. The Rust PHF handler
/// (`array_transform::handle_group_by_v2`) is itself an unimplemented
/// V3-S5 surface stub, so delegation could never have worked.
method groupBy<K>(key_fn: (T) => K) -> Vec<T> {
let mut result = []
let n = self.len()
let mut i = 0
while i < n {
let key = key_fn(self[i])
// First occurrence of `key`? Scan every earlier element.
let mut is_first = true
let mut j = 0
while j < i {
if key_fn(self[j]) == key {
is_first = false
}
j = j + 1
}
if is_first {
// Emit this whole group contiguously, in original order.
let mut k = i
while k < n {
if key_fn(self[k]) == key {
result.push(self[k])
}
k = k + 1
}
}
i = i + 1
}
result
}
method sortBy(key_fn: (T) => number) -> Vec<T> {
self.sort(|a, b| {
let ka = key_fn(a)
let kb = key_fn(b)
if ka < kb { -1 }
else if ka > kb { 1 }
else { 0 }
})
}
}
// -- Numeric-only methods ---------------------------------------------------
trait NumericVec {
method sum() -> number;
method avg() -> number;
method mean() -> number;
method min() -> number;
method max() -> number;
method std() -> number;
method variance() -> number;
method dot(other: Vec<number>) -> number;
method norm() -> number;
method normalize() -> Vec<number>;
method cumsum() -> Vec<number>;
method diff() -> Vec<number>;
method abs() -> Vec<number>;
}
impl NumericVec for Vec {
method sum() -> number { self.sum() }
method avg() -> number { self.avg() }
method mean() -> number { self.mean() }
method min() -> number { self.min() }
method max() -> number { self.max() }
method std() -> number { self.std() }
method variance() -> number { self.variance() }
method dot(other: Vec<number>) -> number { self.dot(other) }
method norm() -> number { self.norm() }
method normalize() -> Vec<number> { self.normalize() }
method cumsum() -> Vec<number> { self.cumsum() }
method diff() -> Vec<number> { self.diff() }
method abs() -> Vec<number> { self.abs() }
}