<commit_before>// Copyright © 2020 Karim Radhouani <medkarimrdi@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/google/gnxi/utils/xpath"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc/metadata"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "run gnmi get on targets",

	RunE: func(cmd *cobra.Command, args []string) error {
		debug := viper.GetBool("debug")
		var err error
		addresses := viper.GetStringSlice("address")
		if len(addresses) == 0 {
			fmt.Println("no grpc server address specified")
			return nil
		}
		username := viper.GetString("username")
		if username == "" {
			if username, err = readUsername(); err != nil {
				return err
			}
		}
		password := viper.GetString("password")
		if password == "" {
			if password, err = readPassword(); err != nil {
				return err
			}
		}
		encodingVal, ok := gnmi.Encoding_value[strings.Replace(strings.ToUpper(viper.GetString("encoding")), "-", "_", -1)]
		if !ok {
			return fmt.Errorf("invalid encoding type '%s'", viper.GetString("encoding"))
		}
		req := &gnmi.GetRequest{
			UseModels: make([]*gnmi.ModelData, 0),
			Path:      make([]*gnmi.Path, 0),
			Encoding:  gnmi.Encoding(encodingVal),
		}
		model := viper.GetString("get-model")
		prefix := viper.GetString("get-prefix")
		if prefix != "" {
			gnmiPrefix, err := xpath.ToGNMIPath(prefix)
			if err != nil {
				return fmt.Errorf("prefix parse error: %v", err)
			}
			req.Prefix = gnmiPrefix
		}
		paths := viper.GetStringSlice("get-path")
		for _, p := range paths {
			gnmiPath, err := xpath.ToGNMIPath(p)
			if err != nil {
				return fmt.Errorf("path parse error: %v", err)
			}
			req.Path = append(req.Path, gnmiPath)
		}
		dataType := viper.GetString("get-type")
		if dataType != "" {
			dti, ok := gnmi.GetRequest_DataType_value[strings.ToUpper(dataType)]
			if !ok {
				return fmt.Errorf("unknown data type %s", dataType)
			}
			req.Type = gnmi.GetRequest_DataType(dti)
		}
		wg := new(sync.WaitGroup)
		wg.Add(len(addresses))
		lock := new(sync.Mutex)
		for _, addr := range addresses {
			go func(address string) {
				defer wg.Done()
				_, _, err := net.SplitHostPort(address)
				if err != nil {
					if strings.Contains(err.Error(), "missing port in address") {
						address = net.JoinHostPort(address, defaultGrpcPort)
					} else {
						logger.Printf("error parsing address '%s': %v", address, err)
						return
					}
				}
				conn, err := createGrpcConn(address)
				if err != nil {
					logger.Printf("connection to %s failed: %v", address, err)
					return
				}
				client := gnmi.NewGNMIClient(conn)
				ctx, cancel := context.WithCancel(context.Background())
				defer cancel()
				ctx = metadata.AppendToOutgoingContext(ctx, "username", username, "password", password)
				xreq := req
				if model != "" {
					capResp, err := client.Capabilities(ctx, &gnmi.CapabilityRequest{})
					if err != nil {
						logger.Printf("%v", err)
						return
					}
					var found bool
					for _, m := range capResp.SupportedModels {
						if m.Name == model {
							if debug {
								logger.Printf("target %s: found model: %v\n", address, m)
							}
							xreq.UseModels = append(xreq.UseModels,
								&gnmi.ModelData{
									Name:         model,
									Organization: m.Organization,
									Version:      m.Version,
								})
							found = true
							break
						}
					}
					if !found {
						logger.Printf("model '%s' not supported by target %s", model, address)
						return
					}
				}
				logger.Printf("sending gnmi GetRequest '%+v' to %s", xreq, address)
				response, err := client.Get(ctx, xreq)
				if err != nil {
					logger.Printf("failed sending GetRequest to %s: %v", address, err)
					return
				}
				printPrefix := ""
				if len(addresses) > 1 && !viper.GetBool("no-prefix") {
					printPrefix = fmt.Sprintf("[%s] ", address)
				}
				lock.Lock()
				printGetResponse(printPrefix, response)
				lock.Unlock()
			}(addr)
		}
		wg.Wait()
		return nil
	},
}

func init() {
	rootCmd.AddCommand(getCmd)

	getCmd.Flags().StringSliceP("path", "", []string{""}, "get request paths")
	getCmd.MarkFlagRequired("path")
	getCmd.Flags().StringP("prefix", "", "", "get request prefix")
	getCmd.Flags().StringP("model", "", "", "get request model")
	getCmd.Flags().StringP("type", "t", "ALL", "the type of data that is requested from the target. one of: ALL, CONFIG, STATE, OPERATIONAL")
	viper.BindPFlag("get-path", getCmd.Flags().Lookup("path"))
	viper.BindPFlag("get-prefix", getCmd.Flags().Lookup("prefix"))
	viper.BindPFlag("get-model", getCmd.Flags().Lookup("model"))
	viper.BindPFlag("get-type", getCmd.Flags().Lookup("type"))
}

func printGetResponse(printPrefix string, response *gnmi.GetResponse) {
	if viper.GetString("format") == "textproto" {
		fmt.Printf("%s\n", indent(printPrefix, proto.MarshalTextString(response)))
		return
	}
	for _, notif := range response.Notification {
		msg := new(msg)
		msg.Timestamp = notif.Timestamp
		t := time.Unix(0, notif.Timestamp)
		msg.Time = &t
		msg.Prefix = gnmiPathToXPath(notif.Prefix)
		for i, upd := range notif.Update {
			if upd.Val == nil {
				if viper.GetBool("debug") {
					logger.Printf("DEBUG: got a nil val update: %+v", upd)
				}
				continue
			}
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		dMsg, err := json.MarshalIndent(msg, printPrefix, "  ")
		if err != nil {
			logger.Printf("error marshling json msg:%v", err)
			return
		}
		fmt.Printf("%s%s\n", printPrefix, string(dMsg))
	}
	fmt.Println()
}
<commit_msg>fix #30 - textproto output fails for Get RPC<commit_after>// Copyright © 2020 Karim Radhouani <medkarimrdi@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/google/gnxi/utils/xpath"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc/metadata"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "run gnmi get on targets",

	RunE: func(cmd *cobra.Command, args []string) error {
		debug := viper.GetBool("debug")
		var err error
		addresses := viper.GetStringSlice("address")
		if len(addresses) == 0 {
			fmt.Println("no grpc server address specified")
			return nil
		}
		username := viper.GetString("username")
		if username == "" {
			if username, err = readUsername(); err != nil {
				return err
			}
		}
		password := viper.GetString("password")
		if password == "" {
			if password, err = readPassword(); err != nil {
				return err
			}
		}
		encodingVal, ok := gnmi.Encoding_value[strings.Replace(strings.ToUpper(viper.GetString("encoding")), "-", "_", -1)]
		if !ok {
			return fmt.Errorf("invalid encoding type '%s'", viper.GetString("encoding"))
		}
		req := &gnmi.GetRequest{
			UseModels: make([]*gnmi.ModelData, 0),
			Path:      make([]*gnmi.Path, 0),
			Encoding:  gnmi.Encoding(encodingVal),
		}
		model := viper.GetString("get-model")
		prefix := viper.GetString("get-prefix")
		if prefix != "" {
			gnmiPrefix, err := xpath.ToGNMIPath(prefix)
			if err != nil {
				return fmt.Errorf("prefix parse error: %v", err)
			}
			req.Prefix = gnmiPrefix
		}
		paths := viper.GetStringSlice("get-path")
		for _, p := range paths {
			gnmiPath, err := xpath.ToGNMIPath(p)
			if err != nil {
				return fmt.Errorf("path parse error: %v", err)
			}
			req.Path = append(req.Path, gnmiPath)
		}
		dataType := viper.GetString("get-type")
		if dataType != "" {
			dti, ok := gnmi.GetRequest_DataType_value[strings.ToUpper(dataType)]
			if !ok {
				return fmt.Errorf("unknown data type %s", dataType)
			}
			req.Type = gnmi.GetRequest_DataType(dti)
		}
		wg := new(sync.WaitGroup)
		wg.Add(len(addresses))
		lock := new(sync.Mutex)
		for _, addr := range addresses {
			go func(address string) {
				defer wg.Done()
				_, _, err := net.SplitHostPort(address)
				if err != nil {
					if strings.Contains(err.Error(), "missing port in address") {
						address = net.JoinHostPort(address, defaultGrpcPort)
					} else {
						logger.Printf("error parsing address '%s': %v", address, err)
						return
					}
				}
				conn, err := createGrpcConn(address)
				if err != nil {
					logger.Printf("connection to %s failed: %v", address, err)
					return
				}
				client := gnmi.NewGNMIClient(conn)
				ctx, cancel := context.WithCancel(context.Background())
				defer cancel()
				ctx = metadata.AppendToOutgoingContext(ctx, "username", username, "password", password)
				xreq := req
				if model != "" {
					capResp, err := client.Capabilities(ctx, &gnmi.CapabilityRequest{})
					if err != nil {
						logger.Printf("%v", err)
						return
					}
					var found bool
					for _, m := range capResp.SupportedModels {
						if m.Name == model {
							if debug {
								logger.Printf("target %s: found model: %v\n", address, m)
							}
							xreq.UseModels = append(xreq.UseModels,
								&gnmi.ModelData{
									Name:         model,
									Organization: m.Organization,
									Version:      m.Version,
								})
							found = true
							break
						}
					}
					if !found {
						logger.Printf("model '%s' not supported by target %s", model, address)
						return
					}
				}
				logger.Printf("sending gnmi GetRequest '%+v' to %s", xreq, address)
				response, err := client.Get(ctx, xreq)
				if err != nil {
					logger.Printf("failed sending GetRequest to %s: %v", address, err)
					return
				}
				printPrefix := ""
				if len(addresses) > 1 && !viper.GetBool("no-prefix") {
					printPrefix = fmt.Sprintf("[%s] ", address)
				}
				lock.Lock()
				printGetResponse(printPrefix, response)
				lock.Unlock()
			}(addr)
		}
		wg.Wait()
		return nil
	},
}

