// The MIT License (MIT)
// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package z

import (
	
	
	
	
	
)

// helper
var mask = []uint8{1, 2, 4, 8, 16, 32, 64, 128}

func getSize( uint64) ( uint64,  uint64) {
	if  < uint64(512) {
		 = uint64(512)
	}
	 = uint64(1)
	for  <  {
		 <<= 1
		++
	}
	return , 
}

func calcSizeByWrongPositives(,  float64) (uint64, uint64) {
	 := -1 *  * math.Log() / math.Pow(float64(0.69314718056), 2)
	 := math.Ceil(float64(0.69314718056) *  / )
	return uint64(), uint64()
}

// NewBloomFilter returns a new bloomfilter.
func ( ...float64) ( *Bloom) {
	var ,  uint64
	if len() == 2 {
		if [1] < 1 {
			,  = calcSizeByWrongPositives([0], [1])
		} else {
			,  = uint64([0]), uint64([1])
		}
	} else {
		log.Fatal("usage: New(float64(number_of_entries), float64(number_of_hashlocations))" +
			" i.e. New(float64(1000), float64(3)) or New(float64(number_of_entries)," +
			" float64(number_of_hashlocations)) i.e. New(float64(1000), float64(0.03))")
	}
	,  := getSize()
	 = &Bloom{
		sizeExp: ,
		size:     - 1,
		setLocs: ,
		shift:   64 - ,
	}
	.Size()
	return 
}

// Bloom filter
type Bloom struct {
	bitset  []uint64
	ElemNum uint64
	sizeExp uint64
	size    uint64
	setLocs uint64
	shift   uint64
}

// <--- http://www.cse.yorku.ca/~oz/hash.html
// modified Berkeley DB Hash (32bit)
// hash is casted to l, h = 16bit fragments
// func (bl Bloom) absdbm(b *[]byte) (l, h uint64) {
// 	hash := uint64(len(*b))
// 	for _, c := range *b {
// 		hash = uint64(c) + (hash << 6) + (hash << bl.sizeExp) - hash
// 	}
// 	h = hash >> bl.shift
// 	l = hash << bl.shift >> bl.shift
// 	return l, h
// }

// Add adds hash of a key to the bloomfilter.
func ( *Bloom) ( uint64) {
	 :=  >> .shift
	 :=  << .shift >> .shift
	for  := uint64(0);  < .setLocs; ++ {
		.Set(( + *) & .size)
		.ElemNum++
	}
}

// Has checks if bit(s) for entry hash is/are set,
// returns true if the hash was added to the Bloom Filter.
func ( Bloom) ( uint64) bool {
	 :=  >> .shift
	 :=  << .shift >> .shift
	for  := uint64(0);  < .setLocs; ++ {
		if !.IsSet(( + *) & .size) {
			return false
		}
	}
	return true
}

// AddIfNotHas only Adds hash, if it's not present in the bloomfilter.
// Returns true if hash was added.
// Returns false if hash was already registered in the bloomfilter.
func ( *Bloom) ( uint64) bool {
	if .Has() {
		return false
	}
	.Add()
	return true
}

// TotalSize returns the total size of the bloom filter.
func ( *Bloom) () int {
	// The bl struct has 5 members and each one is 8 byte. The bitset is a
	// uint64 byte slice.
	return len(.bitset)*8 + 5*8
}

// Size makes Bloom filter with as bitset of size sz.
func ( *Bloom) ( uint64) {
	.bitset = make([]uint64, >>6)
}

// Clear resets the Bloom filter.
func ( *Bloom) () {
	for  := range .bitset {
		.bitset[] = 0
	}
}

// Set sets the bit[idx] of bitset.
func ( *Bloom) ( uint64) {
	 := unsafe.Pointer(uintptr(unsafe.Pointer(&.bitset[>>6])) + uintptr((%64)>>3))
	*(*uint8)() |= mask[%8]
}

// IsSet checks if bit[idx] of bitset is set, returns true/false.
func ( *Bloom) ( uint64) bool {
	 := unsafe.Pointer(uintptr(unsafe.Pointer(&.bitset[>>6])) + uintptr((%64)>>3))
	 := ((*(*uint8)()) >> ( % 8)) & 1
	return  == 1
}

// bloomJSONImExport
// Im/Export structure used by JSONMarshal / JSONUnmarshal
type bloomJSONImExport struct {
	FilterSet []byte
	SetLocs   uint64
}

// NewWithBoolset takes a []byte slice and number of locs per entry,
// returns the bloomfilter with a bitset populated according to the input []byte.
func newWithBoolset( *[]byte,  uint64) *Bloom {
	 := NewBloomFilter(float64(len(*)<<3), float64())
	for ,  := range * {
		*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&.bitset[0])) + uintptr())) = 
	}
	return 
}

// JSONUnmarshal takes JSON-Object (type bloomJSONImExport) as []bytes
// returns bloom32 / bloom64 object.
func ( []byte) (*Bloom, error) {
	 := bloomJSONImExport{}
	if  := json.Unmarshal(, &);  != nil {
		return nil, 
	}
	 := bytes.NewBuffer(.FilterSet)
	 := .Bytes()
	 := newWithBoolset(&, .SetLocs)
	return , nil
}

// JSONMarshal returns JSON-object (type bloomJSONImExport) as []byte.
func ( Bloom) () []byte {
	 := bloomJSONImExport{}
	.SetLocs = .setLocs
	.FilterSet = make([]byte, len(.bitset)<<3)
	for  := range .FilterSet {
		.FilterSet[] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&.bitset[0])) +
			uintptr()))
	}
	,  := json.Marshal()
	if  != nil {
		log.Fatal("json.Marshal failed: ", )
	}
	return 
}