Current section
Files
Jump to
Current section
Files
lib/vector/builder.ex
defmodule A.Vector.Builder do
@moduledoc false
alias A.Vector.{Tail, Node}
require A.Vector.CodeGen, as: C
def from_list(list) when is_list(list) do
do_concat_list([], list)
end
def concat_list(builder, list) when is_list(builder) and is_list(list) do
do_concat_list(builder, list)
end
defp do_concat_list(builder, unquote(C.list_with_rest(C.var(rest)))) when rest != [] do
leaf = unquote(C.array())
builder
|> append_node(leaf)
|> do_concat_list(rest)
end
defp do_concat_list(builder, rest) do
{builder, length(rest), Tail.partial_from_list(rest)}
end
def map_from_list(list, fun) when is_list(list) and is_function(fun, 1) do
concat_map_list([], list, fun)
end
defp concat_map_list(builder, unquote(C.list_with_rest(C.var(rest))), fun) when rest != [] do
leaf = unquote(C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> C.array())
builder
|> append_node(leaf)
|> concat_map_list(rest, fun)
end
defp concat_map_list(builder, rest, fun) do
{builder, length(rest), Enum.map(rest, fun) |> Tail.partial_from_list()}
end
def append_nodes(builder, []), do: builder
def append_nodes(builder, [node | nodes]) do
builder
|> append_node(node)
|> append_nodes(nodes)
end
def append_nodes_with_offset(builder, [node], offset, tail, tail_size) do
shifted_tail = Node.shift(tail, -tail_size)
last_node = Node.from_offset_nodes(node, shifted_tail, offset)
case tail_size - offset do
new_tail_size when new_tail_size > 0 ->
new_tail =
shifted_tail
|> Node.from_offset_nodes(unquote(C.fill_with([], nil) |> C.array()), offset)
|> Node.shift(new_tail_size)
builder
|> append_node(last_node)
|> append_tail(new_tail, new_tail_size)
tail_size_complement ->
new_tail = Node.shift(last_node, tail_size_complement)
append_tail(builder, new_tail, C.branch_factor() + tail_size_complement)
end
end
def append_nodes_with_offset(builder, [node1 | nodes = [node2 | _]], offset, tail, tail_size) do
node = Node.from_offset_nodes(node1, node2, offset)
builder
|> append_node(node)
|> append_nodes_with_offset(nodes, offset, tail, tail_size)
end
def append_node(builder, node)
def append_node([], node), do: [[node]]
def append_node([unquote(C.arguments() |> tl()) | tries], unquote(C.arguments() |> hd())) do
trie_node = unquote(C.reversed_arguments() |> C.array())
[[] | append_node(tries, trie_node)]
end
def append_node([trie | tries], node) do
[[node | trie] | tries]
end
def append_tail(builder, tail, tail_size) do
{builder, tail_size, tail}
end
def to_trie([[] | tries], level) do
to_trie(tries, C.incr_level(level))
end
def to_trie([[dense_trie]], level) do
{level, dense_trie}
end
def to_trie(sparse_trie, level) do
to_sparse_trie(sparse_trie, level)
end
defp to_sparse_trie([children | rest], level) do
node = Node.from_incomplete_reverse_list(children)
case rest do
[] -> {C.incr_level(level), node}
[head | tail] -> to_sparse_trie([[node | head] | tail], C.incr_level(level))
end
end
def from_trie(trie, level, index) do
case :erlang.bsr(index, level) do
C.branch_factor() ->
prepend_single_builder([[trie]], level)
_ ->
do_from_trie(trie, level, index, [])
end
end
defp do_from_trie(trie, level = C.bits(), index, acc) do
current_index = C.radix_search(index, level)
[subtries_list(trie, 1, current_index + 1, []) | acc]
end
defp do_from_trie(trie, level, index, acc) do
current_index = C.radix_search(index, level)
child = elem(trie, current_index)
new_acc = [subtries_list(trie, 1, current_index + 1, []) | acc]
do_from_trie(child, C.decr_level(level), index, new_acc)
end
defp prepend_single_builder(list, _level = 0), do: list
defp prepend_single_builder(list, level) do
prepend_single_builder([[] | list], C.decr_level(level))
end
defp subtries_list(_trie, _index = until, until, acc), do: acc
defp subtries_list(trie, index, until, acc) do
new_acc = [:erlang.element(index, trie) | acc]
subtries_list(trie, index + 1, until, new_acc)
end
@compile {:inline, tail_offset: 3}
def tail_offset([], _level, acc), do: acc
def tail_offset([trie | tries], level, acc) do
trie_size = length(trie) |> Bitwise.bsl(level)
tail_offset(tries, C.incr_level(level), acc + trie_size)
end
end