-module(bitset).
-export([new/0, new/1, from_list/1, insert/2, 
    intersect/1, union/1, difference/2,
    contains/2, foldl/3, foldr/3,
    pmap/2]).

%% Create a new bitset, with no bits set.
%% return     A new bitset
new() ->
  { ?MODULE, { 1, 0 }}.

%% Create a new bitset from the entries in the given list of numbers
%% Elems      A list of integers that should be set in this new bitset
new(Elems) when is_list(Elems) ->
  from_list(Elems, new()).

from_list(Elems) when is_list(Elems) ->
  from_list(Elems, new()).

%% Put a given Key (non-negative integer) into the bitset
%% Key        Non-negative integer to be stored in the set
%% Set        The set in which to store the key
%% return     A new bitset, with the desired key set
insert(Key, { ?MODULE, Tree = { Depth, _}}) when is_integer(Key), Key >= 0 ->
  { ToMake, Path } = split_key(Depth, Key),
  LargeEnough = grow(ToMake, Tree),
  { ?MODULE, set(Path, LargeEnough) }.

%% Determine the intersection of the given list of bitsets.  The resulting 
%% bitset will have bits set where all given sets had them set, and nowhere 
%% else.
%% List       A list of bitsets
%% return     A new bitset that is the intersection of the given bitsets
intersect([]) -> bitset:new();
intersect([Final]) -> Final;
intersect([{ ?MODULE, First }, { ?MODULE, Second } | Rest]) ->
  intersect([{ ?MODULE, imerge(First, Second)} | Rest]).

%% Determine the union of the given list of bitsets.  The resulting bitset will
%% have bits set where any of the given sets had them set, and nowhere else.
%% List       A list of bitsets to union together
%% return     A new bitset which is the union of the given bitsets
union([]) -> bitset:new();
union([Final]) -> Final;
union([{ ?MODULE, First }, { ?MODULE, Second } | Rest]) ->
  union([{ ?MODULE, umerge(First, Second)} | Rest]).

difference({ ?MODULE, Left = { LD, _ }}, { ?MODULE, Right = { RD, _ }}) ->
  { SLeft, SRight } = if
    LD < RD ->
      { grow(RD-LD, Left), Right };
    LD > RD ->
      { Left, grow(LD-RD, Right) };
    LD =:= RD ->
      { Left, Right }
  end,
  { ?MODULE, diff_eq(SLeft, SRight) }.

%% Test whether a bit is set in the given bitset.
%% Key        The key to test
%% Set        The bitset to test against
%% return     true of that bit is set, false otherwise
contains(Key, { ?MODULE, { Depth, _ } = Tree }) 
    when is_integer(Key), Key >= 0 ->
  case split_key(Depth, Key) of
    { 0, Path } ->
      %io:format("Path is ~w~n", [Path]),
      contains2(Path, Tree);
    { _ToMake, _Exists } ->
      %io:format("Key is too large for this set~n"),
      false
  end.

foldl(Fun, Accum0, { ?MODULE, Tree }) -> fold(left, Fun, Accum0, 0, Tree).
foldr(Fun, Accum0, { ?MODULE, Tree }) -> fold(right, Fun, Accum0, 0, Tree).

pmap(Fun, { ?MODULE, Tree }) ->
  pmap(Fun, Tree, 0, 2).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helper for new/1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
