%% orbit-int hash table (storing vertices on a worker) %% %% Author: Patrick Maier %% %% Note: Hash tables have a fixed number of slots but each slot can store %% a list of vertices. The functions is_member/3 and insert/3 %% expect its slot argument to be in range. -module(table). -export([new/1, to_list/1, is_member/3, insert/3, get_freq/1, sum_freqs/2, sum_freqs/1, freq_to_slots/1, freq_to_nonempty_slots/1, freq_to_vertices/1, max_freq/1, avg_freq/1, avg_nonempty_freq/1, fill_deg/1, freq_to_stat/1, freq_from_stat/1]). %% new(Size) creates a table with Size slots, each containing an empty list. new(Size) -> array:new([{size,Size}, {default,[]}, {fixed,true}]). %% to_list(T) converts a table T into a list of its entries. to_list(T) -> lists:flatten(array:to_list(T)). %% is_member(X, I, T) is true iff X is stored in table T at slot I. is_member(X, I, T) -> L = array:get(I, T), lists:member(X, L). %% insert(X, I, T) inserts X into table T at slot I. insert(X, I, T) -> L = array:get(I, T), array:set(I, [X|L], T). %% get_freq computes the fill frequency of table T; %% the output is a list of integers where the number at position I %% indicates how many slots of T are filled with I entries; %% the sum of the output lists equals the number of slots of T. get_freq(T) -> F0 = array:new([{default,0}, {fixed,false}]), F = array:foldl(fun(_, L, F) -> inc(length(L), F) end, F0, T), array:to_list(F). %% freq_to_slots computes the number of slots from a table fill frequency. freq_to_slots(F) -> lists:sum(F). %% freq_to_nonempty_slots computes the number of non empty slots %% from a table fill frequency. freq_to_nonempty_slots(F) -> lists:sum(tl(F)). %% freq_to_vertices computes the number of vertices %% from a table fill frequency. freq_to_vertices(F) -> {_, V} = lists:foldl(fun(N, {I,X}) -> {I + 1, (I * N) + X} end, {0,0}, F), V. %% max_freq returns the maximum fill frequency. max_freq(F) -> length(F) - 1. %% avg_freq returns the average fill frequency avg_freq(F) -> freq_to_vertices(F) / freq_to_slots(F). %% avg_nonempty_freq returns the average fill frequency of non empty slots. avg_nonempty_freq(F) -> case freq_to_vertices(F) of Verts when Verts > 0 -> Verts / freq_to_nonempty_slots(F); _Verts -> 0.0 %% Verts = 0 <=> freq_to_nonempty_slots(F) = 0 end. %% fill_deg determines the filling degree of the table. fill_deg(F) -> freq_to_nonempty_slots(F) / freq_to_slots(F). %% sum_freqs/2 sums two fill frequencies. sum_freqs([], SumF) -> SumF; sum_freqs(F, []) -> F; sum_freqs([N|F], [M|SumF]) -> [N + M | sum_freqs(F, SumF)]. %% sum_freqs/1 sums a list of fill frequencies. sum_freqs(Fs) -> lists:foldl(fun(F, SumF) -> sum_freqs(F, SumF) end, [], Fs). %% freq_to_stat produces a readable statistics from a table fill frequency; %% the input frequency F is itself part of the statistics freq_to_stat(Frequency) -> [{freq, Frequency}, {size, table:freq_to_vertices(Frequency)}, {slots, table:freq_to_slots(Frequency)}, {nonempty_slots, table:freq_to_nonempty_slots(Frequency)}, {fill_deg, table:fill_deg(Frequency)}, {max_freq, table:max_freq(Frequency)}, {avg_freq, table:avg_freq(Frequency)}, {nonempty_avg_freq, table:avg_nonempty_freq(Frequency)}]. %% freq_from_stat extracts a table fill frequency from a statistics Stat %% (assuming Stat was produced by freq_to_stat/1, otherwise returns []); freq_from_stat(Stat) -> case lists:keyfind(freq, 1, Stat) of {_, F} -> F; _Else -> [] end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% auxiliary functions inc(I, F) -> N = array:get(I, F), array:set(I, N + 1, F).