package common import ( "io" "math" "math/rand" "strings" . "github.com/tendermint/tendermint/binary" ) // Not goroutine safe type BitArray []uint64 func NewBitArray(length uint) BitArray { return BitArray(make([]uint64, (length+63)/64)) } func ReadBitArray(r io.Reader, n *int64, err *error) BitArray { lengthTotal := ReadUInt32(r, n, err) lengthWritten := ReadUInt32(r, n, err) if *err != nil { return nil } buf := make([]uint64, int(lengthTotal)) for i := uint32(0); i < lengthWritten; i++ { buf[i] = ReadUInt64(r, n, err) if err != nil { return nil } } return BitArray(buf) } func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) { // Count the last element > 0. lastNonzeroIndex := -1 for i, elem := range bA { if elem > 0 { lastNonzeroIndex = i } } WriteUInt32(w, uint32(len(bA)), &n, &err) WriteUInt32(w, uint32(lastNonzeroIndex+1), &n, &err) for i, elem := range bA { if i > lastNonzeroIndex { break } WriteUInt64(w, elem, &n, &err) } return } func (bA BitArray) GetIndex(i uint) bool { return bA[i/64]&uint64(1<<(i%64)) > 0 } func (bA BitArray) SetIndex(i uint, v bool) { if v { bA[i/64] |= uint64(1 << (i % 64)) } else { bA[i/64] &= ^uint64(1 << (i % 64)) } } func (bA BitArray) Copy() BitArray { c := make([]uint64, len(bA)) copy(c, bA) return BitArray(c) } func (bA BitArray) Or(o BitArray) BitArray { c := bA.Copy() for i, _ := range c { c[i] = o[i] | c[i] } return c } func (bA BitArray) And(o BitArray) BitArray { c := bA.Copy() for i, _ := range c { c[i] = o[i] & c[i] } return c } func (bA BitArray) Not() BitArray { c := bA.Copy() for i, _ := range c { c[i] = ^c[i] } return c } func (bA BitArray) Sub(o BitArray) BitArray { return bA.And(o.Not()) } // NOTE: returns counts or a longer int slice as necessary. func (bA BitArray) AddToCounts(counts []int) []int { for bytei := 0; bytei < len(bA); bytei++ { for biti := 0; biti < 64; biti++ { if (bA[bytei] & (1 << uint(biti))) == 0 { continue } index := 64*bytei + biti if len(counts) <= index { counts = append(counts, make([]int, (index-len(counts)+1))...) } counts[index]++ } } return counts } func (bA BitArray) PickRandom() (int, bool) { randStart := rand.Intn(len(bA)) for i := 0; i < len(bA); i++ { bytei := ((i + randStart) % len(bA)) if bA[bytei] > 0 { randBitStart := rand.Intn(64) for j := 0; j < 64; j++ { biti := ((j + randBitStart) % 64) //fmt.Printf("%X %v %v %v\n", iHas, j, biti, randBitStart) if (bA[bytei] & (1 << uint(biti))) > 0 { return 64*int(bytei) + int(biti), true } } panic("should not happen") } } return 0, false } // Pick an index from this BitArray that is 1 && whose count is lowest. func (bA BitArray) PickRarest(counts []int) (rarest int, ok bool) { smallestCount := math.MaxInt32 for bytei := 0; bytei < len(bA); bytei++ { if bA[bytei] > 0 { for biti := 0; biti < 64; biti++ { if (bA[bytei] & (1 << uint(biti))) == 0 { continue } index := 64*bytei + biti if counts[index] < smallestCount { smallestCount = counts[index] rarest = index ok = true } } panic("should not happen") } } return } func (bA BitArray) String() string { return bA.StringWithIndent("") } func (bA BitArray) StringWithIndent(indent string) string { lines := []string{} bits := "" for i := 0; i < len(bA)*64; i++ { if bA.GetIndex(uint(i)) { bits += "X" } else { bits += "_" } if i%100 == 99 { lines = append(lines, bits) bits = "" } if i%10 == 9 { bits += " " } if i%50 == 49 { bits += " " } } if len(bits) > 0 { lines = append(lines, bits) } return strings.Join(lines, indent) }