---
title: "flexseq"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{flexseq}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
if(requireNamespace("pkgload", quietly = TRUE)) {
  pkgload::load_all(".", quiet = TRUE)
} else if(requireNamespace("Immutables", quietly = TRUE)) {
  library(Immutables)
} else {
  stop("Need either installed 'Immutables' or the 'pkgload' package to render this vignette.")
}
```


## `flexseq` basics

`flexseq` is a persistent sequence type, with many of the same features as an R `list()`, including storing arbitrary items.
All updates return a new object and keep the original unchanged.

```{r}
x <- flexseq(1, 2, 3)
x

x2 <- as_flexseq(letters[1:5])
x2
```
## Names and indexing

Flexseqs may be named, but if any are named they must all have unique non-`NULL` names. Large named flexseqs are somewhat slower than unnamed ones; for efficient key/value stores consider using an `ordered_sequence()`.

```{r}
x <- flexseq(a = 1, b = 2, c = 3)
x
x[c("c", "b")]

x$b <- NULL # delete b
x
```

In general indexing and slicing works as it does with lists: `[]` returns a sublist indexed by integer (by position), character vector (by name if named), or logical vector (by inclusion). Note that these operations are $O(k\log n)$ where $k$ is the length of the indexing vector.

```{r}
x <- as_flexseq(letters[1:6])

x[[3]]

x[c(1, 3, 5)]
```

## End operations

Flexseqs support pushing new elements onto the front or back of the sequences, peeking at the front or back (i.e., getting a copy of the first or last item), and popping from the front or back, which returns the removed element and a copy of the flexseq input with that item removed. `pop_front()` and `pop_back()` both return a list with fields `$value` and `$remaining`; `peek_back()` is the symmetric counterpart to `peek_front()`.

```{r}
x <- as_flexseq(4:6)

x <- x |>
  push_back(100) |>
  push_front(50)

x

xpopped <- pop_front(x)

xpopped$value
xpopped$remaining
```
## Positional operations

Beyond the ends, flexseqs support arbitrary-position persistent operations. `peek_at()` is the non-throwing positional read (compare with `[[`, which errors when out of bounds); `pop_at()` removes an element at a given index and returns `$value`/`$remaining`; `insert_at()` inserts before a given index, or at `length(x) + 1` to append.

```{r}
x <- flexseq("a", "b", "c", "d")

peek_at(x, 10)  # NULL, no error

out <- pop_at(x, 2)
out$value
out$remaining

insert_at(x, 3, c("x", "y"))
```

## Transforming and combining

`c()` concatenates flexseqs, and `fapply()` applies a function to each element of one returning the result as a flexseq, analygous to `lapply()` for R lists.
```{r}
x <- as_flexseq(4:6)
x2 <- as_flexseq(8:10)

c(x, x2)

x <- as_flexseq(1:3)
fapply(x, function(el) el * 10)
```

`merge(x, y)` is provided as an alias for `c(x, y)` so that `merge()` works uniformly across the package's four structure types. For `flexseq`, `c()` and `merge()` are equivalent; the distinction matters for `ordered_sequence`, `interval_index`, and `priority_queue`, where `merge()` performs a proper sorted/priority-aware combine.

`loop()` (re-exported from the **coro** package) enables `for`-loop traversal, yielding elements left-to-right lazily without materializing a list.

```{r}
x <- flexseq("a", "b", "c", "d")

loop(for (el in x) {
  print(el)
})
```

For named flexseqs, `loop()` yields bare values (matching `peek_front()`); use `as.list()` when you need names alongside values.

```{r}
x <- flexseq(a = 1, b = 2, c = 3)
loop(for (el in x) print(el))
```

Plain `for (el in x)` (without `loop()`) does *not* dispatch to the iteration protocol — it walks the underlying finger-tree storage and yields internal nodes rather than sequence elements. Always wrap with `loop()`.

Flexseqs convert back to standard R structures with `as.list()` (preserves names and list semantics) and `unlist()` (atomic vector when possible). `length()` returns the element count in O(1), and `str()` gives a compact diagnostic display.

```{r}
x <- flexseq(a = 1, b = 2, c = 3)

as.list(x)
unlist(x)
length(x)
```
