defmodule A.Vector.Trie do @moduledoc false alias A.Vector.CodeGen, as: C require C import Bitwise alias A.Vector.{Node, Tail} @type value :: term @type leaf(value) :: Node.t(value) @type t(value) :: Node.t(t(value) | value) # BUILD TRIE @spec group_leaves([val]) :: {non_neg_integer, non_neg_integer, [leaf(val)], Tail.t(val)} when val: value def group_leaves(list) do do_group_leaves(list, [], 0) end @compile {:inline, do_group_leaves: 3} defp do_group_leaves(unquote(C.list_with_rest(C.var(rest))), acc, count) when rest != [] do do_group_leaves( rest, [unquote(C.array()) | acc], count + C.branch_factor() ) end for i <- C.range() do defp do_group_leaves(unquote(C.arguments(i)), acc, count) do last = unquote(C.array_with_nils(i)) {count + unquote(i), count, :lists.reverse(acc), last} end end @spec group_map_leaves([v1], (v1 -> v2)) :: {non_neg_integer, non_neg_integer, [leaf(v2)], Tail.t(v2)} when v1: value, v2: value def group_map_leaves(list, fun) do do_group_map_leaves(list, fun, [], 0) end @compile {:inline, do_group_map_leaves: 4} defp do_group_map_leaves(unquote(C.list_with_rest(C.var(rest))), fun, acc, count) when rest != [] do new_leaf = unquote( C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> C.array() ) do_group_map_leaves(rest, fun, [new_leaf | acc], count + C.branch_factor()) end for i <- C.range() do defp do_group_map_leaves(unquote(C.arguments(i)), fun, acc, count) do last = unquote( C.arguments_with_nils(i) |> Enum.map(C.apply_sparse_mapper(C.var(fun))) |> C.array() ) {count + unquote(i), count, :lists.reverse(acc), last} end end def group_leaves_ast(list) do do_group_leaves_ast(list, [], 0) end defp do_group_leaves_ast(unquote(C.list_with_rest(C.var(rest))), acc, count) when rest != [] do do_group_leaves_ast(rest, [unquote(C.array_ast()) | acc], count + C.branch_factor()) end for i <- C.range() do defp do_group_leaves_ast(unquote(C.arguments(i)), acc, count) do last = unquote(C.arguments_with_nils(i) |> C.array_ast()) {count + unquote(i), count, :lists.reverse(acc), last} end end def duplicate(value, n) do div = C.radix_div(n) {level, acc} = do_duplicate(value, div, 0, []) case 1 <<< level do ^n -> [{1, trie}] = acc {C.decr_level(level), trie} _ -> [{count, node} | rest] = acc base_trie = Tail.partial_duplicate(node, count) trie = duplicate_rest(base_trie, rest, count) {level, trie} end end defp do_duplicate(_node, _n = 0, level, acc) do {level, acc} end defp do_duplicate(node, n, level, acc) do new_node = Node.duplicate(node) rem = C.radix_rem(n) div = C.radix_div(n) new_acc = case {rem, acc} do {0, []} -> [] _ -> [{rem, new_node} | acc] end do_duplicate(new_node, div, C.incr_level(level), new_acc) end defp duplicate_rest(trie, _rest = [], _count) do trie end defp duplicate_rest(node, [{child_count, child_node} | rest], count) do child_base = case child_count do 0 -> Node.duplicate(nil) |> Tail.partial_duplicate(1) _ -> Tail.partial_duplicate(child_node, child_count) end child = duplicate_rest(child_base, rest, child_count) put_elem(node, count, child) end @spec from_leaves([leaf(val)]) :: nil | {non_neg_integer, t(val)} when val: value def from_leaves(leaves) def from_leaves([]), do: nil def from_leaves([leaf]), do: {0, leaf} def from_leaves(leaves), do: do_from_nodes(leaves, C.bits()) @compile {:inline, do_from_nodes: 2} defp do_from_nodes(nodes, level) defp do_from_nodes(unquote(C.list_with_rest(C.var(rest))), level) when rest != [] do nodes = [unquote(C.array()) | group_nodes(rest)] do_from_nodes(nodes, C.incr_level(level)) end defp do_from_nodes(nodes, level) do {level, Node.from_incomplete_list(nodes)} end defp group_nodes(nodes) defp group_nodes(unquote(C.list_with_rest(C.var(rest)))) when rest != [] do [unquote(C.array()) | group_nodes(rest)] end defp group_nodes(nodes) do [Node.from_incomplete_list(nodes)] end @spec from_ast_leaves([leaf(val)]) :: nil | {non_neg_integer, t(val)} when val: value def from_ast_leaves(leaves) def from_ast_leaves([]), do: nil def from_ast_leaves([leaf]), do: {0, leaf} def from_ast_leaves(leaves), do: do_from_ast_nodes(leaves, C.bits()) defp do_from_ast_nodes(nodes, level) defp do_from_ast_nodes(unquote(C.list_with_rest(C.var(rest))), level) when rest != [] do nodes = [unquote(C.array_ast()) | group_ast_nodes(rest)] do_from_ast_nodes(nodes, C.incr_level(level)) end defp do_from_ast_nodes(nodes, level) do {level, Node.ast_from_incomplete_list(nodes)} end defp group_ast_nodes(nodes) defp group_ast_nodes(unquote(C.list_with_rest(C.var(rest)))) when rest != [] do [unquote(C.array_ast()) | group_ast_nodes(rest)] end defp group_ast_nodes(nodes) do [Node.ast_from_incomplete_list(nodes)] end @compile {:inline, append_leaf: 4} def append_leaf(trie, level, index, leaf) def append_leaf(trie, _level = 0, _index, leaf) do { unquote(C.var([trie, leaf]) |> C.fill_with(nil) |> C.array()), C.bits() } end def append_leaf(trie, level, index, leaf) do case index >>> level do C.branch_factor() -> new_branch = build_single_branch(leaf, level) { unquote(C.var([trie, new_branch]) |> C.fill_with(nil) |> C.array()), C.incr_level(level) } _ -> new_trie = append_leaf_to_existing(trie, level, index, leaf) {new_trie, level} end end defp append_leaf_to_existing(nil, level, _index, leaf) do build_single_branch(leaf, level) end defp append_leaf_to_existing(trie, _level = C.bits(), index, leaf) do put_elem(trie, C.radix_search(index, C.bits()), leaf) end defp append_leaf_to_existing(trie, level, index, leaf) do current_index = C.radix_search(index, level) child = elem(trie, current_index) new_child = append_leaf_to_existing(child, C.decr_level(level), index, leaf) put_elem(trie, current_index, new_child) end defp build_single_branch(leaf, _level = 0) do leaf end defp build_single_branch(leaf, level) do child = build_single_branch(leaf, C.decr_level(level)) unquote(C.var(child) |> C.value_with_nils() |> C.array()) end @compile {:inline, append_leaves: 4} def append_leaves(trie, level, index, leaves) def append_leaves(trie, level, _index, []), do: {trie, level} def append_leaves(trie, level, index, [leaf | rest]) do {new_trie, new_level} = append_leaf(trie, level, index, leaf) append_leaves(new_trie, new_level, index + C.branch_factor(), rest) end # ACCESS @compile {:inline, first: 2} def first(trie, level) def first(leaf, _level = 0) do elem(leaf, 0) end def first(trie, level) do child = elem(trie, 0) first(child, C.decr_level(level)) end @compile {:inline, lookup: 3} def lookup(trie, index, level) def lookup(leaf, index, _level = 0) do elem(leaf, C.radix_rem(index)) end def lookup(trie, index, level) do current_index = C.radix_search(index, level) child = elem(trie, current_index) lookup(child, index, C.decr_level(level)) end def replace(trie, index, level, value) def replace(leaf, index, _level = 0, value) do current_index = C.radix_rem(index) put_elem(leaf, current_index, value) end def replace(trie, index, level, value) do current_index = C.radix_search(index, level) child = elem(trie, current_index) new_child = replace(child, index, C.decr_level(level), value) put_elem(trie, current_index, new_child) end def update(trie, index, level, fun) def update(leaf, index, _level = 0, fun) do current_index = C.radix_rem(index) Node.update_at(leaf, current_index, fun) end def update(trie, index, level, fun) do current_index = C.radix_search(index, level) child = elem(trie, current_index) new_child = update(child, index, C.decr_level(level), fun) put_elem(trie, current_index, new_child) end # POP LEAF def pop_leaf(trie, level) do {popped, new} = do_nested_pop_leaf(trie, level) case elem(new, 1) do nil -> {popped, elem(new, 0), C.decr_level(level)} _ -> {popped, new, level} end end defp do_nested_pop_leaf(leaves, _level = C.bits()) do do_pop_leaf(leaves) end defp do_nested_pop_leaf(unquote(C.array_with_nils(1)), level) do {popped, trie} = do_nested_pop_leaf(unquote(C.argument_at(0)), C.decr_level(level)) case trie do nil -> {popped, nil} _ -> new_trie = unquote(C.var(trie) |> C.value_with_nils() |> C.array()) {popped, new_trie} end end for i <- C.range(), i > 1 do defp do_nested_pop_leaf(unquote(C.array_with_nils(i)), level) do {popped, unquote(C.argument_at(i - 1))} = do_nested_pop_leaf(unquote(C.argument_at(i - 1)), C.decr_level(level)) new_trie = unquote(C.array_with_nils(i)) {popped, new_trie} end end defp do_pop_leaf(unquote(C.array_with_nils(1))) do {unquote(C.argument_at(0)), nil} end for i <- C.range(), i > 1 do defp do_pop_leaf(unquote(C.array_with_nils(i))) do {unquote(C.argument_at(i - 1)), unquote(C.array_with_nils(i - 1))} end end # LOOPS def to_list(trie, level, acc) # def to_list({arg1, arg2, arg3, arg4}, _level = 0, acc) do # [arg1, arg2, arg3, arg4 | acc] # end def to_list(unquote(C.array()), _level = 0, acc) do unquote(C.list_with_rest(C.var(acc))) end # def to_list({arg1, arg2, nil, _}, level, acc) do # child_level = level - bits # to_list(arg1, child_level, to_list(arg2, child_level, acc)) # end for i <- C.range() do def to_list(unquote(C.array_with_nils(i)), level, acc) do child_level = C.decr_level(level) unquote( C.reversed_arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do to_list(unquote(arg), var!(child_level), unquote(acc)) end end) ) end end def to_reverse_list(trie, level, acc) # def to_reverse_list({arg1, arg2, arg3, arg4}, _level = 0, acc) do # [arg4, arg3, arg2, arg1 | acc] # end def to_reverse_list(unquote(C.array()), _level = 0, acc) do unquote(C.reversed_arguments() |> C.list_with_rest(C.var(acc))) end # def to_reverse_list({arg1, arg2, nil, _}, level, acc) do # child_level = level - bits # to_reverse_list(arg2, child_level, to_reverse_list(arg1, child_level, acc)) # end for i <- C.range() do def to_reverse_list(unquote(C.array_with_nils(i)), level, acc) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do to_reverse_list(unquote(arg), var!(child_level), unquote(acc)) end end) ) end end def member?(trie, level, value) # def member?({arg1, arg2, arg3, arg4}, _level = 0, value) do # (arg1 === value) or (arg2 === value) or (arg3 === value) or (arg4 === value) # end def member?(unquote(C.array()), _level = 0, value) do unquote( C.arguments() |> Enum.map(C.strict_equal_mapper(C.var(value))) |> Enum.reduce(&C.strict_or_reducer/2) ) end # def member?({arg1, arg2, nil, _}, level, value) do # child_level = level - bits # member?(arg1, child_level, value) or member?(arg1, child_level, value) # end for i <- C.range() do def member?(unquote(C.array_with_nils(i)), level, value) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.map(fn arg -> quote do member?(unquote(arg), var!(child_level), var!(value)) end end) |> Enum.reduce(&C.strict_or_reducer/2) ) end end def any?(trie, level) # def any?({arg1, arg2, arg3, arg4}, _level = 0) do # arg1 || arg2 || arg3 || arg4 # end def any?(unquote(C.array()), _level = 0) do unquote(C.arguments() |> Enum.reduce(&C.or_reducer/2)) end # def any?({arg1, arg2, nil, _}, level) do # child_level = level - bits # any?(arg1, child_level) || any?(arg1, child_level) # end for i <- C.range() do def any?(unquote(C.array_with_nils(i)), level) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.map(fn arg -> quote do any?(unquote(arg), var!(child_level)) end end) |> Enum.reduce(&C.or_reducer/2) ) end end def any?(trie, level, fun) # def any?({arg1, arg2, arg3, arg4}, _level = 0, fun) do # fun.(arg1) || fun.(arg2) || fun.(arg3) || fun.(arg4) # end def any?(unquote(C.array()), _level = 0, fun) do unquote( C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> Enum.reduce(&C.or_reducer/2) ) end # def any?({arg1, arg2, nil, _}, level, fun) do # child_level = level - bits # any?(arg1, child_level, fun) || any?(arg1, child_level, fun) # end for i <- C.range() do def any?(unquote(C.array_with_nils(i)), level, fun) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.map(fn arg -> quote do any?(unquote(arg), var!(child_level), var!(fun)) end end) |> Enum.reduce(&C.or_reducer/2) ) end end def all?(trie, level) # def all?({arg1, arg2, arg3, arg4}, _level = 0) do # arg1 && arg2 && arg3 && arg4 # end def all?(unquote(C.array()), _level = 0) do unquote(C.arguments() |> Enum.reduce(&C.and_reducer/2)) end # def all?({arg1, arg2, nil, _}, level) do # child_level = level - bits # all?(arg1, child_level) && all?(arg1, child_level) # end for i <- C.range() do def all?(unquote(C.array_with_nils(i)), level) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.map(fn arg -> quote do all?(unquote(arg), var!(child_level)) end end) |> Enum.reduce(&C.and_reducer/2) ) end end def all?(trie, level, fun) # def all?({arg1, arg2, arg3, arg4}, _level = 0, fun) do # fun.(arg1) && fun.(arg2) && fun.(arg3) && fun.(arg4) # end def all?(unquote(C.array()), _level = 0, fun) do unquote( C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> Enum.reduce(&C.and_reducer/2) ) end # def all?({arg1, arg2, nil, _}, level, fun) do # child_level = level - bits # all?(arg1, child_level, fun) && all?(arg1, child_level, fun) # end for i <- C.range() do def all?(unquote(C.array_with_nils(i)), level, fun) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.map(fn arg -> quote do all?(unquote(arg), var!(child_level), var!(fun)) end end) |> Enum.reduce(&C.and_reducer/2) ) end end def foldl(trie, level, acc, fun) do foldl_leaves(trie, level, acc, fun, &foldl_leaf/3) end # defp foldl_leaf({arg1, arg2, arg3, arg4}, fun, acc) do # fun(arg1, fun(arg2, fun(arg3, fun(arg4, acc)))) # end def foldl_leaf(unquote(C.array()), fun, acc) do unquote( C.arguments() |> Enum.reduce(C.var(acc), fn arg, acc -> quote do var!(fun).(unquote(arg), unquote(acc)) end end) ) end def foldr(trie, level, acc, fun) do foldr_leaves(trie, level, acc, fun, &foldr_leaf/3) end # defp foldr_leaf({arg1, arg2, arg3, arg4}, fun, acc) do # fun(arg1, fun(arg2, fun(arg3, fun(arg4, acc)))) # end def foldr_leaf(unquote(C.array()), fun, acc) do unquote( C.reversed_arguments() |> Enum.reduce(C.var(acc), fn arg, acc -> quote do var!(fun).(unquote(arg), unquote(acc)) end end) ) end def each(trie, level, fun) do foldl_leaves(trie, level, nil, fun, &each_leaf/3) end # defp each_leaf({arg1, arg2, arg3, arg4}, fun, _acc) do # fun.(arg1) # fun.(arg2) # fun.(arg3) # fun.(arg4) # :ok # end def each_leaf(unquote(C.array()), fun, _acc) do unquote( C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> C.block() ) :ok end def sum(trie, level, acc) # def sum({arg1, arg2, arg3, arg4}, _level = 0, acc) do # acc + arg1 + arg2 + arg3 + arg4 # end def sum(unquote(C.array()), _level = 0, acc) do unquote(C.arguments() |> Enum.reduce(C.var(acc), &C.sum_reducer/2)) end # def sum({arg1, arg2, nil, _}, level, acc) do # child_level = level - bits # sum(arg2, child_level, sum(arg1, child_level, acc)) # end for i <- C.range() do def sum(unquote(C.array_with_nils(i)), level, acc) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do sum(unquote(arg), var!(child_level), unquote(acc)) end end) ) end end def product(trie, level, acc) # def product({arg1, arg2, arg3, arg4}, _level = 0, acc) do # acc * arg1 * arg2 * arg3 * arg4 # end def product(unquote(C.array()), _level = 0, acc) do unquote(C.arguments() |> Enum.reduce(C.var(acc), &C.product_reducer/2)) end # def product({arg1, arg2, nil, _}, level, acc) do # child_level = level - bits # product(arg2, child_level, product(arg1, child_level, acc)) # end for i <- C.range() do def product(unquote(C.array_with_nils(i)), level, acc) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do product(unquote(arg), var!(child_level), unquote(acc)) end end) ) end end def intersperse(trie, level, separator, acc) do foldr_leaves(trie, level, acc, separator, &intersperse_leaf/3) end # def intersperse_leaf({arg1, arg2, arg3, arg4}, separator, acc) do # [arg1, separator, arg2, ... separator | acc] # end defp intersperse_leaf(unquote(C.array()), separator, acc) do unquote( C.arguments() |> Enum.intersperse(C.var(separator)) |> Enum.concat([C.var(separator)]) |> C.list_with_rest(C.var(acc)) ) end def join(trie, level, joiner, acc) do foldr_leaves(trie, level, acc, joiner, &join_leaf/3) end # def join({arg1, arg2, arg3, arg4}, joiner, acc) do # [mapper.(arg1), joiner, mapper.(arg2), ... joiner | acc] # end defp join_leaf(unquote(C.array()), joiner, acc) do unquote( C.arguments() |> Enum.map_intersperse(C.var(joiner), C.apply_mapper(C.var(&to_string/1))) |> Enum.concat([C.var(joiner)]) |> C.list_with_rest(C.var(acc)) ) end def map(trie, level, fun) # def map({arg1, arg2, arg3, arg4}, _level = 0, f) do # {f.(arg1), f.(arg2), f.(arg3), f.(arg4)} # end def map(unquote(C.array()), _level = 0, fun) do unquote( C.arguments() |> Enum.map(C.apply_mapper(C.var(fun))) |> C.array() ) end # def map({arg1, arg2, nil, _}, level, f) do # child_level = level - bits # {map(arg1, child_level, f), map(arg2, child_level, f), nil, nil} # end for i <- C.range() do def map(unquote(C.array_with_nils(i)), level, fun) do child_level = C.decr_level(level) unquote( C.arguments_with_nils(i) |> C.sparse_map(fn arg -> quote do map(unquote(arg), var!(child_level), var!(fun)) end end) |> C.array() ) end end defp foldl_leaves(trie, level, acc, params, fun) defp foldl_leaves(leaf, _level = 0, acc, params, fun) do fun.(leaf, params, acc) end # def foldl_leaves({arg1, arg2, nil, _}, level, acc, params, fun) do # child_level = level - bits # foldl_leaves(arg2, child_level, foldl_leaves(arg1, child_level, acc, params, fun), params, fun) # end for i <- C.range() do defp foldl_leaves( unquote(C.array_with_nils(i)), level, acc, params, fun ) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do foldl_leaves(unquote(arg), var!(child_level), unquote(acc), var!(params), var!(fun)) end end) ) end end defp foldr_leaves(trie, level, acc, params, fun) defp foldr_leaves(leaf, _level = 0, acc, params, fun) do fun.(leaf, params, acc) end # def foldr_leaves({arg1, arg2, nil, _}, level, acc, params, fun) do # child_level = level - bits # foldr_leaves(arg1, child_level, foldr_leaves(arg2, child_level, acc, params, fun), params, fun) # end for i <- C.range() do defp foldr_leaves( unquote(C.array_with_nils(i)), level, acc, params, fun ) do child_level = C.decr_level(level) unquote( C.reversed_arguments(i) |> Enum.reduce(C.var(acc), fn arg, acc -> quote do foldr_leaves(unquote(arg), var!(child_level), unquote(acc), var!(params), var!(fun)) end end) ) end end @compile {:inline, slice: 6} def slice(trie, start, last, level, acc, nodes \\ []) def slice(leaf, start, last, _level = 0, acc, nodes) do last_index = C.radix_rem(last) remaining = last - start case remaining - last_index do new_remaining when new_remaining > 0 -> new_acc = partial_slice_leaf(leaf, 0, last_index, acc) slice_next(new_remaining, new_acc, nodes) neg_first_index -> partial_slice_leaf(leaf, -neg_first_index, last_index, acc) end end def slice(trie, start, last, level, acc, nodes) do current_index = C.radix_search(last, level) new_nodes = case current_index do 0 -> nodes _ -> [{trie, level, current_index - 1} | nodes] end child = elem(trie, current_index) slice(child, start, last, C.decr_level(level), acc, new_nodes) end @compile {:inline, do_slice: 4} defp do_slice(leaf, remaining, acc, nodes) do case remaining - C.branch_factor() do new_remaining when new_remaining > 0 -> new_acc = Node.prepend_all(leaf, acc) slice_next(new_remaining, new_acc, nodes) neg_first_index -> partial_slice_leaf(leaf, -neg_first_index, C.branch_factor() - 1, acc) end end @compile {:inline, slice_next: 3} defp slice_next(remaining, acc, [node | nodes]) do {new_leaf, new_nodes} = unpack_slice_nodes(node, nodes) do_slice(new_leaf, remaining, acc, new_nodes) end @compile {:inline, partial_slice_leaf: 4} defp partial_slice_leaf(leaf, index, index, acc) do [elem(leaf, index) | acc] end defp partial_slice_leaf(leaf, until, index, acc) do partial_slice_leaf(leaf, until, index - 1, [elem(leaf, index) | acc]) end @compile {:inline, unpack_slice_nodes: 2} defp unpack_slice_nodes({trie, level, index}, nodes) do case level do 0 -> {trie, nodes} _ -> child = elem(trie, index) new_node = {child, C.decr_level(level), unquote(C.branch_factor() - 1)} case index do 0 -> unpack_slice_nodes(new_node, nodes) _ -> unpack_slice_nodes(new_node, [{trie, level, index - 1} | nodes]) end end end def take(trie, level, amount) do case do_take(trie, level, amount - 1, false) do {0, tail} -> {:small, tail} {tmp_level, tmp_trie} -> {new_tail, new_trie, new_level} = pop_leaf(tmp_trie, tmp_level) {:large, new_trie, new_level, new_tail} end end defp do_take(leaf, _level = 0, last_index, _same_level?) do {0, Node.take(leaf, C.radix_rem(last_index) + 1)} end defp do_take(trie, level, last_index, same_level?) do child_level = C.decr_level(level) radix = C.radix_search(last_index, level) child = elem(trie, radix) case {radix, same_level?} do {0, false} -> do_take(child, child_level, last_index, false) _ -> {_, new_child} = do_take(child, child_level, last_index, true) new_trie = trie |> put_elem(radix, new_child) |> Node.take(radix + 1) {level, new_trie} end end def with_index(trie, level, fun) # def with_index({arg1, arg2, arg3, arg4}, _level = 0, offset) do # {{arg1, offset + 0, {arg2, offset + 1}, {arg3, offset + 2}, {arg4, offset + 3}} # end def with_index(unquote(C.array()), _level = 0, offset) do unquote( C.arguments() |> Enum.with_index() |> Enum.map(fn {arg, index} -> quote do {unquote(arg), var!(offset) + unquote(index)} end end) |> C.array() ) end # def with_index({arg1, arg2, nil, _}, level, offset) do # child_level = level - bits # { # with_index(arg1, child_level, offset + (0 <<< level)), # with_index(arg2, child_level, offset + (1 <<< level)), # nil, # nil # } # end for i <- C.range() do def with_index(unquote(C.array_with_nils(i)), level, offset) do child_level = C.decr_level(level) unquote( C.arguments(i) |> Enum.with_index() |> Enum.map(fn {arg, index} -> quote do with_index( unquote(arg), var!(child_level), var!(offset) + (unquote(index) <<< var!(level)) ) end end) |> C.fill_with(nil) |> C.array() ) end end end