diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00c58b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.beam +.eunit +ebin +*~ +deps +ebin diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e454a52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3c78b4e --- /dev/null +++ b/Makefile @@ -0,0 +1,268 @@ +RELPKG= +REBAR=rebar + +OTPREL=$(shell erl -noshell -eval 'io:format(erlang:system_info(otp_release)), halt().') +PLT=$(HOME)/.dialyzer_plt.$(OTPREL) + +DIALYZE_IGNORE_WARN?=dialyze-ignore-warnings.txt +DIALYZE_NOSPEC_IGNORE_WARN?=dialyze-nospec-ignore-warnings.txt + +dialyzer=dialyzer -q --plt $(PLT) -Wunmatched_returns -r ./lib +dialyzer-nospec=dialyzer -q --plt $(PLT) --no_spec -Wno_undefined_callbacks -r ./lib +dialyzer-eunit=dialyzer -q --plt $(PLT) -Wunmatched_returns -r `find ./lib -name .eunit -print | xargs echo` +dialyzer-eunit-nospec=dialyzer -q --plt $(PLT) --no_spec -Wno_undefined_callbacks -r `find ./lib -name .eunit -print | xargs echo` + +ifeq ($(shell uname -s),Darwin) + ifeq ($(shell uname -m),x86_64) + otp_configure_flags= --enable-darwin-64bit + else + otp_configure_flags= --enable-darwin-universal + endif +else + otp_configure_flags= +endif + +.PHONY: all test \ + compile compile-eqc compile-proper \ + eunit-compile eqc-compile triq-compile proper-compile \ + eunit eqc proper \ + doc \ + build-plt check-plt \ + dialyze dialyze-nospec \ + update-dialyzer-baseline update-dialyzer-nospec-baseline \ + dialyze-eunit dialyze-eunit-nospec \ + dialyze-eqc dialyze-eqc-nospec \ + dialyze-proper dialyze-proper-nospec \ + ctags etags \ + clean realclean distclean \ + otp_make_release_tests otp_run_release_tests + +all: compile + +test: eunit + +deps: get-deps + +get-deps: + $(REBAR) get-deps + +compile: + @echo "compiling: $(RELPKG) ..." + $(REBAR) compile + +compile-eqc: + @echo "compiling-eqc: $(RELPKG) ..." + $(REBAR) compile -D QC -D QC_EQC + +compile-triq: + @echo "compiling-eqc: $(RELPKG) ..." + $(REBAR) compile -D EQC -D TRIQ + +compile-proper: + @echo "compiling-proper: $(RELPKG) ..." + $(REBAR) compile -D QC -D QC_PROPER + +eunit-compile: compile + @echo "eunit test compiling: $(RELPKG) ..." + $(REBAR) compile -D QC -D TRIQ -D TEST + +eqc-compile: compile-eqc + @echo "eqc test compiling: $(RELPKG) ..." + $(REBAR) compile eqc -D QC -D QC_EQC + +triq-compile: compile-triz + @echo "eqc test compiling: $(RELPKG) ..." + $(REBAR) eunit-compile -D EQC -D TRIQ + +proper-compile: compile-proper + @echo "proper test compiling: $(RELPKG) ..." + $(REBAR) eunit-compile -D QC -D QC_PROPER + +eunit: eunit-compile + @echo "eunit testing: $(RELPKG) ..." + $(REBAR) eunit recursive=false + +eqc: eqc-compile + @echo "eqc testing: $(RELPKG) ... not implemented yet" + +triq: triq-compile + @echo "triq testing: $(RELPKG) ... not implemented yet" + +proper: proper-compile + @echo "proper testing: $(RELPKG) ... not implemented yet" + +doc: compile + @echo "edoc generating: $(RELPKG) ..." + $(REBAR) doc + +build-plt: $(PLT) + +check-plt: $(PLT) + dialyzer -q --plt $(PLT) --check_plt + +dialyze: build-plt clean compile + @echo "dialyzing w/spec: $(RELPKG) ..." + $(dialyzer) | grep -v '^ *$$' | tee $(DIALYZE_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_IGNORE_WARN) + +dialyze-nospec: build-plt clean compile + @echo "dialyzing w/o spec: $(RELPKG) ..." + $(dialyzer-nospec) | grep -v '^ *$$' | tee $(DIALYZE_NOSPEC_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_NOSPEC_IGNORE_WARN) + +update-dialyzer-baseline: dialyze + mv -f $(DIALYZE_IGNORE_WARN).log $(DIALYZE_IGNORE_WARN) + +update-dialyzer-nospec-baseline: dialyze-nospec + mv -f $(DIALYZE_NOSPEC_IGNORE_WARN).log $(DIALYZE_NOSPEC_IGNORE_WARN) + +dialyze-eunit: build-plt clean eunit-compile + @echo "dialyzing .eunit w/spec: $(RELPKG) ..." + $(dialyzer-eunit) | grep -v '^ *$$' | tee $(DIALYZE_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_IGNORE_WARN) + +dialyze-eunit-nospec: build-plt clean eunit-compile + @echo "dialyzing .eunit w/o spec: $(RELPKG) ..." + $(REBAR) eunit-compile + $(dialyzer-eunit-nospec) | grep -v '^ *$$' | tee $(DIALYZE_NOSPEC_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_NOSPEC_IGNORE_WARN) + +dialyze-eqc: build-plt clean eqc-compile + @echo "dialyzing .eqc w/spec: $(RELPKG) ..." + $(dialyzer-eunit) | grep -v '^ *$$' | tee $(DIALYZE_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_IGNORE_WARN) + +dialyze-eqc-nospec: build-plt clean eqc-compile + @echo "dialyzing .eqc w/o spec: $(RELPKG) ..." + $(REBAR) eqc-compile + $(dialyzer-eunit-nospec) | grep -v '^ *$$' | tee $(DIALYZE_NOSPEC_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_NOSPEC_IGNORE_WARN) + +dialyze-proper: build-plt clean proper-compile + @echo "dialyzing .proper w/spec: $(RELPKG) ..." + $(dialyzer-eunit) | grep -v '^ *$$' | tee $(DIALYZE_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_IGNORE_WARN) + +dialyze-proper-nospec: build-plt clean proper-compile + @echo "dialyzing .proper w/o spec: $(RELPKG) ..." + $(REBAR) proper-compile + $(dialyzer-eunit-nospec) | grep -v '^ *$$' | tee $(DIALYZE_NOSPEC_IGNORE_WARN).log | fgrep -v -f $(DIALYZE_NOSPEC_IGNORE_WARN) + +ctags: + find ./lib -name "*.[he]rl" -print | fgrep -v .eunit | ctags - + find ./lib -name "*.app.src" -print | fgrep -v .eunit | ctags -a - + find ./lib -name "*.config" -print | fgrep -v .eunit | ctags -a - + find ./lib -name "*.[ch]" -print | fgrep -v .eunit | ctags -a - + find ./lib -name "*.cc" -print | fgrep -v .eunit | ctags -a - + +etags: + find ./lib -name "*.[he]rl" -print | fgrep -v .eunit | etags - + find ./lib -name "*.app.src" -print | fgrep -v .eunit | etags -a - + find ./lib -name "*.config" -print | fgrep -v .eunit | etags -a - + find ./lib -name "*.[ch]" -print | fgrep -v .eunit | etags -a - + find ./lib -name "*.cc" -print | fgrep -v .eunit | etags -a - + +clean: + @echo "cleaning: $(RELPKG) ..." + $(REBAR) clean + +realclean: clean + @echo "realcleaning: $(RELPKG) ..." + rm -f $(PLT) TAGS + +distclean: + @echo "distcleaning: $(RELPKG) ..." + repo forall -v -c 'git clean -fdx --exclude=lib/' + +$(PLT): + @echo "building: $(PLT) ..." + dialyzer -q --build_plt --output_plt $(PLT) --apps \ + asn1 \ + compiler \ + crypto \ + dialyzer \ + diameter \ + edoc \ + erts \ + et \ + eunit \ + gs \ + hipe \ + inets \ + kernel \ + mnesia \ + observer \ + parsetools \ + public_key \ + runtime_tools \ + sasl \ + ssl \ + stdlib \ + syntax_tools \ + tools \ + webtool \ + xmerl + +# rm -rf rebar rebar.git +# . ~/.kerl/installations/r13b04/activate +# make -f rebar.mk rebar +# git commit -m "Update rebar (`$(REBAR) -V | cut -d ' ' -f 8`)" rebar +rebar: rebar.git + (cd $(CURDIR)/rebar.git && make clean && make && cp -f rebar ..) + $(REBAR) -V + +rebar.git: + rm -rf $(CURDIR)/rebar + git clone git://github.com/norton/rebar.git rebar.git + +otp: otp.git + make -C $(CURDIR)/otp.git install + +otp.git: + rm -rf $(CURDIR)/otp + mkdir -p $(CURDIR)/otp + git clone git://github.com/erlang/otp.git otp.git + (cd $(CURDIR)/otp.git && \ + git co OTP_R15B01 && \ + ./otp_build autoconf && \ + ./configure \ + --disable-hipe \ + --enable-debug \ + --enable-kernel-poll \ + --enable-threads \ + --enable-dynamic-ssl-lib \ + --enable-shared-zlib \ + --enable-smp-support \ + $(otp_configure_flags) \ + --prefix=$(CURDIR)/otp) + make -C $(CURDIR)/otp.git + +otp-debug: otp.git + env ERL_TOP=$(CURDIR)/otp.git make -C otp.git/erts/emulator debug FLAVOR=smp + +otp-valgrind: otp.git + env ERL_TOP=$(CURDIR)/otp.git make -C otp.git/erts/emulator valgrind FLAVOR=smp + +cerl-debug: otp.git + env ERL_TOP=$(CURDIR)/otp.git otp.git/bin/cerl -debug + +cerl-valgrind: otp.git + env ERL_TOP=$(CURDIR)/otp.git otp.git/bin/cerl -valgrind + +## See https://github.com/erlang/otp/wiki/Running-tests for details +otp_make_release_tests: otp.git + rm -rf otp.git/release/tests + env ERL_TOP=$(CURDIR)/otp.git ERL_LIBS=$(CURDIR)/otp.git/lib \ + make -C otp.git release_tests + +otp_run_release_tests: otp_make_release_tests + @echo "" + @echo "** Warning killing all local beam, beam.smp, and epmd programs **" + @echo "" + sleep 10 + killall -q -9 beam || true + killall -q -9 beam.smp || true + killall -q -9 epmd || true + @echo "" + @echo "** Open '$(CURDIR)/otp.git/release/tests/test_server/index.html' in your browser**" + @echo "" + sleep 10 + (cd $(CURDIR)/otp.git/release/tests/test_server && \ + env ERL_TOP=$(CURDIR)/otp.git ERL_LIBS=$(CURDIR)/otp.git/lib \ + $(CURDIR)/otp.git/bin/erl \ + -s ts install \ + -s ts run \ + -s erlang halt) diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..dcf7768 --- /dev/null +++ b/rebar.config @@ -0,0 +1,31 @@ +{require_otp_vsn, "R1[45]"}. +{cover_enabled, true}. + +{clean_files, ["*.eunit", "ebin/*.beam"]}. +{eunit_opts, [verbose, {report, {eunit_surefire, [{dir, "."}]}}]}. + +{deps, [ {edown, "0.3.*", {git, "git://github.com/esl/edown.git", {branch, "master"}}} + , {asciiedoc, "0.1.*", {git, "git://github.com/norton/asciiedoc.git", {branch, "master"}}} + , {triq, ".*", {git, "git://github.com/krestenkrab/triq.git", {branch, "master"}}} + ]}. + +{erl_opts, [%{d,'TRIQ',true}, %% define 'PROPER' instead if you must... + {parse_transform, lager_transform}, + fail_on_warning, + warn_unused_vars, + warn_export_all, + warn_shadow_vars, + warn_unused_import, + warn_unused_function, + warn_bif_clash, + warn_unused_record, + warn_deprecated_function, + warn_obsolete_guard, + strict_validation, + warn_export_vars, + warn_exported_vars, + warn_missing_spec, + warn_untyped_record, + debug_info]}. + +{xref_checks, [undefined_function_calls]}. diff --git a/bloom.erl b/src/bloom.erl similarity index 73% rename from bloom.erl rename to src/bloom.erl index e03546b..f7d8036 100644 --- a/bloom.erl +++ b/src/bloom.erl @@ -2,10 +2,19 @@ %% @reference [http://en.wikipedia.org/wiki/Bloom_filter] -module(bloom). --export([new/1, new/2, is_bloom/1, is_element/2, add_element/2]). +-export([new/1, new/2, is_bloom/1, is_element/2, add_element/2, clear/1, count/1]). -import(math, [log/1, pow/2]). -import(erlang, [phash2/2]). +-ifdef(TEST). +-ifdef(EQC). +-include_lib("eqc/include/eqc.hrl"). +-define(QC_OUT(P), + eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). +-endif. +-include_lib("eunit/include/eunit.hrl"). +-endif. + -record(bloom, { m = 0, % The size of the bitmap in bits. bitmap = <<>>, % The bitmap. @@ -14,7 +23,7 @@ keys = 0 % The current number of keys. }). -%% @spec new(capacity) -> bloom() +%% @spec new(capacity) -> bloom(). %% @equiv new(capacity, 0.001) new(N) -> new(N, 0.001). @@ -25,6 +34,21 @@ new(N, E) when N > 0, is_float(E), E > 0, E =< 1 -> {M, K} = calc_least_bits(N, E), #bloom{m=M, bitmap = <<0:((M+7) div 8 * 8)>>, k=K, n=N}. +%% @spec clear(bloom()) -> bloom(). +%% @doc Creates a new empty Bloom filter from an existing one. +clear(#bloom{#bitmap=Bitmap} = B) -> + B#bloom{<<0:bit_size(Bitmap)>>, n=0}. + +%% @spec count(bloom()) -> unsigned(). +%% @doc Returns the number of elements encoded into this Bloom filter. +count(#bloom{#keys=N}) -> + N. + +%% @spec filter_size(bloom()) -> unsigned(). +%% @doc Returns the number of bits used in this Bloom filter. +filter_size(#bloom{#bitmap=Bitmap}) -> + bit_size(Bitmap). + %% @spec is_bloom(bloom()) -> bool() %% @doc Determines if the given argument is a bloom record. is_bloom(#bloom{}) -> true; @@ -61,6 +85,15 @@ set_bits(Bin, [Idx | Idxs]) -> Byte0 = Byte bor Mask, set_bits(<
>, Idxs).
 
