func (c *CuckooFilter) components(data []byte) (uint, uint, []byte) {
	var (
		hash = c.computeHash(data)
		f    = hash[0:c.f]
		i1   = uint(binary.BigEndian.Uint32(hash))
		i2   = i1 ^ uint(binary.BigEndian.Uint32(c.computeHash(f)))
	)
	return i1, i2, f
}
components returns the two hash values used to index into the buckets and
the fingerprint for the given element.
package boom
import (
	"bytes"
	"encoding/binary"
	"errors"
	"hash"
	"hash/fnv"
	"math"
	"math/rand"
)
const maxNumKicks = 500
type bucket [][]byte
func (b bucket) contains(f []byte) bool {
	return b.indexOf(f) != -1
}
func (b bucket) indexOf(f []byte) int {
	for i, fingerprint := range b {
		if bytes.Equal(f, fingerprint) {
			return i
		}
	}
	return -1
}
func (b bucket) getEmptyEntry() (int, error) {
	for i, fingerprint := range b {
		if fingerprint == nil {
			return i, nil
		}
	}
	return -1, errors.New("full")
}
type CuckooFilter struct {
	buckets []bucket
	hash    hash.Hash32  
	m       uint         
	b       uint         
	f       uint         
	count   uint         
	n       uint         
}
func NewCuckooFilter(n uint, fpRate float64) *CuckooFilter {
	var (
		b       = uint(4)
		f       = calculateF(b, fpRate)
		m       = power2(n / uint(f) * 8)
		buckets = make([]bucket, m)
	)
	for i := uint(0); i < m; i++ {
		buckets[i] = make(bucket, b)
	}
	return &CuckooFilter{
		buckets: buckets,
		hash:    fnv.New32(),
		m:       m,
		b:       b,
		f:       uint(f),
		n:       n,
	}
}
func (c *CuckooFilter) Buckets() uint {
	return c.m
}
func (c *CuckooFilter) Capacity() uint {
	return c.n
}
func (c *CuckooFilter) Count() uint {
	return c.count
}
func (c *CuckooFilter) Test(data []byte) bool {
	i1, i2, f := c.components(data)
	return c.buckets[i1%c.m].contains(f) || c.buckets[i2%c.m].contains(f)
}
func (c *CuckooFilter) Add(data []byte) error {
	return c.add(c.components(data))
}
func (c *CuckooFilter) TestAndAdd(data []byte) (bool, error) {
	i1, i2, f := c.components(data)
	if c.buckets[i1%c.m].contains(f) || c.buckets[i2%c.m].contains(f) {
		return true, nil
	}
	return false, c.add(i1, i2, f)
}
func (c *CuckooFilter) TestAndRemove(data []byte) bool {
	i1, i2, f := c.components(data)
	b1 := c.buckets[i1%c.m]
	if idx := b1.indexOf(f); idx != -1 {
		b1[idx] = nil
		c.count--
		return true
	}
	b2 := c.buckets[i2%c.m]
	if idx := b2.indexOf(f); idx != -1 {
		b2[idx] = nil
		c.count--
		return true
	}
	return false
}
func (c *CuckooFilter) Reset() *CuckooFilter {
	buckets := make([]bucket, c.m)
	for i := uint(0); i < c.m; i++ {
		buckets[i] = make(bucket, c.b)
	}
	c.buckets = buckets
	c.count = 0
	return c
}
func (c *CuckooFilter) add(i1, i2 uint, f []byte) error {
	b1 := c.buckets[i1%c.m]
	if idx, err := b1.getEmptyEntry(); err == nil {
		b1[idx] = f
		c.count++
		return nil
	}
	b2 := c.buckets[i2%c.m]
	if idx, err := b2.getEmptyEntry(); err == nil {
		b2[idx] = f
		c.count++
		return nil
	}
	i := i1
	for n := 0; n < maxNumKicks; n++ {
		bucketIdx := i % c.m
		entryIdx := rand.Intn(int(c.b))
		f, c.buckets[bucketIdx][entryIdx] = c.buckets[bucketIdx][entryIdx], f
		i = i ^ uint(binary.BigEndian.Uint32(c.computeHash(f)))
		b := c.buckets[i%c.m]
		if idx, err := b.getEmptyEntry(); err == nil {
			b[idx] = f
			c.count++
			return nil
		}
	}
	return errors.New("full")
}