Xb5.Set (xb5_elixir v1.0.0)

Copy Markdown View Source

An ordered set backed by a B-tree of order 5.

Elements are kept in ascending Erlang term order, and each value appears at most once. Comparisons use == rather than === — so 1 and 1.0 are treated as the same element, unlike MapSet.

Conversion to a list via to_list/1 always yields elements in ascending order.

Erlang interop

Xb5.Set is compatible with the Erlang :xb5_sets module. Build one from an :xb5_sets term via new/1. To go the other way, call unwrap!/1 to extract the size and root node, then pass the result to :xb5_sets.wrap/1.

See also

  • Xb5.Bag — ordered multiset with order-statistic operations (percentile, rank)
  • Xb5.Tree — ordered key-value store.

Examples

iex> set = Xb5.Set.new([3, 1, 2, 1])
Xb5.Set.new([1, 2, 3])
iex> Xb5.Set.member?(set, 2)
true
iex> Xb5.Set.last!(set)
3

Summary

Functions

Deletes value from set.

Returns a set that is set1 without the members of set2.

Checks if set1 and set2 have no members in common.

Checks if two sets are equal.

Filters set by returning only elements for which fun returns a truthy value.

Returns the first (smallest) element in set, or default if set is empty.

Returns the first (smallest) element in set.

Returns the smallest element in set strictly greater (larger) than value, or :error if none exists.

Returns a set containing only members that set1 and set2 have in common.

Returns the last (largest) element in set, or default if set is empty.

Returns the last (largest) element in set.

Returns the largest element in set strictly less (smaller) than value, or :error if none exists.

Applies fun to each element and returns a new set built from the results.

Checks if set contains value.

Returns a new empty set.

Creates a set from an Erlang :xb5_sets term or an enumerable.

Creates a set from an Erlang :xb5_sets term or an enumerable via the transformation function.

Removes and returns {value, updated_set} for the first (smallest) element in set.

Removes and returns {value, updated_set} for the last (largest) element in set.

Inserts value into set if set doesn't already contain it.

Returns a set by excluding the elements from set for which fun returns a truthy value.

Returns the number of elements in set.

Splits set into two sets according to the given function fun.

Returns a lazy stream over all elements of set.

Returns a lazy stream over elements of set starting from value.

Returns structural statistics about the underlying B-tree.

Checks if set1's members are all contained in set2.

Returns a set with elements that are present in only one but not both sets.

Converts set to a sorted list.

Returns a set containing all members of set1 and set2.

Returns the size and root node of set as %{size: n, root: node}.

Types

order()

@type order() :: :asc | :desc

t()

@type t() :: t(value())

t(value)

@type t(value) :: %Xb5.Set{root: :xb5_sets_node.t(value), size: non_neg_integer()}

value()

@type value() :: term()

Functions

delete(set, value)

@spec delete(t(val1), val2) :: t(val1) when val1: value(), val2: value()

Deletes value from set.

Returns a new set which is a copy of set but without value.

Examples

iex> set = Xb5.Set.new([1, 2, 3])
iex> Xb5.Set.delete(set, 4)
Xb5.Set.new([1, 2, 3])
iex> Xb5.Set.delete(set, 2)
Xb5.Set.new([1, 3])

difference(set1, set2)

@spec difference(t(val1), t(val2)) :: t(val1) when val1: value(), val2: value()

Returns a set that is set1 without the members of set2.

Examples

iex> Xb5.Set.difference(Xb5.Set.new([1, 2]), Xb5.Set.new([2, 3, 4]))
Xb5.Set.new([1])

disjoint?(set1, set2)

@spec disjoint?(t(), t()) :: boolean()

Checks if set1 and set2 have no members in common.

Examples

iex> Xb5.Set.disjoint?(Xb5.Set.new([1, 2]), Xb5.Set.new([3, 4]))
true
iex> Xb5.Set.disjoint?(Xb5.Set.new([1, 2]), Xb5.Set.new([2, 3]))
false

equal?(set1, set2)

@spec equal?(t(), t()) :: boolean()

Checks if two sets are equal.

The comparison between elements is done using ==, so for example Xb5.Set.new([1]) is equal to Xb5.Set.new([1.0]).

Examples