from_list([], Bs) -> Bs;
from_list([Head | Tail ], Bs) when is_integer(Head) ->
  from_list(Tail, insert(Head, Bs)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helper for insert/2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% empty entry at the bottom becomes a number with the correct bit set
set([Final], empty) when is_integer(Final), Final >= 0 ->
  { 1, 1 bsl Final };

%% good node at the bottom gets a new bit set
set([Final], { 1, Number }) 
    when is_integer(Final), Final >= 0 ->
  { 1, Number bor (1 bsl Final) };

%% non-bottom node gets a new child
set([Pos0 | Rest], { Depth, Children }) 
    when is_integer(Pos0), Pos0 >= 0 ->
  Pos = Pos0+1,
  New = set(Rest, element(Pos, Children)),
  { Depth, setelement(Pos, Children, New) };

%% non-bottom empty becomes a child container, and gets a child
set([Pos0 | Rest], empty) 
    when is_integer(Pos0), Pos0 >= 0 ->
  Pos      = Pos0 + 1,
  Children = erlang:make_tuple(27, empty),
  Depth    = length(Rest)+1,
  { Depth, setelement(Pos, Children, set(Rest, empty)) }.

%% Function used to make the tree deeper, so it can hold elements that are too
%% large for its current size
grow(0, Tree) -> Tree;
grow(Levels, { ChildDepth, _ } = Tree) 
    when is_integer(Levels), Levels > 0 ->
  %io:format("Growing, Levels is ~p~n", [Levels]),
  Children = erlang:make_tuple(27, empty),
  grow(Levels-1, { ChildDepth+1, setelement(1, Children, Tree) }).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers for intersect/1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
imerge(First = { FDepth, FTree }, Second = { SDepth, STree }) ->
  if
    FDepth > SDepth ->
      imerge(element(1, FTree), Second);
    FDepth < SDepth ->
      imerge(First, element(1, STree));
    FDepth =:= SDepth ->
      imerge_equal(First, Second)
  end.

imerge_equal(_, empty) -> empty;
imerge_equal(empty, _) -> empty;
imerge_equal({ 1, FNumber }, { 1, SNumber }) ->
  { 1, FNumber band SNumber };

imerge_equal({ Depth, FTree }, { Depth, STree }) ->
  NewTree = lists:zipwith(fun imerge_equal/2, 
    tuple_to_list(FTree), tuple_to_list(STree)),
  { Depth, list_to_tuple(NewTree) }.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers for union/1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
umerge(First = { FDepth, _ }, Second = { SDepth, _ }) ->
  if
    FDepth > SDepth ->
      umerge_equal(First, grow(FDepth-SDepth, Second));
    FDepth < SDepth ->
      umerge_equal(grow(SDepth-FDepth, First), Second);
    FDepth =:= SDepth ->
      umerge_equal(First, Second)
  end.

umerge_equal(empty, empty) -> empty;
umerge_equal(First, empty) -> First;
umerge_equal(empty, Second) -> Second;
umerge_equal({ 1, FNumber }, { 1, SNumber }) ->
  { 1, FNumber bor SNumber };

umerge_equal({ Depth, FTree }, { Depth, STree }) ->
  NewTree = lists:zipwith(fun umerge_equal/2,
    tuple_to_list(FTree), tuple_to_list(STree)),
  { Depth, list_to_tuple(NewTree) }.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers for difference/2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff_eq(empty, _) -> empty;
diff_eq(Left, empty) -> Left;
diff_eq({ 1, LNumber }, { 1, RNumber }) ->
  RInvert = ((1 bsl 27 - 1) bxor RNumber),
  { 1, LNumber band RInvert };

diff_eq({ Depth, Left }, { Depth, Right }) ->
  NewTree = lists:zipwith(fun diff_eq/2,
    tuple_to_list(Left), tuple_to_list(Right)),
  { Depth, list_to_tuple(NewTree) }.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers for fold[lr]/3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fold(_Dir, _Fun, Accum, _Pre, empty) -> Accum;
fold(Dir, Fun, Accum, Pre, { 1, Number }) ->
  %io:format("Iterating on leaf, pre is ~p, Number is ~p~n", [ Pre, Number ]),
  lists:foldl(
    fun(Bit, AccumI) ->
        case Number band (1 bsl Bit) of
          0 -> AccumI;
          _ -> Fun(Pre * 27 + Bit, AccumI)
        end
    end,
    Accum,
    case Dir of
      left  -> lists:seq(0, 26);
      right -> lists:reverse(lists:seq(0, 26))
    end);
fold(Dir, Fun, Accum, Pre, { _Depth, Children }) ->
  %io:format("Pre is ~p, Tree is ~p~n", [ Pre, { Depth, Children }]),
  Offset = Pre*27,
  lists:foldl(
    fun(Element, AccumI) ->
        Child = element(Element+1, Children),
        fold(Dir, Fun, AccumI, Offset+Element, Child)
    end,
    Accum,
    case Dir of
      left  -> lists:seq(0,26);
      right -> lists:reverse(lists:seq(0, 26))
    end).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helper for pmap/2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pmap(Fun, Tree, Pre, 0) ->
  pmap_fold(Fun, Tree, Pre);
pmap(_Fun, empty, _, _) ->
  [];
pmap(Fun, Tree = { Depth, _Children }, Pre, _Levels) when Depth =< 3 ->
  pmap_fold(Fun, Tree, Pre);
pmap(Fun, { _Depth, Children }, Pre, Levels) ->
  Offset = Pre*27,
  Root = self(),
  Pids = [ 
    spawn(fun() -> 
          Root ! { self(), pmap(Fun, Child, Offset+Element, Levels-1) } 
      end)
    || { Element, Child } 
    <- lists:zip(lists:seq(0,26), tuple_to_list(Children)) ],
  lists:foldl(
    fun(Pid, Accum) ->
        receive { Pid, Result } -> 
            Result ++ Accum
        end
    end,
    [],
    Pids).

pmap_fold(Fun, Tree, Pre) ->
  fold(
    left,
    fun(Bit, Accum) ->
        case Fun(Bit) of
          { ok, Value } ->
            [ Value | Accum ];
          nil ->
            Accum
        end
    end,
    [],
    Pre,
    Tree).


%% Helper function for split_key, generates X and Y where X*27+Y = Key; used for
%% converting the key to base-27, which is how our values are stored.
%% Key      Non-negative integer to divmod to our satisfaction
%% return   { Next, Diff } where Diff < 27 and Next * 27 + Diff = Key
next_diff(Key) when is_integer(Key), Key >= 0 ->
  Next = Key div 27,
  Diff = Key - ( Next * 27 ),
  { Next, Diff }.

%% Determine the base-27 form of the given non-negative integer.  This will be
%% returned as a list of numbers in the range [0, 26]; each number is a 
%% coefficient in the base-27 form of the given key, with the most significant
%% at the head of the list.
%% Also given to this function is the current depth of the tree; this function
%% will return how much larger the tree would have to be to hold this key, or 0
%% if the tree is already large enough.
%% Depth    The size of the tree we are generating a key address for
%% Key      The key we want to find in the tree
%% return   { NewLevels, Path }
split_key(Depth, Key) when is_integer(Key), Key >= 0 ->
  split_key(Depth, Key, 0, []).

%% Helper functions for split_key/2, with some accumulators added.
split_key(0, 0, New, Path) ->
  { New, Path };

split_key(0, Key, New, Path) when is_integer(Key), Key >= 0 ->
  { Next, Diff } = next_diff(Key),
  split_key(0, Next, New + 1,[ Diff | Path ]);

split_key(Depth, 0, New, Path) ->
  split_key(Depth-1, 0, New, [ 0 | Path ]);

split_key(Depth, Key, New, Path) when is_integer(Key), Key >= 0 ->
  { Next, Diff } = next_diff(Key),
  split_key(Depth-1, Next, New, [ Diff | Path ]).

contains2(_, empty) ->
  %io:format("Empty is false~n"),
  false;

contains2([ Final ], { 1, Number }) 
    when is_integer(Final), Final >= 0 ->
  %io:format("Final is ~p, Number is ~p~n", [Final, Number]),
  (Number band (1 bsl Final)) =/= 0;

contains2([ Pos0 | Rest ], { _Depth, Children }) 
    when is_integer(Pos0), Pos0 >= 0 ->
  Pos = Pos0+1,
  %io:format("Children are ~p, Pos is ~p~n", [Children, Pos]),
  contains2(Rest, element(Pos, Children)).
