anchor.diff:          _ ->
               notfound
       end;
  -lookup(IP, Database) when is_binary(IP) ->
  +lookup(IP, Database) ->
       lookup(parse_ip(IP), Database).
  +
  +parse_ip({A, B, C}) -> <<A:8, B:8, C:8>>;
  +parse_ip({A, B, C, _}) -> <<A:8, B:8, C:8>>;
  +parse_ip(<<A:8, B:8, C:8, _D:8>>) -> <<A:8, B:8, C:8>>;
  +parse_ip(IP) when is_binary(IP) ->
  +    parse_ip(binary_to_list(IP));
  +parse_ip(IP) when is_list(IP) ->
  +    case lists:map(
  +            fun list_to_integer/1,
  +            string:tokens(IP, ".")
  +            ) of
  +        [A, B, C] -> <<A:8, B:8, C:8>>;
  +        [A, B, C, _] -> <<A:8, B:8, C:8>>;
  +        _ -> invalid_ip
  +    end;
  +parse_ip(_) -> invalid_ip.
   
   lookup_ip(C, <<Start:8, End:8, ZNum:16/integer, _/binary>>)
                                                   when C >= Start, C =< End ->
  
positive.code: %%  2. IPsIndex has 65536 parts associated with all possible 16-bit IPs
  %%     (from 0.0.*.* to 255.255.*.*). Every part has 4 bytes:
  %%
  %%  <<BucketStart:16, BucketSize:16>>
  %%
  %%      BucketStart represents the offset of the bucket from PIsBlob associated
  %%      with the target 16-bit IP, and BucketSize represents its size, in bytes.
  
negative.code: %%  1. IPsBlob consists of buckets, each bucket is associated with some
  %%     16-bit IP (M.N.*.*) and contains IP ranges associated with a zone hash:
  %%
  %%  StartIP_1:8, EndIP_1:8, Zone_1:16, StartIP_2:8, EndIP_2:8, Zone_2:16, ...
  %%
  %%      Thus, every IP range uses 4 bytes: 2 bytes for start and end of
  %%      the range and 2 bytes for zone hash.
  