# Indexing and Slicing
Ignition implements MATLAB-compatible indexing/slicing in the VM to ensure one source of truth for semantics. The compiler recognizes patterns and emits specialized instructions that keep the VM simple and fast.
Indexing categories:
- Numeric-only scalar indices → `Index(n)` / `StoreIndex(n)`
- Mixed `:` (colon), `end`, ranges, vectors, logical masks → `IndexSlice` / `StoreSlice`
- `end - k` arithmetic on numeric positions → `IndexSliceEx`
- Ranges using per-dimension `end` arithmetic → `IndexRangeEnd` (N-D) and `Index1DRangeEnd` (1-D)
- Cell content indexing → `IndexCell` / `IndexCellExpand`
All indexing is 1-based and column-major. Out-of-bounds and shape-mismatch conditions are normalized via mex identifiers.
## Stack conventions
- For gather (read): the compiler pushes the base value first, then any numeric index values. The VM pops numeric indices (reversing their order) and then pops the base.
- For scatter (write): the compiler pushes base, then numeric indices, then the RHS value. The VM pops RHS, then numerics, then base.
## Gather: Index(n)
`Index(n)` is used when all indices are numeric scalars (no `:`, `end`, vectors, logical). The VM:
1. Pops `n` numeric indices and reverses to restore left-to-right order
2. Pops the base
3. If base is an object → routes to `subsref(obj, '()', {indices})`
4. Else uses `runmat_runtime::perform_indexing(base, &indices)`
Error cases:
- Non-tensor/non-object with `n > 1`
- Out-of-bounds → `MATLAB:IndexOutOfBounds` or `MATLAB:SubscriptOutOfBounds`
## Gather: IndexSlice(dims, numeric_count, colon_mask, end_mask)
Use when any dimension involves `:`, `end`, ranges/vectors, or logical masks. The VM constructs per-dimension selectors:
- Colon → full range in that dimension
- End → scalar index equal to the dimension length
- Numeric (scalar) → use as 1-based index
- Numeric (vector) → materialize list of indices (1-based)
- Logical mask → dimension length must match; non-zeros become indices
Shape rules:
- Each selector contributes either 1 (scalar) or its length
- In 2-D, shapes are normalized to match MATLAB:
- `(I, scalar)` → `[len(I), 1]`
- `(scalar, J)` → `[1, len(J)]`
- A single result value returns as a scalar `Value::Num` or `Value::String`
Fast 2-D paths (tensors):
- `A(:, j)` returns full column quickly
- `A(i, :)` returns full row quickly
- `A(:, J)` returns `[rows, |J|]`
- `A(I, :)` returns `[|I|, cols]`
String arrays mirror tensor semantics, but return `Value::String` for scalar results and `Value::StringArray` for arrays.
Errors:
- Out-of-bounds → `MATLAB:IndexOutOfBounds`
- Logical mask shape mismatch → `MATLAB:IndexShape`
- Slicing non-tensors/strings → `MATLAB:SliceNonTensor`
### 1-D specialization
For `dims == 1`, the VM supports:
- `A(:)` → all linear elements
- `A(end)` → last element
- Logical mask the same length as `numel(A)` → keep non-zeros
- Vector of indices → gather in order
## Gather with end arithmetic: IndexSliceEx
`IndexSliceEx(dims, numeric_count, colon_mask, end_mask, end_offsets)` applies `end - k` to specific numeric positions. Each `(pos, k)` pairs the position within the numeric indices list (not dimension index) with an offset `k`. The VM maps numeric positions to actual dimensions by skipping colon and plain `end` dims.
## Gather with ranged end arithmetic: IndexRangeEnd / Index1DRangeEnd
Use when the end of a range depends on a dimension length: `i:j:end-k` per dimension.
- `IndexRangeEnd` parameters:
- `dims`, `numeric_count`, `colon_mask`, `end_mask`
- `range_dims`: which dims are ranges
- `range_has_step`: whether each range has a step
- `end_offsets`: `k` for `end-k`
- Stack order: base, then for each range in increasing dimension order push `start[, step]`, then numeric scalar indices
- The VM computes concrete indices per dim, honoring sign of the step; step 0 → `MATLAB:IndexStepZero`
- `Index1DRangeEnd` is a compact form for the common 1-D case
## Scatter: StoreIndex / StoreSlice
`StoreIndex(n)` supports scalar numeric indices only. `StoreSlice` parallels `IndexSlice` with the same selector construction.
Broadcasting rules (tensors):
- RHS can be scalar → broadcast to all selected elements
- RHS can be a tensor whose per-dimension lengths are either 1 or equal to the selection extent in that dimension
- Column-/row-fast paths update entire columns or rows efficiently
String array writes:
- RHS can be a `String`, `StringArray`, or numeric converted to string (for convenience)
Errors:
- Out-of-bounds → `MATLAB:IndexOutOfBounds`
- Shape mismatch for broadcasting → `MATLAB:ShapeMismatch`
- Non-tensor/strings → `MATLAB:SliceNonTensor`
## Scatter with end arithmetic: StoreSliceEx / StoreRangeEnd / StoreSlice1DRangeEnd
`StoreSliceEx` applies `end - k` to numeric positions before performing a generic scatter.
`StoreRangeEnd` mirrors `IndexRangeEnd` for writes: the VM builds per-dimension index lists (including ranged `end-k`) and then performs broadcasting-aware scatter into the selected positions.
`StoreSlice1DRangeEnd` is a compact 1-D writer: base, `start[, step]`, `rhs` and an `offset` for `end-k`.
## Cells: IndexCell and IndexCellExpand
`IndexCell(n)` supports:
- 1-D `C{i}`
- 2-D `C{i,j}`
- For objects: routes to `subsref(obj, '{}', {indices})`
`IndexCellExpand(n, out_count)` expands contents into a comma-list in column-major order. If indices are omitted with `expand_all`, all cell elements expand. This is used for argument expansion at call sites and for building vectors via `PackToRow/PackToCol` when assigning into slices.
Errors:
- Unsupported index type → `MATLAB:CellIndexType`
- Out-of-bounds → `MATLAB:CellSubscriptOutOfBounds`
## Interactions with function calls
- A user function call used as an argument may be expanded into multiple inputs by compiling it via `CallFunctionMulti` and then packed using `PackToRow`/`PackToCol`
- Cell expansion at call-sites is expressed via `Call*ExpandMulti` with `ArgSpec` entries describing which arguments expand and how many indices are consumed
See `vm.rs` for full instruction handlers and `INSTR_SET.md` for exact stack layouts.