Skip to content

Commit

Permalink
Made it so that or_sets can be ordered or unordered. Currently
Browse files Browse the repository at this point in the history
using orddicts which are not very fast.

I will replace it with a gb_tree or some other sorted map.
  • Loading branch information
Eric Moritz committed Apr 27, 2012
1 parent 2a8e22c commit 4930f1d
Showing 1 changed file with 43 additions and 31 deletions.
74 changes: 43 additions & 31 deletions src/or_set.erl
@@ -1,71 +1,78 @@
-module(or_set).
-export([new/0, new/1, add_element/2, del_element/2, is_element/2, merge/2,
from_list/1, to_list/1]).
from_list/2, from_list/1, to_list/1]).
-import(crdt_misc, [sorted_id/0]).

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

-record(orset, {mod, a, r}).
-record(orset, {dictmod, a, r}).


sorted_id() ->
{now(), node(), make_ref()}. % can refs() be serialized?

get_element_set(Element, Dict, #orset{mod=Mod}) ->
case dict:find(Element, Dict) of
get_element_set(Element, Dict, #orset{dictmod=DictMod}) ->
case DictMod:find(Element, Dict) of
{ok, Value} ->
Value;
error ->
Mod:new()
sets:new()
end.

new() ->
new(ordsets).
new(ordered).

new(SetModule) ->
#orset{mod=SetModule, a=dict:new(), r=dict:new()}.
new(ordered) ->
new(orddict);
new(unordered) ->
new(dict);
new(DictModule) ->
#orset{dictmod=DictModule, a=DictModule:new(), r=DictModule:new()}.

add_element(Element, ORSet=#orset{mod=Mod, a=A, r=R}) ->
add_element(Element, ORSet=#orset{dictmod=DictMod, a=A, r=R}) ->
Id = sorted_id(),
ESet = Mod:add_element(Id, get_element_set(Element, A, ORSet)),
ESet = sets:add_element(Id, get_element_set(Element, A, ORSet)),

ORSet#orset{a=dict:store(Element, ESet, A), r=R}.
ORSet#orset{a=DictMod:store(Element, ESet, A), r=R}.

del_element(Element, ORSet=#orset{mod=Mod, a=A, r=R}) ->
del_element(Element, ORSet=#orset{dictmod=DictMod, a=A, r=R}) ->
case is_element(Element, ORSet) of
true->
ESet = Mod:union(get_element_set(Element, A, ORSet),
get_element_set(Element, R, ORSet)),
ESet = sets:union(get_element_set(Element, A, ORSet),
get_element_set(Element, R, ORSet)),

ORSet#orset{a=dict:erase(Element, A),
r=dict:store(Element, ESet, R)};
ORSet#orset{a=DictMod:erase(Element, A),
r=DictMod:store(Element, ESet, R)};
false ->
ORSet
end.

merge(ORSet=#orset{a=A1, r=R1}, #orset{mod=Mod, a=A2, r=R2}) ->
merge(ORSet=#orset{a=A1, r=R1}, #orset{dictmod=DictMod, a=A2, r=R2}) ->
MergeEDict = fun(D1, D2) ->
dict:merge(fun(_K, V1, V2) ->
Mod:union(V1, V2) end,
DictMod:merge(fun(_K, V1, V2) ->
sets:union(V1, V2) end,
D1, D2) end,
ORSet#orset{a=MergeEDict(A1, A2), r=MergeEDict(R1, R2)}.

is_element(Element, ORSet=#orset{mod=Mod, a=A, r=R}) ->
is_element(Element, ORSet=#orset{a=A, r=R}) ->
AESet = get_element_set(Element, A, ORSet),
RESet = get_element_set(Element, R, ORSet),
Items = Mod:subtract(AESet, RESet),
Items = sets:subtract(AESet, RESet),

(Mod:size(Items) > 0).
(sets:size(Items) > 0).

from_list(L) ->
from_list(ordsets, L).
from_list(ordered, L).

from_list(ordered, L) ->
from_list(orddict, L);
from_list(unordered, L) ->
from_list(dict, L);
from_list(DictMod, L) ->
lists:foldl(fun add_element/2, new(DictMod), L);

from_list(SetModule, L) ->
lists:foldl(fun add_element/2, new(SetModule), L).

to_list(ORSet=#orset{a=A}) ->
[Key || Key <- dict:fetch_keys(A), is_element(Key, ORSet)].
to_list(ORSet=#orset{dictmod=DictMod, a=A}) ->
[Key || Key <- DictMod:fetch_keys(A), is_element(Key, ORSet)].

-ifdef(TEST).

Expand Down Expand Up @@ -104,6 +111,11 @@ to_list_test() ->

S3 = merge(S1, S2),

?assert([eric] =:= to_list(S3)).
?assertEqual([eric], to_list(S3)).

to_ordered_list_test() ->
S = from_list(ordered, [glenn, eric, shawn]),
?assertEqual([eric, glenn, shawn], to_list(S)).


-endif.

0 comments on commit 4930f1d

Please sign in to comment.