func init() {
	rootCmd.AddCommand(getCmd)

	getCmd.Flags().StringSliceP("path", "", []string{""}, "get request paths")
	getCmd.MarkFlagRequired("path")
	getCmd.Flags().StringP("prefix", "", "", "get request prefix")
	getCmd.Flags().StringP("model", "", "", "get request model")
	getCmd.Flags().StringP("type", "t", "ALL", "the type of data that is requested from the target. one of: ALL, CONFIG, STATE, OPERATIONAL")
	viper.BindPFlag("get-path", getCmd.Flags().Lookup("path"))
	viper.BindPFlag("get-prefix", getCmd.Flags().Lookup("prefix"))
	viper.BindPFlag("get-model", getCmd.Flags().Lookup("model"))
	viper.BindPFlag("get-type", getCmd.Flags().Lookup("type"))
}

func printGetResponse(printPrefix string, response *gnmi.GetResponse) {
	if viper.GetString("format") == "textproto" {
		fmt.Printf("%s\n", proto.MarshalTextString(response))
		return
	}
	for _, notif := range response.Notification {
		msg := new(msg)
		msg.Timestamp = notif.Timestamp
		t := time.Unix(0, notif.Timestamp)
		msg.Time = &t
		msg.Prefix = gnmiPathToXPath(notif.Prefix)
		for i, upd := range notif.Update {
			if upd.Val == nil {
				if viper.GetBool("debug") {
					logger.Printf("DEBUG: got a nil val update: %+v", upd)
				}
				continue
			}
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		dMsg, err := json.MarshalIndent(msg, printPrefix, "  ")
		if err != nil {
			logger.Printf("error marshling json msg:%v", err)
			return
		}
		fmt.Printf("%s%s\n", printPrefix, string(dMsg))
	}
	fmt.Println()
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// newCmd represents the new command
var newCmd = &cobra.Command{
	Use:   "new",
	Short: "Create a new mercurius project",
	Long:  `Create a new mercurius project`,
	Run: func(cmd *cobra.Command, args []string) {
		// checking number of arguments
		if len(args) > 0 {
			fmt.Println(os.Stderr, "Too many arguments")
			os.Exit(-1)
		}
		initGoPaths()
		setApplicationPath()
		copyNewAppFiles(confValues())
	},
}

const mercuriusPath = "github.com/novatrixtech/mercurius"

var (
	// go related paths
	gopath  string
	srcRoot string

	// mercurius related paths
	mercuriusPkg *build.Package
	appPath      string
	appName      string
	basePath     string
	importPath   string
	skeletonPath string
)

// lookup and set Go related variables
func initGoPaths() {
	// lookup go path
	gopath = build.Default.GOPATH
	if gopath == "" {
		fmt.Println("Abort: GOPATH environment variable is not set. " +
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
		os.Exit(-1)
	}

	// check for go executable
	var err error
	_, err = exec.LookPath("go")
	if err != nil {
		fmt.Println("Go executable not found in PATH.")
		os.Exit(-1)
	}

	//support relative path
	workingDir, _ := os.Getwd()
	goPathList := filepath.SplitList(gopath)
	for _, path := range goPathList {
		if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
			srcRoot = path
			break
		}

		path, _ = filepath.EvalSymlinks(path)
		if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
			srcRoot = path
			break
		}
	}

	if len(srcRoot) == 0 {
		fmt.Println("Abort: could not create a Mercurius application outside of GOPATH.")
		os.Exit(-1)
	}

	// set go src path
	srcRoot = filepath.Join(srcRoot, "src")
}

func setApplicationPath() {
	var err error
	importPath = terminal("What is application path?", "")
	if importPath == "" {
		fmt.Println("Abort: could not create a Mercurius application with empty application path.")
		os.Exit(-1)
	}

	// validate relative path, we cannot use built-in functions
	// since Go import path is valid relative path too.
	// so check basic part of the path, which is "."
	if filepath.IsAbs(importPath) || strings.HasPrefix(importPath, ".") {
		fmt.Printf("Abort: '%s' looks like a directory.  Please provide a Go import path instead.\n", importPath)
	}

	_, err = build.Import(importPath, "", build.FindOnly)
	if err == nil {
		fmt.Printf("Abort: Import path %s already exists.\n", importPath)
	}

	mercuriusPkg, err = build.Import(mercuriusPath, "", build.FindOnly)
	if err != nil {
		fmt.Printf("Abort: Could not find Mercurius source code: %s\n", err)
	}

	appPath = filepath.Join(srcRoot, filepath.FromSlash(importPath))
	appName = filepath.Base(appPath)
	basePath = filepath.ToSlash(filepath.Dir(importPath))

	if basePath == "." {
		// we need to remove the a single '.' when
		// the app is in the $GOROOT/src directory
		basePath = ""
	} else {
		// we need to append a '/' when the app is
		// is a subdirectory such as $GOROOT/src/path/to/mercurius
		basePath += "/"
	}
	// set base project path
	skeletonPath = filepath.Join(mercuriusPkg.Dir, "skeleton")
}

func copyNewAppFiles(cfgs map[string]interface{}) {
	var err error
	err = os.MkdirAll(appPath, 0777)
	if err != nil {
		panic(err)
	}

	mustCopyDir(appPath, skeletonPath, cfgs)

	// Dotfiles are skipped by mustCopyDir, so we have to explicitly copy the .gitignore.
	gitignore := ".gitignore"
	mustCopyFile(filepath.Join(appPath, gitignore), filepath.Join(skeletonPath, gitignore))

}

func confValues() map[string]interface{} {
	// Define default value for cache types
	// See https://go-macaron.com/docs/middlewares/cache for details
	cacheMap := map[string]string{
		"memory":   "",
		"file":     "data/caches",
		"redis":    "addr=127.0.0.1:6379",
		"memcache": "127.0.0.1:11211",
	}

	// define configs
	cfgs := make(map[string]interface{})
	cfgs["AppPath"] = importPath
	cfgs["AppName"] = appName
	cfgs["DBType"] = terminal("What database do you want to use?", "mysql")
	cfgs["DBUser"] = terminal("What is your database user?", "root")
	cfgs["DBPw"] = terminal("What is your database password?", "")
	cfgs["DBName"] = terminal("What is your database name?", "")
	cfgs["DBHost"] = terminal("What is your database host?", "localhost")
	cfgs["DBPort"] = terminal("What is your database port?", "3306")
	cfgs["MaxConn"] = terminal("What is your database max connection pool?", "10")
	cfgs["IdleConn"] = terminal("What is your database idle connection pool?", "10")
	cache := terminal("What cache do you want to use?", "memory")
	cfgs["CacheType"] = cache
	cfgs["CacheCfgs"] = terminal("What is your cache server address?", cacheMap[cache])
	cfgs["Key"] = terminal("What is your oauth key", "")
	return cfgs
}

func init() {
	RootCmd.AddCommand(newCmd)
}
<commit_msg>build import path dynamicaly<commit_after>package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// newCmd represents the new command
var newCmd = &cobra.Command{
	Use:   "new",
	Short: "Create a new mercurius project",
	Long:  `Create a new mercurius project`,
	Run: func(cmd *cobra.Command, args []string) {
		// checking number of arguments
		if len(args) > 0 {
			fmt.Println(os.Stderr, "Too many arguments")
			os.Exit(-1)
		}
		initGoPaths()
		setApplicationPath()
		copyNewAppFiles(confValues())
	},
}

const mercuriusPath = "github.com/novatrixtech/mercurius"

var (
	// go related paths
	gopath  string
	srcRoot string

	// mercurius related paths
	mercuriusPkg *build.Package
	appPath      string
	appName      string
	basePath     string
	importPath   string
	skeletonPath string
)

// lookup and set Go related variables
func initGoPaths() {
	// lookup go path
	gopath = build.Default.GOPATH
	if gopath == "" {
		fmt.Println("Abort: GOPATH environment variable is not set. " +
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
		os.Exit(-1)
	}

	// check for go executable
	var err error
	_, err = exec.LookPath("go")
	if err != nil {
		fmt.Println("Go executable not found in PATH.")
		os.Exit(-1)
	}

	//support relative path
	workingDir, _ := os.Getwd()
	goPathList := filepath.SplitList(gopath)
	for _, path := range goPathList {
		if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
			srcRoot = path
			break
		}

		path, _ = filepath.EvalSymlinks(path)
		if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
			srcRoot = path
			break
		}
	}

	if len(srcRoot) == 0 {
		fmt.Println("Abort: could not create a Mercurius application outside of GOPATH.")
		os.Exit(-1)
	}

	// set go src path
	srcRoot = filepath.Join(srcRoot, "src")
}

func setApplicationPath() {
	var err error
	appName = terminal("What is your application name?", "")
	gitPath := terminal("What is your git or mercurial host?", "github.com")
	gitUser := terminal("What is your git or mercurial username?", "")

	//check if gitUser is not empty to put gitUser between slashes
	if gitUser != "" {
		gitUser = fmt.Sprintf("/%s", gitUser)
	}

	//build import path
	importPath = fmt.Sprintf("%s%s/%s", gitPath, gitUser, appName)

	//check if import path is valid
	if importPath == "" {
		fmt.Println("Abort: could not create a Mercurius application with empty application path.")
		os.Exit(-1)
	}

	// validate relative path, we cannot use built-in functions
	// since Go import path is valid relative path too.
	// so check basic part of the path, which is "."
	if filepath.IsAbs(importPath) || strings.HasPrefix(importPath, ".") {
		fmt.Printf("Abort: '%s' looks like a directory.  Please provide a Go import path instead.\n", importPath)
	}

	_, err = build.Import(importPath, "", build.FindOnly)
	if err == nil {
		fmt.Printf("Abort: Import path %s already exists.\n", importPath)
	}

	mercuriusPkg, err = build.Import(mercuriusPath, "", build.FindOnly)
	if err != nil {
		fmt.Printf("Abort: Could not find Mercurius source code: %s\n", err)
	}

	appPath = filepath.Join(srcRoot, filepath.FromSlash(importPath))
	basePath = filepath.ToSlash(filepath.Dir(importPath))

	if basePath == "." {
		// we need to remove the a single '.' when
		// the app is in the $GOROOT/src directory
		basePath = ""
	} else {
		// we need to append a '/' when the app is
		// is a subdirectory such as $GOROOT/src/path/to/mercurius
		basePath += "/"
	}
	// set base project path
	skeletonPath = filepath.Join(mercuriusPkg.Dir, "skeleton")
}

