anchor.diff:  mv_and(Q) ->
       Q.
   
  -and_subs({or_q,L,R}) ->
  -    and_subs(L) ++ and_subs(R);
  -and_subs(Q) ->
  +%% @doc Converts a query in DNF to a lists of AND queries.
  +clauses({or_q,L,R}) ->
  +    clauses(L) ++ clauses(R);
  +clauses(Q) ->
       [Q].
   
  +%% @doc Converts an AND clause to  a list of TERM and NOT queries.
   flatten({and_q,L,R}) ->
       lists:flatten([flatten(L),flatten(R)]);
   flatten(Q) ->
       [Q].
   
  +%% @doc Partitions a list of TERM and NOT queries into two lists, where the
  +%% first lists contains all terms in TERM queries and the second list contains
  +%% all terms in NOT queries.
   partition(Qs) ->
       {Pos,Neg} = lists:partition(fun (Q) -> is_record(Q, term_q) end, Qs),
       {terms(Pos),terms(Neg)}.
  
positive.code: tree_to_query({term_q,{string,Keyword,_}}) ->
      {#term_q{keyword = Keyword},0}.
  
  dnf({Q,0}) ->
      Q;
  dnf({Q,D}) ->
  
negative.code: -spec execute(utf8_str()) -> gb_set(). %% TODO: might return an error
  execute(Str) ->
      %% parse and preprocess query
      Clauses = [partition(flatten(X)) || X <- clauses(dnf(from_str(Str)))],
      %% fetch data
      Data = plists:mapreduce(
               fun fetch/1, 
  