-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnaive.go
116 lines (105 loc) · 2.74 KB
/
naive.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main
import (
// "fmt"
"sync"
)
type naiveMatcher struct{}
func (b naiveMatcher) fuzzyMatch(pattern, text string, maxDistance int) *Match {
var result *Match
for startPos := 0; startPos < len(text)-len(pattern)+1; startPos++ {
if text[startPos] == pattern[0] {
match := getMatch(pattern, text, maxDistance, startPos)
if result == nil || preferSecond(result, match) {
result = match
}
}
}
return result
}
// finds highlighting matches from startPos
func getMatch(pattern, text string, maxDistance, startPos int) *Match {
textPos := startPos
patternPos := 0
distance := 0
matchStart := -1
var result Match
for ; distance <= maxDistance; textPos++ {
if text[textPos] == pattern[patternPos] { // char matches
if matchStart == -1 { // remember start
matchStart = textPos
}
patternPos++ // consume pattern
} else { // char does not match
if matchStart != -1 { // if there was a start set
result.Positions = append(result.Positions, StartEnd{matchStart, textPos}) // emit the interval
matchStart = -1 // forget the old start
}
distance++ // increase number of non-matches
}
if patternPos == len(pattern) {
break // consumed complete pattern
}
}
result.Distance = distance
if matchStart != -1 { // end of the string
result.Positions = append(result.Positions, StartEnd{matchStart, textPos + 1})
}
if distance <= maxDistance && len(result.Positions) > 0 {
return &result
}
return nil
}
// prefer old distance, longer, earlier matches
func preferSecond(old, potentialNew *Match) bool {
if old.Distance > potentialNew.Distance {
return true
}
lengthA := old.Positions[len(old.Positions)-1].End - old.Positions[0].Start
lengthB := potentialNew.Positions[len(old.Positions)-1].End - potentialNew.Positions[0].Start
if lengthA < lengthB {
return true
}
if old.Positions[0].Start > potentialNew.Positions[0].Start {
return true
}
return false
}
// experimental
func (b naiveMatcher) fuzzyMatchParallel(pattern, text string, maxDistance, threads int) *Match {
ingress := make(chan int)
egress := make(chan *Match)
// scatter
go func() {
for start := 0; start < len(text)-len(pattern); start++ {
if text[start] == pattern[0] {
ingress <- int(start)
}
}
close(ingress)
}()
var wg sync.WaitGroup
for i := 0; i < threads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for startPos := range ingress {
match := getMatch(pattern, text, maxDistance, startPos)
if match != nil {
egress <- match
}
}
}()
}
go func() {
wg.Wait()
close(egress)
}()
// gather
var result *Match
for match := range egress {
if result == nil || preferSecond(result, match) {
result = match
}
}
return result
}