Merge pull request #35 from benbjohnson/roaring-uint64

Support uint64 in roaring bitmaps
This commit is contained in:
tgruben 2016-01-07 16:22:42 -06:00
commit b55b4ac89b
3 changed files with 174 additions and 137 deletions

View file

@ -201,8 +201,8 @@ func (f *Fragment) bitmap(bitmapID uint64) *Bitmap {
// Read bitmap from storage.
bm := NewBitmap()
f.storage.ForEachRange(uint32(bitmapID)*SliceWidth, uint32(bitmapID+1)*SliceWidth, func(i uint32) {
profileID := (f.slice * SliceWidth) + (uint64(i) % SliceWidth)
f.storage.ForEachRange(bitmapID*SliceWidth, (bitmapID+1)*SliceWidth, func(i uint64) {
profileID := (f.slice * SliceWidth) + (i % SliceWidth)
bm.setBit(profileID)
})
@ -292,14 +292,14 @@ func (f *Fragment) ClearBit(bitmapID, profileID uint64) error {
}
// pos translates the bitmap ID and profile ID into a position in the storage bitmap.
func (f *Fragment) pos(bitmapID, profileID uint64) (uint32, error) {
func (f *Fragment) pos(bitmapID, profileID uint64) (uint64, error) {
// Return an error if the profile ID is out of the range of the fragment's slice.
minProfileID := f.slice * SliceWidth
if profileID < minProfileID || profileID >= minProfileID+SliceWidth {
return 0, errors.New("profile out of bounds")
}
return uint32((bitmapID * SliceWidth) + (profileID % SliceWidth)), nil
return (bitmapID * SliceWidth) + (profileID % SliceWidth), nil
}
func (f *Fragment) TopN(src *Bitmap, n int, categories []uint64) []Pair {

View file

@ -23,7 +23,7 @@ const (
// Bitmap represents a roaring bitmap.
type Bitmap struct {
keys []uint16 // keys for containers
keys []uint64 // keys for containers
containers []*container // array and bitmap containers
// Number of operations written to the writer.
@ -34,14 +34,14 @@ type Bitmap struct {
}
// NewBitmap returns a Bitmap with an initial set of values.
func NewBitmap(a ...uint32) *Bitmap {
func NewBitmap(a ...uint64) *Bitmap {
b := &Bitmap{}
b.Add(a...)
return b
}
// Add adds values to the bitmap.
func (b *Bitmap) Add(a ...uint32) error {
func (b *Bitmap) Add(a ...uint64) error {
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeAdd, value: v}
@ -58,9 +58,9 @@ func (b *Bitmap) Add(a ...uint32) error {
return nil
}
func (b *Bitmap) add(v uint32) {
func (b *Bitmap) add(v uint64) {
hb := highbits(v)
i := search(b.keys, hb)
i := search64(b.keys, hb)
// If index is negative then there's not an exact match
// and a container needs to be added.
@ -73,7 +73,7 @@ func (b *Bitmap) add(v uint32) {
}
// Contains returns true if v is in the bitmap.
func (b *Bitmap) Contains(v uint32) bool {
func (b *Bitmap) Contains(v uint64) bool {
c := b.container(highbits(v))
if c == nil {
return false
@ -82,7 +82,7 @@ func (b *Bitmap) Contains(v uint32) bool {
}
// Remove removes values from the bitmap.
func (b *Bitmap) Remove(a ...uint32) error {
func (b *Bitmap) Remove(a ...uint64) error {
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeRemove, value: v}
@ -98,9 +98,9 @@ func (b *Bitmap) Remove(a ...uint32) error {
return nil
}
func (b *Bitmap) remove(v uint32) {
func (b *Bitmap) remove(v uint64) {
hb := highbits(v)
i := search(b.keys, hb)
i := search64(b.keys, hb)
if i < 0 {
return
}
@ -108,8 +108,8 @@ func (b *Bitmap) remove(v uint32) {
}
// Slice returns a slice of all integers in the bitmap.
func (b *Bitmap) Slice() []uint32 {
var a []uint32
func (b *Bitmap) Slice() []uint64 {
var a []uint64
itr := b.iterator()
for v := itr.Seek(0); !itr.EOF(); v = itr.Next() {
a = append(a, v)
@ -118,8 +118,8 @@ func (b *Bitmap) Slice() []uint32 {
}
// SliceRange returns a slice of integers between [start, end).
func (b *Bitmap) SliceRange(start, end uint32) []uint32 {
var a []uint32
func (b *Bitmap) SliceRange(start, end uint64) []uint64 {
var a []uint64
itr := b.iterator()
for v := itr.Seek(start); !itr.EOF() && v < end; v = itr.Next() {
a = append(a, v)
@ -128,7 +128,7 @@ func (b *Bitmap) SliceRange(start, end uint32) []uint32 {
}
// ForEach executes fn for each value in the bitmap.
func (b *Bitmap) ForEach(fn func(uint32)) {
func (b *Bitmap) ForEach(fn func(uint64)) {
itr := b.iterator()
for v := itr.Seek(0); !itr.EOF(); v = itr.Next() {
fn(v)
@ -136,7 +136,7 @@ func (b *Bitmap) ForEach(fn func(uint32)) {
}
// ForEachRange executes fn for each value in the bitmap between [start, end).
func (b *Bitmap) ForEachRange(start, end uint32, fn func(uint32)) {
func (b *Bitmap) ForEachRange(start, end uint64, fn func(uint64)) {
itr := b.iterator()
for v := itr.Seek(start); !itr.EOF() && v < end; v = itr.Next() {
fn(v)
@ -144,15 +144,15 @@ func (b *Bitmap) ForEachRange(start, end uint32, fn func(uint32)) {
}
// container returns the container with the given key.
func (b *Bitmap) container(key uint16) *container {
i := search(b.keys, key)
func (b *Bitmap) container(key uint64) *container {
i := search64(b.keys, key)
if i < 0 {
return nil
}
return b.containers[i]
}
func (b *Bitmap) insertAt(key uint16, c *container, i int) {
func (b *Bitmap) insertAt(key uint64, c *container, i int) {
b.keys = append(b.keys, 0)
copy(b.keys[i+1:], b.keys[i:])
b.keys[i] = key
@ -165,20 +165,20 @@ func (b *Bitmap) insertAt(key uint16, c *container, i int) {
// WriteTo writes b to w.
func (b *Bitmap) WriteTo(w io.Writer) (n int64, err error) {
// Build header before writing individual container blocks.
buf := make([]byte, headerSize+(len(b.keys)*(2+2+4)))
buf := make([]byte, headerSize+(len(b.keys)*(2+8+4)))
binary.LittleEndian.PutUint32(buf[0:], cookie)
binary.LittleEndian.PutUint32(buf[4:], uint32(len(b.keys)))
// Encode keys and cardinality.
for i, key := range b.keys {
binary.LittleEndian.PutUint16(buf[headerSize+i*4:], uint16(key))
binary.LittleEndian.PutUint16(buf[headerSize+i*4+2:], uint16(b.containers[i].n-1))
binary.LittleEndian.PutUint64(buf[headerSize+i*10:], uint64(key))
binary.LittleEndian.PutUint16(buf[headerSize+i*10+8:], uint16(b.containers[i].n-1))
}
// Write the offset for each container block.
offset := uint32(len(buf))
for i, c := range b.containers {
binary.LittleEndian.PutUint32(buf[headerSize+(len(b.keys)*4)+(i*4):], uint32(offset))
binary.LittleEndian.PutUint32(buf[headerSize+(len(b.keys)*10)+(i*4):], uint32(offset))
offset += uint32(c.size())
}
@ -214,21 +214,21 @@ func (b *Bitmap) UnmarshalBinary(data []byte) error {
// Read key count.
keyN := binary.LittleEndian.Uint32(data[4:8])
b.keys = make([]uint16, keyN)
b.keys = make([]uint64, keyN)
b.containers = make([]*container, keyN)
// Read container key headers.
for i, buf := 0, data[8:]; i < int(keyN); i, buf = i+1, buf[4:] {
b.keys[i] = binary.LittleEndian.Uint16(buf[0:2])
for i, buf := 0, data[8:]; i < int(keyN); i, buf = i+1, buf[10:] {
b.keys[i] = binary.LittleEndian.Uint64(buf[0:8])
b.containers[i] = &container{
n: int(binary.LittleEndian.Uint16(buf[2:4])) + 1,
n: int(binary.LittleEndian.Uint16(buf[8:10])) + 1,
mapped: true,
}
}
// Read container offsets and attach data.
opsOffset := 8 + int(keyN)*4
for i, buf := 0, data[8+int(keyN)*4:]; i < int(keyN); i, buf = i+1, buf[4:] {
opsOffset := 8 + int(keyN)*10
for i, buf := 0, data[opsOffset:]; i < int(keyN); i, buf = i+1, buf[4:] {
offset := binary.LittleEndian.Uint32(buf[0:4])
// Verify the offset is within the bounds of the input data.
@ -297,9 +297,9 @@ type iterator struct {
func (itr *iterator) EOF() bool { return itr.i >= len(itr.bitmap.containers) }
// Seek moves to the first value equal to or greater than v.
func (itr *iterator) Seek(seek uint32) uint32 {
func (itr *iterator) Seek(seek uint64) uint64 {
// Move to the correct container.
itr.i = search(itr.bitmap.keys, highbits(seek))
itr.i = search64(itr.bitmap.keys, highbits(seek))
if itr.i < 0 {
itr.i = -itr.i - 1
}
@ -311,7 +311,7 @@ func (itr *iterator) Seek(seek uint32) uint32 {
lb := lowbits(seek)
if c := itr.bitmap.containers[itr.i]; c.isArray() {
// Find index in the container.
itr.j = search(c.array, lb)
itr.j = search16(c.array, lb)
if itr.j < 0 {
itr.j = -itr.j - 1
}
@ -330,7 +330,7 @@ func (itr *iterator) Seek(seek uint32) uint32 {
}
// Next returns the next value in the bitmap.
func (itr *iterator) Next() uint32 {
func (itr *iterator) Next() uint64 {
// Iterate over containers until we find the next value or EOF.
for {
if itr.EOF() {
@ -373,13 +373,13 @@ func (itr *iterator) Next() uint32 {
}
// peek returns the current value.
func (itr *iterator) peek() uint32 {
func (itr *iterator) peek() uint64 {
key := itr.bitmap.keys[itr.i]
c := itr.bitmap.containers[itr.i]
if c.isArray() {
return uint32(key)<<16 | uint32(c.array[itr.j])
return uint64(key)<<16 | uint64(c.array[itr.j])
}
return uint32(key)<<16 | uint32(itr.j)
return uint64(key)<<16 | uint64(itr.j)
}
// The maximum size of array containers.
@ -447,7 +447,7 @@ func (c *container) arrayAdd(v uint16) {
}
// Find index of the integer in the container. Exit if it already exists.
i := search(c.array, v)
i := search16(c.array, v)
if i >= 0 {
return
}
@ -486,7 +486,7 @@ func (c *container) contains(v uint16) bool {
}
func (c *container) arrayContains(v uint16) bool {
return search(c.array, v) >= 0
return search16(c.array, v) >= 0
}
func (c *container) bitmapContains(v uint16) bool {
@ -503,7 +503,7 @@ func (c *container) remove(v uint16) {
}
func (c *container) arrayRemove(v uint16) {
i := search(c.array, v)
i := search16(c.array, v)
if i < 0 {
return
}
@ -590,7 +590,7 @@ const (
// op represents an operation on the bitmap.
type op struct {
typ opType
value uint32
value uint64
}
// apply executes the operation against a bitmap.
@ -611,12 +611,12 @@ func (op *op) WriteTo(w io.Writer) (n int64, err error) {
// Write type and value.
buf[0] = byte(op.typ)
binary.LittleEndian.PutUint32(buf[1:5], op.value)
binary.LittleEndian.PutUint64(buf[1:9], op.value)
// Add checksum at the end.
h := fnv.New32a()
h.Write(buf[0:5])
binary.LittleEndian.PutUint32(buf[5:9], h.Sum32())
h.Write(buf[0:9])
binary.LittleEndian.PutUint32(buf[9:13], h.Sum32())
// Write to writer.
nn, err := w.Write(buf)
@ -631,26 +631,63 @@ func (op *op) UnmarshalBinary(data []byte) error {
// Verify checksum.
h := fnv.New32a()
h.Write(data[0:5])
if chk := binary.LittleEndian.Uint32(data[5:9]); chk != h.Sum32() {
h.Write(data[0:9])
if chk := binary.LittleEndian.Uint32(data[9:13]); chk != h.Sum32() {
return fmt.Errorf("checksum mismatch: exp=%08x, got=%08x", h.Sum32(), chk)
}
// Read type and value.
op.typ = opType(data[0])
op.value = binary.LittleEndian.Uint32(data[1:5])
op.value = binary.LittleEndian.Uint64(data[1:9])
return nil
}
// size returns the encoded size of the op, in bytes.
func (*op) size() int { return 1 + 4 + 4 }
func (*op) size() int { return 1 + 8 + 4 }
func highbits(v uint32) uint16 { return uint16(v >> 16) }
func lowbits(v uint32) uint16 { return uint16(v & 0xFFFF) }
func highbits(v uint64) uint64 { return uint64(v >> 16) }
func lowbits(v uint64) uint16 { return uint16(v & 0xFFFF) }
// search returns the index of v in a.
func search(a []uint16, value uint16) int {
// search16 returns the index of v in a.
func search16(a []uint16, value uint16) int {
// Optimize for elements and the last element.
n := len(a)
if n == 0 {
return -1
} else if a[n-1] == value {
return n - 1
}
// Otherwise perform binary search for exact match.
lo, hi := 0, n-1
for lo+16 <= hi {
i := int(uint((lo + hi)) >> 1)
v := a[i]
if v < value {
lo = i + 1
} else if v > value {
hi = i - 1
} else {
return i
}
}
// If an exact match isn't found then return a negative index.
for ; lo <= hi; lo++ {
v := a[lo]
if v == value {
return lo
} else if v > value {
break
}
}
return -(lo + 1)
}
// search64 returns the index of v in a.
func search64(a []uint64, value uint64) int {
// Optimize for elements and the last element.
n := len(a)
if n == 0 {

View file

@ -13,17 +13,72 @@ import (
"github.com/umbel/pilosa/roaring"
)
// Ensure an empty bitmap returns false if checking for existence.
func TestBitmap_Contains_Empty(t *testing.T) {
if roaring.NewBitmap().Contains(1000) {
t.Fatal("expected false")
}
}
// Ensure an empty bitmap does nothing when removing an element.
func TestBitmap_Remove_Empty(t *testing.T) {
roaring.NewBitmap().Remove(1000)
}
// Ensure a bitmap can return a slice of values.
func TestBitmap_Slice(t *testing.T) {
if a := roaring.NewBitmap(1, 2, 3).Slice(); !reflect.DeepEqual(a, []uint64{1, 2, 3}) {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure an empty bitmap returns an empty slice of values.
func TestBitmap_Slice_Empty(t *testing.T) {
if a := roaring.NewBitmap().Slice(); len(a) != 0 {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure a bitmap can return a slice of values within a range.
func TestBitmap_SliceRange(t *testing.T) {
if a := roaring.NewBitmap(0, 1000001, 1000002, 1000003).SliceRange(1, 1000003); !reflect.DeepEqual(a, []uint64{1000001, 1000002}) {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure a bitmap can loop over a set of values.
func TestBitmap_ForEach(t *testing.T) {
var a []uint64
roaring.NewBitmap(1, 2, 3).ForEach(func(v uint64) {
a = append(a, v)
})
if !reflect.DeepEqual(a, []uint64{1, 2, 3}) {
t.Fatalf("unexpected values: %+v", a)
}
}
// Ensure a bitmap can loop over a set of values in a range.
func TestBitmap_ForEachRange(t *testing.T) {
var a []uint64
roaring.NewBitmap(1, 2, 3, 4).ForEachRange(2, 4, func(v uint64) {
a = append(a, v)
})
if !reflect.DeepEqual(a, []uint64{2, 3}) {
t.Fatalf("unexpected values: %+v", a)
}
}
func TestBitmap_Quick_Array1(t *testing.T) { testBitmapQuick(t, 1000, 1000, 2000) }
func TestBitmap_Quick_Array2(t *testing.T) { testBitmapQuick(t, 10000, 0, 1000) }
func TestBitmap_Quick_Bitmap1(t *testing.T) { testBitmapQuick(t, 10000, 0, 10000) }
func TestBitmap_Quick_Bitmap2(t *testing.T) { testBitmapQuick(t, 10000, 10000, 20000) }
func TestBitmap_Quick_LargeValue(t *testing.T) { testBitmapQuick(t, 10000, 0, math.MaxUint32) }
func TestBitmap_Quick_LargeValue(t *testing.T) { testBitmapQuick(t, 10000, 0, math.MaxInt64) }
// Ensure a bitmap can perform basic operations on randomly generated values.
func testBitmapQuick(t *testing.T, n int, min, max uint32) {
quick.Check(func(a []uint32) bool {
func testBitmapQuick(t *testing.T, n int, min, max uint64) {
quick.Check(func(a []uint64) bool {
bm := roaring.NewBitmap()
m := make(map[uint32]struct{})
m := make(map[uint64]struct{})
// Add values to the bitmap and set.
for _, v := range a {
@ -45,7 +100,7 @@ func testBitmapQuick(t *testing.T, n int, min, max uint32) {
}
// Verify slices are equal.
if got, exp := bm.Slice(), uint32SetSlice(m); !reflect.DeepEqual(got, exp) {
if got, exp := bm.Slice(), uint64SetSlice(m); !reflect.DeepEqual(got, exp) {
t.Fatalf("unexpected values:\n\ngot=%+v\n\nexp=%+v\n\n", got, exp)
}
@ -62,85 +117,30 @@ func testBitmapQuick(t *testing.T, n int, min, max uint32) {
return true
}, &quick.Config{
Values: func(values []reflect.Value, rand *rand.Rand) {
values[0] = reflect.ValueOf(GenerateUint32Slice(n, min, max, rand))
values[0] = reflect.ValueOf(GenerateUint64Slice(n, min, max, rand))
},
})
}
// Ensure an empty bitmap returns false if checking for existence.
func TestBitmap_Contains_Empty(t *testing.T) {
if roaring.NewBitmap().Contains(1000) {
t.Fatal("expected false")
}
}
// Ensure an empty bitmap does nothing when removing an element.
func TestBitmap_Remove_Empty(t *testing.T) {
roaring.NewBitmap().Remove(1000)
}
// Ensure a bitmap can return a slice of values.
func TestBitmap_Slice(t *testing.T) {
if a := roaring.NewBitmap(1, 2, 3).Slice(); !reflect.DeepEqual(a, []uint32{1, 2, 3}) {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure an empty bitmap returns an empty slice of values.
func TestBitmap_Slice_Empty(t *testing.T) {
if a := roaring.NewBitmap().Slice(); len(a) != 0 {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure a bitmap can return a slice of values within a range.
func TestBitmap_SliceRange(t *testing.T) {
if a := roaring.NewBitmap(0, 1000001, 1000002, 1000003).SliceRange(1, 1000003); !reflect.DeepEqual(a, []uint32{1000001, 1000002}) {
t.Fatalf("unexpected slice: %+v", a)
}
}
// Ensure a bitmap can loop over a set of values.
func TestBitmap_ForEach(t *testing.T) {
var a []uint32
roaring.NewBitmap(1, 2, 3).ForEach(func(v uint32) {
a = append(a, v)
})
if !reflect.DeepEqual(a, []uint32{1, 2, 3}) {
t.Fatalf("unexpected values: %+v", a)
}
}
// Ensure a bitmap can loop over a set of values in a range.
func TestBitmap_ForEachRange(t *testing.T) {
var a []uint32
roaring.NewBitmap(1, 2, 3, 4).ForEachRange(2, 4, func(v uint32) {
a = append(a, v)
})
if !reflect.DeepEqual(a, []uint32{2, 3}) {
t.Fatalf("unexpected values: %+v", a)
}
}
func TestBitmap_Marshal_Quick_Array1(t *testing.T) { testBitmapMarshalQuick(t, 1000, 1000, 2000) }
func TestBitmap_Marshal_Quick_Array2(t *testing.T) { testBitmapMarshalQuick(t, 10000, 0, 1000) }
func TestBitmap_Marshal_Quick_Bitmap1(t *testing.T) { testBitmapMarshalQuick(t, 10000, 0, 10000) }
func TestBitmap_Marshal_Quick_Bitmap2(t *testing.T) { testBitmapMarshalQuick(t, 10000, 10000, 20000) }
func TestBitmap_Marshal_Quick_LargeValue(t *testing.T) {
testBitmapMarshalQuick(t, 100, 0, math.MaxUint32)
testBitmapMarshalQuick(t, 100, 0, math.MaxInt64)
}
// Ensure a bitmap can be marshaled and unmarshaled.
func testBitmapMarshalQuick(t *testing.T, n int, min, max uint32) {
func testBitmapMarshalQuick(t *testing.T, n int, min, max uint64) {
if testing.Short() {
t.Skip("short")
}
quick.Check(func(a0, a1 []uint32) bool {
quick.Check(func(a0, a1 []uint64) bool {
// Create bitmap with initial values set.
bm := roaring.NewBitmap(a0...)
set := make(map[uint32]struct{})
set := make(map[uint64]struct{})
for _, v := range a0 {
set[v] = struct{}{}
}
@ -173,12 +173,12 @@ func testBitmapMarshalQuick(t *testing.T, n int, min, max uint32) {
}
// Verify the original bitmap has the correct set of values.
if exp, got := uint32SetSlice(set), bm.Slice(); !reflect.DeepEqual(exp, got) {
if exp, got := uint64SetSlice(set), bm.Slice(); !reflect.DeepEqual(exp, got) {
t.Fatalf("mismatch: %s\n\nexp=%+v\n\ngot=%+v\n\n", diff(exp, got), exp, got)
}
// Verify the bitmap loaded with the ops log has the correct set of values.
if exp, got := uint32SetSlice(set), bm2.Slice(); !reflect.DeepEqual(exp, got) {
if exp, got := uint64SetSlice(set), bm2.Slice(); !reflect.DeepEqual(exp, got) {
t.Fatalf("mismatch: %s\n\nexp=%+v\n\ngot=%+v\n\n", diff(exp, got), exp, got)
}
}
@ -186,39 +186,39 @@ func testBitmapMarshalQuick(t *testing.T, n int, min, max uint32) {
return true
}, &quick.Config{
Values: func(values []reflect.Value, rand *rand.Rand) {
values[0] = reflect.ValueOf(GenerateUint32Slice(n, min, max, rand))
values[1] = reflect.ValueOf(GenerateUint32Slice(100, min, max, rand))
values[0] = reflect.ValueOf(GenerateUint64Slice(n, min, max, rand))
values[1] = reflect.ValueOf(GenerateUint64Slice(100, min, max, rand))
},
})
}
// GenerateUint32Slice generates between [0, n) random uint32 numbers between min and max.
func GenerateUint32Slice(n int, min, max uint32, rand *rand.Rand) []uint32 {
a := make([]uint32, rand.Intn(n))
// GenerateUint64Slice generates between [0, n) random uint64 numbers between min and max.
func GenerateUint64Slice(n int, min, max uint64, rand *rand.Rand) []uint64 {
a := make([]uint64, rand.Intn(n))
for i := range a {
a[i] = min + uint32(rand.Intn(int(max-min)))
a[i] = min + uint64(rand.Int63n(int64(max-min)))
}
return a
}
// uint32SetSlice returns the values in a uint32 set.
func uint32SetSlice(m map[uint32]struct{}) []uint32 {
a := make([]uint32, 0, len(m))
// uint64SetSlice returns the values in a uint64 set.
func uint64SetSlice(m map[uint64]struct{}) []uint64 {
a := make([]uint64, 0, len(m))
for v := range m {
a = append(a, v)
}
sort.Sort(uint32Slice(a))
sort.Sort(uint64Slice(a))
return a
}
// uint32Slice represents a sortable slice of uint32 numbers.
type uint32Slice []uint32
// uint64Slice represents a sortable slice of uint64 numbers.
type uint64Slice []uint64
func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p uint32Slice) Len() int { return len(p) }
func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p uint64Slice) Len() int { return len(p) }
func (p uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
func diff(a, b []uint32) string {
func diff(a, b []uint64) string {
if len(a) != len(b) {
return fmt.Sprintf("len: %d != %d", len(a), len(b))
}