func copyNewAppFiles(cfgs map[string]interface{}) {
	var err error
	err = os.MkdirAll(appPath, 0777)
	if err != nil {
		panic(err)
	}

	mustCopyDir(appPath, skeletonPath, cfgs)

	// Dotfiles are skipped by mustCopyDir, so we have to explicitly copy the .gitignore.
	gitignore := ".gitignore"
	mustCopyFile(filepath.Join(appPath, gitignore), filepath.Join(skeletonPath, gitignore))

}

func confValues() map[string]interface{} {
	// Define default value for cache types
	// See https://go-macaron.com/docs/middlewares/cache for details
	cacheMap := map[string]string{
		"memory":   "",
		"file":     "data/caches",
		"redis":    "addr=127.0.0.1:6379",
		"memcache": "127.0.0.1:11211",
	}

	// define configs
	cfgs := make(map[string]interface{})
	cfgs["AppPath"] = importPath
	cfgs["AppName"] = appName
	cfgs["DBType"] = terminal("What database do you want to use?", "mysql")
	cfgs["DBUser"] = terminal("What is your database user?", "root")
	cfgs["DBPw"] = terminal("What is your database password?", "")
	cfgs["DBName"] = terminal("What is your database name?", "")
	cfgs["DBHost"] = terminal("What is your database host?", "localhost")
	cfgs["DBPort"] = terminal("What is your database port?", "3306")
	cfgs["MaxConn"] = terminal("What is your database max connection pool?", "10")
	cfgs["IdleConn"] = terminal("What is your database idle connection pool?", "10")
	cache := terminal("What cache do you want to use?", "memory")
	cfgs["CacheType"] = cache
	cfgs["CacheCfgs"] = terminal("What is your cache server address?", cacheMap[cache])
	cfgs["Key"] = terminal("What is your oauth key", "")
	return cfgs
}

func init() {
	RootCmd.AddCommand(newCmd)
}
<|endoftext|>


<commit_before>// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/unknwon/com"
	"github.com/unknwon/log"
	"github.com/urfave/cli"
	"gopkg.in/fsnotify/fsnotify.v1"

	"github.com/unknwon/bra/modules/setting"
)

var (
	lastBuild time.Time
	eventTime = make(map[string]int64)

	runningCmd  *exec.Cmd
	runningLock = &sync.Mutex{}
	shutdown    = make(chan bool)
)

var CmdRun = cli.Command{
	Name:   "run",
	Usage:  "start monitoring and notifying",
	Action: runRun,
	Flags:  []cli.Flag{},
}

// isTmpFile returns true if the event was for temporary files.
func isTmpFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

// hasWatchExt returns true if the file name has watched extension.
func hasWatchExt(name string) bool {
	for _, ext := range setting.Cfg.Run.WatchExts {
		if strings.HasSuffix(name, ext) {
			return true
		}
	}
	return false
}

// runCommand represents a command to run after notified.
type runCommand struct {
	Envs []string
	Name string
	Args []string

	osEnvAdded bool
}

func (cmd *runCommand) String() string {
	if len(cmd.Envs) > 0 {
		return fmt.Sprintf("%v %s %v", cmd.Envs, cmd.Name, cmd.Args)
	}
	return fmt.Sprintf("%s %v", cmd.Name, cmd.Args)
}

func parseRunCommand(args []string) *runCommand {
	runCmd := new(runCommand)
	i := 0
	for _, arg := range args {
		if !strings.Contains(arg, "=") {
			break
		}
		runCmd.Envs = append(runCmd.Envs, arg)
		i++
	}

	if len(runCmd.Envs) > 0 {
		runCmd.osEnvAdded = true
		runCmd.Envs = append(runCmd.Envs, os.Environ()...)
	}

	runCmd.Name = args[i]
	runCmd.Args = args[i+1:]
	return runCmd
}

func parseRunCommands(cmds [][]string) []*runCommand {
	runCmds := make([]*runCommand, len(cmds))
	for i, args := range cmds {
		runCmds[i] = parseRunCommand(args)
	}
	return runCmds
}

func envFromFiles() []string {
	envs := make([]string, 0)

	for _, envFile := range setting.Cfg.Run.EnvFiles {
		b, err := ioutil.ReadFile(envFile)
		if err != nil {
			log.Warn("Fail to read env file %q: %v", envFile, err)
			continue
		}

		envs = append(envs, strings.Split(string(b), "\n")...)
	}

	return envs
}

func notify(cmds []*runCommand) {
	runningLock.Lock()
	defer func() {
		runningCmd = nil
		runningLock.Unlock()
	}()

	for _, cmd := range cmds {
		command := exec.Command(cmd.Name, cmd.Args...)
		command.Env = cmd.Envs

		envFromFiles := envFromFiles()
		if len(envFromFiles) > 0 {
			command.Env = append(command.Env, envFromFiles...)
			if !cmd.osEnvAdded {
				command.Env = append(command.Env, os.Environ()...)
			}
		}

		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		if err := command.Start(); err != nil {
			log.Error("Fail to start command: %v - %v", cmd, err)
			fmt.Print("\x07")
			return
		}

		log.Debug("Running: %s", cmd)
		runningCmd = command
		done := make(chan error)
		go func() {
			done <- command.Wait()
		}()

		isShutdown := false
		select {
		case err := <-done:
			if isShutdown {
				return
			} else if err != nil {
				log.Warn("Fail to execute command: %v - %v", cmd, err)
				fmt.Print("\x07")
				return
			}
		case <-shutdown:
			isShutdown = true
			gracefulKill()
			return
		}
	}
	log.Info("Notify operations are done!")
}

func gracefulKill() {
	// Directly kill the process on Windows or under request.
	if runtime.GOOS == "windows" || !setting.Cfg.Run.GracefulKill {
		runningCmd.Process.Kill()
		return
	}

	// Given process a chance to exit itself.
	runningCmd.Process.Signal(os.Interrupt)

	// Wait for timeout, and force kill after that.
	for i := 0; i < setting.Cfg.Run.InterruptTimeout; i++ {
		time.Sleep(1 * time.Second)

		if runningCmd.ProcessState == nil || runningCmd.ProcessState.Exited() {
			return
		}
	}

	log.Info("Fail to graceful kill, force killing...")
	runningCmd.Process.Kill()
}

func catchSignals() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM)
	<-sigs

	if runningCmd != nil {
		shutdown <- true
	}
	os.Exit(0)
}

