Initial QSfera import
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
coverage.txt
|
||||
fuzz/fuzz-fuzz.zip
|
||||
fuzz/corpus/corpus/*
|
||||
fuzz/corpus/suppressions/*
|
||||
fuzz/corpus/crashes/*
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Agniva De Sarker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
all: test install
|
||||
|
||||
install:
|
||||
go install
|
||||
|
||||
lint:
|
||||
gofmt -l -s -w . && go vet .
|
||||
|
||||
test:
|
||||
go test -race -v -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
bench:
|
||||
go test -run=XXX -bench=. -benchmem -count=5
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
levenshtein  [](https://goreportcard.com/report/github.com/agnivade/levenshtein) [](https://pkg.go.dev/github.com/agnivade/levenshtein)
|
||||
===========
|
||||
|
||||
[Go](http://golang.org) package to calculate the [Levenshtein Distance](http://en.wikipedia.org/wiki/Levenshtein_distance)
|
||||
|
||||
The library is fully capable of working with non-ascii strings. But the strings are not normalized. That is left as a user-dependant use case. Please normalize the strings before passing it to the library if you have such a requirement.
|
||||
- https://blog.golang.org/normalization
|
||||
|
||||
#### Limitation
|
||||
|
||||
As a performance optimization, the library can handle strings only up to 65536 characters (runes). If you need to handle strings larger than that, please pin to version 1.0.3.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
go get github.com/agnivade/levenshtein
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/agnivade/levenshtein"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s1 := "kitten"
|
||||
s2 := "sitting"
|
||||
distance := levenshtein.ComputeDistance(s1, s2)
|
||||
fmt.Printf("The distance between %s and %s is %d.\n", s1, s2, distance)
|
||||
// Output:
|
||||
// The distance between kitten and sitting is 3.
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
```
|
||||
name time/op
|
||||
Simple/ASCII-4 330ns ± 2%
|
||||
Simple/French-4 617ns ± 2%
|
||||
Simple/Nordic-4 1.16µs ± 4%
|
||||
Simple/Tibetan-4 1.05µs ± 1%
|
||||
|
||||
name alloc/op
|
||||
Simple/ASCII-4 96.0B ± 0%
|
||||
Simple/French-4 128B ± 0%
|
||||
Simple/Nordic-4 192B ± 0%
|
||||
Simple/Tibetan-4 144B ± 0%
|
||||
|
||||
name allocs/op
|
||||
Simple/ASCII-4 1.00 ± 0%
|
||||
Simple/French-4 1.00 ± 0%
|
||||
Simple/Nordic-4 1.00 ± 0%
|
||||
Simple/Tibetan-4 1.00 ± 0%
|
||||
```
|
||||
|
||||
Comparisons with other libraries
|
||||
--------------------------------
|
||||
|
||||
```
|
||||
name time/op
|
||||
Leven/ASCII/agniva-4 353ns ± 1%
|
||||
Leven/ASCII/arbovm-4 485ns ± 1%
|
||||
Leven/ASCII/dgryski-4 395ns ± 0%
|
||||
Leven/French/agniva-4 648ns ± 1%
|
||||
Leven/French/arbovm-4 791ns ± 0%
|
||||
Leven/French/dgryski-4 682ns ± 0%
|
||||
Leven/Nordic/agniva-4 1.28µs ± 1%
|
||||
Leven/Nordic/arbovm-4 1.52µs ± 1%
|
||||
Leven/Nordic/dgryski-4 1.32µs ± 1%
|
||||
Leven/Tibetan/agniva-4 1.12µs ± 1%
|
||||
Leven/Tibetan/arbovm-4 1.31µs ± 0%
|
||||
Leven/Tibetan/dgryski-4 1.16µs ± 0%
|
||||
```
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Package levenshtein is a Go implementation to calculate Levenshtein Distance.
|
||||
//
|
||||
// Implementation taken from
|
||||
// https://gist.github.com/andrei-m/982927#gistcomment-1931258
|
||||
package levenshtein
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// minLengthThreshold is the length of the string beyond which
|
||||
// an allocation will be made. Strings smaller than this will be
|
||||
// zero alloc.
|
||||
const minLengthThreshold = 32
|
||||
|
||||
// ComputeDistance computes the levenshtein distance between the two
|
||||
// strings passed as an argument. The return value is the levenshtein distance
|
||||
//
|
||||
// Works on runes (Unicode code points) but does not normalize
|
||||
// the input strings. See https://blog.golang.org/normalization
|
||||
// and the golang.org/x/text/unicode/norm package.
|
||||
func ComputeDistance(a, b string) int {
|
||||
if len(a) == 0 {
|
||||
return utf8.RuneCountInString(b)
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return utf8.RuneCountInString(a)
|
||||
}
|
||||
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
|
||||
// We need to convert to []rune if the strings are non-ASCII.
|
||||
// This could be avoided by using utf8.RuneCountInString
|
||||
// and then doing some juggling with rune indices,
|
||||
// but leads to far more bounds checks. It is a reasonable trade-off.
|
||||
s1 := []rune(a)
|
||||
s2 := []rune(b)
|
||||
|
||||
// swap to save some memory O(min(a,b)) instead of O(a)
|
||||
if len(s1) > len(s2) {
|
||||
s1, s2 = s2, s1
|
||||
}
|
||||
|
||||
// remove trailing identical runes.
|
||||
for i := 0; i < len(s1); i++ {
|
||||
if s1[len(s1)-1-i] != s2[len(s2)-1-i] {
|
||||
s1 = s1[:len(s1)-i]
|
||||
s2 = s2[:len(s2)-i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading identical runes.
|
||||
for i := 0; i < len(s1); i++ {
|
||||
if s1[i] != s2[i] {
|
||||
s1 = s1[i:]
|
||||
s2 = s2[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lenS1 := len(s1)
|
||||
lenS2 := len(s2)
|
||||
|
||||
// Init the row.
|
||||
var x []uint16
|
||||
if lenS1+1 > minLengthThreshold {
|
||||
x = make([]uint16, lenS1+1)
|
||||
} else {
|
||||
// We make a small optimization here for small strings.
|
||||
// Because a slice of constant length is effectively an array,
|
||||
// it does not allocate. So we can re-slice it to the right length
|
||||
// as long as it is below a desired threshold.
|
||||
x = make([]uint16, minLengthThreshold)
|
||||
x = x[:lenS1+1]
|
||||
}
|
||||
|
||||
// we start from 1 because index 0 is already 0.
|
||||
for i := 1; i < len(x); i++ {
|
||||
x[i] = uint16(i)
|
||||
}
|
||||
|
||||
// make a dummy bounds check to prevent the 2 bounds check down below.
|
||||
// The one inside the loop is particularly costly.
|
||||
_ = x[lenS1]
|
||||
// fill in the rest
|
||||
for i := 1; i <= lenS2; i++ {
|
||||
prev := uint16(i)
|
||||
for j := 1; j <= lenS1; j++ {
|
||||
current := x[j-1] // match
|
||||
if s2[i-1] != s1[j-1] {
|
||||
current = min(x[j-1]+1, prev+1, x[j]+1)
|
||||
}
|
||||
x[j-1] = prev
|
||||
prev = current
|
||||
}
|
||||
x[lenS1] = prev
|
||||
}
|
||||
return int(x[lenS1])
|
||||
}
|
||||
Reference in New Issue
Block a user