iex> Xb5.Set.equal?(Xb5.Set.new([1, 2]), Xb5.Set.new([2, 1, 1]))
true
iex> Xb5.Set.equal?(Xb5.Set.new([1, 2]), Xb5.Set.new([3, 4]))
false
iex> Xb5.Set.equal?(Xb5.Set.new([1]), Xb5.Set.new([1.0]))
true

filter(set, fun)

@spec filter(t(a), (a -> as_boolean(term()))) :: t(a) when a: value()

Filters set by returning only elements for which fun returns a truthy value.

Also see reject/2 which discards all elements where the function returns a truthy value.

Examples

iex> Xb5.Set.filter(Xb5.Set.new(1..5), fn x -> x > 3 end)
Xb5.Set.new([4, 5])

iex> Xb5.Set.filter(Xb5.Set.new(["a", :b, "c"]), &is_atom/1)
Xb5.Set.new([:b])

first(set, default \\ nil)

@spec first(t(value()), default) :: value() | default when default: term()

Returns the first (smallest) element in set, or default if set is empty.

Examples

iex> Xb5.Set.first(Xb5.Set.new([1, 2, 3]))
1
iex> Xb5.Set.first(Xb5.Set.new())
nil
iex> Xb5.Set.first(Xb5.Set.new(), :empty)
:empty

first!(set)

@spec first!(t(value())) :: value()

Returns the first (smallest) element in set.

Raises Xb5.EmptyError if set is empty.

Examples

iex> Xb5.Set.first!(Xb5.Set.new([1, 2, 3]))
1
iex> Xb5.Set.first!(Xb5.Set.new())
** (Xb5.EmptyError) empty error

higher(set, value)

@spec higher(t(value()), value()) :: {:ok, value()} | :error

Returns the smallest element in set strictly greater (larger) than value, or :error if none exists.

value does not need to be a member of set.

Examples

iex> Xb5.Set.higher(Xb5.Set.new([1, 2, 3]), 2)
{:ok, 3}
iex> Xb5.Set.higher(Xb5.Set.new([1, 2, 3]), 1.5)
{:ok, 2}
iex> Xb5.Set.higher(Xb5.Set.new([1, 2, 3]), 3)
:error

intersection(set1, set2)

@spec intersection(t(value()), t(value())) :: t(value())

Returns a set containing only members that set1 and set2 have in common.

Examples

iex> Xb5.Set.intersection(Xb5.Set.new([1, 2]), Xb5.Set.new([2, 3, 4]))
Xb5.Set.new([2])

iex> Xb5.Set.intersection(Xb5.Set.new([1, 2]), Xb5.Set.new([3, 4]))
Xb5.Set.new([])

last(set, default \\ nil)

@spec last(t(value()), default) :: value() | default when default: term()

Returns the last (largest) element in set, or default if set is empty.

Examples

iex> Xb5.Set.last(Xb5.Set.new([1, 2, 3]))
3
iex> Xb5.Set.last(Xb5.Set.new())
nil
iex> Xb5.Set.last(Xb5.Set.new(), :empty)
:empty

last!(set)

@spec last!(t(value())) :: value()

Returns the last (largest) element in set.

Raises Xb5.EmptyError if set is empty.

Examples

iex> Xb5.Set.last!(Xb5.Set.new([1, 2, 3]))
3
iex> Xb5.Set.last!(Xb5.Set.new())
** (Xb5.EmptyError) empty error

lower(set, value)

@spec lower(t(value()), value()) :: {:ok, value()} | :error

Returns the largest element in set strictly less (smaller) than value, or :error if none exists.

value does not need to be a member of set.

Examples

iex> Xb5.Set.lower(Xb5.Set.new([1, 2, 3]), 2)
{:ok, 1}
iex> Xb5.Set.lower(Xb5.Set.new([1, 2, 3]), 1.5)
{:ok, 1}
iex> Xb5.Set.lower(Xb5.Set.new([1, 2, 3]), 1)
:error

map(set, fun)

@spec map(t(a), (a -> b)) :: t(b) when a: value(), b: value()

Applies fun to each element and returns a new set built from the results.

Because the mapped elements may not be unique, they are deduplicated.

Examples

iex> Xb5.Set.map(Xb5.Set.new([1, 2, 3]), fn x -> x * 2 end)
Xb5.Set.new([2, 4, 6])
iex> Xb5.Set.map(Xb5.Set.new([1, 2, 3]), fn _ -> :same end)
Xb5.Set.new([:same])

member?(set, value)

@spec member?(t(), value()) :: boolean()

