# `ExDataSketch.Backend`
[🔗](https://github.com/thanos/ex_data_sketch/blob/main/lib/ex_data_sketch/backend.ex#L1)

Behaviour defining the computation backend for ExDataSketch.

All sketch computations are dispatched through a backend module. This
abstraction allows swapping between the pure Elixir implementation and
an optional Rust NIF backend without changing the public API.

## Implementing a Backend

A backend module must implement all callbacks defined in this behaviour.
The canonical sketch state is always an Elixir binary. Backend functions
receive and return binaries; they never own persistent sketch state.

## Available Backends

- `ExDataSketch.Backend.Pure` -- Pure Elixir (always available, default).
- `ExDataSketch.Backend.Rust` -- Rust NIF acceleration (optional).
  Accelerates batch operations (`update_many`, `merge`, `estimate`).
  Falls back to Pure if the NIF is not compiled.
  Check with `ExDataSketch.Backend.Rust.available?/0`.

## Backend Selection

The backend is resolved in this order:

1. Per-sketch `:backend` option (e.g., `HLL.new(backend: Backend.Rust)`).
2. Application config: `config :ex_data_sketch, backend: Backend.Pure`.
3. Default: `ExDataSketch.Backend.Pure`.

# `hash64`

```elixir
@type hash64() :: non_neg_integer()
```

# `opts`

```elixir
@type opts() :: keyword()
```

# `state_bin`

```elixir
@type state_bin() :: binary()
```

# `bloom_count`

```elixir
@callback bloom_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of set bits (popcount) in Bloom state.

# `bloom_member?`

```elixir
@callback bloom_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single hash64 value in Bloom state.

# `bloom_merge`

```elixir
@callback bloom_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two Bloom state binaries (bitwise OR of bitsets).

# `bloom_new`

```elixir
@callback bloom_new(opts()) :: state_bin()
```

Create a new Bloom filter state binary with the given options.

# `bloom_put`

```elixir
@callback bloom_put(state_bin(), hash64(), opts()) :: state_bin()
```

Update Bloom state by setting bit positions for a single hash64 value.

# `bloom_put_many`

```elixir
@callback bloom_put_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Update Bloom state by setting bit positions for a list of hash64 values.

# `cms_estimate`

```elixir
@callback cms_estimate(state_bin(), hash64(), opts()) :: non_neg_integer()
```

Estimate the count for a given hash64 from CMS state.

# `cms_merge`

```elixir
@callback cms_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two CMS state binaries (element-wise add).

# `cms_new`

```elixir
@callback cms_new(opts()) :: state_bin()
```

Create a new CMS state binary with the given options.

# `cms_update`

```elixir
@callback cms_update(state_bin(), hash64(), pos_integer(), opts()) :: state_bin()
```

Update CMS state with a single hash64 and increment.

# `cms_update_many`

```elixir
@callback cms_update_many(state_bin(), [{hash64(), pos_integer()}], opts()) :: state_bin()
```

Update CMS state with a list of {hash64, increment} pairs.

# `cqf_count`

```elixir
@callback cqf_count(state_bin(), opts()) :: non_neg_integer()
```

Return the total count of all items from CQF state (sum of multiplicities).

# `cqf_delete`

```elixir
@callback cqf_delete(state_bin(), hash64(), opts()) :: state_bin()
```

Delete a single occurrence of hash64 from CQF state (decrement count).

# `cqf_estimate_count`

```elixir
@callback cqf_estimate_count(state_bin(), hash64(), opts()) :: non_neg_integer()
```

Return the estimated count of a single hash64 in CQF state.

# `cqf_member?`

```elixir
@callback cqf_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single hash64 value in CQF state.

# `cqf_merge`

```elixir
@callback cqf_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two CQF state binaries (multiset union: counts summed).

# `cqf_new`

```elixir
@callback cqf_new(opts()) :: state_bin()
```

Create a new CQF state binary with the given options.

# `cqf_put`

```elixir
@callback cqf_put(state_bin(), hash64(), opts()) :: state_bin()
```

Insert a single hash64 into CQF state, incrementing its count.

# `cqf_put_many`

```elixir
@callback cqf_put_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Insert a list of hash64 values into CQF state.

# `cuckoo_count`

```elixir
@callback cuckoo_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of stored items from Cuckoo state.

# `cuckoo_delete`

```elixir
@callback cuckoo_delete(state_bin(), hash64(), opts()) ::
  {:ok, state_bin()} | {:error, :not_found}
```

Delete a single hash64 from Cuckoo state. Returns {:ok, state} or {:error, :not_found}.

# `cuckoo_member?`

```elixir
@callback cuckoo_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single hash64 value in Cuckoo state.

# `cuckoo_new`

```elixir
@callback cuckoo_new(opts()) :: state_bin()
```

Create a new Cuckoo filter state binary with the given options.

# `cuckoo_put`

```elixir
@callback cuckoo_put(state_bin(), hash64(), opts()) ::
  {:ok, state_bin()} | {:error, :full}
```

Insert a single hash64 into Cuckoo state. Returns {:ok, state} or {:error, :full}.

# `cuckoo_put_many`

```elixir
@callback cuckoo_put_many(state_bin(), [hash64()], opts()) ::
  {:ok, state_bin()} | {:error, :full, state_bin()}
```

Insert a list of hash64 values into Cuckoo state. Returns {:ok, state} or {:error, :full, state}.

# `ddsketch_count`

```elixir
@callback ddsketch_count(state_bin(), opts()) :: non_neg_integer()
```

Return the count of items inserted into DDSketch state.

# `ddsketch_max`

```elixir
@callback ddsketch_max(state_bin(), opts()) :: float() | nil
```

Return the maximum value in DDSketch state, or nil if empty.

# `ddsketch_merge`

```elixir
@callback ddsketch_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two DDSketch state binaries.

# `ddsketch_min`

```elixir
@callback ddsketch_min(state_bin(), opts()) :: float() | nil
```

Return the minimum value in DDSketch state, or nil if empty.

# `ddsketch_new`

```elixir
@callback ddsketch_new(opts()) :: state_bin()
```

Create a new DDSketch state binary with the given options.

# `ddsketch_quantile`

```elixir
@callback ddsketch_quantile(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate value at a given normalized rank from DDSketch state.

# `ddsketch_rank`

```elixir
@callback ddsketch_rank(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate normalized rank of a given value from DDSketch state.

# `ddsketch_update`

```elixir
@callback ddsketch_update(state_bin(), float(), opts()) :: state_bin()
```

Update DDSketch state with a single float64 value.

# `ddsketch_update_many`

```elixir
@callback ddsketch_update_many(state_bin(), [float()], opts()) :: state_bin()
```

Update DDSketch state with a list of float64 values in a single pass.

# `fi_count`

```elixir
@callback fi_count(state_bin(), opts()) :: non_neg_integer()
```

Return the total count of observed items from FrequentItems state.

# `fi_entry_count`

```elixir
@callback fi_entry_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of distinct tracked entries from FrequentItems state.

# `fi_estimate`

```elixir
@callback fi_estimate(state_bin(), binary(), opts()) ::
  {:ok, map()} | {:error, :not_tracked}
```

Return the frequency estimate for a given item_bytes from FrequentItems state.

# `fi_merge`

```elixir
@callback fi_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two FrequentItems state binaries.

# `fi_new`

```elixir
@callback fi_new(opts()) :: state_bin()
```

Create a new FrequentItems state binary with the given options.

# `fi_top_k`

```elixir
@callback fi_top_k(state_bin(), non_neg_integer(), opts()) :: [map()]
```

Return the top-k entries sorted by count descending from FrequentItems state.

# `fi_update`

```elixir
@callback fi_update(state_bin(), binary(), opts()) :: state_bin()
```

Update FrequentItems state with a single item_bytes value.

# `fi_update_many`

```elixir
@callback fi_update_many(state_bin(), [binary()], opts()) :: state_bin()
```

Update FrequentItems state with a list of item_bytes values in a single pass.

# `hll_estimate`

```elixir
@callback hll_estimate(state_bin(), opts()) :: float()
```

Estimate cardinality from HLL state.

# `hll_merge`

```elixir
@callback hll_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two HLL state binaries (register-wise max).

# `hll_new`

```elixir
@callback hll_new(opts()) :: state_bin()
```

Create a new HLL state binary with the given options.

# `hll_update`

```elixir
@callback hll_update(state_bin(), hash64(), opts()) :: state_bin()
```

Update HLL state with a single hash64 value.

# `hll_update_many`

```elixir
@callback hll_update_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Update HLL state with a list of hash64 values in a single pass.

# `iblt_count`

```elixir
@callback iblt_count(state_bin(), opts()) :: non_neg_integer()
```

Return the item count from IBLT state.

# `iblt_delete`

```elixir
@callback iblt_delete(state_bin(), hash64(), hash64(), opts()) :: state_bin()
```

Delete a key_hash and value_hash from IBLT state.

# `iblt_list_entries`

```elixir
@callback iblt_list_entries(state_bin(), opts()) ::
  {:ok, %{positive: [{hash64(), hash64()}], negative: [{hash64(), hash64()}]}}
  | {:error, :decode_failed}
```

List entries by peeling the IBLT. Returns {:ok, entries} or {:error, :decode_failed}.

# `iblt_member?`

```elixir
@callback iblt_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single key_hash in IBLT state.

# `iblt_merge`

```elixir
@callback iblt_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two IBLT state binaries cell-wise (set union).

# `iblt_new`

```elixir
@callback iblt_new(opts()) :: state_bin()
```

Create a new IBLT state binary with the given options.

# `iblt_put`

```elixir
@callback iblt_put(state_bin(), hash64(), hash64(), opts()) :: state_bin()
```

Insert a key_hash and value_hash into IBLT state.

# `iblt_put_many`

```elixir
@callback iblt_put_many(state_bin(), [{hash64(), hash64()}], opts()) :: state_bin()
```

Insert a list of {key_hash, value_hash} pairs into IBLT state.

# `iblt_subtract`

```elixir
@callback iblt_subtract(state_bin(), state_bin(), opts()) :: state_bin()
```

Subtract two IBLT state binaries cell-wise (set difference).

# `kll_cdf`

```elixir
@callback kll_cdf(state_bin(), [float()], opts()) :: [float()] | nil
```

Return the CDF at the given split points from KLL state.

# `kll_count`

```elixir
@callback kll_count(state_bin(), opts()) :: non_neg_integer()
```

Return the count of items inserted into KLL state.

# `kll_max`

```elixir
@callback kll_max(state_bin(), opts()) :: float() | nil
```

Return the maximum value in KLL state, or nil if empty.

# `kll_merge`

```elixir
@callback kll_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two KLL state binaries.

# `kll_min`

```elixir
@callback kll_min(state_bin(), opts()) :: float() | nil
```

Return the minimum value in KLL state, or nil if empty.

# `kll_new`

```elixir
@callback kll_new(opts()) :: state_bin()
```

Create a new KLL state binary with the given options.

# `kll_pmf`

```elixir
@callback kll_pmf(state_bin(), [float()], opts()) :: [float()] | nil
```

Return the PMF at the given split points from KLL state.

# `kll_quantile`

```elixir
@callback kll_quantile(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate value at a given normalized rank from KLL state.

# `kll_rank`

```elixir
@callback kll_rank(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate normalized rank of a given value from KLL state.

# `kll_update`

```elixir
@callback kll_update(state_bin(), float(), opts()) :: state_bin()
```

Update KLL state with a single float64 value.

# `kll_update_many`

```elixir
@callback kll_update_many(state_bin(), [float()], opts()) :: state_bin()
```

Update KLL state with a list of float64 values in a single pass.

# `mg_count`

```elixir
@callback mg_count(state_bin(), opts()) :: non_neg_integer()
```

Return the total count of observed items from MisraGries state.

# `mg_entry_count`

```elixir
@callback mg_entry_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of distinct tracked entries from MisraGries state.

# `mg_estimate`

```elixir
@callback mg_estimate(state_bin(), binary(), opts()) :: non_neg_integer()
```

Return the frequency estimate for a given item_bytes from MisraGries state.

# `mg_merge`

```elixir
@callback mg_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two MisraGries state binaries.

# `mg_new`

```elixir
@callback mg_new(opts()) :: state_bin()
```

Create a new MisraGries state binary with the given options.

# `mg_top_k`

```elixir
@callback mg_top_k(state_bin(), non_neg_integer(), opts()) :: [
  {binary(), non_neg_integer()}
]
```

Return the top-k entries sorted by count descending from MisraGries state.

# `mg_update`

```elixir
@callback mg_update(state_bin(), binary(), opts()) :: state_bin()
```

Update MisraGries state with a single item_bytes value.

# `mg_update_many`

```elixir
@callback mg_update_many(state_bin(), [binary()], opts()) :: state_bin()
```

Update MisraGries state with a list of item_bytes values in a single pass.

# `quotient_count`

```elixir
@callback quotient_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of stored items from Quotient state.

# `quotient_delete`

```elixir
@callback quotient_delete(state_bin(), hash64(), opts()) :: state_bin()
```

Delete a single hash64 from Quotient state.

# `quotient_member?`

```elixir
@callback quotient_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single hash64 value in Quotient state.

# `quotient_merge`

```elixir
@callback quotient_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two Quotient state binaries.

# `quotient_new`

```elixir
@callback quotient_new(opts()) :: state_bin()
```

Create a new Quotient filter state binary with the given options.

# `quotient_put`

```elixir
@callback quotient_put(state_bin(), hash64(), opts()) :: state_bin()
```

Insert a single hash64 into Quotient state.

# `quotient_put_many`

```elixir
@callback quotient_put_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Insert a list of hash64 values into Quotient state.

# `req_cdf`

```elixir
@callback req_cdf(state_bin(), [float()], opts()) :: [float()] | nil
```

Return the CDF at the given split points from REQ state.

# `req_count`

```elixir
@callback req_count(state_bin(), opts()) :: non_neg_integer()
```

Return the count of items inserted into REQ state.

# `req_max`

```elixir
@callback req_max(state_bin(), opts()) :: float() | nil
```

Return the maximum value in REQ state, or nil if empty.

# `req_merge`

```elixir
@callback req_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two REQ state binaries.

# `req_min`

```elixir
@callback req_min(state_bin(), opts()) :: float() | nil
```

Return the minimum value in REQ state, or nil if empty.

# `req_new`

```elixir
@callback req_new(opts()) :: state_bin()
```

Create a new REQ state binary with the given options.

# `req_pmf`

```elixir
@callback req_pmf(state_bin(), [float()], opts()) :: [float()] | nil
```

Return the PMF at the given split points from REQ state.

# `req_quantile`

```elixir
@callback req_quantile(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate value at a given normalized rank from REQ state.

# `req_rank`

```elixir
@callback req_rank(state_bin(), float(), opts()) :: float() | nil
```

Return the approximate normalized rank of a given value from REQ state.

# `req_update`

```elixir
@callback req_update(state_bin(), float(), opts()) :: state_bin()
```

Update REQ state with a single float64 value.

# `req_update_many`

```elixir
@callback req_update_many(state_bin(), [float()], opts()) :: state_bin()
```

Update REQ state with a list of float64 values in a single pass.

# `theta_compact`

```elixir
@callback theta_compact(state_bin(), opts()) :: state_bin()
```

Compact Theta state: sort entries and discard any above theta.

# `theta_estimate`

```elixir
@callback theta_estimate(state_bin(), opts()) :: float()
```

Estimate cardinality from Theta state.

# `theta_from_components`

```elixir
@callback theta_from_components(non_neg_integer(), non_neg_integer(), [non_neg_integer()]) ::
  state_bin()
```

Build Theta state binary from raw components (k, theta, sorted entries list).

# `theta_merge`

```elixir
@callback theta_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two Theta state binaries (set union).

# `theta_new`

```elixir
@callback theta_new(opts()) :: state_bin()
```

Create a new Theta state binary with the given options.

# `theta_update`

```elixir
@callback theta_update(state_bin(), hash64(), opts()) :: state_bin()
```

Update Theta state with a single hash64 value.

# `theta_update_many`

```elixir
@callback theta_update_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Update Theta state with a list of hash64 values in a single pass.

# `ull_estimate`

```elixir
@callback ull_estimate(state_bin(), opts()) :: float()
```

Estimate cardinality from ULL state.

# `ull_merge`

```elixir
@callback ull_merge(state_bin(), state_bin(), opts()) :: state_bin()
```

Merge two ULL state binaries (register-wise max).

# `ull_new`

```elixir
@callback ull_new(opts()) :: state_bin()
```

Create a new ULL state binary with the given options.

# `ull_update`

```elixir
@callback ull_update(state_bin(), hash64(), opts()) :: state_bin()
```

Update ULL state with a single hash64 value.

# `ull_update_many`

```elixir
@callback ull_update_many(state_bin(), [hash64()], opts()) :: state_bin()
```

Update ULL state with a list of hash64 values in a single pass.

# `xor_build`

```elixir
@callback xor_build([hash64()], opts()) :: {:ok, state_bin()} | {:error, :build_failed}
```

Build an XorFilter from a list of hash64 values. Returns {:ok, state} or {:error, :build_failed}.

# `xor_count`

```elixir
@callback xor_count(state_bin(), opts()) :: non_neg_integer()
```

Return the number of items the XorFilter was built from.

# `xor_member?`

```elixir
@callback xor_member?(state_bin(), hash64(), opts()) :: boolean()
```

Test membership of a single hash64 value in XorFilter state.

# `default`

```elixir
@spec default() :: module()
```

Returns the default backend module.

Checks application config first, falls back to `ExDataSketch.Backend.Pure`.

## Examples

    iex> backend = ExDataSketch.Backend.default()
    iex> backend == ExDataSketch.Backend.Pure
    true

# `resolve`

```elixir
@spec resolve(keyword()) :: module()
```

Resolves the backend from options or application config.

## Examples

    iex> ExDataSketch.Backend.resolve(backend: ExDataSketch.Backend.Pure)
    ExDataSketch.Backend.Pure

    iex> ExDataSketch.Backend.resolve([])
    ExDataSketch.Backend.Pure

---

*Consult [api-reference.md](api-reference.md) for complete listing*
