Skip to content

Commit

Permalink
Use go 1.23 iterators.
Browse files Browse the repository at this point in the history
Add All() to Sequence to return all the positions and digits.
Add Backward() to FiniteSequence to return the same from end to beginning.
Add All() to Positions to return all the ranges.
Add Matches() to return all 0 based positions where pattern is found in s.
Add BackwardMatches() to return the same from last to first.
  • Loading branch information
keep94 committed Aug 17, 2024
1 parent b61d57c commit d6e9ab0
Show file tree
Hide file tree
Showing 10 changed files with 442 additions and 62 deletions.
77 changes: 75 additions & 2 deletions v3/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ func ExampleDigitsToString() {
// 32050807
}

func ExampleMatches() {

// sqrt(2) = 0.14142135... * 10^1
n := sqroot.Sqrt(2)

// '14' matches at index 0, 2, 144, ...
count := 0
for index := range sqroot.Matches(n, []int{1, 4}) {
fmt.Println(index)
count++
if count == 3 {
break
}
}
// Output:
// 0
// 2
// 144
}

func ExampleFind() {

// sqrt(2) = 0.14142135... * 10^1
Expand Down Expand Up @@ -117,6 +137,23 @@ func ExampleFindLastN() {
// [945 916 631]
}

func ExampleBackwardMatches() {
n := sqroot.Sqrt(2)
count := 0
iterator := sqroot.BackwardMatches(n.WithEnd(1000), []int{1, 4})
for index := range iterator {
fmt.Println(index)
count++
if count == 3 {
break
}
}
// Output:
// 945
// 916
// 631
}

func ExampleFindR() {
n := sqroot.Sqrt(2)
matches := sqroot.FindR(n.WithEnd(1000), []int{1, 4})
Expand Down Expand Up @@ -157,6 +194,26 @@ func ExampleFiniteNumber_Iterator() {
// {Position:5 Value:5}
}

func ExampleFiniteNumber_All() {

// sqrt(7) = 0.26457513110... * 10^1
n := sqroot.Sqrt(7)

for index, value := range n.All() {
fmt.Println(index, value)
if index == 5 {
break
}
}
// Output:
// 0 2
// 1 6
// 2 4
// 3 5
// 4 7
// 5 5
}

func ExampleFiniteNumber_Reverse() {

// sqrt(7) = 0.26457513110... * 10^1
Expand All @@ -175,6 +232,23 @@ func ExampleFiniteNumber_Reverse() {
// {Position:0 Value:2}
}

func ExampleFiniteNumber_Backward() {

// sqrt(7) = 0.26457513110... * 10^1
n := sqroot.Sqrt(7).WithSignificant(6)

for index, value := range n.Backward() {
fmt.Println(index, value)
}
// Output:
// 5 5
// 4 7
// 3 5
// 2 4
// 1 6
// 0 2
}

func ExampleFiniteNumber_WithStart() {

// sqrt(29) = 5.3851648...
Expand Down Expand Up @@ -299,8 +373,7 @@ func ExamplePositions() {
builder.AddRange(0, 7).AddRange(40, 50)
positions := builder.AddRange(5, 10).Build()
fmt.Printf("End: %d\n", positions.End())
iter := positions.Ranges()
for pr, ok := iter(); ok; pr, ok = iter() {
for pr := range positions.All() {
fmt.Printf("%+v\n", pr)
}
// Output:
Expand Down
83 changes: 63 additions & 20 deletions v3/find.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
package sqroot

import (
"github.com/keep94/consume2"
"iter"
"slices"
)

// Matches returns all the 0 based positions in s where pattern is found.
func Matches(s Sequence, pattern []int) iter.Seq[int] {
return matches(s, slices.Clone(pattern))
}

// BackwardMatches returns all the 0 based positions in s where pattern is
// found from last to first.
func BackwardMatches(s FiniteSequence, pattern []int) iter.Seq[int] {
patternInReverse := patternReverse(pattern)
return func(yield func(index int) bool) {
gen := findR(s, patternInReverse)
for index := gen(); index != -1; index = gen() {
if !yield(index) {
return
}
}
}
}

// Find returns a function that returns the next zero based index of the
// match for pattern in s. If s has a finite number of digits and there
// are no more matches for pattern, the returned function returns -1.
// Pattern is a sequence of digits between 0 and 9.
func Find(s Sequence, pattern []int) func() int {
if len(pattern) == 0 {
return zeroPattern(s.Iterator())
}
patternCopy := make([]int, len(pattern))
copy(patternCopy, pattern)
return kmp(s.Iterator(), patternCopy, false)
return find(s, slices.Clone(pattern))
}

// FindFirst finds the zero based index of the first match of pattern in s.
// FindFirst returns -1 if pattern is not found only if s has a finite number
// of digits. If s has an infinite number of digits and pattern is not found,
// FindFirst will run forever. pattern is a sequence of digits between 0 and 9.
func FindFirst(s Sequence, pattern []int) int {
iter := find(s, pattern)
return iter()
return collectFirst(matches(s, pattern))
}

// FindFirstN works like FindFirst but it finds the first n matches and
Expand All @@ -33,39 +47,42 @@ func FindFirst(s Sequence, pattern []int) int {
// number of digits, and there are not n matches available.
// pattern is a sequence of digits between 0 and 9.
func FindFirstN(s Sequence, pattern []int, n int) []int {
return asIntSlice(find(s, pattern), consume2.PSlice[int](0, n))
return collectFirstN(matches(s, pattern), n)
}

// FindAll finds all the matches of pattern in s and returns the zero based
// index of each match. pattern is a sequence of digits between 0 and 9.
func FindAll(s FiniteSequence, pattern []int) []int {
return asIntSlice(find(s, pattern), consume2.Identity[int]())
return slices.Collect(matches(s, pattern))
}

// FindLast finds the zero based index of the last match of pattern in s.
// FindLast returns -1 if pattern is not found in s. pattern is a sequence of
// digits between 0 and 9.
func FindLast(s FiniteSequence, pattern []int) int {
iter := FindR(s, pattern)
return iter()
return collectFirst(BackwardMatches(s, pattern))
}

// FindLastN works like FindLast but it finds the last n matches and
// returns the zero based index of each match. Last matches come first
// in returned array. pattern is a sequence of digits between 0 and 9.
func FindLastN(s FiniteSequence, pattern []int, n int) []int {
return asIntSlice(FindR(s, pattern), consume2.PSlice[int](0, n))
return collectFirstN(BackwardMatches(s, pattern), n)
}

// FindR returns a function that starts at the end of s and returns the
// previous zero based index of the match for pattern in s with each call.
// If there are no more matches for pattern, the returned function returns
// -1. pattern is a sequence of digits between 0 and 9.
func FindR(s FiniteSequence, pattern []int) func() int {
if len(pattern) == 0 {
return findR(s, patternReverse(pattern))
}

func findR(s FiniteSequence, patternInReverse []int) func() int {
if len(patternInReverse) == 0 {
return zeroPattern(s.Reverse())
}
return kmp(s.Reverse(), patternReverse(pattern), true)
return kmp(s.Reverse(), patternInReverse, true)
}

func find(s Sequence, pattern []int) func() int {
Expand All @@ -75,8 +92,34 @@ func find(s Sequence, pattern []int) func() int {
return kmp(s.Iterator(), pattern, false)
}

func asIntSlice(
gen func() int, pipeline consume2.Pipeline[int, int]) (result []int) {
consume2.FromIntGenerator(gen, pipeline.AppendTo(&result))
return
func matches(s Sequence, pattern []int) iter.Seq[int] {
return func(yield func(index int) bool) {
gen := find(s, pattern)
for index := gen(); index != -1; index = gen() {
if !yield(index) {
return
}
}
}
}

func collectFirstN(seq iter.Seq[int], n int) []int {
if n <= 0 {
return nil
}
var result []int
for index := range seq {
result = append(result, index)
if len(result) == n {
break
}
}
return result
}

func collectFirst(seq iter.Seq[int]) int {
for index := range seq {
return index
}
return -1
}
26 changes: 26 additions & 0 deletions v3/find_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@ func TestFindAll(t *testing.T) {
assert.Equal(t, []int{2, 12, 22, 32}, hits)
}

func TestMatches(t *testing.T) {
s := fakeNumber().WithSignificant(40)
pattern := []int{3, 4}
iterator := Matches(s, pattern)
pattern[0] = 5
pattern[1] = 7
var hits []int
for index := range iterator {
hits = append(hits, index)
}
assert.Equal(t, []int{2, 12, 22, 32}, hits)
}

func TestBackwardMatches(t *testing.T) {
s := fakeNumber().WithSignificant(40)
pattern := []int{3, 4}
iterator := BackwardMatches(s, pattern)
pattern[0] = 5
pattern[1] = 7
var hits []int
for index := range iterator {
hits = append(hits, index)
}
assert.Equal(t, []int{32, 22, 12, 2}, hits)
}

func TestFind(t *testing.T) {
pattern := []int{3, 4}
matches := Find(fakeNumber(), pattern)
Expand Down
2 changes: 1 addition & 1 deletion v3/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/keep94/sqroot/v3

go 1.20
go 1.23

require (
github.com/keep94/consume2 v0.7.0
Expand Down
34 changes: 25 additions & 9 deletions v3/numberspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Digit struct {

type numberSpec interface {
IteratorAt(index, limit int) func() (Digit, bool)
Scan(index, limit int, yield func(index, value int) bool)
At(index int) int
FirstN(n int) []int8
}
Expand Down Expand Up @@ -89,6 +90,22 @@ func (m *memoizer) IteratorAt(index, limit int) func() (Digit, bool) {
}
}

func (m *memoizer) Scan(index, limit int, yield func(index, value int) bool) {
if index < 0 {
panic("index must be non-negative")
}
data, ok := m.wait(index)
for ok && index < limit {
if !yield(index, int(data[index])) {
return
}
index++
if index == len(data) {
data, ok = m.wait(index)
}
}
}

func (m *memoizer) wait(index int) ([]int8, bool) {
m.mu.Lock()
defer m.mu.Unlock()
Expand Down Expand Up @@ -169,21 +186,20 @@ func (l *limitSpec) At(index int) int {
}

func (l *limitSpec) IteratorAt(index, limit int) func() (Digit, bool) {
index = minInt(index, l.limit)
limit = minInt(limit, l.limit)
index = min(index, l.limit)
limit = min(limit, l.limit)
return l.delegate.IteratorAt(index, limit)
}

func (l *limitSpec) Scan(index, limit int, yield func(index, value int) bool) {
index = min(index, l.limit)
limit = min(limit, l.limit)
l.delegate.Scan(index, limit, yield)
}

func (l *limitSpec) FirstN(n int) []int8 {
if n > l.limit {
n = l.limit
}
return l.delegate.FirstN(n)
}

func minInt(a, b int) int {
if a < b {
return a
}
return b
}
12 changes: 12 additions & 0 deletions v3/positions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sqroot

import (
"iter"
"sort"
)

Expand Down Expand Up @@ -101,6 +102,17 @@ func (p Positions) Ranges() func() (pr PositionRange, ok bool) {
}
}

// All returns all the non overlapping ranges of positions in p.
func (p Positions) All() iter.Seq[PositionRange] {
return func(yield func(pr PositionRange) bool) {
for _, pr := range p.ranges {
if !yield(pr) {
return
}
}
}
}

// End returns the last zero based position in p plus 1. If p is the zero
// value, End returns 0.
func (p Positions) End() int {
Expand Down
Loading

0 comments on commit d6e9ab0

Please sign in to comment.