Skip to content

Commit

Permalink
Now with inplace operations
Browse files Browse the repository at this point in the history
  • Loading branch information
lemire committed Feb 11, 2014
1 parent a0f359b commit dcd7017
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 29 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Example use:
if b.Test(1000) {
b.Clear(1000)
}
for i := b.NextSet(int64(0)); i >= 0; i = b.NextSet(i + 1) {
for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {
frmt.Println("The following bit is set:",i);
}
if B.Intersection(bitset.New(100).Set(10)).Count() > 1 {
Expand Down
148 changes: 122 additions & 26 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ import (
"math"
)



/////////////
// Design issue: I think that a slice/array in Go has a length of type int
// as per the spec http://golang.org/ref/spec#Length_and_capacity
// yet this code assumes that the length is uint. I think that this is wrong
//////////


// Word size of a bit set
const wordSize = uint(64)

Expand All @@ -71,11 +80,12 @@ type BitSetError string
// fixup b.set to be non-nil and return the field value
func (b *BitSet) safeSet() []uint64 {
if b.set == nil {
b.set = make([]uint64, wordsNeeded(0))
b.set = make([]uint64, wordsNeeded(0))
}
return b.set
}

// Daniel: I think this should return an int since this is the type used for array lengths in Go
func wordsNeeded(i uint) uint {
if i > (math.MaxUint64 - wordSize + 1 ) { // safer?
// if i == math.MaxUint64 {
Expand Down Expand Up @@ -156,8 +166,8 @@ func (b *BitSet) Flip(i uint) *BitSet {
}

// return the next bit set from the specified index, including possibly the current index
// returns -1 if none is found
// inspired by the Java API: for i:=int64(0); i>=0; i = NextSet(i) {...}
// along with an error code (true = valid, false = no set bit found)
// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...}
func (b *BitSet) NextSet(i uint) (uint,bool) {
x := i >> log2WordSize
if x >= b.length {
Expand All @@ -169,7 +179,7 @@ func (b *BitSet) NextSet(i uint) (uint,bool) {
return i + trailingZeroes64(w),true
}
x = x + 1
for x < wordsNeeded(b.length) {
for x < uint(len(b.set)) {
if b.set[x] != 0 {
return x * wordSize + trailingZeroes64(b.set[x]),true
}
Expand All @@ -189,6 +199,7 @@ func (b *BitSet) ClearAll() *BitSet {
return b
}

// Daniel: should return an int
// Query words used in a bit set
func (b *BitSet) wordCount() uint {
return wordsNeeded(b.length)
Expand All @@ -197,7 +208,9 @@ func (b *BitSet) wordCount() uint {
// Clone this BitSet
func (b *BitSet) Clone() *BitSet {
c := New(b.length)
copy(c.set, b.safeSet())
if b.set != nil {// Clone should not modify current object
copy(c.set, b.set)
}
return c
}

Expand All @@ -208,7 +221,9 @@ func (b *BitSet) Copy(c *BitSet) (count uint) {
if c == nil {
return
}
copy(c.set, b.safeSet())
if b.set != nil {// Copy should not modify current object
copy(c.set, b.set)
}
count = c.length
if b.length < c.length {
count = b.length
Expand Down Expand Up @@ -292,7 +307,11 @@ func (b *BitSet) Equal(c *BitSet) bool {
if b.length != c.length {
return false
}
for p, v := range b.safeSet() {
if b.length == 0 { // if they have both length == 0, then could have nil set
return true
}
// testing for equality shoud not transform the bitset (no call to safeSet)
for p, v := range b.set {
if c.set[p] != v {
return false
}
Expand All @@ -312,16 +331,32 @@ func (b *BitSet) Difference(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
result = b.Clone() // clone b (in case b is bigger than compare)
szl := compare.wordCount()
for i, word := range b.safeSet() {
if uint(i) >= szl {
break
}
result.set[i] = word &^ compare.set[i]
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l ; i++ {
result.set[i] = b.set[i] &^ compare.set[i]
}
return
}

// Difference of base set and other set
// This is the BitSet equivalent of &^ (and not)
func (b *BitSet) InPlaceDifference(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l ; i++ {
b.set[i] &^= compare.set[i]
}
}



// Convenience function: return two bitsets ordered by
// increasing length. Note: neither can be nil
func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) {
Expand All @@ -340,29 +375,71 @@ func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) {
panicIfNull(compare)
b, compare = sortByLength(b, compare)
result = New(b.length)
for i, word := range b.safeSet() {
for i, word := range b.set {
result.set[i] = word & compare.set[i]
}
return
}

// Intersection of base set and other set
// This is the BitSet equivalent of & (and)
func (b *BitSet) InPlaceIntersection(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l ; i++ {
b.set[i] &= compare.set[i]
}
for i := l; i < len(b.set) ; i++ {
b.set[i] = 0
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
return
}



// Union of base set and other set
// This is the BitSet equivalent of | (or)
func (b *BitSet) Union(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
result = compare.Clone()
szl := compare.wordCount()
for i, word := range b.safeSet() {
if uint(i) >= szl {
break
}
for i, word := range b.set {
result.set[i] = word | compare.set[i]
}
return
}


// Union of base set and other set
// This is the BitSet equivalent of | (or)
func (b *BitSet) InPlaceUnion(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
for i := 0; i < l ; i++ {
b.set[i] |= compare.set[i]
}
if len(compare.set) > l {
for i := l; i < len(compare.set) ; i++ {
b.set[i] = compare.set[i]
}
}
}

// SymmetricDifference of base set and other set
// This is the BitSet equivalent of ^ (xor)
func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
Expand All @@ -371,16 +448,34 @@ func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
b, compare = sortByLength(b, compare)
// compare is bigger, so clone it
result = compare.Clone()
szl := b.wordCount()
for i, word := range b.safeSet() {
if uint(i) >= szl {
break
}
for i, word := range b.set {
result.set[i] = word ^ compare.set[i]
}
return
}

// SymmetricDifference of base set and other set
// This is the BitSet equivalent of ^ (xor)
func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
for i := 0; i < l ; i++ {
b.set[i] ^= compare.set[i]
}
if len(compare.set) > l {
for i := l; i < len(compare.set) ; i++ {
b.set[i] = compare.set[i]
}
}
}

// Is the length an exact multiple of word sizes?
func (b *BitSet) isEven() bool {
return b.length%wordSize == 0
Expand All @@ -397,7 +492,7 @@ func (b *BitSet) cleanLastWord() {
func (b *BitSet) Complement() (result *BitSet) {
panicIfNull(b)
result = New(b.length)
for i, word := range b.safeSet() {
for i, word := range b.set {
result.set[i] = ^word
}
result.cleanLastWord()
Expand Down Expand Up @@ -431,9 +526,10 @@ func (b *BitSet) Any() bool {
}

// Dump as bits
// if the bitset is empty, one word is automatically allocated
func (b *BitSet) DumpAsBits() string {
buffer := bytes.NewBufferString("")
b.safeSet()
b.safeSet() // it is a bit odd that dumping as bits should modify the bitset!
i := int(wordsNeeded(b.length) - 1)
for ; i >= 0; i-- {
fmt.Fprintf(buffer, "%064b.", b.set[i])
Expand Down
Loading

0 comments on commit dcd7017

Please sign in to comment.