anchor.diff:                          dict:fetch(any, Dict);
                       _ ->
                           gb_sets:union(
  -                          [element(2, Y)
  -                           || Y <- [dict:find(X, Dict) || X <- ZoneIds],
  -                              Y /= error])
  +                          [element(2, X)
  +                           || X <- [dict:find(Y, Dict) || Y <- ZoneIds],
  +                              X /= error])
                   end
           end,
       DocIds =
  
positive.code:     Destruct = fun (Q) ->
                         case Q of
                             #not_q{sub_q = S} ->
                                 {S#term_q.keyword,S#term_q.zone_ids};
                             #term_q{keyword = Keyword, zone_ids = ZoneIds} ->
                                 {Keyword, ZoneIds}
                         end
  
negative.code:     tree_to_query(ParseTree).
  
  scan(<<>>) ->
      [{'$end', 1}];
  scan(<<C/utf8, Rest/bytes>>) ->
      case C of
          %% parentheses
          $( -> [{'(', 1} | scan(Rest)];
          $) -> [{')', 1} | scan(Rest)];
          $[ -> [{'[', 1} | scan(Rest)];
          $] -> [{']', 1} | scan(Rest)];
          %% boolean operators
          $& -> [{'&', 1} | scan(Rest)];
          $| -> [{'|', 1} | scan(Rest)];
          $! -> [{'!', 1} | scan(Rest)];
          %% comma
          $, -> [{',',  1} | scan(Rest)];
          %% space (ignored)
          32 -> scan(Rest);
          %% begin of a single quoted string
          $' ->
              Regex = [<<"^((?:[^'\\\\]|\\\\')*)'(.*)$">>],
              Options = [unicode, {capture, all_but_first, binary}],
              case re:run(Rest, Regex, Options) of
                  {match, [String, RestRest]} ->
                      [{string, String, 1} | scan(RestRest)]
              end;
          %% begin of a keyword or an ID
          _ ->
              Regex = [<<"^([a-z0-9_]*)(.*)$">>],
              Options = [unicode, {capture, all_but_first, binary}],
              case re:run(<<C/utf8,Rest/bytes>>, Regex, Options) of
                  {match, [Word, RestRest]} ->
                      Token = case Word of
                                  <<"in">> -> {'in', 1};
                                  Id -> {id, Id, 1}
                              end,
                      [Token|scan(RestRest)]
              end
      end.
  
  tree_to_query({and_q, SubTreeL, SubTreeR}) ->
      {L, DepthL} = tree_to_query(SubTreeL),
      {R, DepthR} = tree_to_query(SubTreeR),
      {#and_q{l_sub_q = L,r_sub_q = R}, 1 + max(DepthL, DepthR)};
  tree_to_query({or_q, SubTreeL, SubTreeR}) ->
      {L, DepthL} = tree_to_query(SubTreeL),
      {R, DepthR} = tree_to_query(SubTreeR),
      {#or_q{l_sub_q = L, r_sub_q = R}, 1 + max(DepthL, DepthR)};
  tree_to_query({not_q, SubTree}) ->
      {Sub,Depth} = tree_to_query(SubTree),
      {#not_q{sub_q = Sub}, 1 + Depth};
  tree_to_query({term_q, {string, Keyword, _}, ZoneIds}) ->
      Ids = [binary:bin_to_list(Id) || {_, Id, _} <- ZoneIds],
      {#term_q{keyword  = Keyword, zone_ids = Ids}, 0}.
  
  %% @doc Rewrites a query to disjunctive normal form.
  -spec dnf({q(),pos_integer()}) -> q().
  dnf({Q, 0}) ->
      Q;
  dnf({Q, D}) ->
      dnf(mv_not(Q), D).
  
  dnf(Q, 1) ->
      Q;
  dnf(Q, D) ->
      dnf(mv_and(Q), D - 1).
  
  mv_not({and_q, L, R}) ->
      {and_q, mv_not(L), mv_not(R)};
  