Tree changes

This commit is contained in:
martinsumner 2017-01-20 16:15:00 +00:00
parent 860a8a95e3
commit 220d493b5a

View file

@ -17,15 +17,16 @@
from_orderedset/1, from_orderedset/1,
to_list/1, to_list/1,
match_range/3, match_range/3,
% search_range/3, search_range/4,
match/2, match/2,
search/2, search/3,
tsize/1 tsize/1,
empty/0
]). ]).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-define(SKIP_WIDTH, 32). -define(SKIP_WIDTH, 16).
%%%============================================================================ %%%============================================================================
@ -34,53 +35,64 @@
from_orderedlist(OrderedList) -> from_orderedlist(OrderedList) ->
L = length(OrderedList), L = length(OrderedList),
{tree, L, from_orderedlist(OrderedList, gb_trees:empty(), L)}. {tree, L, from_orderedlist(OrderedList, empty_tree(), L)}.
from_orderedset(Table) -> from_orderedset(Table) ->
from_orderedlist(ets:tab2list(Table)). from_orderedlist(ets:tab2list(Table)).
match(Key, {tree, _L, Tree}) -> match(Key, {tree, _L, Tree}) ->
Iter = gb_trees:iterator_from(Key, Tree), Iter = tree_iterator_from(Key, Tree),
case gb_trees:next(Iter) of case tree_next(Iter) of
none -> none ->
none; none;
{_NK, SL, _Iter} -> {_NK, SL, _Iter} ->
lookup_match(Key, SL) lookup_match(Key, SL)
end. end.
match_range(StartKey, EndKey, {tree, _L, Tree}) -> search(Key, {tree, _L, Tree}, StartKeyFun) ->
Iter0 = gb_trees:iterator_from(StartKey, Tree), Iter = tree_iterator_from(Key, Tree),
case gb_trees:next(Iter0) of case tree_next(Iter) of
none ->
[];
{NK, SL, Iter1} ->
PredFun =
fun({K, _V}) ->
K < StartKey
end,
{_LHS, RHS} = lists:splitwith(PredFun, SL),
lookup_match_range(EndKey, {NK, RHS}, Iter1, [])
end.
search(Key, {tree, _L, Tree}) ->
Iter = gb_trees:iterator_from(Key, Tree),
case gb_trees:next(Iter) of
none -> none ->
none; none;
{_NK, SL, _Iter} -> {_NK, SL, _Iter} ->
lookup_best(Key, SL) {K, V} = lookup_best(Key, SL),
case K < StartKeyFun(V) of
true ->
none;
false ->
{K, V}
end
end. end.
match_range(StartRange, EndRange, {tree, _L, Tree}) ->
EndRangeFun =
fun(ER, FirstRHSKey, _FirstRHSValue) ->
ER == FirstRHSKey
end,
lookup_range_start(StartRange, EndRange, Tree, EndRangeFun).
search_range(StartRange, EndRange, {tree, _L, Tree}, StartKeyFun) ->
EndRangeFun =
fun(ER, _FirstRHSKey, FirstRHSValue) ->
StartRHSKey = StartKeyFun(FirstRHSValue),
ER >= StartRHSKey
end,
lookup_range_start(StartRange, EndRange, Tree, EndRangeFun).
to_list({tree, _L, Tree}) -> to_list({tree, _L, Tree}) ->
FoldFun = FoldFun =
fun({_MK, SL}, Acc) -> fun({_MK, SL}, Acc) ->
Acc ++ SL Acc ++ SL
end, end,
lists:foldl(FoldFun, [], gb_trees:to_list(Tree)). lists:foldl(FoldFun, [], tree_to_list(Tree)).
tsize({tree, L, _Tree}) -> tsize({tree, L, _Tree}) ->
L. L.
empty() ->
{tree, 0, empty_tree()}.
%%%============================================================================ %%%============================================================================
%%% Internal Functions %%% Internal Functions
%%%============================================================================ %%%============================================================================
@ -92,7 +104,7 @@ from_orderedlist(OrdList, Tree, L) ->
SubLL = min(?SKIP_WIDTH, L), SubLL = min(?SKIP_WIDTH, L),
{Head, Tail} = lists:split(SubLL, OrdList), {Head, Tail} = lists:split(SubLL, OrdList),
{LastK, _LastV} = lists:last(Head), {LastK, _LastV} = lists:last(Head),
from_orderedlist(Tail, gb_trees:insert(LastK, Head, Tree), L - SubLL). from_orderedlist(Tail, tree_insert(LastK, Head, Tree), L - SubLL).
lookup_match(_Key, []) -> lookup_match(_Key, []) ->
none; none;
@ -108,30 +120,71 @@ lookup_best(Key, [{EK, EV}|_Tail]) when EK >= Key ->
lookup_best(Key, [_Top|Tail]) -> lookup_best(Key, [_Top|Tail]) ->
lookup_best(Key, Tail). lookup_best(Key, Tail).
lookup_match_range(EndKey, {NK0, SL0}, Iter0, Output) -> lookup_range_start(StartRange, EndRange, Tree, EndRangeFun) ->
Iter0 = tree_iterator_from(StartRange, Tree),
case tree_next(Iter0) of
none ->
[];
{NK, SL, Iter1} ->
PredFun =
fun({K, _V}) ->
K < StartRange
end,
{_LHS, RHS} = lists:splitwith(PredFun, SL),
lookup_range_end(EndRange, {NK, RHS}, Iter1, [], EndRangeFun)
end.
lookup_range_end(EndRange, {NK0, SL0}, Iter0, Output, EndRangeFun) ->
PredFun = PredFun =
fun({K, _V}) -> fun({K, _V}) ->
not leveled_codec:endkey_passed(EndKey, K) not leveled_codec:endkey_passed(EndRange, K)
end, end,
case leveled_codec:endkey_passed(EndKey, NK0) of case leveled_codec:endkey_passed(EndRange, NK0) of
true -> true ->
{LHS, RHS} = lists:splitwith(PredFun, SL0), {LHS, RHS} = lists:splitwith(PredFun, SL0),
case RHS of case RHS of
[{EndKey, FirstValue}|_Tail] -> [] ->
Output ++ LHS ++ [{EndKey, FirstValue}]; Output ++ LHS;
_ -> [{FirstRHSKey, FirstRHSValue}|_Rest] ->
Output ++ LHS case EndRangeFun(EndRange, FirstRHSKey, FirstRHSValue) of
true ->
Output ++ LHS ++ [{FirstRHSKey, FirstRHSValue}];
false ->
Output ++ LHS
end
end; end;
false -> false ->
UpdOutput = Output ++ SL0, UpdOutput = Output ++ SL0,
case gb_trees:next(Iter0) of case tree_next(Iter0) of
none -> none ->
UpdOutput; UpdOutput;
{NK1, SL1, Iter1} -> {NK1, SL1, Iter1} ->
lookup_match_range(EndKey, {NK1, SL1}, Iter1, UpdOutput) lookup_range_end(EndRange,
{NK1, SL1},
Iter1,
UpdOutput,
EndRangeFun)
end end
end. end.
%%%============================================================================
%%% Balance tree implementation
%%%============================================================================
empty_tree() ->
gb_trees:empty().
tree_insert(K, V, T) ->
gb_trees:insert(K, V, T).
tree_to_list(T) ->
gb_trees:to_list(T).
tree_iterator_from(K, T) ->
gb_trees:iterator_from(K, T).
tree_next(I) ->
gb_trees:next(I).
%%%============================================================================ %%%============================================================================
%%% Test %%% Test
@ -166,6 +219,28 @@ generate_randomkeys(Seqn, Count, Acc, BucketLow, BRange) ->
BucketLow, BucketLow,
BRange). BRange).
tree_search_test() ->
MapFun =
fun(N) ->
{N * 4, N * 4 - 2}
end,
KL = lists:map(MapFun, lists:seq(1, 50)),
T = from_orderedlist(KL),
StartKeyFun = fun(V) -> V end,
?assertMatch([], search_range(0, 1, T, StartKeyFun)),
?assertMatch([], search_range(201, 202, T, StartKeyFun)),
?assertMatch([{4, 2}], search_range(2, 4, T, StartKeyFun)),
?assertMatch([{4, 2}], search_range(2, 5, T, StartKeyFun)),
?assertMatch([{4, 2}, {8, 6}], search_range(2, 6, T, StartKeyFun)),
?assertMatch(50, length(search_range(2, 200, T, StartKeyFun))),
?assertMatch(50, length(search_range(2, 198, T, StartKeyFun))),
?assertMatch(49, length(search_range(2, 197, T, StartKeyFun))),
?assertMatch(49, length(search_range(4, 197, T, StartKeyFun))),
?assertMatch(48, length(search_range(5, 197, T, StartKeyFun))).
tree_test() -> tree_test() ->
N = 4000, N = 4000,
@ -243,13 +318,15 @@ match_fun(Tree) ->
end. end.
search_exactmatch_fun(Tree) -> search_exactmatch_fun(Tree) ->
StartKeyFun = fun(_V) -> all end,
fun({K, V}) -> fun({K, V}) ->
?assertMatch({K, V}, search(K, Tree)) ?assertMatch({K, V}, search(K, Tree, StartKeyFun))
end. end.
search_nearmatch_fun(Tree) -> search_nearmatch_fun(Tree) ->
StartKeyFun = fun(_V) -> all end,
fun({K, {NK, NV}}) -> fun({K, {NK, NV}}) ->
?assertMatch({NK, NV}, search(K, Tree)) ?assertMatch({NK, NV}, search(K, Tree, StartKeyFun))
end. end.
-endif. -endif.