func runRun(ctx *cli.Context) error {
	setup(ctx)

	go catchSignals()
	go notify(parseRunCommands(setting.Cfg.Run.InitCmds))

	watchPathes := append([]string{setting.WorkDir}, setting.Cfg.Run.WatchDirs...)
	if setting.Cfg.Run.WatchAll {
		subdirs := make([]string, 0, 10)
		for _, dir := range watchPathes[1:] {
			var dirs []string
			var err error
			if setting.Cfg.Run.FollowSymlinks {
				dirs, err = com.LgetAllSubDirs(setting.UnpackPath(dir))
			} else {
				dirs, err = com.GetAllSubDirs(setting.UnpackPath(dir))
			}

			if err != nil {
				log.Fatal("Fail to get sub-directories: %v", err)
			}

			for i := range dirs {
				if !setting.IgnoreDir(dirs[i]) {
					subdirs = append(subdirs, path.Join(dir, dirs[i]))
				}
			}
		}
		watchPathes = append(watchPathes, subdirs...)
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Fail to create new watcher: %v", err)
	}
	defer watcher.Close()

	go func() {
		runCmds := parseRunCommands(setting.Cfg.Run.Cmds)

		for {
			select {
			case e := <-watcher.Events:
				needsNotify := true

				if isTmpFile(e.Name) || !hasWatchExt(e.Name) || setting.IgnoreFile(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if lastBuild.Add(time.Duration(setting.Cfg.Run.BuildDelay) * time.Millisecond).
					After(time.Now()) {
					continue
				}
				lastBuild = time.Now()

				showName := e.String()
				if !log.NonColor {
					showName = strings.Replace(showName, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
				}

				if e.Op&fsnotify.Remove != fsnotify.Remove && e.Op&fsnotify.Rename != fsnotify.Rename {
					mt, err := com.FileMTime(e.Name)
					if err != nil {
						log.Error("Fail to get file modify time: %v", err)
						continue
					}
					if eventTime[e.Name] == mt {
						log.Debug("Skipped %s", showName)
						needsNotify = false
					}
					eventTime[e.Name] = mt
				}

				if needsNotify {
					log.Info(showName)
					if runningCmd != nil && runningCmd.Process != nil {
						if runningCmd.Args[0] == "sudo" && runtime.GOOS == "linux" {
							// 给父进程发送一个TERM信号，试图杀死它和它的子进程
							rootCmd := exec.Command("sudo", "kill", "-TERM", com.ToStr(runningCmd.Process.Pid))
							rootCmd.Stdout = os.Stdout
							rootCmd.Stderr = os.Stderr
							if err := rootCmd.Run(); err != nil {
								log.Error("Fail to start rootCmd %s", err.Error())
								fmt.Print("\x07")
							}
						} else {
							shutdown <- true
						}
					}
					go notify(runCmds)
				}
			}
		}
	}()

	log.Info("Following directories are monitored:")
	for i, p := range watchPathes {
		if err = watcher.Add(setting.UnpackPath(p)); err != nil {
			log.Fatal("Fail to watch directory(%s): %v", p, err)
		}
		if i > 0 && !log.NonColor {
			p = strings.Replace(p, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
			p = strings.Replace(p, "$WORKDIR", "\033[47;30m$WORKDIR\033[0m", 1)
		}
		fmt.Printf("-> %s\n", p)
	}
	select {}
	return nil
}
<commit_msg>Make env files export compatible (#29)<commit_after>// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/unknwon/com"
	"github.com/unknwon/log"
	"github.com/urfave/cli"
	"gopkg.in/fsnotify/fsnotify.v1"

	"github.com/unknwon/bra/modules/setting"
)

var (
	lastBuild time.Time
	eventTime = make(map[string]int64)

	runningCmd  *exec.Cmd
	runningLock = &sync.Mutex{}
	shutdown    = make(chan bool)
)

var CmdRun = cli.Command{
	Name:   "run",
	Usage:  "start monitoring and notifying",
	Action: runRun,
	Flags:  []cli.Flag{},
}

// isTmpFile returns true if the event was for temporary files.
func isTmpFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

// hasWatchExt returns true if the file name has watched extension.
func hasWatchExt(name string) bool {
	for _, ext := range setting.Cfg.Run.WatchExts {
		if strings.HasSuffix(name, ext) {
			return true
		}
	}
	return false
}

// runCommand represents a command to run after notified.
type runCommand struct {
	Envs []string
	Name string
	Args []string

	osEnvAdded bool
}

func (cmd *runCommand) String() string {
	if len(cmd.Envs) > 0 {
		return fmt.Sprintf("%v %s %v", cmd.Envs, cmd.Name, cmd.Args)
	}
	return fmt.Sprintf("%s %v", cmd.Name, cmd.Args)
}

func parseRunCommand(args []string) *runCommand {
	runCmd := new(runCommand)
	i := 0
	for _, arg := range args {
		if !strings.Contains(arg, "=") {
			break
		}
		runCmd.Envs = append(runCmd.Envs, arg)
		i++
	}

	if len(runCmd.Envs) > 0 {
		runCmd.osEnvAdded = true
		runCmd.Envs = append(runCmd.Envs, os.Environ()...)
	}

	runCmd.Name = args[i]
	runCmd.Args = args[i+1:]
	return runCmd
}

func parseRunCommands(cmds [][]string) []*runCommand {
	runCmds := make([]*runCommand, len(cmds))
	for i, args := range cmds {
		runCmds[i] = parseRunCommand(args)
	}
	return runCmds
}

func envFromFiles() []string {
	envs := make([]string, 0)

	for _, envFile := range setting.Cfg.Run.EnvFiles {
		b, err := ioutil.ReadFile(envFile)
		if err != nil {
			log.Warn("Fail to read env file %q: %v", envFile, err)
			continue
		}

		envLines := strings.Split(string(b), "\n")
		for _, env := range envLines {
			envs = append(envs, strings.TrimPrefix(env, "export "))
		}
	}

	return envs
}

func notify(cmds []*runCommand) {
	runningLock.Lock()
	defer func() {
		runningCmd = nil
		runningLock.Unlock()
	}()

	for _, cmd := range cmds {
		command := exec.Command(cmd.Name, cmd.Args...)
		command.Env = cmd.Envs

		envFromFiles := envFromFiles()
		if len(envFromFiles) > 0 {
			command.Env = append(command.Env, envFromFiles...)
			if !cmd.osEnvAdded {
				command.Env = append(command.Env, os.Environ()...)
			}
		}

		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		if err := command.Start(); err != nil {
			log.Error("Fail to start command: %v - %v", cmd, err)
			fmt.Print("\x07")
			return
		}

		log.Debug("Running: %s", cmd)
		runningCmd = command
		done := make(chan error)
		go func() {
			done <- command.Wait()
		}()

		isShutdown := false
		select {
		case err := <-done:
			if isShutdown {
				return
			} else if err != nil {
				log.Warn("Fail to execute command: %v - %v", cmd, err)
				fmt.Print("\x07")
				return
			}
		case <-shutdown:
			isShutdown = true
			gracefulKill()
			return
		}
	}
	log.Info("Notify operations are done!")
}

func gracefulKill() {
	// Directly kill the process on Windows or under request.
	if runtime.GOOS == "windows" || !setting.Cfg.Run.GracefulKill {
		runningCmd.Process.Kill()
		return
	}

	// Given process a chance to exit itself.
	runningCmd.Process.Signal(os.Interrupt)

	// Wait for timeout, and force kill after that.
	for i := 0; i < setting.Cfg.Run.InterruptTimeout; i++ {
		time.Sleep(1 * time.Second)

		if runningCmd.ProcessState == nil || runningCmd.ProcessState.Exited() {
			return
		}
	}

	log.Info("Fail to graceful kill, force killing...")
	runningCmd.Process.Kill()
}

func catchSignals() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM)
	<-sigs

	if runningCmd != nil {
		shutdown <- true
	}
	os.Exit(0)
}

func runRun(ctx *cli.Context) error {
	setup(ctx)

	go catchSignals()
	go notify(parseRunCommands(setting.Cfg.Run.InitCmds))

	watchPathes := append([]string{setting.WorkDir}, setting.Cfg.Run.WatchDirs...)
	if setting.Cfg.Run.WatchAll {
		subdirs := make([]string, 0, 10)
		for _, dir := range watchPathes[1:] {
			var dirs []string
			var err error
			if setting.Cfg.Run.FollowSymlinks {
				dirs, err = com.LgetAllSubDirs(setting.UnpackPath(dir))
			} else {
				dirs, err = com.GetAllSubDirs(setting.UnpackPath(dir))
			}

			if err != nil {
				log.Fatal("Fail to get sub-directories: %v", err)
			}

			for i := range dirs {
				if !setting.IgnoreDir(dirs[i]) {
					subdirs = append(subdirs, path.Join(dir, dirs[i]))
				}
			}
		}
		watchPathes = append(watchPathes, subdirs...)
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Fail to create new watcher: %v", err)
	}
	defer watcher.Close()

	go func() {
		runCmds := parseRunCommands(setting.Cfg.Run.Cmds)

		for {
			select {
			case e := <-watcher.Events:
				needsNotify := true

				if isTmpFile(e.Name) || !hasWatchExt(e.Name) || setting.IgnoreFile(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if lastBuild.Add(time.Duration(setting.Cfg.Run.BuildDelay) * time.Millisecond).
					After(time.Now()) {
					continue
				}
				lastBuild = time.Now()

				showName := e.String()
				if !log.NonColor {
					showName = strings.Replace(showName, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
				}

				if e.Op&fsnotify.Remove != fsnotify.Remove && e.Op&fsnotify.Rename != fsnotify.Rename {
					mt, err := com.FileMTime(e.Name)
					if err != nil {
						log.Error("Fail to get file modify time: %v", err)
						continue
					}
					if eventTime[e.Name] == mt {
						log.Debug("Skipped %s", showName)
						needsNotify = false
					}
					eventTime[e.Name] = mt
				}

				if needsNotify {
					log.Info(showName)
					if runningCmd != nil && runningCmd.Process != nil {
						if runningCmd.Args[0] == "sudo" && runtime.GOOS == "linux" {
							// 给父进程发送一个TERM信号，试图杀死它和它的子进程
							rootCmd := exec.Command("sudo", "kill", "-TERM", com.ToStr(runningCmd.Process.Pid))
							rootCmd.Stdout = os.Stdout
							rootCmd.Stderr = os.Stderr
							if err := rootCmd.Run(); err != nil {
								log.Error("Fail to start rootCmd %s", err.Error())
								fmt.Print("\x07")
							}
						} else {
							shutdown <- true
						}
					}
					go notify(runCmds)
				}
			}
		}
	}()

	log.Info("Following directories are monitored:")
	for i, p := range watchPathes {
		if err = watcher.Add(setting.UnpackPath(p)); err != nil {
			log.Fatal("Fail to watch directory(%s): %v", p, err)
		}
		if i > 0 && !log.NonColor {
			p = strings.Replace(p, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
			p = strings.Replace(p, "$WORKDIR", "\033[47;30m$WORKDIR\033[0m", 1)
		}
		fmt.Printf("-> %s\n", p)
	}
	select {}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ldap

import (
	"errors"
	"github.com/mavricknz/asn1-ber"
)

/*
CompareRequest ::= [APPLICATION 14] SEQUENCE {
    entry           LDAPDN,
    ava             AttributeValueAssertion }

AttributeValueAssertion ::= SEQUENCE {
    attributeDesc   AttributeDescription,
    assertionValue  AssertionValue }
*/

type CompareRequest struct {
	DN       string
	Name     string
	Value    string
	Controls []Control
}

func (l *LDAPConnection) Compare(req *CompareRequest) *Error {
	messageID, ok := l.nextMessageID()
	if !ok {
		return NewError(ErrorClosing, errors.New("MessageID channel is closed."))
	}

	encodedCompare, err := encodeCompareRequest(req)
	if err != nil {
		return err
	}

	packet, err := requestBuildPacket(messageID, encodedCompare, req.Controls)
	if err != nil {
		return err
	}

	// CompareTrue = 6, CompareFalse = 5
	// returns an "Error"
	return l.sendReqRespPacket(messageID, packet)
}

func encodeCompareRequest(req *CompareRequest) (*ber.Packet, *Error) {
	p := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, ApplicationMap[ApplicationCompareRequest])
	p.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimative, ber.TagOctetString, req.DN, "LDAP DN"))
	ava, err := encodeItem([]string{req.Name, "=", req.Value})
	if err != nil {
		return nil, err
	}
	p.AppendChild(ava)
	return p, nil
}

func NewCompareRequest(dn, name, value string) (req *CompareRequest) {
	req = &CompareRequest{DN: dn, Name: name, Value: value, Controls: make([]Control, 0)}
	return
}
<commit_msg>Changed return to bool, error. Easier to handle.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ldap

import (
	"github.com/mavricknz/asn1-ber"
)

/*
CompareRequest ::= [APPLICATION 14] SEQUENCE {
    entry           LDAPDN,
    ava             AttributeValueAssertion }

AttributeValueAssertion ::= SEQUENCE {
    attributeDesc   AttributeDescription,
    assertionValue  AssertionValue }
*/

type CompareRequest struct {
	DN       string
	Name     string
	Value    string
	Controls []Control
}

func (l *LDAPConnection) Compare(req *CompareRequest) (bool, error) {
	messageID, ok := l.nextMessageID()
	if !ok {
		return false, NewLDAPError(ErrorClosing, "MessageID channel is closed.")
	}

	encodedCompare, err := encodeCompareRequest(req)
	if err != nil {
		return false, err
	}

	packet, err := requestBuildPacket(messageID, encodedCompare, req.Controls)
	if err != nil {
		return false, err
	}

	// CompareTrue = 6, CompareFalse = 5
	// returns an "Error"
	err = l.sendReqRespPacket(messageID, packet)
	if lerr, ok := err.(*LDAPError); ok {
		return lerr.ResultCode == LDAPResultCompareTrue, nil
	} else {
		return false, err
	}
	//return l.sendReqRespPacket(messageID, packet)
}

func encodeCompareRequest(req *CompareRequest) (*ber.Packet, error) {
	p := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, ApplicationMap[ApplicationCompareRequest])
	p.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimative, ber.TagOctetString, req.DN, "LDAP DN"))
	ava, err := encodeItem([]string{req.Name, "=", req.Value})
	if err != nil {
		return nil, err
	}
	p.AppendChild(ava)
	return p, nil
}

