func WithKeyHash(keyHash *chainhash.Hash) *GCSBuilder {
	return WithKeyHashPNM(keyHash, DefaultP, 0, DefaultM)
}
WithKeyHash creates a GCSBuilder with key derived from the specified
chainhash.Hash. Probability is set to 20 (2^-20 collision probability).
Estimated filter size is set to zero, which means more reallocations are
done when building the filter.
package builder
import (
	"crypto/rand"
	"fmt"
	"math"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/txscript"
	"github.com/soteria-dag/soterd/wire"
	"github.com/soteria-dag/soterd/soterutil/gcs"
)
const (
	DefaultP = 19
	DefaultM uint64 = 784931
)
type GCSBuilder struct {
	p uint8
	m uint64
	key [gcs.KeySize]byte
	data map[string]struct{}
	err  error
}
func RandomKey() ([gcs.KeySize]byte, error) {
	var key [gcs.KeySize]byte
	randKey := make([]byte, gcs.KeySize)
	_, err := rand.Read(randKey)
	if err != nil {
		return key, err
	}
	copy(key[:], randKey[:])
	return key, nil
}
func DeriveKey(keyHash *chainhash.Hash) [gcs.KeySize]byte {
	var key [gcs.KeySize]byte
	copy(key[:], keyHash.CloneBytes()[:])
	return key
}
func (b *GCSBuilder) Key() ([gcs.KeySize]byte, error) {
	if b.err != nil {
		return [gcs.KeySize]byte{}, b.err
	}
	return b.key, nil
}
func (b *GCSBuilder) SetKey(key [gcs.KeySize]byte) *GCSBuilder {
	if b.err != nil {
		return b
	}
	copy(b.key[:], key[:])
	return b
}
func (b *GCSBuilder) SetKeyFromHash(keyHash *chainhash.Hash) *GCSBuilder {
	if b.err != nil {
		return b
	}
	return b.SetKey(DeriveKey(keyHash))
}
func (b *GCSBuilder) SetP(p uint8) *GCSBuilder {
	if b.err != nil {
		return b
	}
	if p > 32 {
		b.err = gcs.ErrPTooBig
		return b
	}
	b.p = p
	return b
}
func (b *GCSBuilder) SetM(m uint64) *GCSBuilder {
	if b.err != nil {
		return b
	}
	if m > uint64(math.MaxUint32) {
		b.err = gcs.ErrPTooBig
		return b
	}
	b.m = m
	return b
}
func (b *GCSBuilder) Preallocate(n uint32) *GCSBuilder {
	if b.err != nil {
		return b
	}
	if b.data == nil {
		b.data = make(map[string]struct{}, n)
	}
	return b
}
func (b *GCSBuilder) AddEntry(data []byte) *GCSBuilder {
	if b.err != nil {
		return b
	}
	b.data[string(data)] = struct{}{}
	return b
}
func (b *GCSBuilder) AddEntries(data [][]byte) *GCSBuilder {
	if b.err != nil {
		return b
	}
	for _, entry := range data {
		b.AddEntry(entry)
	}
	return b
}
func (b *GCSBuilder) AddHash(hash *chainhash.Hash) *GCSBuilder {
	if b.err != nil {
		return b
	}
	return b.AddEntry(hash.CloneBytes())
}
func (b *GCSBuilder) AddWitness(witness wire.TxWitness) *GCSBuilder {
	if b.err != nil {
		return b
	}
	return b.AddEntries(witness)
}
func (b *GCSBuilder) Build() (*gcs.Filter, error) {
	if b.err != nil {
		return nil, b.err
	}
	if b.p == 0 {
		return nil, fmt.Errorf("p value is not set, cannot build")
	}
	if b.m == 0 {
		return nil, fmt.Errorf("m value is not set, cannot build")
	}
	dataSlice := make([][]byte, 0, len(b.data))
	for item := range b.data {
		dataSlice = append(dataSlice, []byte(item))
	}
	return gcs.BuildGCSFilter(b.p, b.m, b.key, dataSlice)
}
func WithKeyPNM(key [gcs.KeySize]byte, p uint8, n uint32, m uint64) *GCSBuilder {
	b := GCSBuilder{}
	return b.SetKey(key).SetP(p).SetM(m).Preallocate(n)
}
func WithKeyPM(key [gcs.KeySize]byte, p uint8, m uint64) *GCSBuilder {
	return WithKeyPNM(key, p, 0, m)
}
func WithKey(key [gcs.KeySize]byte) *GCSBuilder {
	return WithKeyPNM(key, DefaultP, 0, DefaultM)
}
func WithKeyHashPNM(keyHash *chainhash.Hash, p uint8, n uint32,
	m uint64) *GCSBuilder {
	return WithKeyPNM(DeriveKey(keyHash), p, n, m)
}
func WithKeyHashPM(keyHash *chainhash.Hash, p uint8, m uint64) *GCSBuilder {
	return WithKeyHashPNM(keyHash, p, 0, m)
}