+%% set2(N, Bin) ->
+%%     <> = Bin,
+%%     <>.
+
+%% a(N, B) ->
+%%     fun (<>) ->
+%%             <>
+%%     end(B).
+
 % Find the optimal bitmap size and number of hashes.
 calc_least_bits(N, E) -> calc_least_bits(N, E, 1, 0, 0).
 calc_least_bits(N, E, K, MinM, BestK) ->
diff --git a/src/ebloom.erl b/src/ebloom.erl
new file mode 100644
index 0000000..ce60a4b
--- /dev/null
+++ b/src/ebloom.erl
@@ -0,0 +1,87 @@
+%% -------------------------------------------------------------------
+%%
+%% Copyright (c) 2010 Basho Technologies, Inc.  All Rights Reserved.
+%%
+%% This file is provided to you under the Apache License,
+%% Version 2.0 (the "License"); you may not use this file
+%% except in compliance with the License.  You may obtain
+%% a copy of the License at
+%%
+%%   http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied.  See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+%% -------------------------------------------------------------------
+
+%% This module in bloomerl serves to provide drop-in compatibility with
+%% the ebloom module.
+
+-module(ebloom).
+-author('Dave Smith ').
+
+-export([new/3,
+         insert/2,
+         contains/2,
+         clear/1,
+         size/1,
+         elements/1,
+         effective_fpp/1,
+         intersect/2,
+         union/2,
+         difference/2,
+         serialize/1,
+         deserialize/1]).
+
+
+-spec new(integer(), float(), integer()) -> {ok, reference()}.
+new(Count, FalseProb, _Seed) ->
+    bloom:new(Count, FalseProb).
+
+-spec insert(reference(), binary()) -> ok.
+insert(Ref, Bin) ->
+    bloom:add_element(Key, Ref).
+
+-spec contains(reference(), binary()) -> true | false.
+contains(Ref, Bin) ->
+    is_element(Bin, Ref).
+
+-spec clear(reference()) -> ok.
+clear(Ref) ->
+    bloom:clear(Ref).
+
+-spec size(reference()) -> integer().
+size(Ref) ->
+    bloom:filter_size(Ref).
+
+-spec elements(reference()) -> integer().
+elements(Ref) ->
+    bloom:count(Ref).
+
+-spec effective_fpp(reference()) -> float().
+effective_fpp(_Ref) ->
+    raise not_yet_implemented.
+
+-spec intersect(reference(), reference()) -> ok.
+intersect(_Ref, _OtherRef) ->
+    raise not_yet_implemented.
+
+-spec union(reference(), reference()) -> ok.
+union(_Ref, _OtherRef) ->
+    raise not_yet_implemented.
+
+-spec difference(reference(), reference()) -> ok.
+difference(_Ref, _OtherRef) ->
+    raise not_yet_implemented.
+
+-spec serialize(reference()) -> binary().
+serialize(Ref) ->
+    erlang:term_to_binary(Ref).
+
+-spec deserialize(binary()) -> {ok, reference()}.
+deserialize(Bin) ->
+    erlang:binary_to_term(Bin).
diff --git a/test/bloom_SUITE.erl b/test/bloom_SUITE.erl
new file mode 100644
index 0000000..dee3c2d
--- /dev/null
+++ b/test/bloom_SUITE.erl
@@ -0,0 +1,7 @@
+
+basic_test() ->
+    {ok, Ref} = new(5, 0.01, 123),
+    0 = elements(Ref),
+    insert(Ref, <<"abcdef">>),
+    true = contains(Ref, <<"abcdef">>),
+    false = contains(Ref, <<"zzzzzz">>).
diff --git a/test/ebloom_SUITE.erl b/test/ebloom_SUITE.erl
new file mode 100644
index 0000000..dab534c
--- /dev/null
+++ b/test/ebloom_SUITE.erl
@@ -0,0 +1,38 @@
+
+basic_test() ->
+    {ok, Ref} = new(5, 0.01, 123),
+    0 = elements(Ref),
+    insert(Ref, <<"abcdef">>),
+    true = contains(Ref, <<"abcdef">>),
+    false = contains(Ref, <<"zzzzzz">>).
+
+%% union_test() ->
+%%     {ok, Ref} = new(5, 0.01, 123),
+%%     {ok, Ref2} = new(5, 0.01, 123),
+%%     insert(Ref, <<"abcdef">>),
+%%     false = contains(Ref2, <<"abcdef">>),
+%%     union(Ref2, Ref),
+%%     true = contains(Ref2, <<"abcdef">>).
+
+serialize_test() ->
+    {ok, Ref} = new(5, 0.01, 123),
+    {ok, Ref2} = new(5, 0.01, 123),
+    Bin = serialize(Ref),
+    Bin2 = serialize(Ref2),
+    true = (Bin =:= Bin2),
+    insert(Ref, <<"abcdef">>),
+    Bin3 = serialize(Ref),
+    {ok, Ref3} = deserialize(Bin3),
+    true = contains(Ref3, <<"abcdef">>),
+    false = contains(Ref3, <<"rstuvw">>).
+
+clear_test() ->
+    {ok, Ref} = new(5, 0.01, 123),
+    0 = elements(Ref),
+    insert(Ref, <<"1">>),
+    insert(Ref, <<"2">>),
+    insert(Ref, <<"3">>),
+    3 = elements(Ref),
+    clear(Ref),
+    0 = elements(Ref),
+    false = contains(Ref, <<"1">>).