---
title: "Random Number Generation"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Random Number Generation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

In this vignette, you will learn how to generate random numbers in {anvl}, which is different from base R, where random number generation uses a global state (`.Random.seed`) that is automatically updated after each call:

```{r}
set.seed(42)
.Random.seed[2:4]
rnorm(3)
.Random.seed[2:4]
rnorm(3)
.Random.seed[2:4]
```

In {anvl}, the random state must be explicitly passed around.
This is because we are following a functional programming paradigm where functions are pure and don't have side effects.

**Note:** This explicit state-passing behavior might change in the future to provide a more R-like experience, but for now you need to manage the state yourself.

To generate random numbers, you first need to create an initial RNG state, which is simply a `ui64[2]`.
You can convert a seed into a state using `nv_rng_state()`:

```{r}
library(anvl)
state <- nv_rng_state(42L)
state
```

The main functions for generating random numbers are `nv_runif()`, `nv_rdunif()`, `nv_rbinom()`, and `nv_rnorm()`.
All those functions return a list with two elements:

1. The **new** RNG state (to be used for subsequent random number generation).
2. The generated random numbers.

Let's generate some uniform random numbers:

```{r}
result <- nv_runif(state, dtype = "f32", shape = c(2, 3))
result[[1]]  # new state
result[[2]]  # random numbers
```

For normally distributed random numbers:

```{r}
result <- nv_rnorm(state, dtype = "f32", shape = c(2, 3), mu = 0, sigma = 1)
result[[2]]
```

One thing to avoid is to reuse the same state for multiple calls as done in the example below:

```{r}
result1 <- nv_runif(state, dtype = "f32", shape = 3L)
result2 <- nv_runif(state, dtype = "f32", shape = 3L)
list(first = result1[[2]], second = result2[[2]])
```

As you can see, both calls produced identical random numbers because we used the same state for both.
To get different random numbers in subsequent calls, you need to pass the **new** state returned by the previous call:

```{r}
result1 <- nv_runif(state, dtype = "f32", shape = 3L)
new_state <- result1[[1]]
result2 <- nv_runif(new_state, dtype = "f32", shape = 3L)
list(first = result1[[2]], second = result2[[2]])
```

Now we get different random numbers because we properly propagated the state.