Checks if set contains value.

Membership is tested using ==, not ===, so for example member?(set, 1.0) will match an element 1.

Examples

iex> Xb5.Set.member?(Xb5.Set.new([1, 2, 3]), 2)
true
iex> Xb5.Set.member?(Xb5.Set.new([1, 2, 3]), 4)
false

new()

@spec new() :: t()

Returns a new empty set.

Examples

iex> Xb5.Set.new()
Xb5.Set.new([])

new(input)

@spec new(:xb5_sets.set(value()) | Enumerable.t()) :: t(value())

Creates a set from an Erlang :xb5_sets term or an enumerable.

When given an enumerable, elements are deduplicated and stored in ascending order. When given an Erlang :xb5_sets term, the underlying structure is reused directly.

Examples

iex> Xb5.Set.new([:b, :a, 3])
Xb5.Set.new([3, :a, :b])
iex> Xb5.Set.new([3, 3, 3, 2, 2, 1])
Xb5.Set.new([1, 2, 3])

new(input, transform)

@spec new(:xb5_sets.set() | Enumerable.t(), (term() -> value())) :: t(value())

Creates a set from an Erlang :xb5_sets term or an enumerable via the transformation function.

The results of transform are deduplicated and stored in ascending order.

Examples

iex> Xb5.Set.new([1, 2, 1], fn x -> 2 * x end)
Xb5.Set.new([2, 4])

pop_first!(set)

@spec pop_first!(t(value())) :: {value(), t(value())}

Removes and returns {value, updated_set} for the first (smallest) element in set.

Raises Xb5.EmptyError if set is empty.

Examples

iex> Xb5.Set.pop_first!(Xb5.Set.new([1, 2, 3]))
{1, Xb5.Set.new([2, 3])}
iex> Xb5.Set.pop_first!(Xb5.Set.new())
** (Xb5.EmptyError) empty error

pop_last!(set)

@spec pop_last!(t(value())) :: {value(), t(value())}

Removes and returns {value, updated_set} for the last (largest) element in set.

Raises Xb5.EmptyError if set is empty.

Examples

iex> Xb5.Set.pop_last!(Xb5.Set.new([1, 2, 3]))
{3, Xb5.Set.new([1, 2])}
iex> Xb5.Set.pop_last!(Xb5.Set.new())
** (Xb5.EmptyError) empty error

put(set, value)

@spec put(t(value()), new_value) :: t(value() | new_value) when new_value: value()

Inserts value into set if set doesn't already contain it.

Examples

iex> Xb5.Set.put(Xb5.Set.new([1, 2, 3]), 3)
Xb5.Set.new([1, 2, 3])
iex> Xb5.Set.put(Xb5.Set.new([1, 2, 3]), 4)
Xb5.Set.new([1, 2, 3, 4])

reject(set, fun)

@spec reject(t(a), (a -> as_boolean(term()))) :: t(a) when a: value()

Returns a set by excluding the elements from set for which fun returns a truthy value.

See also filter/2.

Examples

iex> Xb5.Set.reject(Xb5.Set.new(1..5), fn x -> rem(x, 2) != 0 end)
Xb5.Set.new([2, 4])

iex> Xb5.Set.reject(Xb5.Set.new(["a", :b, "c"]), &is_atom/1)
Xb5.Set.new(["a", "c"])

size(set)

@spec size(t()) :: non_neg_integer()

Returns the number of elements in set.

Examples

iex> Xb5.Set.size(Xb5.Set.new([1, 2, 3]))
3

split_with(set, fun)

@spec split_with(t(), (term() -> as_boolean(term()))) :: {t(), t()}

Splits set into two sets according to the given function fun.

Returns a tuple with the first set containing all elements for which fun returned a truthy value, and a second set with all elements for which fun returned a falsy value (false or nil).

Examples

iex> {while_true, while_false} = Xb5.Set.split_with(Xb5.Set.new([1, 2, 3, 4]), fn v -> rem(v, 2) == 0 end)
iex> while_true
Xb5.Set.new([2, 4])
iex> while_false
Xb5.Set.new([1, 3])

iex> {while_true, while_false} = Xb5.Set.split_with(Xb5.Set.new(), fn v -> v > 50 end)
iex> while_true
Xb5.Set.new([])
iex> while_false
Xb5.Set.new([])

stream(set, order \\ :asc)

