---
title: "ring applications"
author: "Rich FitzJohn"
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{ring applications}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

``` {r echo=FALSE, results="hide"}
knitr::opts_chunk$set(
  error=FALSE,
  fig.width=7,
  fig.height=5)
set.seed(1)
```

The bytes-based ring buffer in the main vignette is a better data
structure to implement the simulation than the environment buffer
is because the expected elements in each entry of the buffer are
the same.  But with a bit of S3 syntactic sugar we can do a bit
better.  This vignette is an attempt at creating "ring" versions of
a vector and matrix data type.

NOTE: this vignette used to be implemented in the package itself,
but I pulled it out of the package because I felt that the
implementation wasn't quite right, and that it may not be ideal to
have objects that appear to have normal-R semantics operate by side
effect.  However, this may give some ideas for how to use ring
buffers in practice.

## Ring vector

The actual code for the buffer here is available in the package via
`system.file("examples/ring_vector.R", package = "ring")` (the path
depends on your R and package installations).

``` {r echo = FALSE, results = "asis"}
local({
  path <- system.file("examples/ring_vector.R", package = "ring")
  source(path, local = FALSE)
  writeLines(c("```r", readLines(path), "```"))
})
```

Then create an integer ring vector of length 5:
``` {r }
v <- ring_vector(5, "integer", FALSE)
```

Convert back out to be an R vector (involves a copy)
``` {r }
v[]
```

To add things to the vector, use the `push` generic:
``` {r }
push(v, 1L)
v[]
```

This can push multiple items on at once:
``` {r }
push(v, 2:4)
v[]
length(v)
```

Random read access works:
``` {r }
v[3]
v[[1]]
```

Resetting the buffer zeros this all:
``` {r }
v$buf$reset()
length(v)
```

Returning to the simulation example from the main vignette:
``` {r }
buf <- ring_vector(5, "integer", FALSE)
h <- integer(20)
x <- 0L
push(buf, x)
h[1L] <- x

step <- function(x) {
  if (runif(1) < 0.5) x - 1L else x + 1L
}

set.seed(1)
for (i in seq_len(length(h) - 1L)) {
  x <- step(x)
  push(buf, x)
  h[i + 1L] <- x
}
```

The whole history:
``` {r }
h
```

The last 5 steps:
``` {r }
buf[]
```

Now, rewriting again, this time with the step function taking the
buffer itself.  This simplifies the implementation, with most of
the details being handled by the S3 methods for `length`, `push`
and `[`.
``` {r }
step <- function(x) {
  if (length(x) > 1) {
    p <- mean(diff(x[])) / 2 + 0.5
  } else {
    p <- 0.5
  }
  if (runif(1) < p) x[length(x)] - 1L else x[length(x)] + 1L
}

buf <- ring_vector(5, "integer", FALSE)
h <- integer(100)
x <- 0L

push(buf, x)
h[1L] <- x

set.seed(1)
for (i in seq_len(length(h) - 1L)) {
  x <- step(buf)
  push(buf, x)
  h[i + 1L] <- x
}

par(mar=c(4, 4, .5, .5))
plot(h, type="l", xlab="step", ylab="y", las=1)
```

## Ring matrix with `ring_matrix`

The `ring_matrix` data structure generalises the `ring_vector`; it
is a buffer that looks to R like a matrix that grows by adding rows
at the bottom and shrinks by consuming rows at the top.

``` {r echo = FALSE, results = "asis"}
local({
  path <- system.file("examples/ring_matrix.R", package = "ring")
  source(path, local = FALSE)
  dat <- readLines(path)
  writeLines(c("```r", dat[!grepl("^###", dat)], "```"))
})
```

This is even more contrived than above, but consider simultaneously
simulating the movement of `n` random particles with the same
reflecting random walk as above.  First create a 10 x 5 ring
matrix:

``` {r }
n <- 10
m <- ring_matrix(5, n, "integer", FALSE)
```

The current state of the matrix is:
``` {r }
m[]
```

We can set the initial state as:
``` {r }
push(m, matrix(0L, 1, n))
m[]

step <- function(m) {
  if (nrow(m) > 1) {
    p <- colMeans(diff(m[])) / 2 + 0.5
  } else {
    p <- rep(0.5, ncol(m))
  }
  m[nrow(m), ] + as.integer(ifelse(runif(length(p)) < p, -1, 1L))
}

m <- ring_matrix(5, n, "integer", FALSE)
x <- rep(0L, n)
push(m, x)

h <- matrix(NA, 200, n)
h[1, ] <- x
set.seed(1)
for (i in seq_len(nrow(h) - 1L)) {
  x <- step(m)
  push(m, x)
  h[i + 1L, ] <- x
}

par(mar=c(4, 4, .5, .5))
matplot(h, type="l", lty=1, las=1)
```