Current section
Files
Jump to
Current section
Files
lib/vector/codegen.ex
defmodule A.Vector.CodeGen do
@moduledoc false
# Notes: averaged overhead
# Enum.sum(for i <- 1..100, do: A.Vector.new(1..i) |> :erts_debug.size_shared()) / 100
# 2^3 = 8 -> 87.2
# 2^4 = 16 -> 86.96
# 2^5 = 32 -> 93.56
# :array -> 77.3
# Notes: averaged over 1..1000, the trend is the opposite (:array 638.18, vec32 577.016)
@bits 4
@branch_factor :math.pow(2, @bits) |> round()
@range 1..@branch_factor
@arguments_ast Macro.generate_arguments(@branch_factor, nil)
@wildcard quote do: _
defmacro bits do
@bits
end
defmacro branch_factor do
@branch_factor
end
defmacro incr_level(level) do
quote do
unquote(level) + unquote(@bits)
end
end
defmacro decr_level(level) do
quote do
unquote(level) - unquote(@bits)
end
end
defmacro radix_div(i) do
quote do
Bitwise.>>>(unquote(i), unquote(@bits))
end
end
defmacro radix_rem(i) do
quote do
Bitwise.&&&(unquote(i), unquote(@branch_factor - 1))
end
end
defmacro array_type(declaration, value) do
quote do
@type unquote(declaration) :: unquote({:{}, [], List.duplicate(value, @branch_factor)})
end
end
defmacro radix_search(index, level) do
quote do
unquote(index)
|> Bitwise.>>>(unquote(level))
|> Bitwise.&&&(unquote(@branch_factor - 1))
end
end
def range do
@range
end
def block(lines) when is_list(lines) do
{:__block__, [], lines}
end
def array() do
do_array(@arguments_ast)
end
def array(args) do
args
|> validate_args_length()
|> do_array()
end
def array_ast() do
{:{}, [], [:{}, [], @arguments_ast]}
end
def array_ast(args) do
validate_args_length(args)
{:{}, [], [:{}, [], args]}
end
def arguments do
@arguments_ast
end
for i <- @range do
def arguments(unquote(i)) do
unquote(
@arguments_ast
|> Enum.take(i)
|> Macro.escape()
)
end
end
for i <- @range do
def argument_at(unquote(i - 1)) do
unquote(
@arguments_ast
|> Enum.at(i - 1)
|> Macro.escape()
)
end
end
def reversed_arguments() do
unquote(
@arguments_ast
|> Enum.reverse()
|> Macro.escape()
)
end
for i <- @range do
def reversed_arguments(unquote(i)) do
unquote(
@arguments_ast
|> Enum.take(i)
|> Enum.reverse()
|> Macro.escape()
)
end
end
def duplicate_argument(arg) do
List.duplicate(arg, @branch_factor)
end
for i <- @range do
def arguments_with_nils(unquote(i)) do
unquote(
@arguments_ast
|> Enum.take(i)
|> Kernel.++(List.duplicate(nil, @branch_factor - i))
|> Macro.escape()
)
end
end
for i <- @range do
def arguments_with_wildcards(unquote(i)) do
unquote(
@arguments_ast
|> Enum.take(i)
|> Kernel.++(List.duplicate(@wildcard, @branch_factor - i))
|> Macro.escape()
)
end
end
def array_with_wildcards(n) do
n
|> arguments_with_wildcards()
|> do_array()
end
def array_with_nils(n) do
n
|> arguments_with_nils()
|> do_array()
end
def value_with_nils(value) do
[value] |> fill_with(nil)
end
def fill_with(args, value) do
missing = @branch_factor - length(args)
args ++ List.duplicate(value, missing)
end
def map_until(args \\ @arguments_ast, n, fun) when is_integer(n) and is_function(fun, 1) do
args
|> Enum.with_index()
|> Enum.map(fn
{arg, i} when i < n -> fun.(arg)
{arg, _} -> arg
end)
end
def list_with_rest(args \\ @arguments_ast, rest_variable) do
case length(args) do
0 ->
rest_variable
len ->
List.update_at(args, len - 1, fn last_arg ->
quote do
unquote(last_arg) | unquote(rest_variable)
end
end)
end
end
defmacro var(variable) do
Macro.escape(variable)
end
defp validate_args_length(args) do
# raise on unexpected args
unquote(@branch_factor) = length(args)
args
end
def do_array(args) do
{:{}, [], args}
end
def sparse_map(args, fun) do
Enum.map(args, fn
nil -> nil
arg -> fun.(arg)
end)
end
# MAPPERS
def apply_mapper(fun) do
fn arg ->
quote do
unquote(fun).(unquote(arg))
end
end
end
def apply_sparse_mapper(fun) do
fn
nil ->
nil
arg ->
quote do
unquote(fun).(unquote(arg))
end
end
end
def strict_equal_mapper(val) do
fn arg ->
quote do
unquote(arg) === unquote(val)
end
end
end
# REDUCERS
def sum_reducer(arg, acc) do
quote do
unquote(acc) + unquote(arg)
end
end
def product_reducer(arg, acc) do
quote do
unquote(acc) * unquote(arg)
end
end
def strict_or_reducer(arg, acc) do
quote do
unquote(acc) or unquote(arg)
end
end
def or_reducer(arg, acc) do
quote do
unquote(acc) || unquote(arg)
end
end
def strict_and_reducer(arg, acc) do
quote do
unquote(acc) and unquote(arg)
end
end
def and_reducer(arg, acc) do
quote do
unquote(acc) && unquote(arg)
end
end
end