---
title: "Examples and Recipes"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Examples and Recipes}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, echo = FALSE, message = FALSE}
knitr::opts_chunk$set(collapse = T, comment = "#>")
options(tibble.print_min = 5)

library(dplyr)
library(ipaddress)
```

This vignette demonstrates some typical example usage and recipes when learning about ipaddress.


## Addresses from integers

Although IPv4 addresses are usually displayed in a human-readable format (e.g. `192.168.0.1`), they are often saved to disk as integers to reduce storage requirements.
R is unable to store the entire range of IPv4 addresses in its integer data type, but they can instead be stored in its numeric data type.
More details are found under `help("ip_to_integer")`.

Given this, it's quite possible that you'll receive IPv4 addresses represented as integers, and we'll want to convert them to the `ip_address()` vector type. Here's an example of how to do that:

```{r}
integer_to_ip(c(0, 3232235521, 4294967295))
```


## Networks from address ranges

There are multiple equivalent ways to represent an IP network:

1. start address + prefix length (e.g. CIDR notation)
2. start address + netmask
3. start address + hostmask
4. start address + end address

Although the `ip_network()` function can handle the first 3 options, we use the `common_network()` function for the final option.

```{r}
tibble(
  start = ip_address(c("192.168.0.0", "2001:db8::")),
  end = ip_address(c("192.168.0.15", "2001:db8::ffff:ffff:ffff"))
) %>%
  mutate(network = common_network(start, end))
```

Note that this approach assumes the two addresses do actually correspond to the first and last addresses of the network, otherwise an expanded network will be returned (see `help("common_network")` for details).


## Is an address in a network?

A very common task is to check if an address is within a network, so the ipaddress package provides a couple of different functions to help with this workflow: `is_within()` and `is_within_any()`.
We also provide `is_subnet()` and `is_supernet()` to test if a network is within another network.

To see how these functions can be used in practice, let's consider a couple of IP networks:

```{r}
my_networks <- tibble(
  network = ip_network(c("192.168.0.0/16", "2001:db8::/32")),
  label = c("Private", "Documentation")
)

my_networks
```

and a handful of addresses:

```{r}
my_addresses <- tibble(
  address = ip_address(c("192.168.100.1", "1.2.3.4", "2001:db8::8a2e:370:7334", "::1"))
)
```

First, we'll check if each address is in _any_ of our networks.

```{r}
my_addresses %>%
  mutate(in_network = is_within_any(address, my_networks$network))
```

But what if we need to know _which_ of our networks the address was found in?
We can do that using the excellent [fuzzyjoin](https://cran.r-project.org/package=fuzzyjoin) package together with the `is_within()` function.

```{r, eval=rlang::is_installed("fuzzyjoin")}
my_addresses %>%
  fuzzyjoin::fuzzy_left_join(my_networks, c("address" = "network"), is_within)
```

## Randomly generate public addresses

ipaddress provides functions to sample from a specific network (`sample_network()`) or the entire address space (`sample_ipv4()` and `sample_ipv6()`).
However, it can be more difficult to sample from the majority of address space, while excluding certain networks.

A good example is sampling public IPv4 addresses.
The simplest solution is to use an accept-reject algorithm -- sampling the entire IPv4 address space and rejecting addresses that are reserved.

```{r accept-reject}
sample_public <- function(size) {
  result <- sample_ipv4(size)

  all_public <- FALSE
  while (!all_public) {
    public <- is_global(result)
    n_replace <- sum(!public)

    if (n_replace == 0) {
      all_public <- TRUE
    } else {
      result[!public] <- sample_ipv4(n_replace)
    }
  }

  result
}
```

We now sample 10 addresses and make sure they are all public addresses.

```{r}
tibble(address = sample_public(10)) %>%
  mutate(is_public = is_global(address))
```
