func (k KMP) DiscoverDiscords(kDiscords int, exclusionZone int) ([]int, error) {
	return nil, errors.New("Discords for KMP has not been implemented yet.")
}
DiscoverDiscords has not been implemented yet
package matrixprofile
import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"sort"
	"github.com/matrix-profile-foundation/go-matrixprofile/util"
	"gonum.org/v1/gonum/dsp/fourier"
	"gonum.org/v1/plot/plotter"
)
type KMP struct {
	T     [][]float64     
	tMean [][]float64     
	tStd  [][]float64     
	tF    [][]complex128  
	n     int             
	W     int             
	MP    [][]float64     
	Idx   [][]int         
}
func NewKMP(t [][]float64, w int) (*KMP, error) {
	if t == nil || len(t) == 0 {
		return nil, fmt.Errorf("slice is nil or has a length of 0 dimensions")
	}
	k := KMP{
		T: t,
		W: w,
		n: len(t[0]),
	}
	for d := 0; d < len(t); d++ {
		if len(t[d]) != k.n {
			return nil, fmt.Errorf("timeseries %d has a length of %d and doesn't match the first timeseries with length %d", d, len(t[d]), k.n)
		}
	}
	if k.W*2 >= k.n {
		return nil, fmt.Errorf("subsequence length must be less than half the timeseries")
	}
	if k.W < 2 {
		return nil, fmt.Errorf("subsequence length must be at least 2")
	}
	k.tMean = make([][]float64, len(t))
	k.tStd = make([][]float64, len(t))
	k.tF = make([][]complex128, len(t))
	k.MP = make([][]float64, len(t))
	k.Idx = make([][]int, len(t))
	for d := 0; d < len(t); d++ {
		k.tMean[d] = make([]float64, k.n-k.W+1)
		k.tStd[d] = make([]float64, k.n-k.W+1)
		k.tF[d] = make([]complex128, k.n-k.W+1)
		k.MP[d] = make([]float64, k.n-k.W+1)
		k.Idx[d] = make([]int, k.n-k.W+1)
	}
	for d := 0; d < len(t); d++ {
		for i := 0; i < k.n-k.W+1; i++ {
			k.MP[d][i] = math.Inf(1)
			k.Idx[d][i] = math.MaxInt64
		}
	}
	if err := k.initCaches(); err != nil {
		return nil, err
	}
	return &k, nil
}
func (k KMP) Save(filepath, format string) error {
	var err error
	switch format {
	case "json":
		f, err := os.Open(filepath)
		if err != nil {
			f, err = os.Create(filepath)
			if err != nil {
				return err
			}
		}
		defer f.Close()
		out, err := json.Marshal(k)
		if err != nil {
			return err
		}
		_, err = f.Write(out)
	default:
		return fmt.Errorf("invalid save format, %s", format)
	}
	return err
}
func (k *KMP) Load(filepath, format string) error {
	var err error
	switch format {
	case "json":
		f, err := os.Open(filepath)
		if err != nil {
			return err
		}
		defer f.Close()
		b, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		err = json.Unmarshal(b, k)
	default:
		return fmt.Errorf("invalid load format, %s", format)
	}
	return err
}
func (k *KMP) initCaches() error {
	var err error
	for d := 0; d < len(k.T); d++ {
		k.tMean[d], k.tStd[d], err = util.MovMeanStd(k.T[d], k.W)
		if err != nil {
			return err
		}
	}
	fft := fourier.NewFFT(k.n)
	for d := 0; d < len(k.T); d++ {
		k.tF[d] = fft.Coefficients(nil, k.T[d])
	}
	return nil
}
func (k *KMP) Compute() error {
	return k.mStomp()
}
func (k *KMP) mStomp() error {
	var err error
	cachedDots := make([][]float64, len(k.T))
	fft := fourier.NewFFT(k.n)
	k.crossCorrelate(0, fft, cachedDots)
	var D [][]float64
	D = make([][]float64, len(k.T))
	for d := 0; d < len(D); d++ {
		D[d] = make([]float64, k.n-k.W+1)
	}
	dots := make([][]float64, len(k.T))
	for d := 0; d < len(dots); d++ {
		dots[d] = make([]float64, k.n-k.W+1)
		copy(dots[d], cachedDots[d])
	}
	for idx := 0; idx < k.n-k.W+1; idx++ {
		for d := 0; d < len(dots); d++ {
			if idx > 0 {
				for j := k.n - k.W; j > 0; j-- {
					dots[d][j] = dots[d][j-1] - k.T[d][j-1]*k.T[d][idx-1] + k.T[d][j+k.W-1]*k.T[d][idx+k.W-1]
				}
				dots[d][0] = cachedDots[d][idx]
			}
			for i := 0; i < k.n-k.W+1; i++ {
				D[d][i] = math.Sqrt(2 * float64(k.W) * math.Abs(1-(dots[d][i]-float64(k.W)*k.tMean[d][i]*k.tMean[d][idx])/(float64(k.W)*k.tStd[d][i]*k.tStd[d][idx])))
			}
			util.ApplyExclusionZone(D[d], idx, k.W/2)
		}
		k.columnWiseSort(D)
		k.columnWiseCumSum(D)
		for d := 0; d < len(D); d++ {
			for i := 0; i < k.n-k.W+1; i++ {
				if D[d][i]/(float64(d)+1) < k.MP[d][i] {
					k.MP[d][i] = D[d][i] / (float64(d) + 1)
					k.Idx[d][i] = idx
				}
			}
		}
	}
	return err
}
func (k KMP) crossCorrelate(idx int, fft *fourier.FFT, D [][]float64) {
	qpad := make([]float64, k.n)
	var qf []complex128
	var dot []float64
	for d := 0; d < len(D); d++ {
		for i := 0; i < k.W; i++ {
			qpad[i] = k.T[d][idx+k.W-i-1]
		}
		qf = fft.Coefficients(nil, qpad)
		for i := 0; i < len(qf); i++ {
			qf[i] = k.tF[d][i] * qf[i]
		}
		dot = fft.Sequence(nil, qf)
		for i := 0; i < k.n-k.W+1; i++ {
			dot[k.W-1+i] = dot[k.W-1+i] / float64(k.n)
		}
		D[d] = dot[k.W-1:]
	}
}
func (k KMP) columnWiseSort(D [][]float64) {
	dist := make([]float64, len(D))
	for i := 0; i < k.n-k.W+1; i++ {
		for d := 0; d < len(D); d++ {
			dist[d] = D[d][i]
		}
		sort.Float64s(dist)
		for d := 0; d < len(D); d++ {
			D[d][i] = dist[d]
		}
	}
}
func (k KMP) columnWiseCumSum(D [][]float64) {
	for d := 0; d < len(D); d++ {
		if d > 0 {
			for i := 0; i < k.n-k.W+1; i++ {
				D[d][i] += D[d-1][i]
			}
		}
	}
}
func (k KMP) Analyze(mo *MPOpts, ao *AnalyzeOpts) error {
	return errors.New("Analyze for KMP has not been implemented yet.")
}
func (k KMP) DiscoverMotifs(kMotifs int, r float64) ([]MotifGroup, error) {
	return nil, errors.New("Motifs for KMP has not been implemented yet.")
}