# README
![radix test](https://github.com/hertogp/radix/actions/workflows/elixir.yml/badge.svg)
[Online Radix Documentation](https://hexdocs.pm/radix).
<!-- @MODULEDOC -->
A bitwise radix tree for prefix based matching on bitstring keys of any length.
Radix provides a [radix tree](https://en.wikipedia.org/wiki/Radix_tree) whose
radius is 2, has path-compression and no one-way branching. Entries consist of
{key, value}-pairs whose insertion/deletion is always based on an exact
key-match. Retrieval can be either exact or is based on a prefix match.
## Examples
iex> t = new()
...> |> put(<<1, 1, 1>>, "1.1.1/24")
...> |> put(<<1, 1, 1, 0::6>>, "1.1.1.0/30")
...> |> put(<<1, 1, 1, 1::1>>, "1.1.1.128/25")
...> |> put(<<255>>, "255/8")
iex>
iex>
iex> # longest prefix match
iex>
iex> lookup(t, <<1, 1, 1, 255>>)
{<<1, 1, 1, 1::1>>, "1.1.1.128/25"}
iex>
iex>
iex> # more specific matches (includes search key if present)
iex>
iex> more(t, <<1, 1, 1>>)
[{<<1, 1, 1, 0::size(6)>>, "1.1.1.0/30"}, {<<1, 1, 1>>, "1.1.1/24"}, {<<1, 1, 1, 1::size(1)>>, "1.1.1.128/25"}]
iex>
iex>
iex> # less specific matches (includes search key if present)
iex>
iex> less(t, <<1, 1, 1, 3>>)
[{<<1, 1, 1, 0::size(6)>>, "1.1.1.0/30"}, {<<1, 1, 1>>, "1.1.1/24"}]
iex>
iex> # exact match
iex> get(t, <<1, 1, 1, 0::6>>)
{<<1, 1, 1, 0::6>>, "1.1.1.0/30"}
iex>
iex> get(t, <<1, 1, 1, 0>>)
nil
iex>
iex> dot(t) |> (&File.write("img/readme.dot", &1)).()
The radix tree above looks something like this:
![Radix](img/readme.dot.png)
The tree is represented by two types of nodes:
- *internal node*, as a `{bit, left, right}`-tuple, and
- *leaf node*, which is either `nil` or a non-empty list of `{key,value}`-pairs
The `bit` denotes the bitposition to check in a key during a tree traversal,
where `0` means go `left` and `1` means go `right`. A `bit` beyond a `key`'s
length is considered to be `0`. Path-compression means not all bits are
checked during tree traversal, only those that differentiate the keys stored
below the current `internal` node. Hence, branches are formed as keys with
different patterns are stored in the tree.
The `leaf` node can have a list of `{key, value}`-pairs where all longer keys
have all shorter keys as their prefix. In other words, they all agree on the
bits that were checked to arrive at that node. The `key` is stored alongside
the `value` since, due to path-compression, a final match is needed to ensure
a correct match. Hence, retrieval functions return the `{key, value}`-pair,
rather than just the `value`, since the stored key is not always equal to the
given search key (e.g. when doing a longest prefix match).
Since binaries are bitstrings too, they work as well:
iex> t = new([{"A.new", "new"}, {"A.newer", "newer"}, {"B.newest", "newest"}])
iex> more(t, "A.") |> Enum.reverse()
[{"A.new", "new"}, {"A.newer", "newer"}]
#
iex> lookup(t, "A.newest")
{"A.new", "new"}
#
iex> more(t, "C.")
[]
<!-- @MODULEDOC -->
Note: if Radix is used only for `put`/`get` operations, using a regular map
is way faster.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `radix` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:radix, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/radix](https://hexdocs.pm/radix).