Current section
Files
Jump to
Current section
Files
src/ddskerl_exact.erl
-module(ddskerl_exact).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC("""
Quantile summary exact implemenation.
This implements an exact count of all elements seen,
which is the slowest and most memory consuming algorithm.
It is on the other hand an exact algorithm,
hence it can be used for comparison against all other approximations.
""").
-include("./ddskerl.hrl").
-export([new/0, total/1, sum/1, insert/2, merge/2, quantile/2]).
?DOC("DDSketch instance.").
-opaque ddsketch() :: #ddskerl_exact{}.
-export_type([ddsketch/0]).
?DOC("Create a new DDSketch instance.").
-spec new() -> ddsketch().
new() ->
#ddskerl_exact{}.
?DOC("Get the total number of elements in the DDSketch.").
-spec total(ddsketch()) -> non_neg_integer().
total(#ddskerl_exact{total = Total}) ->
Total.
?DOC("Get the sum of elements in the DDSketch.").
-spec sum(ddsketch()) -> non_neg_integer().
sum(#ddskerl_exact{sum = Sum}) ->
Sum.
?DOC("Insert a value into the DDSketch.").
-spec insert(ddsketch(), number()) -> ddsketch().
insert(#ddskerl_exact{data = Data, total = Total, sum = Sum} = S, Val) when 0 =< Val ->
NewData = maps:update_with(Val, fun(X) -> X + 1 end, 1, Data),
S#ddskerl_exact{data = NewData, total = Total + 1, sum = Sum + Val}.
?DOC("Merge two DDSketch instances.").
-spec merge(ddsketch(), ddsketch()) -> ddsketch().
merge(
#ddskerl_exact{data = Data1, total = Total1, sum = Sum1},
#ddskerl_exact{data = Data2, total = Total2, sum = Sum2}
) ->
Data = maps:merge_with(fun(_K, Val1, Val2) -> Val1 + Val2 end, Data1, Data2),
#ddskerl_exact{data = Data, total = Total1 + Total2, sum = Sum1 + Sum2}.
?DOC("Calculate the quantile of a DDSketch.").
-spec quantile(ddsketch(), number()) -> number() | undefined.
quantile(#ddskerl_exact{data = Data, total = Total}, Quantile) when 0 =< Quantile, Quantile =< 1 ->
TotalQuantile = Total * Quantile,
SortedData = lists:sort(fun({Key1, _}, {Key2, _}) -> Key1 =< Key2 end, maps:to_list(Data)),
try
lists:foldl(
fun({Key, Val}, Acc) ->
NewAccumulatedRank = Acc + Val,
case TotalQuantile =< NewAccumulatedRank of
true ->
throw({found, Key});
false ->
NewAccumulatedRank
end
end,
0,
SortedData
)
of
_ -> undefined
catch
throw:{found, Key} -> Key
end.