@spec stream(t(value()), order()) :: Enumerable.t()

Returns a lazy stream over all elements of set.

order controls traversal direction: :asc (ascending, the default) or :desc (descending).

Examples

iex> set = Xb5.Set.new([1, 2, 3])
iex> Xb5.Set.stream(set) |> Enum.to_list()
[1, 2, 3]
iex> Xb5.Set.stream(set, :desc) |> Enum.to_list()
[3, 2, 1]
iex> Xb5.Set.stream(Xb5.Set.new()) |> Enum.to_list()
[]

stream_from(set, value, order \\ :asc)

@spec stream_from(t(value()), value(), order()) :: Enumerable.t()

Returns a lazy stream over elements of set starting from value.

For :asc (the default), starts at the first element greater than or equal to value. For :desc, starts at the first element less than or equal to value. Returns an empty stream if no such element exists.

Examples

iex> set = Xb5.Set.new([1, 2, 3, 4, 5])
iex> Xb5.Set.stream_from(set, 3) |> Enum.to_list()
[3, 4, 5]
iex> Xb5.Set.stream_from(set, 3, :desc) |> Enum.to_list()
[3, 2, 1]
iex> Xb5.Set.stream_from(set, 6) |> Enum.to_list()
[]

structural_stats(set)

@spec structural_stats(t()) :: :xb5_structural_stats.t()

Returns structural statistics about the underlying B-tree.

Useful for inspecting tree balance and node utilization.

Examples

iex> Xb5.Set.structural_stats(Xb5.Set.new(1..100))
[
  height: 4,
  node_counts: [
    internal4: 2,
    internal3: 3,
    internal2: 3,
    internal1: 1,
    leaf4: 6,
    leaf3: 14,
    leaf2: 5,
    leaf1: 0
  ],
  node_percentages: [
    internal4: 5.9,
    internal3: 8.8,
    internal2: 8.8,
    internal1: 2.9,
    leaf4: 17.6,
    leaf3: 41.2,
    leaf2: 14.7,
    leaf1: 0.0
  ],
  total_keys: 100,
  key_percentages: [
    internal4: 8.0,
    internal3: 9.0,
    internal2: 6.0,
    internal1: 1.0,
    leaf4: 24.0,
    leaf3: 42.0,
    leaf2: 10.0,
    leaf1: 0.0
  ],
  avg_keys_per_node: 2.9411764705882355,
  avg_keys_per_internal_node: 2.6666666666666665,
  avg_keys_per_leaf_node: 3.04
]

subset?(set1, set2)

@spec subset?(t(), t()) :: boolean()

Checks if set1's members are all contained in set2.

This function checks if set1 is a subset of set2.

Examples

iex> Xb5.Set.subset?(Xb5.Set.new([1, 2]), Xb5.Set.new([1, 2, 3]))
true
iex> Xb5.Set.subset?(Xb5.Set.new([1, 2, 3]), Xb5.Set.new([1, 2]))
false

symmetric_difference(set1, set2)

@spec symmetric_difference(t(val1), t(val2)) :: t(val1 | val2)
when val1: value(), val2: value()

Returns a set with elements that are present in only one but not both sets.

Implemented as union(difference(set1, set2), difference(set2, set1)).

Examples

iex> Xb5.Set.symmetric_difference(Xb5.Set.new([1, 2, 3]), Xb5.Set.new([2, 3, 4]))
Xb5.Set.new([1, 4])

to_list(set)

@spec to_list(t(value())) :: [value()]

Converts set to a sorted list.

Examples

iex> Xb5.Set.to_list(Xb5.Set.new([1, 2, 3]))
[1, 2, 3]

union(set1, set2)

@spec union(t(val1), t(val2)) :: t(val1 | val2) when val1: value(), val2: value()

Returns a set containing all members of set1 and set2.

Examples

iex> Xb5.Set.union(Xb5.Set.new([1, 2]), Xb5.Set.new([2, 3, 4]))
Xb5.Set.new([1, 2, 3, 4])

unwrap!(set)

@spec unwrap!(t(value())) :: :xb5_sets.unwrapped_set(value())

Returns the size and root node of set as %{size: n, root: node}.

Pass the result to :xb5_sets.wrap/1 to obtain a proper :xb5_sets term.

Examples

iex> %{size: size} = Xb5.Set.unwrap!(Xb5.Set.new([1, 2, 3]))
iex> size
3