mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
196 lines
4.5 KiB
Go
196 lines
4.5 KiB
Go
// package lcs provides functions to calculate Longest Common Subsequence (LCS)
|
|
// values from two arbitrary arrays.
|
|
package lcs
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
)
|
|
|
|
// Lcs is the interface to calculate the LCS of two arrays.
|
|
type Lcs interface {
|
|
// Values calculates the LCS value of the two arrays.
|
|
Values() (values []interface{})
|
|
// ValueContext is a context aware version of Values()
|
|
ValuesContext(ctx context.Context) (values []interface{}, err error)
|
|
// IndexPairs calculates paris of indices which have the same value in LCS.
|
|
IndexPairs() (pairs []IndexPair)
|
|
// IndexPairsContext is a context aware version of IndexPairs()
|
|
IndexPairsContext(ctx context.Context) (pairs []IndexPair, err error)
|
|
// Length calculates the length of the LCS.
|
|
Length() (length int)
|
|
// LengthContext is a context aware version of Length()
|
|
LengthContext(ctx context.Context) (length int, err error)
|
|
// Left returns one of the two arrays to be compared.
|
|
Left() (leftValues []interface{})
|
|
// Right returns the other of the two arrays to be compared.
|
|
Right() (righttValues []interface{})
|
|
}
|
|
|
|
// IndexPair represents an pair of indeices in the Left and Right arrays found in the LCS value.
|
|
type IndexPair struct {
|
|
Left int
|
|
Right int
|
|
}
|
|
|
|
type lcs struct {
|
|
left []interface{}
|
|
right []interface{}
|
|
/* for caching */
|
|
table [][]int
|
|
indexPairs []IndexPair
|
|
values []interface{}
|
|
}
|
|
|
|
// New creates a new LCS calculator from two arrays.
|
|
func New(left, right []interface{}) Lcs {
|
|
return &lcs{
|
|
left: left,
|
|
right: right,
|
|
table: nil,
|
|
indexPairs: nil,
|
|
values: nil,
|
|
}
|
|
}
|
|
|
|
// Table implements Lcs.Table()
|
|
func (lcs *lcs) Table() (table [][]int) {
|
|
table, _ = lcs.TableContext(context.Background())
|
|
return table
|
|
}
|
|
|
|
// Table implements Lcs.TableContext()
|
|
func (lcs *lcs) TableContext(ctx context.Context) (table [][]int, err error) {
|
|
if lcs.table != nil {
|
|
return lcs.table, nil
|
|
}
|
|
|
|
sizeX := len(lcs.left) + 1
|
|
sizeY := len(lcs.right) + 1
|
|
|
|
table = make([][]int, sizeX)
|
|
for x := 0; x < sizeX; x++ {
|
|
table[x] = make([]int, sizeY)
|
|
}
|
|
|
|
for y := 1; y < sizeY; y++ {
|
|
select { // check in each y to save some time
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
// nop
|
|
}
|
|
for x := 1; x < sizeX; x++ {
|
|
increment := 0
|
|
if reflect.DeepEqual(lcs.left[x-1], lcs.right[y-1]) {
|
|
increment = 1
|
|
}
|
|
table[x][y] = max(table[x-1][y-1]+increment, table[x-1][y], table[x][y-1])
|
|
}
|
|
}
|
|
|
|
lcs.table = table
|
|
return table, nil
|
|
}
|
|
|
|
// Table implements Lcs.Length()
|
|
func (lcs *lcs) Length() (length int) {
|
|
length, _ = lcs.LengthContext(context.Background())
|
|
return length
|
|
}
|
|
|
|
// Table implements Lcs.LengthContext()
|
|
func (lcs *lcs) LengthContext(ctx context.Context) (length int, err error) {
|
|
table, err := lcs.TableContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return table[len(lcs.left)][len(lcs.right)], nil
|
|
}
|
|
|
|
// Table implements Lcs.IndexPairs()
|
|
func (lcs *lcs) IndexPairs() (pairs []IndexPair) {
|
|
pairs, _ = lcs.IndexPairsContext(context.Background())
|
|
return pairs
|
|
}
|
|
|
|
// Table implements Lcs.IndexPairsContext()
|
|
func (lcs *lcs) IndexPairsContext(ctx context.Context) (pairs []IndexPair, err error) {
|
|
if lcs.indexPairs != nil {
|
|
return lcs.indexPairs, nil
|
|
}
|
|
|
|
table, err := lcs.TableContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pairs = make([]IndexPair, table[len(table)-1][len(table[0])-1])
|
|
|
|
for x, y := len(lcs.left), len(lcs.right); x > 0 && y > 0; {
|
|
if reflect.DeepEqual(lcs.left[x-1], lcs.right[y-1]) {
|
|
pairs[table[x][y]-1] = IndexPair{Left: x - 1, Right: y - 1}
|
|
x--
|
|
y--
|
|
} else {
|
|
if table[x-1][y] >= table[x][y-1] {
|
|
x--
|
|
} else {
|
|
y--
|
|
}
|
|
}
|
|
}
|
|
|
|
lcs.indexPairs = pairs
|
|
|
|
return pairs, nil
|
|
}
|
|
|
|
// Table implements Lcs.Values()
|
|
func (lcs *lcs) Values() (values []interface{}) {
|
|
values, _ = lcs.ValuesContext(context.Background())
|
|
return values
|
|
}
|
|
|
|
// Table implements Lcs.ValuesContext()
|
|
func (lcs *lcs) ValuesContext(ctx context.Context) (values []interface{}, err error) {
|
|
if lcs.values != nil {
|
|
return lcs.values, nil
|
|
}
|
|
|
|
pairs, err := lcs.IndexPairsContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
values = make([]interface{}, len(pairs))
|
|
for i, pair := range pairs {
|
|
values[i] = lcs.left[pair.Left]
|
|
}
|
|
lcs.values = values
|
|
|
|
return values, nil
|
|
}
|
|
|
|
// Table implements Lcs.Left()
|
|
func (lcs *lcs) Left() (leftValues []interface{}) {
|
|
leftValues = lcs.left
|
|
return
|
|
}
|
|
|
|
// Table implements Lcs.Right()
|
|
func (lcs *lcs) Right() (rightValues []interface{}) {
|
|
rightValues = lcs.right
|
|
return
|
|
}
|
|
|
|
func max(first int, rest ...int) (max int) {
|
|
max = first
|
|
for _, value := range rest {
|
|
if value > max {
|
|
max = value
|
|
}
|
|
}
|
|
return
|
|
}
|