Skip to content

Commit

Permalink
ds: graph rewrite started
Browse files Browse the repository at this point in the history
  • Loading branch information
golightlyb committed May 28, 2024
1 parent 8a2caaa commit b1ec623
Show file tree
Hide file tree
Showing 33 changed files with 1,620 additions and 2,378 deletions.
65 changes: 58 additions & 7 deletions ds/bitseq/bitseq.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ type Store struct {
// buckets is the sequence of bits packed into a slice of uint64 values.
// Trailing zero bits are not necessarily backed by a real bucket.
buckets []uint64

// numTrue is the count of true bits
numTrue int
}

// CountTrue returns the number of true bits in the store.
func (s Store) CountTrue() int {
return s.numTrue
}

// String implements the stringer interface, and prints a bit sequence as '1'
// and '0' characters from left-to-right. Trailing zeroes are omitted.
func (s *Store) String() string {
func (s Store) String() string {
var buf strings.Builder
var trailingZeros = 0

Expand All @@ -51,6 +59,14 @@ func (s *Store) String() string {
return buf.String()
}

// Clear resets the sequence to zeroes.
func (s *Store) Clear() {
for i := 0; i < cap(s.buckets); i++ {
s.buckets[i] = 0
}
s.numTrue = 0
}

// Crop attempts to reclaim any surplus backing memory consumed by trailing
// zero bits.
func (s *Store) Crop() {
Expand All @@ -66,7 +82,7 @@ func (s *Store) Crop() {

// croppedBuckets returns a subslice of buckets. The subslice excludes any
// buckets that are solely trailing zeros.
func (s *Store) croppedBuckets() []uint64 {
func (s Store) croppedBuckets() []uint64 {
if s.buckets == nil { return nil }

end := len(s.buckets)
Expand All @@ -93,7 +109,7 @@ const magic = uint64(
(uint64('1') << 56))

// Write writes an opaque binary representation of the Store into w.
func(s *Store) Write(w io.Writer) error {
func(s Store) Write(w io.Writer) error {
var err error
var crc uint64

Expand Down Expand Up @@ -146,20 +162,22 @@ func (s *Store) Set(index int, bit bool) {
s.buckets = s.buckets[0:cap(s.buckets)]
}
if bit {
if 0 == (s.buckets[bucket] & (1 << offset)) { s.numTrue++ }
s.buckets[bucket] = s.buckets[bucket] | (1 << offset)
} else {
if 0 != (s.buckets[bucket] & (1 << offset)) { s.numTrue-- }
s.buckets[bucket] = s.buckets[bucket] & (^(1 << offset))
}
}

// Get looks up a bit at a given index, returning true iff it has been set.
// Panics if index is less than zero.
func (s *Store) Get(index int) bool {
func (s Store) Get(index int) bool {
if (index < 0) { panic(ErrRange) }
return s.getFromBucket(fromIndex(index))
}

func (s *Store) getFromBucket(bucket, offset int) bool {
func (s Store) getFromBucket(bucket, offset int) bool {
if bucket < len(s.buckets) {
return (s.buckets[bucket] & (1 << offset)) != 0
} else {
Expand All @@ -169,7 +187,7 @@ func (s *Store) getFromBucket(bucket, offset int) bool {

// NextFalse returns the index of the next false bit found after the given
// index. To start at the beginning, start with NextFalse(-1).
func (s *Store) NextFalse(after int) int {
func (s Store) NextFalse(after int) int {
buckets := len(s.buckets)
start := after + 1
bucket := start / 64
Expand Down Expand Up @@ -199,7 +217,7 @@ func (s *Store) NextFalse(after int) int {
// index. To start at the beginning, start with NextTrue(-1). If the second
// return value is false, then the search has finished, and the remaining
// sequence is an infinite sequence of false bits.
func (s *Store) NextTrue(after int) (int, bool) {
func (s Store) NextTrue(after int) (int, bool) {
buckets := len(s.buckets)
start := after + 1
bucket := start / 64
Expand Down Expand Up @@ -227,3 +245,36 @@ func (s *Store) NextTrue(after int) (int, bool) {

return -1, false
}

// PrevTrue returns the index of the previous true bit found before the given
// index. To start at the end, start with PrevTrue(-1). If the second
// return value is false, then the search has finished, and the remaining
// sequence prefix is either empty or all false bits.
func (s Store) PrevTrue(before int) (int, bool) {
if before <= 0 { return -1, false }
start := before - 1
bucket := start / 64

for i := bucket; i >= 0; i-- {
if s.buckets[i] == 0 { continue }

var offset, limit int
if i == bucket {
// First bucket - start search midway through. If the entire
// prefix is trailing zeros, we can skip early.
offset = start % 64
limit = bits.LeadingZeros64(s.buckets[i])
} else {
// Beginning of a subsequent bucket, skip zero prefix/suffixes.
offset = 64 - bits.TrailingZeros64(s.buckets[i])
limit = bits.LeadingZeros64(s.buckets[i])
}

for j := offset; j >= limit; j-- {
index := (i * 64) + j
if s.getFromBucket(i, j) { return index, true }
}
}

return -1, false
}
31 changes: 17 additions & 14 deletions ds/bitseq/bitseq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,42 @@ func TestStore_String(t *testing.T) {
const F = false
var s bitseq.Store
var args = []struct{
index int
value bool
expected string
index int
value bool
expectedTrue int
expected string
}{
/* 0 */ { 5, F, ""},
/* 1 */ { 2, T, "001"},
/* 0 */ { 5, F, 0, ""},
/* 1 */ { 2, T, 1, "001"},

// unset
/* 2 */ { 2, F, ""},
/* 2 */ { 2, F, 0, ""},

// set then set again earlier in sequence
/* 3 */ { 3, T, "0001"},
/* 4 */ { 1, T, "0101"},
/* 3 */ { 3, T, 1, "0001"},
/* 4 */ { 1, T, 2, "0101"},

// redundant unset
/* 5 */ { 2, F, "0101"},
/* 5 */ { 2, F, 2, "0101"},

// double set
/* 6 */ { 8, T, "010100001"},
/* 7 */ { 8, T, "010100001"},
/* 6 */ { 8, T, 3, "010100001"},
/* 7 */ { 8, T, 3, "010100001"},

// set and unset past 64-bit boundary
/* 8 */ { 65, T, "010100001000000000000000000000000000000000000000000000000000000001"},
/* 9 */ { 65, F, "010100001"},
/* 8 */ { 65, T, 4, "010100001000000000000000000000000000000000000000000000000000000001"},
/* 9 */ { 65, F, 3, "010100001"},

// Trailing zeros don't allocate so this is fine
/* 10 */ { math.MaxInt, F, "010100001"},
/* 10 */ { math.MaxInt, F, 3, "010100001"},
}

for i, arg := range args {
s.Set(arg.index, arg.value)
got := s.String()
numTrue := s.CountTrue()
expect(t, got == arg.expected, "%d: got %q, expected %q", i, got, arg.expected)
expect(t, numTrue == arg.expectedTrue, "%d: got %d true, expected %d", i, numTrue, arg.expectedTrue)
}
}

Expand Down
45 changes: 0 additions & 45 deletions ds/digraph/TODO.md

This file was deleted.

Loading

0 comments on commit b1ec623

Please sign in to comment.