Packages
ra
2.16.13
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-beta.1
2.17.3
2.17.2
2.17.1
2.17.0
2.16.13
2.16.12
2.16.11
2.16.10
2.16.9
2.16.8
2.16.7
2.16.6
2.16.5
2.16.4
2.16.3
2.16.2
2.16.1
2.16.0
2.16.0-pre.12
2.16.0-pre.11
2.16.0-pre.10
2.16.0-pre.9
2.16.0-pre.8
2.16.0-pre.7
2.16.0-pre.6
2.16.0-pre.5
2.16.0-pre.4
2.16.0-pre.3
2.16.0-pre.2
2.16.0-pre.1
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.13.0-pre.1
2.12.0
2.11.0
2.11.0-pre.1
2.10.2-pre.2
2.10.2-pre.1
2.10.1
2.10.0
2.10.0-pre.3
2.10.0-pre.2
2.10.0-pre.1
2.9.10-pre.1
2.9.1
2.9.1-pre.2
2.9.1-pre.1
2.9.0
2.8.0
retired
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-pre.3
2.7.0-pre.2
2.7.0-pre.1
2.6.3
2.6.2
2.6.1
2.6.0-pre.1
2.5.1
2.5.1-pre.1
2.5.0
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
retired
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.9.6
0.9.5
0.9.4
0.9.2
0.3.3
retired
0.3.2
retired
0.3.1
retired
Raft library
Current section
Files
Jump to
Current section
Files
src/ra_lol.erl
-module(ra_lol).
%% sorted list of list
-export([
new/0,
append/2,
search/2,
takewhile/2,
from_list/1,
from_list/2,
to_list/1,
len/1
]).
-define(MAX_ROW_LEN, 64).
-type row() :: [term()].
-type gt_fun() :: fun((Item, Item) -> boolean()).
-record(?MODULE, {len = 0 :: non_neg_integer(),
append_row_len = 0 :: non_neg_integer(),
gt_fun :: gt_fun(),
rows = [] :: [row()]}).
-opaque state() :: #?MODULE{}.
%% a search continuation
-opaque cont() :: [row()].
-export_type([state/0,
cont/0]).
-spec new() -> state().
new() ->
#?MODULE{gt_fun = fun erlang:'>'/2}.
-spec new(gt_fun()) -> state().
new(GtFun) ->
#?MODULE{gt_fun = GtFun}.
%% @doc append an item that is greater than the last appended item
-spec append(Item, state()) ->
state() | out_of_order
when Item :: term().
append(Item, #?MODULE{rows = []} = State) ->
State#?MODULE{rows = [[Item]],
len = 1,
append_row_len = 0};
append(Item,
#?MODULE{len = Len,
gt_fun = GtFun,
append_row_len = RowLen,
rows = [[LastItem | _] = Row | Rows]} = State) ->
case GtFun(Item, LastItem) of
true ->
case RowLen of
?MAX_ROW_LEN ->
%% time for a new row
State#?MODULE{rows = [[Item], Row | Rows],
len = Len + 1,
append_row_len = 1};
_ ->
State#?MODULE{rows = [[Item | Row] | Rows],
len = Len + 1,
append_row_len = RowLen + 1}
end;
false ->
out_of_order
end.
-spec search(fun((term()) -> higher | lower | equal),
state() | cont()) ->
{term(), cont()} | undefined.
search(SearchFun, #?MODULE{rows = Rows}) ->
search(SearchFun, Rows);
search(SearchFun, Rows) when is_list(Rows) ->
case find_row(SearchFun, Rows) of
[] ->
undefined;
[SearchRow | RemRows] ->
case search_row(SearchFun, SearchRow) of
undefined ->
undefined;
{Item, Rem} ->
{Item, [Rem | RemRows]}
end
end.
-spec takewhile(fun((Item) -> boolean()), state()) ->
{[Item], state()}
when Item :: term().
takewhile(Fun, #?MODULE{gt_fun = GtFun} = State) ->
%% not the most efficient but rarely used
{Taken, Left} = lists:splitwith(Fun, to_list(State)),
{Taken, from_list(GtFun, lists:reverse(Left))}.
%% @doc initialise from a list sorted in ascending order
-spec from_list(list()) -> state().
from_list(List) ->
from_list(fun erlang:'>'/2, List).
-spec from_list(gt_fun(), list()) -> state().
from_list(GtFun, List)
when is_list(List) ->
lists:foldl(fun append/2, new(GtFun), List).
-spec to_list(state()) -> list().
to_list(#?MODULE{rows = Rows}) ->
lists:append(Rows).
-spec len(state()) -> non_neg_integer().
len(#?MODULE{len = Len}) ->
Len.
%% Internals
search_row(_SearchFun, []) ->
undefined;
search_row(SearchFun, [Item | Rem]) ->
case SearchFun(Item) of
equal ->
{Item, Rem};
lower ->
search_row(SearchFun, Rem);
higher ->
undefined
end.
find_row(SearchFun, [_Row, Row | Rem] = Rows) ->
%% if last item of the second rows is higher than searching for
%% then return all rows
case SearchFun(hd(Row)) of
higher ->
Rows;
_ ->
%% else keep searching
find_row(SearchFun, [Row | Rem])
end;
find_row(_SearchFun, Rows) ->
Rows.
%%% ===================
%%% Internal unit tests
%%% ===================
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
basic_test() ->
Items = lists:seq(1, 100),
L0 = ra_lol:from_list(Items),
?assertEqual(100, ra_lol:len(L0)),
?assertEqual(Items, lists:reverse(ra_lol:to_list(L0))),
?assertMatch(out_of_order, ra_lol:append(1, L0)),
L1 = ra_lol:append(101, L0),
?assertEqual(101, ra_lol:len(L1)),
SearchFun = fun (T) ->
fun (Item) ->
if T == Item -> equal;
T > Item -> higher;
true -> lower
end
end
end,
[begin
{T, _} = ra_lol:search(SearchFun(T), L1)
end || T <- Items ++ [101]],
%% test searching with a continuation
_ = lists:foldl(fun (T, Acc) ->
{T, Cont} = ra_lol:search(SearchFun(T), Acc),
Cont
end, L1, lists:reverse(Items ++ [101])),
TakeFun = fun(Item) -> Item > 50 end,
{Taken, L2} = takewhile(TakeFun, L1),
?assertEqual(50, ra_lol:len(L2)),
?assertEqual(51, length(Taken)),
?assertMatch(out_of_order, ra_lol:append(50, L2)),
L3 = ra_lol:append(51, L2),
?assertEqual(51, ra_lol:len(L3)),
ok.
-endif.