func NewCompareRequest(dn, name, value string) (req *CompareRequest) {
	req = &CompareRequest{DN: dn, Name: name, Value: value, Controls: make([]Control, 0)}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Dataence, LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package compare

import (
	"fmt"
	"reflect"

	"github.com/surge/glog"
)

type Comparator func(k1, k2 interface{}) (bool, error)

var (
	BuiltinLessThan    Comparator = builtinLessThan
	BuiltinGreaterThan Comparator = builtinGreaterThan
	BuiltinEqual       Comparator = builtinEqual
)

func builtinLessThan(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		return false, fmt.Errorf("skiplist/BuiltinLessThan: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 < k2.(string), nil

	case int64:
		return k1 < k2.(int64), nil

	case int32:
		return k1 < k2.(int32), nil

	case int16:
		return k1 < k2.(int16), nil

	case int8:
		return k1 < k2.(int8), nil

	case int:
		return k1 < k2.(int), nil

	case float32:
		return k1 < k2.(float32), nil

	case float64:
		return k1 < k2.(float64), nil

	case uint:
		return k1 < k2.(uint), nil

	case uint8:
		return k1 < k2.(uint8), nil

	case uint16:
		return k1 < k2.(uint16), nil

	case uint32:
		return k1 < k2.(uint32), nil

	case uint64:
		return k1 < k2.(uint64), nil

	case uintptr:
		return k1 < k2.(uintptr), nil
	}

	return false, fmt.Errorf("skiplist/BuiltinLessThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}

func builtinGreaterThan(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		return false, fmt.Errorf("skiplist/BuiltinGreaterThan: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 > k2.(string), nil

	case int64:
		return k1 > k2.(int64), nil

	case int32:
		return k1 > k2.(int32), nil

	case int16:
		return k1 > k2.(int16), nil

	case int8:
		return k1 > k2.(int8), nil

	case int:
		return k1 > k2.(int), nil

	case float32:
		return k1 > k2.(float32), nil

	case float64:
		return k1 > k2.(float64), nil

	case uint:
		return k1 > k2.(uint), nil

	case uint8:
		return k1 > k2.(uint8), nil

	case uint16:
		return k1 > k2.(uint16), nil

	case uint32:
		return k1 > k2.(uint32), nil

	case uint64:
		return k1 > k2.(uint64), nil

	case uintptr:
		return k1 > k2.(uintptr), nil
	}

	return false, fmt.Errorf("skiplist/BuiltinGreaterThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}

func builtinEqual(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		glog.Debugf("skiplist/BuiltinEqual: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
		return false, fmt.Errorf("skiplist/BuiltinEqual: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 == k2.(string), nil

	case int64:
		return k1 == k2.(int64), nil

	case int32:
		return k1 == k2.(int32), nil

	case int16:
		return k1 == k2.(int16), nil

	case int8:
		return k1 == k2.(int8), nil

	case int:
		return k1 == k2.(int), nil

	case float32:
		return k1 == k2.(float32), nil

	case float64:
		return k1 == k2.(float64), nil

	case uint:
		return k1 == k2.(uint), nil

	case uint8:
		return k1 == k2.(uint8), nil

	case uint16:
		return k1 == k2.(uint16), nil

	case uint32:
		return k1 == k2.(uint32), nil

	case uint64:
		return k1 == k2.(uint64), nil

	case uintptr:
		return k1 == k2.(uintptr), nil
	}

	glog.Debugf("skiplist/BuiltinLessThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	return false, fmt.Errorf("skiplist/BuiltinLessThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}
<commit_msg>argh, removing glog.Debugf<commit_after>// Copyright (c) 2014 Dataence, LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package compare

import (
	"fmt"
	"reflect"
)

type Comparator func(k1, k2 interface{}) (bool, error)

var (
	BuiltinLessThan    Comparator = builtinLessThan
	BuiltinGreaterThan Comparator = builtinGreaterThan
	BuiltinEqual       Comparator = builtinEqual
)

func builtinLessThan(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		return false, fmt.Errorf("skiplist/BuiltinLessThan: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 < k2.(string), nil

	case int64:
		return k1 < k2.(int64), nil

	case int32:
		return k1 < k2.(int32), nil

	case int16:
		return k1 < k2.(int16), nil

	case int8:
		return k1 < k2.(int8), nil

	case int:
		return k1 < k2.(int), nil

	case float32:
		return k1 < k2.(float32), nil

	case float64:
		return k1 < k2.(float64), nil

	case uint:
		return k1 < k2.(uint), nil

	case uint8:
		return k1 < k2.(uint8), nil

	case uint16:
		return k1 < k2.(uint16), nil

	case uint32:
		return k1 < k2.(uint32), nil

	case uint64:
		return k1 < k2.(uint64), nil

	case uintptr:
		return k1 < k2.(uintptr), nil
	}

	return false, fmt.Errorf("skiplist/BuiltinLessThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}

func builtinGreaterThan(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		return false, fmt.Errorf("skiplist/BuiltinGreaterThan: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 > k2.(string), nil

	case int64:
		return k1 > k2.(int64), nil

	case int32:
		return k1 > k2.(int32), nil

	case int16:
		return k1 > k2.(int16), nil

	case int8:
		return k1 > k2.(int8), nil

	case int:
		return k1 > k2.(int), nil

	case float32:
		return k1 > k2.(float32), nil

	case float64:
		return k1 > k2.(float64), nil

	case uint:
		return k1 > k2.(uint), nil

	case uint8:
		return k1 > k2.(uint8), nil

	case uint16:
		return k1 > k2.(uint16), nil

	case uint32:
		return k1 > k2.(uint32), nil

	case uint64:
		return k1 > k2.(uint64), nil

	case uintptr:
		return k1 > k2.(uintptr), nil
	}

	return false, fmt.Errorf("skiplist/BuiltinGreaterThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}

func builtinEqual(k1, k2 interface{}) (bool, error) {
	if reflect.TypeOf(k1) != reflect.TypeOf(k2) {
		return false, fmt.Errorf("skiplist/BuiltinEqual: k1.(%s) and k2.(%s) have different types",
			reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
	}

	switch k1 := k1.(type) {
	case string:
		return k1 == k2.(string), nil

	case int64:
		return k1 == k2.(int64), nil

	case int32:
		return k1 == k2.(int32), nil

	case int16:
		return k1 == k2.(int16), nil

	case int8:
		return k1 == k2.(int8), nil

	case int:
		return k1 == k2.(int), nil

	case float32:
		return k1 == k2.(float32), nil

	case float64:
		return k1 == k2.(float64), nil

	case uint:
		return k1 == k2.(uint), nil

	case uint8:
		return k1 == k2.(uint8), nil

	case uint16:
		return k1 == k2.(uint16), nil

	case uint32:
		return k1 == k2.(uint32), nil

	case uint64:
		return k1 == k2.(uint64), nil

	case uintptr:
		return k1 == k2.(uintptr), nil
	}

	return false, fmt.Errorf("skiplist/BuiltinLessThan: unsupported types for k1.(%s) and k2.(%s)",
		reflect.TypeOf(k1).Name(), reflect.TypeOf(k2).Name())
}
<|endoftext|>


<commit_before>// Provide functions for CMUDict management.
package cmudict

import (
	"bufio"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
)

// DefaultDictPath gets the CMU dictionary file location from the CMUDICT_DATA environment variable.
func DefaultDictPath() string {
	dir := os.Getenv("CMUDICT_DATA")
	if dir == "" {
		log.Fatal("Set CMUDICT_DATA variable to directory of dictionary file")
	}
	return os.Join(dir, "cmudict.0.7a")
}

// DefaultSymbolPath gets the CMU symbols file location from the CMUDICT_DATA environment variable.
func DefaultSymbolPath() string {
	dir := os.Getenv("CMUDICT_DATA")
	if dir == "" {
		log.Fatal("Set CMUDICT_DATA variable to directory of symbols file")
	}
	return os.Join(dir, "cmudict.0.7a.symbols")
}

// GetPhonemes returns the list of phonemes from a string, with or without accents.
func GetPhonemes(pron string, accent bool) []string {
	phonemes := strings.Split(pron, " ")
	if !accent {
		for i, phoneme := range phonemes {
			phonemes[i] = StripAccent(phoneme)
		}
	}
	return phonemes
}

// HasAccent checks if a phoneme has an accent number.
func HasAccent(phoneme string) bool {
	n := len(phoneme)
	last := phoneme[n-1]
	return last == '0' || last == '1' || last == '2'
}

// IsVowel checks if a phoneme is a vowel, including ER.
func IsVowel(phoneme string) bool {
	first := phoneme[0]
	return first == 'A' || first == 'E' || first == 'I' || first == 'O' || first == 'U'
}

// LoadDict loads the CMU dictionary file and returns it as a map.
func LoadDict(file string) map[string]string {
	// Open file.
	handle, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// Scan file line by line.
	dict := make(map[string]string)
	scanner := bufio.NewScanner(handle)
	for scanner.Scan() {
		line := scanner.Text()
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}

		// Skip comments
		if line[:3] == ";;;" {
			continue
		}
		fields := strings.Split(line, "  ")
		word := fields[0]
		pron := fields[1]
		dict[word] = pron
	}
	return dict
}

// LoadSymbols loads the CMU symbols file and returns it as a map.
// This function removes the vowels symbols without accent numbers.
func LoadSymbols(file string, accent bool) map[string]bool {
	// Open file.
	handle, err := os.Open()
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// Scan file line by line.
	symbols := make(map[string]bool)
	scanner := bufio.NewScanner(handle)
	for scanner.Scan() {
		phoneme := scanner.Text()
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
		if IsVowel(phoneme) {
			if HasAccent(phoneme) {
				if !accent {
					// Strip accents since requested to do so.
					phoneme = StripAccent(phoneme)
				}
			} else {
				// Remove vowel symbols that already had no accent numbers.
				continue
			}
		}
		symbols[phoneme] = true
	}
	return symbols
}

// StripAccent removes the accent number from a phoneme.
func StripAccent(phoneme string) string {
	n := len(phoneme)
	last := phoneme[n-1]
	if last == '0' || last == '1' || last == '2' {
		phoneme = phoneme[:n-1]
	}
	return phoneme
}

// StripIndex removes the index number in parentheses from the end of a word.
func StripIndex(word string) string {
	n := len(word)
	if n > 3 {
		last := word[n-1]
		if last == ')' {
			next := word[n-3]
			if next == '(' {
				word = word[:n-3]
			}
		}
	}
	return word
}
<commit_msg>Bug fixes<commit_after>// Provide functions for CMUDict management.
package cmudict

import (
	"bufio"
	"log"
	"os"
	"path"
	"strings"
)

// DefaultDictPath gets the CMU dictionary file location from the CMUDICT_DATA environment variable.
func DefaultDictPath() string {
	dir := os.Getenv("CMUDICT_DATA")
	if dir == "" {
		log.Fatal("Set CMUDICT_DATA variable to directory of dictionary file")
	}
	return path.Join(dir, "cmudict.0.7a")
}

// DefaultSymbolPath gets the CMU symbols file location from the CMUDICT_DATA environment variable.
func DefaultSymbolPath() string {
	dir := os.Getenv("CMUDICT_DATA")
	if dir == "" {
		log.Fatal("Set CMUDICT_DATA variable to directory of symbols file")
	}
	return path.Join(dir, "cmudict.0.7a.symbols")
}

// GetPhonemes returns the list of phonemes from a string, with or without accents.
func GetPhonemes(pron string, accent bool) []string {
	phonemes := strings.Split(pron, " ")
	if !accent {
		for i, phoneme := range phonemes {
			phonemes[i] = StripAccent(phoneme)
		}
	}
	return phonemes
}

// HasAccent checks if a phoneme has an accent number.
func HasAccent(phoneme string) bool {
	n := len(phoneme)
	last := phoneme[n-1]
	return last == '0' || last == '1' || last == '2'
}

// IsVowel checks if a phoneme is a vowel, including ER.
func IsVowel(phoneme string) bool {
	first := phoneme[0]
	return first == 'A' || first == 'E' || first == 'I' || first == 'O' || first == 'U'
}

// LoadDict loads the CMU dictionary file and returns it as a map.
func LoadDict(file string) map[string]string {
	// Open file.
	handle, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// Scan file line by line.
	dict := make(map[string]string)
	scanner := bufio.NewScanner(handle)
	for scanner.Scan() {
		line := scanner.Text()
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}

		// Skip comments
		if line[:3] == ";;;" {
			continue
		}
		fields := strings.Split(line, "  ")
		word := fields[0]
		pron := fields[1]
		dict[word] = pron
	}
	return dict
}

// LoadSymbols loads the CMU symbols file and returns it as a map.
// This function removes the vowels symbols without accent numbers.
func LoadSymbols(file string, accent bool) map[string]bool {
	// Open file.
	handle, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// Scan file line by line.
	symbols := make(map[string]bool)
	scanner := bufio.NewScanner(handle)
	for scanner.Scan() {
		phoneme := scanner.Text()
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
		if IsVowel(phoneme) {
			if HasAccent(phoneme) {
				if !accent {
					// Strip accents since requested to do so.
					phoneme = StripAccent(phoneme)
				}
			} else {
				// Remove vowel symbols that already had no accent numbers.
				continue
			}
		}
		symbols[phoneme] = true
	}
	return symbols
}

// StripAccent removes the accent number from a phoneme.
func StripAccent(phoneme string) string {
	n := len(phoneme)
	last := phoneme[n-1]
	if last == '0' || last == '1' || last == '2' {
		phoneme = phoneme[:n-1]
	}
	return phoneme
}

// StripIndex removes the index number in parentheses from the end of a word.
func StripIndex(word string) string {
	n := len(word)
	if n > 3 {
		last := word[n-1]
		if last == ')' {
			next := word[n-3]
			if next == '(' {
				word = word[:n-3]
			}
		}
	}
	return word
}
<|endoftext|>


<commit_before>// Copyright ©2013 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mat

import (
	"gonum.org/v1/gonum/lapack"
	"gonum.org/v1/gonum/lapack/lapack64"
)

const (
	badFact   = "mat: use without successful factorization"
	noVectors = "mat: eigenvectors not computed"
)

// EigenSym is a type for creating and manipulating the Eigen decomposition of
// symmetric matrices.
type EigenSym struct {
	vectorsComputed bool

	values  []float64
	vectors *Dense
}

// Factorize computes the eigenvalue decomposition of the symmetric matrix a.
// The Eigen decomposition is defined as
//  A = P * D * P^-1
// where D is a diagonal matrix containing the eigenvalues of the matrix, and
// P is a matrix of the eigenvectors of A. Factorize computes the eigenvalues
// in ascending order. If the vectors input argument is false, the eigenvectors
// are not computed.
//
// Factorize returns whether the decomposition succeeded. If the decomposition
// failed, methods that require a successful factorization will panic.
func (e *EigenSym) Factorize(a Symmetric, vectors bool) (ok bool) {
	// kill previous decomposition
	e.vectorsComputed = false
	e.values = e.values[:]

	n := a.Symmetric()
	sd := NewSymDense(n, nil)
	sd.CopySym(a)

	jobz := lapack.EVNone
	if vectors {
		jobz = lapack.EVCompute
	}
	w := make([]float64, n)
	work := []float64{0}
	lapack64.Syev(jobz, sd.mat, w, work, -1)

	work = getFloat64s(int(work[0]), false)
	ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
	putFloat64s(work)
	if !ok {
		e.vectorsComputed = false
		e.values = nil
		e.vectors = nil
		return false
	}
	e.vectorsComputed = vectors
	e.values = w
	e.vectors = NewDense(n, n, sd.mat.Data)
	return true
}

// succFact returns whether the receiver contains a successful factorization.
func (e *EigenSym) succFact() bool {
	return len(e.values) != 0
}

// Values extracts the eigenvalues of the factorized matrix. If dst is
// non-nil, the values are stored in-place into dst. In this case
// dst must have length n, otherwise Values will panic. If dst is
// nil, then a new slice will be allocated of the proper length and filled
// with the eigenvalues.
//
// Values panics if the Eigen decomposition was not successful.
func (e *EigenSym) Values(dst []float64) []float64 {
	if !e.succFact() {
		panic(badFact)
	}
	if dst == nil {
		dst = make([]float64, len(e.values))
	}
	if len(dst) != len(e.values) {
		panic(ErrSliceLengthMismatch)
	}
	copy(dst, e.values)
	return dst
}

// VectorsTo stores the eigenvectors of the decomposition into the columns of
// dst.
//
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is
// non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
// panic if the eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization.
func (e *EigenSym) VectorsTo(dst *Dense) {
	if !e.succFact() {
		panic(badFact)
	}
	if !e.vectorsComputed {
		panic(noVectors)
	}
	r, c := e.vectors.Dims()
	if dst.IsEmpty() {
		dst.ReuseAs(r, c)
	} else {
		r2, c2 := dst.Dims()
		if r != r2 || c != c2 {
			panic(ErrShape)
		}
	}
	dst.Copy(e.vectors)
}

// EigenKind specifies the computation of eigenvectors during factorization.
type EigenKind int

const (
	// EigenNone specifies to not compute any eigenvectors.
	EigenNone EigenKind = 0
	// EigenLeft specifies to compute the left eigenvectors.
	EigenLeft EigenKind = 1 << iota
	// EigenRight specifies to compute the right eigenvectors.
	EigenRight
	// EigenBoth is a convenience value for computing both eigenvectors.
	EigenBoth EigenKind = EigenLeft | EigenRight
)

// Eigen is a type for creating and using the eigenvalue decomposition of a dense matrix.
type Eigen struct {
	n int // The size of the factorized matrix.

	kind EigenKind

	values   []complex128
	rVectors *CDense
	lVectors *CDense
}

// succFact returns whether the receiver contains a successful factorization.
func (e *Eigen) succFact() bool {
	return e.n != 0
}

// Factorize computes the eigenvalues of the square matrix a, and optionally
// the eigenvectors.
//
// A right eigenvalue/eigenvector combination is defined by
//  A * x_r = λ * x_r
// where x_r is the column vector called an eigenvector, and λ is the corresponding
// eigenvalue.
//
// Similarly, a left eigenvalue/eigenvector combination is defined by
//  x_l * A = λ * x_l
// The eigenvalues, but not the eigenvectors, are the same for both decompositions.
//
// Typically eigenvectors refer to right eigenvectors.
//
// In all cases, Factorize computes the eigenvalues of the matrix. kind
// specifies which of the eigenvectors, if any, to compute. See the EigenKind
// documentation for more information.
// Eigen panics if the input matrix is not square.
//
// Factorize returns whether the decomposition succeeded. If the decomposition
// failed, methods that require a successful factorization will panic.
func (e *Eigen) Factorize(a Matrix, kind EigenKind) (ok bool) {
	// kill previous factorization.
	e.n = 0
	e.kind = 0
	// Copy a because it is modified during the Lapack call.
	r, c := a.Dims()
	if r != c {
		panic(ErrShape)
	}
	var sd Dense
	sd.CloneFrom(a)

	left := kind&EigenLeft != 0
	right := kind&EigenRight != 0

	var vl, vr Dense
	jobvl := lapack.LeftEVNone
	jobvr := lapack.RightEVNone
	if left {
		vl = *NewDense(r, r, nil)
		jobvl = lapack.LeftEVCompute
	}
	if right {
		vr = *NewDense(c, c, nil)
		jobvr = lapack.RightEVCompute
	}

	wr := getFloat64s(c, false)
	defer putFloat64s(wr)
	wi := getFloat64s(c, false)
	defer putFloat64s(wi)

	work := []float64{0}
	lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
	work = getFloat64s(int(work[0]), false)
	first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
	putFloat64s(work)

	if first != 0 {
		e.values = nil
		return false
	}
	e.n = r
	e.kind = kind

	// Construct complex eigenvalues from float64 data.
	values := make([]complex128, r)
	for i, v := range wr {
		values[i] = complex(v, wi[i])
	}
	e.values = values

	// Construct complex eigenvectors from float64 data.
	var cvl, cvr CDense
	if left {
		cvl = *NewCDense(r, r, nil)
		e.complexEigenTo(&cvl, &vl)
		e.lVectors = &cvl
	} else {
		e.lVectors = nil
	}
	if right {
		cvr = *NewCDense(c, c, nil)
		e.complexEigenTo(&cvr, &vr)
		e.rVectors = &cvr
	} else {
		e.rVectors = nil
	}
	return true
}

// Kind returns the EigenKind of the decomposition. If no decomposition has been
// computed, Kind returns -1.
func (e *Eigen) Kind() EigenKind {
	if !e.succFact() {
		return -1
	}
	return e.kind
}

// Values extracts the eigenvalues of the factorized matrix. If dst is
// non-nil, the values are stored in-place into dst. In this case
// dst must have length n, otherwise Values will panic. If dst is
// nil, then a new slice will be allocated of the proper length and
// filed with the eigenvalues.
//
// Values panics if the Eigen decomposition was not successful.
func (e *Eigen) Values(dst []complex128) []complex128 {
	if !e.succFact() {
		panic(badFact)
	}
	if dst == nil {
		dst = make([]complex128, e.n)
	}
	if len(dst) != e.n {
		panic(ErrSliceLengthMismatch)
	}
	copy(dst, e.values)
	return dst
}

// complexEigenTo extracts the complex eigenvectors from the real matrix d
// and stores them into the complex matrix dst.
//
// The columns of the returned n×n dense matrix contain the eigenvectors of the
// decomposition in the same order as the eigenvalues.
// If the j-th eigenvalue is real, then
//  dst[:,j] = d[:,j],
// and if it is not real, then the elements of the j-th and (j+1)-th columns of d
// form complex conjugate pairs and the eigenvectors are recovered as
//  dst[:,j]   = d[:,j] + i*d[:,j+1],
//  dst[:,j+1] = d[:,j] - i*d[:,j+1],
// where i is the imaginary unit.
func (e *Eigen) complexEigenTo(dst *CDense, d *Dense) {
	r, c := d.Dims()
	cr, cc := dst.Dims()
	if r != cr {
		panic("size mismatch")
	}
	if c != cc {
		panic("size mismatch")
	}
	for j := 0; j < c; j++ {
		if imag(e.values[j]) == 0 {
			for i := 0; i < r; i++ {
				dst.set(i, j, complex(d.at(i, j), 0))
			}
			continue
		}
		for i := 0; i < r; i++ {
			real := d.at(i, j)
			imag := d.at(i, j+1)
			dst.set(i, j, complex(real, imag))
			dst.set(i, j+1, complex(real, -imag))
		}
		j++
	}
}

// VectorsTo stores the right eigenvectors of the decomposition into the columns
// of dst. The computed eigenvectors are normalized to have Euclidean norm equal
// to 1 and largest component real.
//
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is
// non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
// panic if the eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization.
func (e *Eigen) VectorsTo(dst *CDense) {
	if !e.succFact() {
		panic(badFact)
	}
	if e.kind&EigenRight == 0 {
		panic(noVectors)
	}
	if dst.IsEmpty() {
		dst.ReuseAs(e.n, e.n)
	} else {
		r, c := dst.Dims()
		if r != e.n || c != e.n {
			panic(ErrShape)
		}
	}
	dst.Copy(e.rVectors)
}

// LeftVectorsTo stores the left eigenvectors of the decomposition into the
// columns of dst. The computed eigenvectors are normalized to have Euclidean
// norm equal to 1 and largest component real.
//
// If dst is empty, LeftVectorsTo will resize dst to be n×n. When dst is
// non-empty, LeftVectorsTo will panic if dst is not n×n. LeftVectorsTo will also
// panic if the left eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization
func (e *Eigen) LeftVectorsTo(dst *CDense) {
	if !e.succFact() {
		panic(badFact)
	}
	if e.kind&EigenLeft == 0 {
		panic(noVectors)
	}
	if dst.IsEmpty() {
		dst.ReuseAs(e.n, e.n)
	} else {
		r, c := dst.Dims()
		if r != e.n || c != e.n {
			panic(ErrShape)
		}
	}
	dst.Copy(e.lVectors)
}
<commit_msg>mat: clarify docs for EigenSym.Values<commit_after>// Copyright ©2013 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mat

import (
	"gonum.org/v1/gonum/lapack"
	"gonum.org/v1/gonum/lapack/lapack64"
)

const (
	badFact   = "mat: use without successful factorization"
	noVectors = "mat: eigenvectors not computed"
)

// EigenSym is a type for creating and manipulating the Eigen decomposition of
// symmetric matrices.
type EigenSym struct {
	vectorsComputed bool

	values  []float64
	vectors *Dense
}

// Factorize computes the eigenvalue decomposition of the symmetric matrix a.
// The Eigen decomposition is defined as
//  A = P * D * P^-1
// where D is a diagonal matrix containing the eigenvalues of the matrix, and
// P is a matrix of the eigenvectors of A. Factorize computes the eigenvalues
// in ascending order. If the vectors input argument is false, the eigenvectors
// are not computed.
//
// Factorize returns whether the decomposition succeeded. If the decomposition
// failed, methods that require a successful factorization will panic.
func (e *EigenSym) Factorize(a Symmetric, vectors bool) (ok bool) {
	// kill previous decomposition
	e.vectorsComputed = false
	e.values = e.values[:]

	n := a.Symmetric()
	sd := NewSymDense(n, nil)
	sd.CopySym(a)

	jobz := lapack.EVNone
	if vectors {
		jobz = lapack.EVCompute
	}
	w := make([]float64, n)
	work := []float64{0}
	lapack64.Syev(jobz, sd.mat, w, work, -1)

	work = getFloat64s(int(work[0]), false)
	ok = lapack64.Syev(jobz, sd.mat, w, work, len(work))
	putFloat64s(work)
	if !ok {
		e.vectorsComputed = false
		e.values = nil
		e.vectors = nil
		return false
	}
	e.vectorsComputed = vectors
	e.values = w
	e.vectors = NewDense(n, n, sd.mat.Data)
	return true
}

// succFact returns whether the receiver contains a successful factorization.
func (e *EigenSym) succFact() bool {
	return len(e.values) != 0
}

// Values extracts the eigenvalues of the factorized matrix in ascending order.
// If dst is non-nil, the values are stored in-place into dst. In this case dst
// must have length n, otherwise Values will panic. If dst is nil, then a new
// slice will be allocated of the proper length and filled with the eigenvalues.
//
// Values panics if the Eigen decomposition was not successful.
func (e *EigenSym) Values(dst []float64) []float64 {
	if !e.succFact() {
		panic(badFact)
	}
	if dst == nil {
		dst = make([]float64, len(e.values))
	}
	if len(dst) != len(e.values) {
		panic(ErrSliceLengthMismatch)
	}
	copy(dst, e.values)
	return dst
}

// VectorsTo stores the eigenvectors of the decomposition into the columns of
// dst.
//
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is
// non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
// panic if the eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization.
func (e *EigenSym) VectorsTo(dst *Dense) {
	if !e.succFact() {
		panic(badFact)
	}
	if !e.vectorsComputed {
		panic(noVectors)
	}
	r, c := e.vectors.Dims()
	if dst.IsEmpty() {
		dst.ReuseAs(r, c)
	} else {
		r2, c2 := dst.Dims()
		if r != r2 || c != c2 {
			panic(ErrShape)
		}
	}
	dst.Copy(e.vectors)
}

// EigenKind specifies the computation of eigenvectors during factorization.
type EigenKind int

const (
	// EigenNone specifies to not compute any eigenvectors.
	EigenNone EigenKind = 0
	// EigenLeft specifies to compute the left eigenvectors.
	EigenLeft EigenKind = 1 << iota
	// EigenRight specifies to compute the right eigenvectors.
	EigenRight
	// EigenBoth is a convenience value for computing both eigenvectors.
	EigenBoth EigenKind = EigenLeft | EigenRight
)

// Eigen is a type for creating and using the eigenvalue decomposition of a dense matrix.
type Eigen struct {
	n int // The size of the factorized matrix.

	kind EigenKind

	values   []complex128
	rVectors *CDense
	lVectors *CDense
}

// succFact returns whether the receiver contains a successful factorization.
func (e *Eigen) succFact() bool {
	return e.n != 0
}

// Factorize computes the eigenvalues of the square matrix a, and optionally
// the eigenvectors.
//
// A right eigenvalue/eigenvector combination is defined by
//  A * x_r = λ * x_r
// where x_r is the column vector called an eigenvector, and λ is the corresponding
// eigenvalue.
//
// Similarly, a left eigenvalue/eigenvector combination is defined by
//  x_l * A = λ * x_l
// The eigenvalues, but not the eigenvectors, are the same for both decompositions.
//
// Typically eigenvectors refer to right eigenvectors.
//
// In all cases, Factorize computes the eigenvalues of the matrix. kind
// specifies which of the eigenvectors, if any, to compute. See the EigenKind
// documentation for more information.
// Eigen panics if the input matrix is not square.
//
// Factorize returns whether the decomposition succeeded. If the decomposition
// failed, methods that require a successful factorization will panic.
func (e *Eigen) Factorize(a Matrix, kind EigenKind) (ok bool) {
	// kill previous factorization.
	e.n = 0
	e.kind = 0
	// Copy a because it is modified during the Lapack call.
	r, c := a.Dims()
	if r != c {
		panic(ErrShape)
	}
	var sd Dense
	sd.CloneFrom(a)

	left := kind&EigenLeft != 0
	right := kind&EigenRight != 0

	var vl, vr Dense
	jobvl := lapack.LeftEVNone
	jobvr := lapack.RightEVNone
	if left {
		vl = *NewDense(r, r, nil)
		jobvl = lapack.LeftEVCompute
	}
	if right {
		vr = *NewDense(c, c, nil)
		jobvr = lapack.RightEVCompute
	}

	wr := getFloat64s(c, false)
	defer putFloat64s(wr)
	wi := getFloat64s(c, false)
	defer putFloat64s(wi)

	work := []float64{0}
	lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, -1)
	work = getFloat64s(int(work[0]), false)
	first := lapack64.Geev(jobvl, jobvr, sd.mat, wr, wi, vl.mat, vr.mat, work, len(work))
	putFloat64s(work)

	if first != 0 {
		e.values = nil
		return false
	}
	e.n = r
	e.kind = kind

	// Construct complex eigenvalues from float64 data.
	values := make([]complex128, r)
	for i, v := range wr {
		values[i] = complex(v, wi[i])
	}
	e.values = values

	// Construct complex eigenvectors from float64 data.
	var cvl, cvr CDense
	if left {
		cvl = *NewCDense(r, r, nil)
		e.complexEigenTo(&cvl, &vl)
		e.lVectors = &cvl
	} else {
		e.lVectors = nil
	}
	if right {
		cvr = *NewCDense(c, c, nil)
		e.complexEigenTo(&cvr, &vr)
		e.rVectors = &cvr
	} else {
		e.rVectors = nil
	}
	return true
}

// Kind returns the EigenKind of the decomposition. If no decomposition has been
// computed, Kind returns -1.
func (e *Eigen) Kind() EigenKind {
	if !e.succFact() {
		return -1
	}
	return e.kind
}

// Values extracts the eigenvalues of the factorized matrix. If dst is
// non-nil, the values are stored in-place into dst. In this case
// dst must have length n, otherwise Values will panic. If dst is
// nil, then a new slice will be allocated of the proper length and
// filed with the eigenvalues.
//
// Values panics if the Eigen decomposition was not successful.
func (e *Eigen) Values(dst []complex128) []complex128 {
	if !e.succFact() {
		panic(badFact)
	}
	if dst == nil {
		dst = make([]complex128, e.n)
	}
	if len(dst) != e.n {
		panic(ErrSliceLengthMismatch)
	}
	copy(dst, e.values)
	return dst
}

// complexEigenTo extracts the complex eigenvectors from the real matrix d
// and stores them into the complex matrix dst.
//
// The columns of the returned n×n dense matrix contain the eigenvectors of the
// decomposition in the same order as the eigenvalues.
// If the j-th eigenvalue is real, then
//  dst[:,j] = d[:,j],
// and if it is not real, then the elements of the j-th and (j+1)-th columns of d
// form complex conjugate pairs and the eigenvectors are recovered as
//  dst[:,j]   = d[:,j] + i*d[:,j+1],
//  dst[:,j+1] = d[:,j] - i*d[:,j+1],
// where i is the imaginary unit.
func (e *Eigen) complexEigenTo(dst *CDense, d *Dense) {
	r, c := d.Dims()
	cr, cc := dst.Dims()
	if r != cr {
		panic("size mismatch")
	}
	if c != cc {
		panic("size mismatch")
	}
	for j := 0; j < c; j++ {
		if imag(e.values[j]) == 0 {
			for i := 0; i < r; i++ {
				dst.set(i, j, complex(d.at(i, j), 0))
			}
			continue
		}
		for i := 0; i < r; i++ {
			real := d.at(i, j)
			imag := d.at(i, j+1)
			dst.set(i, j, complex(real, imag))
			dst.set(i, j+1, complex(real, -imag))
		}
		j++
	}
}

// VectorsTo stores the right eigenvectors of the decomposition into the columns
// of dst. The computed eigenvectors are normalized to have Euclidean norm equal
// to 1 and largest component real.
//
// If dst is empty, VectorsTo will resize dst to be n×n. When dst is
// non-empty, VectorsTo will panic if dst is not n×n. VectorsTo will also
// panic if the eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization.
func (e *Eigen) VectorsTo(dst *CDense) {
	if !e.succFact() {
		panic(badFact)
	}
	if e.kind&EigenRight == 0 {
		panic(noVectors)
	}
	if dst.IsEmpty() {
		dst.ReuseAs(e.n, e.n)
	} else {
		r, c := dst.Dims()
		if r != e.n || c != e.n {
			panic(ErrShape)
		}
	}
	dst.Copy(e.rVectors)
}

// LeftVectorsTo stores the left eigenvectors of the decomposition into the
// columns of dst. The computed eigenvectors are normalized to have Euclidean
// norm equal to 1 and largest component real.
//
// If dst is empty, LeftVectorsTo will resize dst to be n×n. When dst is
// non-empty, LeftVectorsTo will panic if dst is not n×n. LeftVectorsTo will also
// panic if the left eigenvectors were not computed during the factorization,
// or if the receiver does not contain a successful factorization
func (e *Eigen) LeftVectorsTo(dst *CDense) {
	if !e.succFact() {
		panic(badFact)
	}
	if e.kind&EigenLeft == 0 {
		panic(noVectors)
	}
	if dst.IsEmpty() {
		dst.ReuseAs(e.n, e.n)
	} else {
		r, c := dst.Dims()
		if r != e.n || c != e.n {
			panic(ErrShape)
		}
	}
	dst.Copy(e.lVectors)
}
<|endoftext|>


<commit_before>package math

import (
	"math"
	"math/big"

	"github.com/EricLagergren/decimal"
	"github.com/EricLagergren/decimal/internal/arith"
)

// Sqrt sets z to the square root of x and returns z.
// The precision of Sqrt is determined by z's Context.
// Sqrt will panic on negative values since decimal.Big cannot
// represent imaginary numbers.
func Sqrt(z *decimal.Big, x *decimal.Big) *decimal.Big {
	if x.SignBit() {
		panic("math.Sqrt: cannot take square root of negative number")
	}

	switch {
	case x.IsNaN():
		panic(ErrNaN{"square root of NaN"})
	case x.IsInf():
		return z.SetInf()
	case x.Sign() == 0:
		return z.SetMantScale(0, 0)
	}

	// First fast path---check if x is a perfect square. If it is, we can avoid
	// having to inflate x and can possibly use can use the hardware SQRT.
	// Note that we can only catch perfect squares that aren't big.Ints.
	if sq, ok := perfectSquare(x); ok {
		return z.SetMantScale(sq, 0).SetContext(x.Context())
	}

	zp := z.Context().Precision()

	// Temporary inflation. Should be enough to accurately determine the sqrt
	// with at least zp digits after the radix.
	zpadj := int(zp) << 1

	var tmp *decimal.Big
	if z != x {
		zctx := z.Context()
		tmp = z.Set(x)
		tmp.SetContext(zctx)
	} else {
		tmp = new(decimal.Big).Set(x)
	}
	shiftRadixRight(tmp, zpadj)

	// Second fast path. Check to see if we can calculate the square root without
	// using big.Int
	if !x.IsBig() && zpadj <= 19 {
		n := tmp.Int64()
		ix := n >> uint((arith.BitLen(n)+1)>>1)
		var p int64
		for {
			p = ix
			ix += n / ix
			ix >>= 1
			if ix == p {
				return z.SetMantScale(ix, zp)
			}
		}
	}

	// x isn't a perfect square or x is a big.Int

	n := tmp.Int()
	ix := new(big.Int).Rsh(n, uint((n.BitLen()+1)>>1))

	var a, p big.Int
	for {
		p.Set(ix)
		ix.Add(ix, a.Quo(n, ix)).Rsh(ix, 1)
		if ix.Cmp(&p) == 0 {
			return z.SetBigMantScale(ix, zp)
		}
	}
}

// perfectSquare algorithm slightly partially borrowed from
// https://stackoverflow.com/a/295678/2967113
func perfectSquare(x *decimal.Big) (square int64, ok bool) {
	if x.IsBig() || !x.IsInt() {
		return 0, false
	}
	xc := x.Int64()
	h := xc & 0xF
	if h > 9 {
		return 0, false
	}
	if h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8 {
		// "Show that floating point sqrt(x*x) >= x for all long x."
		// https://math.stackexchange.com/a/238885/153292
		tst := int64(math.Sqrt(float64(xc)))
		return tst, tst*tst == xc
	}
	return 0, false
}
<commit_msg>math: Make Sqrt signature prettier<commit_after>package math

import (
	"math"
	"math/big"

	"github.com/EricLagergren/decimal"
	"github.com/EricLagergren/decimal/internal/arith"
)

// Sqrt sets z to the square root of x and returns z.
// The precision of Sqrt is determined by z's Context.
// Sqrt will panic on negative values since decimal.Big cannot
// represent imaginary numbers.
func Sqrt(z, x *decimal.Big) *decimal.Big {
	if x.SignBit() {
		panic("math.Sqrt: cannot take square root of negative number")
	}

	switch {
	case x.IsNaN():
		panic(ErrNaN{"square root of NaN"})
	case x.IsInf():
		return z.SetInf()
	case x.Sign() == 0:
		return z.SetMantScale(0, 0)
	}

	// First fast path---check if x is a perfect square. If it is, we can avoid
	// having to inflate x and can possibly use can use the hardware SQRT.
	// Note that we can only catch perfect squares that aren't big.Ints.
	if sq, ok := perfectSquare(x); ok {
		return z.SetMantScale(sq, 0).SetContext(x.Context())
	}

	zp := z.Context().Precision()

	// Temporary inflation. Should be enough to accurately determine the sqrt
	// with at least zp digits after the radix.
	zpadj := int(zp) << 1

	var tmp *decimal.Big
	if z != x {
		zctx := z.Context()
		tmp = z.Set(x)
		tmp.SetContext(zctx)
	} else {
		tmp = new(decimal.Big).Set(x)
	}
	shiftRadixRight(tmp, zpadj)

	// Second fast path. Check to see if we can calculate the square root without
	// using big.Int
	if !x.IsBig() && zpadj <= 19 {
		n := tmp.Int64()
		ix := n >> uint((arith.BitLen(n)+1)>>1)
		var p int64
		for {
			p = ix
			ix += n / ix
			ix >>= 1
			if ix == p {
				return z.SetMantScale(ix, zp)
			}
		}
	}

	// x isn't a perfect square or x is a big.Int

	n := tmp.Int()
	ix := new(big.Int).Rsh(n, uint((n.BitLen()+1)>>1))

	var a, p big.Int
	for {
		p.Set(ix)
		ix.Add(ix, a.Quo(n, ix)).Rsh(ix, 1)
		if ix.Cmp(&p) == 0 {
			return z.SetBigMantScale(ix, zp)
		}
	}
}

// perfectSquare algorithm slightly partially borrowed from
// https://stackoverflow.com/a/295678/2967113
func perfectSquare(x *decimal.Big) (square int64, ok bool) {
	if x.IsBig() || !x.IsInt() {
		return 0, false
	}
	xc := x.Int64()
	h := xc & 0xF
	if h > 9 {
		return 0, false
	}
	if h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8 {
		// "Show that floating point sqrt(x*x) >= x for all long x."
		// https://math.stackexchange.com/a/238885/153292
		tst := int64(math.Sqrt(float64(xc)))
		return tst, tst*tst == xc
	}
	return 0, false
}
<|endoftext|>