coffee/bloomfilter.coffee
JsonBloomfilter = (options = {}) ->
@options =
size: 100,
hashes: 4,
seed: (new Date().getTime() / 1000),
bits: null
items = delete options["items"]
@options[key] = value for key, value of options
@bits = new JsonBloomfilter.BitArray(@options["size"], @options["bits"])
@add(items) if items
this
JsonBloomfilter.build = (capacity_or_items, error_rate) ->
capacity = JsonBloomfilter.capacity_for(capacity_or_items)
items = JsonBloomfilter.items_for(capacity_or_items)
throw new Error("Capacity needs to be a positive integer") if capacity <= 0
new JsonBloomfilter({size: JsonBloomfilter.size_for(capacity, error_rate), hashes: JsonBloomfilter.hashes_for(capacity, error_rate), items: items})
JsonBloomfilter.capacity_for = (capacity_or_items) ->
if capacity_or_items instanceof Array
capacity_or_items.length
else
capacity_or_items
JsonBloomfilter.items_for = (capacity_or_items) ->
if capacity_or_items instanceof Array
capacity_or_items
else
null
JsonBloomfilter.size_for = (capacity, error_rate) ->
Math.ceil(capacity * Math.log(error_rate) / Math.log(1.0 / Math.pow(2,Math.log(2))))
JsonBloomfilter.hashes_for = (capacity, error_rate) ->
Math.round(Math.log(2) * @size_for(capacity, error_rate) / capacity)
JsonBloomfilter.prototype.add = (keys) ->
for key in [].concat(keys)
@bits.add(index) for index in @indexesFor(key)
return
JsonBloomfilter.prototype.test = (keys) ->
for key in [].concat(keys)
for index in @indexesFor(key)
return false if @bits.get(index) == 0
true
JsonBloomfilter.prototype.clear = ->
@bits = new JsonBloomfilter.BitArray(@options["size"])
return
JsonBloomfilter.prototype.toHash = ->
hash = {}
hash[key] = value for key, value of @options
hash["bits"] = @bits.field
hash
JsonBloomfilter.prototype.toJson = ->
JSON.stringify @toHash()
JsonBloomfilter.prototype.indexesFor = (key) ->
indexes = []
for index in [0..@options["hashes"]-1]
indexes.push (JsonBloomfilter.Zlib.crc32("#{key}:#{index+@options["seed"]}") % @options["size"])
indexes