Current section

Files

Jump to
aja lib vector builder.ex
Raw

lib/vector/builder.ex

defmodule Aja.Vector.Builder do
@moduledoc false
alias Aja.Vector.{Tail, Node}
require Aja.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, amount) do
do_from_trie(trie, level, amount, [])
end
def do_from_trie(_trie, _level, 0, acc), do: [[] | acc]
def do_from_trie(trie, level, amount, acc) do
case :erlang.bsr(amount, level) do
C.branch_factor() ->
prepend_single_builder([[trie] | acc], level)
full_nodes_count ->
reverse_full_nodes = Node.take_reverse(trie, full_nodes_count)
acc = [reverse_full_nodes | acc]
case amount - :erlang.bsl(full_nodes_count, level) do
0 ->
prepend_single_builder(acc, C.decr_level(level))
remaining ->
:erlang.element(full_nodes_count + 1, trie)
|> do_from_trie(C.decr_level(level), remaining, acc)
end
end
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
@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