<commit_before>package etcd

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"
	"sync"
	"time"
)

// Errors introduced by handling requests
var (
	ErrRequestCancelled = errors.New("sending request is cancelled")
)

type RawRequest struct {
	Method       string
	RelativePath string
	Values       url.Values
	Cancel       <-chan bool
}

// NewRawRequest returns a new RawRequest
func NewRawRequest(method, relativePath string, values url.Values, cancel <-chan bool) *RawRequest {
	return &RawRequest{
		Method:       method,
		RelativePath: relativePath,
		Values:       values,
		Cancel:       cancel,
	}
}

// getCancelable issues a cancelable GET request
func (c *Client) getCancelable(key string, options Options,
	cancel <-chan bool) (*RawResponse, error) {
	logger.Debugf("get %s [%s]", key, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_GET_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("GET", p, nil, cancel)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// get issues a GET request
func (c *Client) get(key string, options Options) (*RawResponse, error) {
	return c.getCancelable(key, options, nil)
}

// put issues a PUT request
func (c *Client) put(key string, value string, ttl uint64,
	options Options) (*RawResponse, error) {

	logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_PUT_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("PUT", p, buildValues(value, ttl), nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// post issues a POST request
func (c *Client) post(key string, value string, ttl uint64) (*RawResponse, error) {
	logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
	p := keyToPath(key)

	req := NewRawRequest("POST", p, buildValues(value, ttl), nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// delete issues a DELETE request
func (c *Client) delete(key string, options Options) (*RawResponse, error) {
	logger.Debugf("delete %s [%s]", key, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_DELETE_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("DELETE", p, nil, nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// SendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {
	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	var numReqs = 1

	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.Cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.Cancel:
				cancelled <- true
				logger.Debug("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	// If we connect to a follower and consistency is required, retry until
	// we connect to a leader
	sleep := 25 * time.Millisecond
	maxSleep := time.Second

	for attempt := 0; ; attempt++ {
		if attempt > 0 {
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			case <-time.After(sleep):
				sleep = sleep * 2
				if sleep > maxSleep {
					sleep = maxSleep
				}
			}
		}

		logger.Debug("Connecting to etcd: attempt ", attempt+1, " for ", rr.RelativePath)

		// get httpPath if not set
		if httpPath == "" {
			httpPath = c.getHttpPath(rr.RelativePath)
		}

		// Return a cURL command if curlChan is set
		if c.cURLch != nil {
			command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
			for key, value := range rr.Values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			if c.credentials != nil {
				command += fmt.Sprintf(" -u %s", c.credentials.username)
			}
			c.sendCURL(command)
		}

		logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)

		req, err := func() (*http.Request, error) {
			reqLock.Lock()
			defer reqLock.Unlock()

			if rr.Values == nil {
				if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
					return nil, err
				}
			} else {
				body := strings.NewReader(rr.Values.Encode())
				if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
					return nil, err
				}

				req.Header.Set("Content-Type",
					"application/x-www-form-urlencoded; param=value")
			}
			return req, nil
		}()

		if err != nil {
			return nil, err
		}

		if c.credentials != nil {
			req.SetBasicAuth(c.credentials.username, c.credentials.password)
		}

		resp, err = c.httpClient.Do(req)
		// clear previous httpPath
		httpPath = ""
		defer func() {
			if resp != nil {
				resp.Body.Close()
			}
		}()

		// If the request was cancelled, return ErrRequestCancelled directly
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}

		numReqs++

		// network error, change a machine!
		if err != nil {
			logger.Debug("network error: ", err.Error())
			lastResp := http.Response{}
			if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil {
				return nil, checkErr
			}

			c.cluster.failure()
			continue
		}

		// if there is no error, it should receive response
		logger.Debug("recv.response.from ", httpPath)

		if validHttpStatusCode[resp.StatusCode] {
			// try to read byte code and break the loop
			respBody, err = ioutil.ReadAll(resp.Body)
			if err == nil {
				logger.Debug("recv.success ", httpPath)
				break
			}
			// ReadAll error may be caused due to cancel request
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			default:
			}

			if err == io.ErrUnexpectedEOF {
				// underlying connection was closed prematurely, probably by timeout
				// TODO: empty body or unexpectedEOF can cause http.Transport to get hosed;
				// this allows the client to detect that and take evasive action. Need
				// to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed.
				respBody = []byte{}
				break
			}
		}

		if resp.StatusCode == http.StatusTemporaryRedirect {
			u, err := resp.Location()

			if err != nil {
				logger.Warning(err)
			} else {
				// set httpPath for following redirection
				httpPath = u.String()
			}
			resp.Body.Close()
			continue
		}

		if checkErr := checkRetry(c.cluster, numReqs, *resp,
			errors.New("Unexpected HTTP status code")); checkErr != nil {
			return nil, checkErr
		}
		resp.Body.Close()
	}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}

// DefaultCheckRetry defines the retrying behaviour for bad HTTP requests
// If we have retried 2 * machine number, stop retrying.
// If status code is InternalServerError, sleep for 200ms.
func DefaultCheckRetry(cluster *Cluster, numReqs int, lastResp http.Response,
	err error) error {

	if numReqs > 2*len(cluster.Machines) {
		errStr := fmt.Sprintf("failed to propose on members %v twice [last error: %v]", cluster.Machines, err)
		return newError(ErrCodeEtcdNotReachable, errStr, 0)
	}

	if isEmptyResponse(lastResp) {
		// always retry if it failed to get response from one machine
		return nil
	}
	if !shouldRetry(lastResp) {
		body := []byte("nil")
		if lastResp.Body != nil {
			if b, err := ioutil.ReadAll(lastResp.Body); err == nil {
				body = b
			}
		}
		errStr := fmt.Sprintf("unhandled http status [%s] with body [%s]", http.StatusText(lastResp.StatusCode), body)
		return newError(ErrCodeUnhandledHTTPStatus, errStr, 0)
	}
	// sleep some time and expect leader election finish
	time.Sleep(time.Millisecond * 200)
	logger.Warning("bad response status code", lastResp.StatusCode)
	return nil
}

func isEmptyResponse(r http.Response) bool { return r.StatusCode == 0 }

// shouldRetry returns whether the reponse deserves retry.
func shouldRetry(r http.Response) bool {
	// TODO: only retry when the cluster is in leader election
	// We cannot do it exactly because etcd doesn't support it well.
	return r.StatusCode == http.StatusInternalServerError
}

func (c *Client) getHttpPath(s ...string) string {
	fullPath := c.cluster.pick() + "/" + version
	for _, seg := range s {
		fullPath = fullPath + "/" + seg
	}
	return fullPath
}

// buildValues builds a url.Values map according to the given value and ttl
func buildValues(value string, ttl uint64) url.Values {
	v := url.Values{}

	if value != "" {
		v.Set("value", value)
	}

	if ttl > 0 {
		v.Set("ttl", fmt.Sprintf("%v", ttl))
	}

	return v
}

// convert key string to http path exclude version, including URL escaping
// for example: key[foo] -> path[keys/foo]
// key[/%z] -> path[keys/%25z]
// key[/] -> path[keys/]
func keyToPath(key string) string {
	// URL-escape our key, except for slashes
	p := strings.Replace(url.QueryEscape(path.Join("keys", key)), "%2F", "/", -1)

	// corner case: if key is "/" or "//" ect
	// path join will clear the tailing "/"
	// we need to add it back
	if p == "keys" {
		p = "keys/"
	}

	return p
}
<commit_msg>add space to DefaultCheckRetry warning<commit_after>package etcd

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"
	"sync"
	"time"
)

// Errors introduced by handling requests
var (
	ErrRequestCancelled = errors.New("sending request is cancelled")
)

type RawRequest struct {
	Method       string
	RelativePath string
	Values       url.Values
	Cancel       <-chan bool
}

// NewRawRequest returns a new RawRequest
func NewRawRequest(method, relativePath string, values url.Values, cancel <-chan bool) *RawRequest {
	return &RawRequest{
		Method:       method,
		RelativePath: relativePath,
		Values:       values,
		Cancel:       cancel,
	}
}

// getCancelable issues a cancelable GET request
func (c *Client) getCancelable(key string, options Options,
	cancel <-chan bool) (*RawResponse, error) {
	logger.Debugf("get %s [%s]", key, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_GET_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("GET", p, nil, cancel)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// get issues a GET request
func (c *Client) get(key string, options Options) (*RawResponse, error) {
	return c.getCancelable(key, options, nil)
}

// put issues a PUT request
func (c *Client) put(key string, value string, ttl uint64,
	options Options) (*RawResponse, error) {

	logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_PUT_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("PUT", p, buildValues(value, ttl), nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// post issues a POST request
func (c *Client) post(key string, value string, ttl uint64) (*RawResponse, error) {
	logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
	p := keyToPath(key)

	req := NewRawRequest("POST", p, buildValues(value, ttl), nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// delete issues a DELETE request
func (c *Client) delete(key string, options Options) (*RawResponse, error) {
	logger.Debugf("delete %s [%s]", key, c.cluster.pick())
	p := keyToPath(key)

	str, err := options.toParameters(VALID_DELETE_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	req := NewRawRequest("DELETE", p, nil, nil)
	resp, err := c.SendRequest(req)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// SendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {
	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	var numReqs = 1

	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.Cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.Cancel:
				cancelled <- true
				logger.Debug("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	// If we connect to a follower and consistency is required, retry until
	// we connect to a leader
	sleep := 25 * time.Millisecond
	maxSleep := time.Second

	for attempt := 0; ; attempt++ {
		if attempt > 0 {
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			case <-time.After(sleep):
				sleep = sleep * 2
				if sleep > maxSleep {
					sleep = maxSleep
				}
			}
		}

		logger.Debug("Connecting to etcd: attempt ", attempt+1, " for ", rr.RelativePath)

		// get httpPath if not set
		if httpPath == "" {
			httpPath = c.getHttpPath(rr.RelativePath)
		}

		// Return a cURL command if curlChan is set
		if c.cURLch != nil {
			command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
			for key, value := range rr.Values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			if c.credentials != nil {
				command += fmt.Sprintf(" -u %s", c.credentials.username)
			}
			c.sendCURL(command)
		}

		logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)

		req, err := func() (*http.Request, error) {
			reqLock.Lock()
			defer reqLock.Unlock()

			if rr.Values == nil {
				if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
					return nil, err
				}
			} else {
				body := strings.NewReader(rr.Values.Encode())
				if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
					return nil, err
				}

				req.Header.Set("Content-Type",
					"application/x-www-form-urlencoded; param=value")
			}
			return req, nil
		}()

		if err != nil {
			return nil, err
		}

		if c.credentials != nil {
			req.SetBasicAuth(c.credentials.username, c.credentials.password)
		}

		resp, err = c.httpClient.Do(req)
		// clear previous httpPath
		httpPath = ""
		defer func() {
			if resp != nil {
				resp.Body.Close()
			}
		}()

		// If the request was cancelled, return ErrRequestCancelled directly
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}

		numReqs++

		// network error, change a machine!
		if err != nil {
			logger.Debug("network error: ", err.Error())
			lastResp := http.Response{}
			if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil {
				return nil, checkErr
			}

			c.cluster.failure()
			continue
		}

		// if there is no error, it should receive response
		logger.Debug("recv.response.from ", httpPath)

		if validHttpStatusCode[resp.StatusCode] {
			// try to read byte code and break the loop
			respBody, err = ioutil.ReadAll(resp.Body)
			if err == nil {
				logger.Debug("recv.success ", httpPath)
				break
			}
			// ReadAll error may be caused due to cancel request
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			default:
			}

			if err == io.ErrUnexpectedEOF {
				// underlying connection was closed prematurely, probably by timeout
				// TODO: empty body or unexpectedEOF can cause http.Transport to get hosed;
				// this allows the client to detect that and take evasive action. Need
				// to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed.
				respBody = []byte{}
				break
			}
		}

		if resp.StatusCode == http.StatusTemporaryRedirect {
			u, err := resp.Location()

			if err != nil {
				logger.Warning(err)
			} else {
				// set httpPath for following redirection
				httpPath = u.String()
			}
			resp.Body.Close()
			continue
		}

		if checkErr := checkRetry(c.cluster, numReqs, *resp,
			errors.New("Unexpected HTTP status code")); checkErr != nil {
			return nil, checkErr
		}
		resp.Body.Close()
	}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}

// DefaultCheckRetry defines the retrying behaviour for bad HTTP requests
// If we have retried 2 * machine number, stop retrying.
// If status code is InternalServerError, sleep for 200ms.
func DefaultCheckRetry(cluster *Cluster, numReqs int, lastResp http.Response,
	err error) error {

	if numReqs > 2*len(cluster.Machines) {
		errStr := fmt.Sprintf("failed to propose on members %v twice [last error: %v]", cluster.Machines, err)
		return newError(ErrCodeEtcdNotReachable, errStr, 0)
	}

	if isEmptyResponse(lastResp) {
		// always retry if it failed to get response from one machine
		return nil
	}
	if !shouldRetry(lastResp) {
		body := []byte("nil")
		if lastResp.Body != nil {
			if b, err := ioutil.ReadAll(lastResp.Body); err == nil {
				body = b
			}
		}
		errStr := fmt.Sprintf("unhandled http status [%s] with body [%s]", http.StatusText(lastResp.StatusCode), body)
		return newError(ErrCodeUnhandledHTTPStatus, errStr, 0)
	}
	// sleep some time and expect leader election finish
	time.Sleep(time.Millisecond * 200)
	logger.Warning("bad response status code ", lastResp.StatusCode)
	return nil
}

func isEmptyResponse(r http.Response) bool { return r.StatusCode == 0 }

// shouldRetry returns whether the reponse deserves retry.
func shouldRetry(r http.Response) bool {
	// TODO: only retry when the cluster is in leader election
	// We cannot do it exactly because etcd doesn't support it well.
	return r.StatusCode == http.StatusInternalServerError
}

func (c *Client) getHttpPath(s ...string) string {
	fullPath := c.cluster.pick() + "/" + version
	for _, seg := range s {
		fullPath = fullPath + "/" + seg
	}
	return fullPath
}

// buildValues builds a url.Values map according to the given value and ttl
func buildValues(value string, ttl uint64) url.Values {
	v := url.Values{}

	if value != "" {
		v.Set("value", value)
	}

	if ttl > 0 {
		v.Set("ttl", fmt.Sprintf("%v", ttl))
	}

	return v
}

// convert key string to http path exclude version, including URL escaping
// for example: key[foo] -> path[keys/foo]
// key[/%z] -> path[keys/%25z]
// key[/] -> path[keys/]
func keyToPath(key string) string {
	// URL-escape our key, except for slashes
	p := strings.Replace(url.QueryEscape(path.Join("keys", key)), "%2F", "/", -1)

	// corner case: if key is "/" or "//" ect
	// path join will clear the tailing "/"
	// we need to add it back
	if p == "keys" {
		p = "keys/"
	}

	return p
}
<|endoftext|>


<commit_before>package core

import (
	"github.com/raviqqe/tisp/src/lib/util"
	"reflect"
)

// Object represents an object in the language.
// Hackingly, it can be *Thunk so that tail calls are eliminated.
// See also Thunk.Eval().
type Object interface{}

type callable interface {
	call(Arguments) Object
}

// equalable must be implemented for every type other than error type.
type equalable interface {
	equal(equalable) Object
}

// Equal returns true when arguments are equal and false otherwise.
// Comparing error objects is invalid and it should return an error object.
var Equal = NewStrictFunction(
	NewSignature(
		[]string{"x", "y"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		var es [2]equalable

		for i, o := range os {
			e, ok := o.(equalable)

			if !ok {
				return TypeError(o, "Equalable")
			}

			es[i] = e
		}

		if !areSameType(es[0], es[1]) {
			return False
		}

		return es[0].equal(es[1])
	})

type listable interface {
	toList() Object
}

var ToList = NewStrictFunction(
	NewSignature(
		[]string{"listLike"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		o := os[0]
		l, ok := o.(listable)

		if !ok {
			return TypeError(o, "Listable")
		}

		return l.toList()
	})

// ordered must be implemented for every type other than error type.
// This interface should not be used in exported functions and exists only to
// make keys for collections in rbt package.
type ordered interface {
	less(ordered) bool // can panic
}

func less(x1, x2 interface{}) bool {
	o1, ok := x1.(ordered)

	if !ok {
		panic(notOrderedError(x1))
	}

	o2, ok := x2.(ordered)

	if !ok {
		panic(notOrderedError(x2))
	}

	if !areSameType(o1, o2) {
		return reflect.TypeOf(o1).Name() < reflect.TypeOf(o2).Name()
	}

	return o1.less(o2)
}

func areSameType(x1, x2 interface{}) bool {
	return reflect.TypeOf(x1) == reflect.TypeOf(x2)
}

type mergable interface {
	merge(ts ...*Thunk) Object
}

var Merge = NewLazyFunction(
	NewSignature(
		[]string{"x"}, []OptionalArgument{}, "ys",
		[]string{}, []OptionalArgument{}, "",
	),
	func(ts ...*Thunk) Object {
		o := ts[0].Eval()
		m, ok := o.(mergable)

		if !ok {
			return TypeError(o, "Mergable")
		}

		o = ts[1].Eval()
		l, ok := o.(ListType)

		if !ok {
			return NotListError(o)
		}

		ts, err := l.ToThunks()

		if err != nil {
			return err
		}

		if len(ts) == 0 {
			return m
		}

		return m.merge(ts...)
	})

type deletable interface {
	delete(Object) (deletable, Object)
}

var Delete = NewStrictFunction(
	NewSignature(
		[]string{"collection"}, []OptionalArgument{}, "elems",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		d, ok := os[0].(deletable)

		if !ok {
			return TypeError(os[0], "Deletable")
		}

		l, ok := os[1].(ListType)

		if !ok {
			return NotListError(os[1])
		}

		os, err := l.ToObjects()

		if err != nil {
			return err
		}

		for _, o := range os {
			if _, ok := o.(ErrorType); ok {
				return o
			}

			var err Object
			d, err = d.delete(o)

			if err != nil {
				return err
			}
		}

		return d
	})

// stringable is an interface for something convertable into StringType.
// This should be implemented for all types including error type.
type stringable interface {
	string() Object
}

// ToString converts some object into one of StringType.
var ToString = NewStrictFunction(
	NewSignature(
		[]string{"x"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		s, ok := os[0].(stringable)

		if !ok {
			util.Fail("%#v is not stringable.", os[0])
		}

		return s.string()
	})

// TODO: Create collection interface integrating some existing interfaces with
// methods of index, insert, merge, delete, size (or len?), include and toList.
// It should be implemented by StringType, ListType, DictionaryType and SetType.
<commit_msg>Sort definitions of interfaces<commit_after>package core

import (
	"github.com/raviqqe/tisp/src/lib/util"
	"reflect"
)

// Object represents an object in the language.
// Hackingly, it can be *Thunk so that tail calls are eliminated.
// See also Thunk.Eval().
type Object interface{}

type callable interface {
	call(Arguments) Object
}

// stringable is an interface for something convertable into StringType.
// This should be implemented for all types including error type.
type stringable interface {
	string() Object
}

// ToString converts some object into one of StringType.
var ToString = NewStrictFunction(
	NewSignature(
		[]string{"x"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		s, ok := os[0].(stringable)

		if !ok {
			util.Fail("%#v is not stringable.", os[0])
		}

		return s.string()
	})

// equalable must be implemented for every type other than error type.
type equalable interface {
	equal(equalable) Object
}

// Equal returns true when arguments are equal and false otherwise.
// Comparing error objects is invalid and it should return an error object.
var Equal = NewStrictFunction(
	NewSignature(
		[]string{"x", "y"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		var es [2]equalable

		for i, o := range os {
			e, ok := o.(equalable)

			if !ok {
				return TypeError(o, "Equalable")
			}

			es[i] = e
		}

		if !areSameType(es[0], es[1]) {
			return False
		}

		return es[0].equal(es[1])
	})

// ordered must be implemented for every type other than error type.
// This interface should not be used in exported functions and exists only to
// make keys for collections in rbt package.
type ordered interface {
	less(ordered) bool // can panic
}

func less(x1, x2 interface{}) bool {
	o1, ok := x1.(ordered)

	if !ok {
		panic(notOrderedError(x1))
	}

	o2, ok := x2.(ordered)

	if !ok {
		panic(notOrderedError(x2))
	}

	if !areSameType(o1, o2) {
		return reflect.TypeOf(o1).Name() < reflect.TypeOf(o2).Name()
	}

	return o1.less(o2)
}

func areSameType(x1, x2 interface{}) bool {
	return reflect.TypeOf(x1) == reflect.TypeOf(x2)
}

type mergable interface {
	merge(ts ...*Thunk) Object
}

var Merge = NewLazyFunction(
	NewSignature(
		[]string{"x"}, []OptionalArgument{}, "ys",
		[]string{}, []OptionalArgument{}, "",
	),
	func(ts ...*Thunk) Object {
		o := ts[0].Eval()
		m, ok := o.(mergable)

		if !ok {
			return TypeError(o, "Mergable")
		}

		o = ts[1].Eval()
		l, ok := o.(ListType)

		if !ok {
			return NotListError(o)
		}

		ts, err := l.ToThunks()

		if err != nil {
			return err
		}

		if len(ts) == 0 {
			return m
		}

		return m.merge(ts...)
	})

type deletable interface {
	delete(Object) (deletable, Object)
}

var Delete = NewStrictFunction(
	NewSignature(
		[]string{"collection"}, []OptionalArgument{}, "elems",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		d, ok := os[0].(deletable)

		if !ok {
			return TypeError(os[0], "Deletable")
		}

		l, ok := os[1].(ListType)

		if !ok {
			return NotListError(os[1])
		}

		os, err := l.ToObjects()

		if err != nil {
			return err
		}

		for _, o := range os {
			if _, ok := o.(ErrorType); ok {
				return o
			}

			var err Object
			d, err = d.delete(o)

			if err != nil {
				return err
			}
		}

		return d
	})

type listable interface {
	toList() Object
}

var ToList = NewStrictFunction(
	NewSignature(
		[]string{"listLike"}, []OptionalArgument{}, "",
		[]string{}, []OptionalArgument{}, "",
	),
	func(os ...Object) Object {
		o := os[0]
		l, ok := o.(listable)

		if !ok {
			return TypeError(o, "Listable")
		}

		return l.toList()
	})

// TODO: Create collection interface integrating some existing interfaces with
// methods of index, insert, merge, delete, size (or len?), include and toList.
// It should be implemented by StringType, ListType, DictionaryType and SetType.
<|endoftext|>


<commit_before>package game

import (
	"strconv"

	"buildblast/lib/coords"
	"buildblast/lib/physics"
	"buildblast/lib/vmath"
)

var WorldItemHalfExtents = vmath.Vec3{
	float64(1) / 8,
	float64(1) / 8,
	float64(1) / 8,
}

type WorldItem struct {
	worldItemState WorldItemState
}

type WorldItemState struct {
	EntityState EntityState
	ItemKind    Item
}

var globalWorldItemId uint64

func NewWorldItem(kind Item, pos coords.World) *WorldItem {
	globalWorldItemId++
	return &WorldItem{
		worldItemState: WorldItemState{
			EntityState: EntityState{
				EntityId: EntityId("worldItem" + strconv.FormatUint(globalWorldItemId, 10)),
				Body: physics.Body{
					Pos:         pos.Vec3(),
					HalfExtents: WorldItemHalfExtents,
				},
			},
			ItemKind: kind,
		},
	}
}

func (wi *WorldItem) State() WorldItemState {
	return wi.worldItemState
}

func (wi *WorldItem) EntityId() EntityId {
	return wi.worldItemState.EntityState.EntityId
}

func (wi *WorldItem) Body() physics.Body {
	return wi.worldItemState.EntityState.Body
}

func (wi *WorldItem) Wpos() coords.World {
	return wi.worldItemState.EntityState.Wpos()
}

func (wi *WorldItem) Look() coords.Direction {
	return wi.worldItemState.EntityState.Look()
}

func (wi *WorldItem) Tick(dt int64, w *World) bool {
	body := wi.Body()
	// These have really low gravity
	body.Vel.Y += float64(dt) / 1000 * -0.2

	var closestBody *physics.Body
	var closestDist float64
	for _, b := range w.Biotics() {
		bioticBody := b.Body()
		dist := body.Pos.DistBetween(&bioticBody.Pos)
		if dist < 10.0 {
			if closestBody == nil || dist < closestDist {
				closestBody = &bioticBody
				closestDist = dist
			}
		}
	}

	if closestBody != nil {
		dir := body.Pos.To(&closestBody.Pos)
		dir.Y = 0
		dir.SetLength(100 * (1 / (closestDist * closestDist)) * float64(dt) / 1000)
		body.Vel.X = dir.X
		body.Vel.Z = dir.Z
	} else {
		body.Vel.X = 0
		body.Vel.Z = 0
	}

	box := body.Box()
	move := box.AttemptMove(w, body.Vel)

	if move.X == 0 {
		body.Vel.X = 0
	}
	if move.Y == 0 {
		body.Vel.Y = 0
	}
	if move.Z == 0 {
		body.Vel.Z = 0
	}

	if move.X == 0 && move.Y == 0 && move.Z == 0 {
		return false
	}

	body.Vel = move
	body.Pos.Add(&move)
	wi.worldItemState.EntityState.Body = body
	return true
}
<commit_msg>Make the blocks a little bit slower when close to you<commit_after>package game

import (
	"strconv"

	"buildblast/lib/coords"
	"buildblast/lib/physics"
	"buildblast/lib/vmath"
)

var WorldItemHalfExtents = vmath.Vec3{
	float64(1) / 8,
	float64(1) / 8,
	float64(1) / 8,
}

type WorldItem struct {
	worldItemState WorldItemState
}

type WorldItemState struct {
	EntityState EntityState
	ItemKind    Item
}

var globalWorldItemId uint64

func NewWorldItem(kind Item, pos coords.World) *WorldItem {
	globalWorldItemId++
	return &WorldItem{
		worldItemState: WorldItemState{
			EntityState: EntityState{
				EntityId: EntityId("worldItem" + strconv.FormatUint(globalWorldItemId, 10)),
				Body: physics.Body{
					Pos:         pos.Vec3(),
					HalfExtents: WorldItemHalfExtents,
				},
			},
			ItemKind: kind,
		},
	}
}

func (wi *WorldItem) State() WorldItemState {
	return wi.worldItemState
}

func (wi *WorldItem) EntityId() EntityId {
	return wi.worldItemState.EntityState.EntityId
}

func (wi *WorldItem) Body() physics.Body {
	return wi.worldItemState.EntityState.Body
}

func (wi *WorldItem) Wpos() coords.World {
	return wi.worldItemState.EntityState.Wpos()
}

func (wi *WorldItem) Look() coords.Direction {
	return wi.worldItemState.EntityState.Look()
}

func (wi *WorldItem) Tick(dt int64, w *World) bool {
	body := wi.Body()
	// These have really low gravity
	body.Vel.Y += float64(dt) / 1000 * -0.2

	var closestBody *physics.Body
	var closestDist float64
	for _, b := range w.Biotics() {
		bioticBody := b.Body()
		dist := body.Pos.DistBetween(&bioticBody.Pos)
		if dist < 10.0 {
			if closestBody == nil || dist < closestDist {
				closestBody = &bioticBody
				closestDist = dist
			}
		}
	}

	if closestBody != nil {
		dir := body.Pos.To(&closestBody.Pos)
		dir.Y = 0
		dir.SetLength(30 * (1 / closestDist) * float64(dt) / 1000)
		body.Vel.X = dir.X
		body.Vel.Z = dir.Z
	} else {
		body.Vel.X = 0
		body.Vel.Z = 0
	}

	box := body.Box()
	move := box.AttemptMove(w, body.Vel)

	if move.X == 0 {
		body.Vel.X = 0
	}
	if move.Y == 0 {
		body.Vel.Y = 0
	}
	if move.Z == 0 {
		body.Vel.Z = 0
	}

	if move.X == 0 && move.Y == 0 && move.Z == 0 {
		return false
	}

	body.Vel = move
	body.Pos.Add(&move)
	wi.worldItemState.EntityState.Body = body
	return true
}
<|endoftext|>


<commit_before>/*
 * Network packet analysis framework.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Provides encoding and decoding for ICMPv6 packets.
package icmpv6

import "fmt"

import "github.com/ghedo/go.pkt/packet"
import "github.com/ghedo/go.pkt/packet/ipv4"

type Packet struct {
    Type      Type
    Code      Code
    Checksum  uint16 `string:"sum"`
    csum_seed uint32 `cmp:"skip" string:"skip"`
    Body      uint32 `cmp:"skip" string:"skip"`
    pkt_payload packet.Packet `cmp:"skip" string:"skip"`
}

type Type uint8
type Code uint8

const (
    DstUnreachable Type = 0
    PacketTooBig        = 1
    TimeExceeded        = 2
    ParamProblem        = 4
    Private1            = 100
    Private2            = 101
    Reserved1           = 127
    EchoRequest         = 128
    EchoReply           = 129
    /* TODO: more types */
)

func Make() *Packet {
    return &Packet{
        Type: EchoRequest,
    }
}

func (p *Packet) GetType() packet.Type {
    return packet.ICMPv6
}

func (p *Packet) GetLength() uint16 {
    if p.pkt_payload != nil {
        return p.pkt_payload.GetLength() + 8
    }

    return 8
}

func (p *Packet) Equals(other packet.Packet) bool {
    return packet.Compare(p, other)
}

func (p *Packet) Answers(other packet.Packet) bool {
    if other == nil || other.GetType() != packet.ICMPv6 {
        return false
    }

    if other.(*Packet).Type == EchoRequest && p.Type == EchoReply {
        return true
    }

    return false
}

func (p *Packet) Pack(buf *packet.Buffer) error {
    buf.WriteN(byte(p.Type))
    buf.WriteN(byte(p.Code))
    buf.WriteN(uint16(0x00))
    buf.WriteN(p.Body)

    if p.csum_seed != 0 {
        p.Checksum = ipv4.CalculateChecksum(buf.LayerBytes(), p.csum_seed)
        buf.PutUint16N(2, p.Checksum)
    }

    return nil
}

func (p *Packet) Unpack(buf *packet.Buffer) error {
    buf.ReadN(&p.Type)
    buf.ReadN(&p.Code)
    buf.ReadN(&p.Checksum)

    /* TODO: data */
    buf.ReadN(&p.Body)

    return nil
}

func (p *Packet) Payload() packet.Packet {
    return p.pkt_payload
}

func (p *Packet) GuessPayloadType() packet.Type {
    switch p.Type {
    case DstUnreachable, PacketTooBig, TimeExceeded, ParamProblem:
        return packet.IPv6
    }

    return packet.None
}

func (p *Packet) SetPayload(pl packet.Packet) error {
    switch p.Type {
    case DstUnreachable, PacketTooBig, TimeExceeded, ParamProblem:
        p.pkt_payload = pl
    }

    return nil
}

func (p *Packet) InitChecksum(csum uint32) {
    p.csum_seed = csum
}

func (p *Packet) String() string {
    return packet.Stringify(p)
}

func (t Type) String() string {
    switch t {
    case DstUnreachable:    return "dst-unreach"
    case PacketTooBig:      return "too-big"
    case TimeExceeded:      return "timeout"
    case ParamProblem:      return "param-problem"
    case EchoRequest:       return "echo-request"
    case EchoReply:         return "echo-reply"
    default:                return "unknown"
    }
}

func (c Code) String() string {
    if c != 0 {
        return fmt.Sprintf("%x", uint8(c))
    }

    return ""
}
<commit_msg>icmpv6: fix type definitions<commit_after>/*
 * Network packet analysis framework.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Provides encoding and decoding for ICMPv6 packets.
package icmpv6

import "fmt"

import "github.com/ghedo/go.pkt/packet"
import "github.com/ghedo/go.pkt/packet/ipv4"

type Packet struct {
    Type      Type
    Code      Code
    Checksum  uint16 `string:"sum"`
    csum_seed uint32 `cmp:"skip" string:"skip"`
    Body      uint32 `cmp:"skip" string:"skip"`
    pkt_payload packet.Packet `cmp:"skip" string:"skip"`
}

type Type uint8
type Code uint8

const (
    DstUnreachable Type = 1
    PacketTooBig        = 2
    TimeExceeded        = 3
    ParamProblem        = 4
    Private1            = 100
    Private2            = 101
    Reserved1           = 127
    EchoRequest         = 128
    EchoReply           = 129
    /* TODO: more types */
)

func Make() *Packet {
    return &Packet{
        Type: EchoRequest,
    }
}

func (p *Packet) GetType() packet.Type {
    return packet.ICMPv6
}

func (p *Packet) GetLength() uint16 {
    if p.pkt_payload != nil {
        return p.pkt_payload.GetLength() + 8
    }

    return 8
}

func (p *Packet) Equals(other packet.Packet) bool {
    return packet.Compare(p, other)
}

func (p *Packet) Answers(other packet.Packet) bool {
    if other == nil || other.GetType() != packet.ICMPv6 {
        return false
    }

    if other.(*Packet).Type == EchoRequest && p.Type == EchoReply {
        return true
    }

    return false
}

func (p *Packet) Pack(buf *packet.Buffer) error {
    buf.WriteN(byte(p.Type))
    buf.WriteN(byte(p.Code))
    buf.WriteN(uint16(0x00))
    buf.WriteN(p.Body)

    if p.csum_seed != 0 {
        p.Checksum = ipv4.CalculateChecksum(buf.LayerBytes(), p.csum_seed)
        buf.PutUint16N(2, p.Checksum)
    }

    return nil
}

func (p *Packet) Unpack(buf *packet.Buffer) error {
    buf.ReadN(&p.Type)
    buf.ReadN(&p.Code)
    buf.ReadN(&p.Checksum)

    /* TODO: data */
    buf.ReadN(&p.Body)

    return nil
}

func (p *Packet) Payload() packet.Packet {
    return p.pkt_payload
}

func (p *Packet) GuessPayloadType() packet.Type {
    switch p.Type {
    case DstUnreachable, PacketTooBig, TimeExceeded, ParamProblem:
        return packet.IPv6
    }

    return packet.None
}

func (p *Packet) SetPayload(pl packet.Packet) error {
    switch p.Type {
    case DstUnreachable, PacketTooBig, TimeExceeded, ParamProblem:
        p.pkt_payload = pl
    }

    return nil
}

func (p *Packet) InitChecksum(csum uint32) {
    p.csum_seed = csum
}

func (p *Packet) String() string {
    return packet.Stringify(p)
}

func (t Type) String() string {
    switch t {
    case DstUnreachable:    return "dst-unreach"
    case PacketTooBig:      return "too-big"
    case TimeExceeded:      return "timeout"
    case ParamProblem:      return "param-problem"
    case EchoRequest:       return "echo-request"
    case EchoReply:         return "echo-reply"
    default:                return "unknown"
    }
}

func (c Code) String() string {
    if c != 0 {
        return fmt.Sprintf("%x", uint8(c))
    }

    return ""
}
<|endoftext|>


<commit_before>package api

import (
	"sync"
	"unsafe"

	. "github.com/ying32/govcl/vcl/types"
)

type TGoParam struct {
	Type  uint8
	Value uintptr
}

var (
	eventCallbackMap   sync.Map
	messageCallbackMap sync.Map
	threadSync         sync.Mutex
)

func DBoolToGoBool(val uintptr) bool {
	if val != 0 {
		return true
	}
	return false
}

func GoBoolToDBool(val bool) uintptr {
	if val {
		return 1
	}
	return 0
}

// hashOf  Delphi IniFiles.pas中的TStringHash.HashOf
func hashOf(fn interface{}) uintptr {
	var result uint32
	p := (*byte)(unsafe.Pointer(&fn))
	for i := 0; i < int(unsafe.Sizeof(fn)); i++ {
		result = ((result << 2) | (result >> (unsafe.Sizeof(result)*8 - 2))) ^ uint32(*p)
		p = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1))
	}
	return uintptr(result)
}

func addEventToMap(f interface{}) uintptr {
	p := hashOf(f)
	eventCallbackMap.Store(p, f)
	return p
}

func EventCallbackOf(Id uintptr) (interface{}, bool) {
	return eventCallbackMap.Load(Id)
}

func RemoveEventCallbackOf(Id uintptr) {
	eventCallbackMap.Delete(Id)
}

func addMessageEventToMap(f interface{}) uintptr {
	p := hashOf(f)
	messageCallbackMap.Store(p, f)
	return p
}

func MessageCallbackOf(Id uintptr) (interface{}, bool) {
	return messageCallbackMap.Load(Id)
}

func SetEventCallback(ptr uintptr) {
	setEventCallback.Call(ptr)
}

func SetMessageCallback(ptr uintptr) {
	setMessageCallback.Call(ptr)
}

func DGetParam(index int, ptr uintptr) TGoParam {
	p := TGoParam{}
	dGetParam.Call(uintptr(index), ptr, uintptr(unsafe.Pointer(&p)))
	return p
}

func DGetStringArrOf(p uintptr, index int) string {
	r, _, _ := dGetStringArrOf.Call(p, uintptr(index))
	return DStrToGoStr(r)
}

func DStrLen(p uintptr) int {
	ret, _, _ := dStrLen.Call(p)
	return int(ret)
}

func DMove(src, dest uintptr, llen int) {
	dMove.Call(src, dest, uintptr(llen))
}

func DShowMessage(s string) {
	dShowMessage.Call(GoStrToDStr(s))
}

func DGetMainInstance() uintptr {
	ret, _, _ := dGetMainInstance.Call()
	return ret
}

func DMessageDlg(Msg string, DlgType TMsgDlgType, Buttons TMsgDlgButtons, HelpCtx int32) int32 {
	ret, _, _ := dMessageDlg.Call(GoStrToDStr(Msg), uintptr(DlgType), uintptr(Buttons), uintptr(HelpCtx))
	return int32(ret)
}

func DSetReportMemoryLeaksOnShutdown(v bool) {
	dSetReportMemoryLeaksOnShutdown.Call(GoBoolToDBool(v))
}

func DTextToShortCut(val string) TShortCut {
	ret, _, _ := dTextToShortCut.Call(GoStrToDStr(val))
	return TShortCut(ret)
}

func DShortCutToText(val TShortCut) string {
	ret, _, _ := dShortCutToText.Call(uintptr(val))
	return DStrToGoStr(ret)
}

func DSysOpen(filename string) {
	dSysOpen.Call(GoStrToDStr(filename))
}

func DExtractFilePath(filename string) string {
	r, _, _ := dExtractFilePath.Call(GoStrToDStr(filename))
	return DStrToGoStr(r)
}

func DFileExists(filename string) bool {
	r, _, _ := dFileExists.Call(GoStrToDStr(filename))
	return DBoolToGoBool(r)
}

func DSelectDirectory1(options TSelectDirOpts) (bool, string) {
	var ptr uintptr
	r, _, _ := dSelectDirectory1.Call(uintptr(unsafe.Pointer(&ptr)), uintptr(options), 0)
	v := DBoolToGoBool(r)
	if v {
		return true, DStrToGoStr(ptr)
	}
	return false, ""
}

func DSelectDirectory2(caption, root string, options TSelectDirExtOpts, parent uintptr) (bool, string) {
	var ptr uintptr
	r, _, _ := dSelectDirectory2.Call(GoStrToDStr(caption), GoStrToDStr(root), uintptr(unsafe.Pointer(&ptr)),
		uintptr(options), parent)
	v := DBoolToGoBool(r)
	if v {
		return true, DStrToGoStr(ptr)
	}
	return false, ""
}

func DSynchronize(fn interface{}) {
	threadSync.Lock()
	defer threadSync.Unlock()
	id := addEventToMap(fn)
	dSynchronize.Call(id)
	RemoveEventCallbackOf(id)
}

func DInputBox(aCaption, aPrompt, aDefault string) string {
	r, _, _ := dInputBox.Call(GoStrToDStr(aCaption), GoStrToDStr(aPrompt), GoStrToDStr(aDefault))
	return DStrToGoStr(r)
}

func DInputQuery(aCaption, aPrompt string, value *string) bool {
	if value == nil {
		return false
	}
	var strPtr uintptr
	r, _, _ := dInputQuery.Call(GoStrToDStr(aCaption), GoStrToDStr(aPrompt), GoStrToDStr(*value), uintptr(unsafe.Pointer(&strPtr)))
	if strPtr != 0 {
		*value = DStrToGoStr(strPtr)
	}
	return DBoolToGoBool(r)
}

// DSysLocaled
func DSysLocale(aInfo *TSysLocale) {
	dSysLocale.Call(uintptr(unsafe.Pointer(aInfo)))
}

// Shortcut
//DCreateURLShortCut
func DCreateURLShortCut(aDestPath, aShortCutName, aURL string) {
	dCreateURLShortCut.Call(GoStrToDStr(aDestPath), GoStrToDStr(aShortCutName), GoStrToDStr(aURL))
}

//DCreateShortCut
func DCreateShortCut(aDestPath, aShortCutName, aSrcFileName, aIconFileName, aDescription, aCmdArgs string) bool {
	r, _, _ := dCreateShortCut.Call(GoStrToDStr(aDestPath), GoStrToDStr(aShortCutName), GoStrToDStr(aSrcFileName),
		GoStrToDStr(aIconFileName), GoStrToDStr(aDescription), GoStrToDStr(aCmdArgs))
	return DBoolToGoBool(r)
}

// SetProperty
// DSetPropertyValue
func DSetPropertyValue(instance uintptr, propName, value string) {
	dSetPropertyValue.Call(instance, GoStrToDStr(propName), GoStrToDStr(value))
}

// DSetPropertySecValue
func DSetPropertySecValue(instance uintptr, propName, secPropName, value string) {
	dSetPropertySecValue.Call(instance, GoStrToDStr(propName), GoStrToDStr(secPropName), GoStrToDStr(value))
}

// guid
// DGUIDToString
func DGUIDToString(guid TGUID) string {
	r, _, _ := dGUIDToString.Call(uintptr(unsafe.Pointer(&guid)))
	return DStrToGoStr(r)
}

// DStringToGUID
func DStringToGUID(str string) TGUID {
	var guid TGUID
	dStringToGUID.Call(GoStrToDStr(str), uintptr(unsafe.Pointer(&guid)))
	return guid
}

// DCreateGUID
func DCreateGUID() TGUID {
	var guid TGUID
	dCreateGUID.Call(uintptr(unsafe.Pointer(&guid)))
	return guid
}

// LibResouces
func DGetLibResouceCount() int32 {
	r, _, _ := dGetLibResouceCount.Call()
	return int32(r)
}

func DGetLibResouceItem(aIndex int32) (ret TLibResouce) {
	item := struct {
		Name     uintptr
		ValuePtr uintptr
	}{}
	dGetLibResouceItem.Call(uintptr(aIndex), uintptr(unsafe.Pointer(&item)))
	ret.Name = DStrToGoStr(item.Name)
	ret.Ptr = item.ValuePtr
	return
}

func DModifyLibResouce(aPtr uintptr, aValue string) {
	dModifyLibResouce.Call(aPtr, GoStrToDStr(aValue))
}
<commit_msg>Fix: "hashOf" returns 0 directly when interface{} is empty.<commit_after>package api

import (
	"reflect"
	"sync"
	"unsafe"

	. "github.com/ying32/govcl/vcl/types"
)

type TGoParam struct {
	Type  uint8
	Value uintptr
}

var (
	eventCallbackMap   sync.Map
	messageCallbackMap sync.Map
	threadSync         sync.Mutex
)

func DBoolToGoBool(val uintptr) bool {
	if val != 0 {
		return true
	}
	return false
}

func GoBoolToDBool(val bool) uintptr {
	if val {
		return 1
	}
	return 0
}

// hashOf  Delphi IniFiles.pas中的TStringHash.HashOf
func hashOf(val interface{}) uintptr {
	if reflect.ValueOf(val).Pointer() == 0 {
		return 0
	}
	var result uint32
	p := (*byte)(unsafe.Pointer(&val))
	for i := 0; i < int(unsafe.Sizeof(val)); i++ {
		result = ((result << 2) | (result >> (unsafe.Sizeof(result)*8 - 2))) ^ uint32(*p)
		p = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1))
	}
	return uintptr(result)
}

func addEventToMap(f interface{}) uintptr {
	p := hashOf(f)
	eventCallbackMap.Store(p, f)
	return p
}

func EventCallbackOf(Id uintptr) (interface{}, bool) {
	return eventCallbackMap.Load(Id)
}

func RemoveEventCallbackOf(Id uintptr) {
	eventCallbackMap.Delete(Id)
}

func addMessageEventToMap(f interface{}) uintptr {
	p := hashOf(f)
	messageCallbackMap.Store(p, f)
	return p
}

func MessageCallbackOf(Id uintptr) (interface{}, bool) {
	return messageCallbackMap.Load(Id)
}

func SetEventCallback(ptr uintptr) {
	setEventCallback.Call(ptr)
}

func SetMessageCallback(ptr uintptr) {
	setMessageCallback.Call(ptr)
}

func DGetParam(index int, ptr uintptr) TGoParam {
	p := TGoParam{}
	dGetParam.Call(uintptr(index), ptr, uintptr(unsafe.Pointer(&p)))
	return p
}

func DGetStringArrOf(p uintptr, index int) string {
	r, _, _ := dGetStringArrOf.Call(p, uintptr(index))
	return DStrToGoStr(r)
}

func DStrLen(p uintptr) int {
	ret, _, _ := dStrLen.Call(p)
	return int(ret)
}

func DMove(src, dest uintptr, llen int) {
	dMove.Call(src, dest, uintptr(llen))
}

func DShowMessage(s string) {
	dShowMessage.Call(GoStrToDStr(s))
}

func DGetMainInstance() uintptr {
	ret, _, _ := dGetMainInstance.Call()
	return ret
}

func DMessageDlg(Msg string, DlgType TMsgDlgType, Buttons TMsgDlgButtons, HelpCtx int32) int32 {
	ret, _, _ := dMessageDlg.Call(GoStrToDStr(Msg), uintptr(DlgType), uintptr(Buttons), uintptr(HelpCtx))
	return int32(ret)
}

func DSetReportMemoryLeaksOnShutdown(v bool) {
	dSetReportMemoryLeaksOnShutdown.Call(GoBoolToDBool(v))
}

func DTextToShortCut(val string) TShortCut {
	ret, _, _ := dTextToShortCut.Call(GoStrToDStr(val))
	return TShortCut(ret)
}

func DShortCutToText(val TShortCut) string {
	ret, _, _ := dShortCutToText.Call(uintptr(val))
	return DStrToGoStr(ret)
}

func DSysOpen(filename string) {
	dSysOpen.Call(GoStrToDStr(filename))
}

func DExtractFilePath(filename string) string {
	r, _, _ := dExtractFilePath.Call(GoStrToDStr(filename))
	return DStrToGoStr(r)
}

func DFileExists(filename string) bool {
	r, _, _ := dFileExists.Call(GoStrToDStr(filename))
	return DBoolToGoBool(r)
}

func DSelectDirectory1(options TSelectDirOpts) (bool, string) {
	var ptr uintptr
	r, _, _ := dSelectDirectory1.Call(uintptr(unsafe.Pointer(&ptr)), uintptr(options), 0)
	v := DBoolToGoBool(r)
	if v {
		return true, DStrToGoStr(ptr)
	}
	return false, ""
}

func DSelectDirectory2(caption, root string, options TSelectDirExtOpts, parent uintptr) (bool, string) {
	var ptr uintptr
	r, _, _ := dSelectDirectory2.Call(GoStrToDStr(caption), GoStrToDStr(root), uintptr(unsafe.Pointer(&ptr)),
		uintptr(options), parent)
	v := DBoolToGoBool(r)
	if v {
		return true, DStrToGoStr(ptr)
	}
	return false, ""
}

func DSynchronize(fn interface{}) {
	threadSync.Lock()
	defer threadSync.Unlock()
	id := addEventToMap(fn)
	dSynchronize.Call(id)
	RemoveEventCallbackOf(id)
}

func DInputBox(aCaption, aPrompt, aDefault string) string {
	r, _, _ := dInputBox.Call(GoStrToDStr(aCaption), GoStrToDStr(aPrompt), GoStrToDStr(aDefault))
	return DStrToGoStr(r)
}

func DInputQuery(aCaption, aPrompt string, value *string) bool {
	if value == nil {
		return false
	}
	var strPtr uintptr
	r, _, _ := dInputQuery.Call(GoStrToDStr(aCaption), GoStrToDStr(aPrompt), GoStrToDStr(*value), uintptr(unsafe.Pointer(&strPtr)))
	if strPtr != 0 {
		*value = DStrToGoStr(strPtr)
	}
	return DBoolToGoBool(r)
}

// DSysLocaled
func DSysLocale(aInfo *TSysLocale) {
	dSysLocale.Call(uintptr(unsafe.Pointer(aInfo)))
}

// Shortcut
//DCreateURLShortCut
func DCreateURLShortCut(aDestPath, aShortCutName, aURL string) {
	dCreateURLShortCut.Call(GoStrToDStr(aDestPath), GoStrToDStr(aShortCutName), GoStrToDStr(aURL))
}

//DCreateShortCut
func DCreateShortCut(aDestPath, aShortCutName, aSrcFileName, aIconFileName, aDescription, aCmdArgs string) bool {
	r, _, _ := dCreateShortCut.Call(GoStrToDStr(aDestPath), GoStrToDStr(aShortCutName), GoStrToDStr(aSrcFileName),
		GoStrToDStr(aIconFileName), GoStrToDStr(aDescription), GoStrToDStr(aCmdArgs))
	return DBoolToGoBool(r)
}

// SetProperty
// DSetPropertyValue
func DSetPropertyValue(instance uintptr, propName, value string) {
	dSetPropertyValue.Call(instance, GoStrToDStr(propName), GoStrToDStr(value))
}

// DSetPropertySecValue
func DSetPropertySecValue(instance uintptr, propName, secPropName, value string) {
	dSetPropertySecValue.Call(instance, GoStrToDStr(propName), GoStrToDStr(secPropName), GoStrToDStr(value))
}

// guid
// DGUIDToString
func DGUIDToString(guid TGUID) string {
	r, _, _ := dGUIDToString.Call(uintptr(unsafe.Pointer(&guid)))
	return DStrToGoStr(r)
}

// DStringToGUID
func DStringToGUID(str string) TGUID {
	var guid TGUID
	dStringToGUID.Call(GoStrToDStr(str), uintptr(unsafe.Pointer(&guid)))
	return guid
}

// DCreateGUID
func DCreateGUID() TGUID {
	var guid TGUID
	dCreateGUID.Call(uintptr(unsafe.Pointer(&guid)))
	return guid
}

// LibResouces
func DGetLibResouceCount() int32 {
	r, _, _ := dGetLibResouceCount.Call()
	return int32(r)
}

func DGetLibResouceItem(aIndex int32) (ret TLibResouce) {
	item := struct {
		Name     uintptr
		ValuePtr uintptr
	}{}
	dGetLibResouceItem.Call(uintptr(aIndex), uintptr(unsafe.Pointer(&item)))
	ret.Name = DStrToGoStr(item.Name)
	ret.Ptr = item.ValuePtr
	return
}

func DModifyLibResouce(aPtr uintptr, aValue string) {
	dModifyLibResouce.Call(aPtr, GoStrToDStr(aValue))
}
<|endoftext|>


<commit_before>package scheduler

import (
	"regexp"

	log "github.com/hashicorp/go-hclog"
	memdb "github.com/hashicorp/go-memdb"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Context is used to track contextual information used for placement
type Context interface {
	// State is used to inspect the current global state
	State() State

	// Plan returns the current plan
	Plan() *structs.Plan

	// Logger provides a way to log
	Logger() log.Logger

	// Metrics returns the current metrics
	Metrics() *structs.AllocMetric

	// Reset is invoked after making a placement
	Reset()

	// ProposedAllocs returns the proposed allocations for a node
	// which is the existing allocations, removing evictions, and
	// adding any planned placements.
	ProposedAllocs(nodeID string) ([]*structs.Allocation, error)

	// RegexpCache is a cache of regular expressions
	RegexpCache() map[string]*regexp.Regexp

	// VersionConstraintCache is a cache of version constraints
	VersionConstraintCache() map[string]version.Constraints

	// Eligibility returns a tracker for node eligibility in the context of the
	// eval.
	Eligibility() *EvalEligibility
}

// EvalCache is used to cache certain things during an evaluation
type EvalCache struct {
	reCache         map[string]*regexp.Regexp
	constraintCache map[string]version.Constraints
}

func (e *EvalCache) RegexpCache() map[string]*regexp.Regexp {
	if e.reCache == nil {
		e.reCache = make(map[string]*regexp.Regexp)
	}
	return e.reCache
}

func (e *EvalCache) VersionConstraintCache() map[string]version.Constraints {
	if e.constraintCache == nil {
		e.constraintCache = make(map[string]version.Constraints)
	}
	return e.constraintCache
}

// EvalContext is a Context used during an Evaluation
type EvalContext struct {
	EvalCache
	state       State
	plan        *structs.Plan
	logger      log.Logger
	metrics     *structs.AllocMetric
	eligibility *EvalEligibility
}

// NewEvalContext constructs a new EvalContext
func NewEvalContext(s State, p *structs.Plan, log log.Logger) *EvalContext {
	ctx := &EvalContext{
		state:   s,
		plan:    p,
		logger:  log,
		metrics: new(structs.AllocMetric),
	}
	return ctx
}

func (e *EvalContext) State() State {
	return e.state
}

func (e *EvalContext) Plan() *structs.Plan {
	return e.plan
}

func (e *EvalContext) Logger() log.Logger {
	return e.logger
}

func (e *EvalContext) Metrics() *structs.AllocMetric {
	return e.metrics
}

func (e *EvalContext) SetState(s State) {
	e.state = s
}

func (e *EvalContext) Reset() {
	e.metrics = new(structs.AllocMetric)
}

func (e *EvalContext) ProposedAllocs(nodeID string) ([]*structs.Allocation, error) {
	// Get the existing allocations that are non-terminal
	ws := memdb.NewWatchSet()
	existingAlloc, err := e.state.AllocsByNodeTerminal(ws, nodeID, false)
	if err != nil {
		return nil, err
	}

	// Determine the proposed allocation by first removing allocations
	// that are planned evictions and adding the new allocations.
	proposed := existingAlloc
	if update := e.plan.NodeUpdate[nodeID]; len(update) > 0 {
		proposed = structs.RemoveAllocs(existingAlloc, update)
	}

	// We create an index of the existing allocations so that if an inplace
	// update occurs, we do not double count and we override the old allocation.
	proposedIDs := make(map[string]*structs.Allocation, len(proposed))
	for _, alloc := range proposed {
		proposedIDs[alloc.ID] = alloc
	}
	for _, alloc := range e.plan.NodeAllocation[nodeID] {
		proposedIDs[alloc.ID] = alloc
	}

	// Materialize the proposed slice
	proposed = make([]*structs.Allocation, 0, len(proposedIDs))
	for _, alloc := range proposedIDs {
		proposed = append(proposed, alloc)
	}

	return proposed, nil
}

func (e *EvalContext) Eligibility() *EvalEligibility {
	if e.eligibility == nil {
		e.eligibility = NewEvalEligibility()
	}

	return e.eligibility
}

type ComputedClassFeasibility byte

const (
	// EvalComputedClassUnknown is the initial state until the eligibility has
	// been explicitly marked to eligible/ineligible or escaped.
	EvalComputedClassUnknown ComputedClassFeasibility = iota

	// EvalComputedClassIneligible is used to mark the computed class as
	// ineligible for the evaluation.
	EvalComputedClassIneligible

	// EvalComputedClassIneligible is used to mark the computed class as
	// eligible for the evaluation.
	EvalComputedClassEligible

	// EvalComputedClassEscaped signals that computed class can not determine
	// eligibility because a constraint exists that is not captured by computed
	// node classes.
	EvalComputedClassEscaped
)

// EvalEligibility tracks eligibility of nodes by computed node class over the
// course of an evaluation.
type EvalEligibility struct {
	// job tracks the eligibility at the job level per computed node class.
	job map[string]ComputedClassFeasibility

	// jobEscaped marks whether constraints have escaped at the job level.
	jobEscaped bool

	// taskGroups tracks the eligibility at the task group level per computed
	// node class.
	taskGroups map[string]map[string]ComputedClassFeasibility

	// tgEscapedConstraints is a map of task groups to whether constraints have
	// escaped.
	tgEscapedConstraints map[string]bool

	// quotaReached marks that the quota limit has been reached for the given
	// quota
	quotaReached string
}

// NewEvalEligibility returns an eligibility tracker for the context of an evaluation.
func NewEvalEligibility() *EvalEligibility {
	return &EvalEligibility{
		job:                  make(map[string]ComputedClassFeasibility),
		taskGroups:           make(map[string]map[string]ComputedClassFeasibility),
		tgEscapedConstraints: make(map[string]bool),
	}
}

// SetJob takes the job being evaluated and calculates the escaped constraints
// at the job and task group level.
func (e *EvalEligibility) SetJob(job *structs.Job) {
	// Determine whether the job has escaped constraints.
	e.jobEscaped = len(structs.EscapedConstraints(job.Constraints)) != 0

	// Determine the escaped constraints per task group.
	for _, tg := range job.TaskGroups {
		constraints := tg.Constraints
		for _, task := range tg.Tasks {
			constraints = append(constraints, task.Constraints...)
		}

		e.tgEscapedConstraints[tg.Name] = len(structs.EscapedConstraints(constraints)) != 0
	}
}

// HasEscaped returns whether any of the constraints in the passed job have
// escaped computed node classes.
func (e *EvalEligibility) HasEscaped() bool {
	if e.jobEscaped {
		return true
	}

	for _, escaped := range e.tgEscapedConstraints {
		if escaped {
			return true
		}
	}

	return false
}

// GetClasses returns the tracked classes to their eligibility, across the job
// and task groups.
func (e *EvalEligibility) GetClasses() map[string]bool {
	elig := make(map[string]bool)

	// Go through the job.
	for class, feas := range e.job {
		switch feas {
		case EvalComputedClassEligible:
			elig[class] = true
		case EvalComputedClassIneligible:
			elig[class] = false
		}
	}

	// Go through the task groups.
	for _, classes := range e.taskGroups {
		for class, feas := range classes {
			switch feas {
			case EvalComputedClassEligible:
				elig[class] = true
			case EvalComputedClassIneligible:
				// Only mark as ineligible if it hasn't been marked before. This
				// prevents one task group marking a class as ineligible when it
				// is eligible on another task group.
				if _, ok := elig[class]; !ok {
					elig[class] = false
				}
			}
		}
	}

	return elig
}

// JobStatus returns the eligibility status of the job.
func (e *EvalEligibility) JobStatus(class string) ComputedClassFeasibility {
	// COMPAT: Computed node class was introduced in 0.3. Clients running < 0.3
	// will not have a computed class. The safest value to return is the escaped
	// case, since it disables any optimization.
	if e.jobEscaped || class == "" {
		return EvalComputedClassEscaped
	}

	if status, ok := e.job[class]; ok {
		return status
	}
	return EvalComputedClassUnknown
}

// SetJobEligibility sets the eligibility status of the job for the computed
// node class.
func (e *EvalEligibility) SetJobEligibility(eligible bool, class string) {
	if eligible {
		e.job[class] = EvalComputedClassEligible
	} else {
		e.job[class] = EvalComputedClassIneligible
	}
}

// TaskGroupStatus returns the eligibility status of the task group.
func (e *EvalEligibility) TaskGroupStatus(tg, class string) ComputedClassFeasibility {
	// COMPAT: Computed node class was introduced in 0.3. Clients running < 0.3
	// will not have a computed class. The safest value to return is the escaped
	// case, since it disables any optimization.
	if class == "" {
		return EvalComputedClassEscaped
	}

	if escaped, ok := e.tgEscapedConstraints[tg]; ok {
		if escaped {
			return EvalComputedClassEscaped
		}
	}

	if classes, ok := e.taskGroups[tg]; ok {
		if status, ok := classes[class]; ok {
			return status
		}
	}
	return EvalComputedClassUnknown
}

// SetTaskGroupEligibility sets the eligibility status of the task group for the
// computed node class.
func (e *EvalEligibility) SetTaskGroupEligibility(eligible bool, tg, class string) {
	var eligibility ComputedClassFeasibility
	if eligible {
		eligibility = EvalComputedClassEligible
	} else {
		eligibility = EvalComputedClassIneligible
	}

	if classes, ok := e.taskGroups[tg]; ok {
		classes[class] = eligibility
	} else {
		e.taskGroups[tg] = map[string]ComputedClassFeasibility{class: eligibility}
	}
}

// SetQuotaLimitReached marks that the quota limit has been reached for the
// given quota
func (e *EvalEligibility) SetQuotaLimitReached(quota string) {
	e.quotaReached = quota
}

// QuotaLimitReached returns the quota name if the quota limit has been reached.
func (e *EvalEligibility) QuotaLimitReached() string {
	return e.quotaReached
}
<commit_msg>Preempted allocations should be removed from proposed allocations<commit_after>package scheduler

import (
	"regexp"

	log "github.com/hashicorp/go-hclog"
	memdb "github.com/hashicorp/go-memdb"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Context is used to track contextual information used for placement
type Context interface {
	// State is used to inspect the current global state
	State() State

	// Plan returns the current plan
	Plan() *structs.Plan

	// Logger provides a way to log
	Logger() log.Logger

	// Metrics returns the current metrics
	Metrics() *structs.AllocMetric

	// Reset is invoked after making a placement
	Reset()

	// ProposedAllocs returns the proposed allocations for a node
	// which is the existing allocations, removing evictions, and
	// adding any planned placements.
	ProposedAllocs(nodeID string) ([]*structs.Allocation, error)

	// RegexpCache is a cache of regular expressions
	RegexpCache() map[string]*regexp.Regexp

	// VersionConstraintCache is a cache of version constraints
	VersionConstraintCache() map[string]version.Constraints

	// Eligibility returns a tracker for node eligibility in the context of the
	// eval.
	Eligibility() *EvalEligibility
}

// EvalCache is used to cache certain things during an evaluation
type EvalCache struct {
	reCache         map[string]*regexp.Regexp
	constraintCache map[string]version.Constraints
}

func (e *EvalCache) RegexpCache() map[string]*regexp.Regexp {
	if e.reCache == nil {
		e.reCache = make(map[string]*regexp.Regexp)
	}
	return e.reCache
}

func (e *EvalCache) VersionConstraintCache() map[string]version.Constraints {
	if e.constraintCache == nil {
		e.constraintCache = make(map[string]version.Constraints)
	}
	return e.constraintCache
}

// EvalContext is a Context used during an Evaluation
type EvalContext struct {
	EvalCache
	state       State
	plan        *structs.Plan
	logger      log.Logger
	metrics     *structs.AllocMetric
	eligibility *EvalEligibility
}

// NewEvalContext constructs a new EvalContext
func NewEvalContext(s State, p *structs.Plan, log log.Logger) *EvalContext {
	ctx := &EvalContext{
		state:   s,
		plan:    p,
		logger:  log,
		metrics: new(structs.AllocMetric),
	}
	return ctx
}

func (e *EvalContext) State() State {
	return e.state
}

func (e *EvalContext) Plan() *structs.Plan {
	return e.plan
}

func (e *EvalContext) Logger() log.Logger {
	return e.logger
}

func (e *EvalContext) Metrics() *structs.AllocMetric {
	return e.metrics
}

func (e *EvalContext) SetState(s State) {
	e.state = s
}

func (e *EvalContext) Reset() {
	e.metrics = new(structs.AllocMetric)
}

func (e *EvalContext) ProposedAllocs(nodeID string) ([]*structs.Allocation, error) {
	// Get the existing allocations that are non-terminal
	ws := memdb.NewWatchSet()
	existingAlloc, err := e.state.AllocsByNodeTerminal(ws, nodeID, false)
	if err != nil {
		return nil, err
	}

	// Determine the proposed allocation by first removing allocations
	// that are planned evictions and adding the new allocations.
	proposed := existingAlloc
	if update := e.plan.NodeUpdate[nodeID]; len(update) > 0 {
		proposed = structs.RemoveAllocs(existingAlloc, update)
	}

	// Remove any allocs that are being preempted
	nodePreemptedAllocs := e.plan.NodePreemptions[nodeID]
	if len(nodePreemptedAllocs) > 0 {
		proposed = structs.RemoveAllocs(existingAlloc, nodePreemptedAllocs)
	}

	// We create an index of the existing allocations so that if an inplace
	// update occurs, we do not double count and we override the old allocation.
	proposedIDs := make(map[string]*structs.Allocation, len(proposed))
	for _, alloc := range proposed {
		proposedIDs[alloc.ID] = alloc
	}
	for _, alloc := range e.plan.NodeAllocation[nodeID] {
		proposedIDs[alloc.ID] = alloc
	}

	// Materialize the proposed slice
	proposed = make([]*structs.Allocation, 0, len(proposedIDs))
	for _, alloc := range proposedIDs {
		proposed = append(proposed, alloc)
	}

	return proposed, nil
}

func (e *EvalContext) Eligibility() *EvalEligibility {
	if e.eligibility == nil {
		e.eligibility = NewEvalEligibility()
	}

	return e.eligibility
}

type ComputedClassFeasibility byte

const (
	// EvalComputedClassUnknown is the initial state until the eligibility has
	// been explicitly marked to eligible/ineligible or escaped.
	EvalComputedClassUnknown ComputedClassFeasibility = iota

	// EvalComputedClassIneligible is used to mark the computed class as
	// ineligible for the evaluation.
	EvalComputedClassIneligible

	// EvalComputedClassIneligible is used to mark the computed class as
	// eligible for the evaluation.
	EvalComputedClassEligible

	// EvalComputedClassEscaped signals that computed class can not determine
	// eligibility because a constraint exists that is not captured by computed
	// node classes.
	EvalComputedClassEscaped
)

// EvalEligibility tracks eligibility of nodes by computed node class over the
// course of an evaluation.
type EvalEligibility struct {
	// job tracks the eligibility at the job level per computed node class.
	job map[string]ComputedClassFeasibility

	// jobEscaped marks whether constraints have escaped at the job level.
	jobEscaped bool

	// taskGroups tracks the eligibility at the task group level per computed
	// node class.
	taskGroups map[string]map[string]ComputedClassFeasibility

	// tgEscapedConstraints is a map of task groups to whether constraints have
	// escaped.
	tgEscapedConstraints map[string]bool

	// quotaReached marks that the quota limit has been reached for the given
	// quota
	quotaReached string
}

// NewEvalEligibility returns an eligibility tracker for the context of an evaluation.
func NewEvalEligibility() *EvalEligibility {
	return &EvalEligibility{
		job:                  make(map[string]ComputedClassFeasibility),
		taskGroups:           make(map[string]map[string]ComputedClassFeasibility),
		tgEscapedConstraints: make(map[string]bool),
	}
}

// SetJob takes the job being evaluated and calculates the escaped constraints
// at the job and task group level.
func (e *EvalEligibility) SetJob(job *structs.Job) {
	// Determine whether the job has escaped constraints.
	e.jobEscaped = len(structs.EscapedConstraints(job.Constraints)) != 0

	// Determine the escaped constraints per task group.
	for _, tg := range job.TaskGroups {
		constraints := tg.Constraints
		for _, task := range tg.Tasks {
			constraints = append(constraints, task.Constraints...)
		}

		e.tgEscapedConstraints[tg.Name] = len(structs.EscapedConstraints(constraints)) != 0
	}
}

// HasEscaped returns whether any of the constraints in the passed job have
// escaped computed node classes.
func (e *EvalEligibility) HasEscaped() bool {
	if e.jobEscaped {
		return true
	}

	for _, escaped := range e.tgEscapedConstraints {
		if escaped {
			return true
		}
	}

	return false
}

// GetClasses returns the tracked classes to their eligibility, across the job
// and task groups.
func (e *EvalEligibility) GetClasses() map[string]bool {
	elig := make(map[string]bool)

	// Go through the job.
	for class, feas := range e.job {
		switch feas {
		case EvalComputedClassEligible:
			elig[class] = true
		case EvalComputedClassIneligible:
			elig[class] = false
		}
	}

	// Go through the task groups.
	for _, classes := range e.taskGroups {
		for class, feas := range classes {
			switch feas {
			case EvalComputedClassEligible:
				elig[class] = true
			case EvalComputedClassIneligible:
				// Only mark as ineligible if it hasn't been marked before. This
				// prevents one task group marking a class as ineligible when it
				// is eligible on another task group.
				if _, ok := elig[class]; !ok {
					elig[class] = false
				}
			}
		}
	}

	return elig
}

// JobStatus returns the eligibility status of the job.
func (e *EvalEligibility) JobStatus(class string) ComputedClassFeasibility {
	// COMPAT: Computed node class was introduced in 0.3. Clients running < 0.3
	// will not have a computed class. The safest value to return is the escaped
	// case, since it disables any optimization.
	if e.jobEscaped || class == "" {
		return EvalComputedClassEscaped
	}

	if status, ok := e.job[class]; ok {
		return status
	}
	return EvalComputedClassUnknown
}

// SetJobEligibility sets the eligibility status of the job for the computed
// node class.
func (e *EvalEligibility) SetJobEligibility(eligible bool, class string) {
	if eligible {
		e.job[class] = EvalComputedClassEligible
	} else {
		e.job[class] = EvalComputedClassIneligible
	}
}

// TaskGroupStatus returns the eligibility status of the task group.
func (e *EvalEligibility) TaskGroupStatus(tg, class string) ComputedClassFeasibility {
	// COMPAT: Computed node class was introduced in 0.3. Clients running < 0.3
	// will not have a computed class. The safest value to return is the escaped
	// case, since it disables any optimization.
	if class == "" {
		return EvalComputedClassEscaped
	}

	if escaped, ok := e.tgEscapedConstraints[tg]; ok {
		if escaped {
			return EvalComputedClassEscaped
		}
	}

	if classes, ok := e.taskGroups[tg]; ok {
		if status, ok := classes[class]; ok {
			return status
		}
	}
	return EvalComputedClassUnknown
}

// SetTaskGroupEligibility sets the eligibility status of the task group for the
// computed node class.
func (e *EvalEligibility) SetTaskGroupEligibility(eligible bool, tg, class string) {
	var eligibility ComputedClassFeasibility
	if eligible {
		eligibility = EvalComputedClassEligible
	} else {
		eligibility = EvalComputedClassIneligible
	}

	if classes, ok := e.taskGroups[tg]; ok {
		classes[class] = eligibility
	} else {
		e.taskGroups[tg] = map[string]ComputedClassFeasibility{class: eligibility}
	}
}

// SetQuotaLimitReached marks that the quota limit has been reached for the
// given quota
func (e *EvalEligibility) SetQuotaLimitReached(quota string) {
	e.quotaReached = quota
}

// QuotaLimitReached returns the quota name if the quota limit has been reached.
func (e *EvalEligibility) QuotaLimitReached() string {
	return e.quotaReached
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 David R. Jenni. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
agodoc is a wrapper around godoc for use with Acme.
It shows the documentation of the identifier under the cursor.
*/
package main

import (
	"bufio"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"9fans.net/go/acme"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
)

type bodyReader struct{ *acme.Win }

func (r bodyReader) Read(data []byte) (int, error) {
	return r.Win.Read("body", data)
}

func main() {
	win, err := openWin()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot open window: %v\n", err)
		os.Exit(1)
	}

	filename, off, err := selection(win)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot get selection: %v\n", err)
		os.Exit(1)
	}

	prg, err := loadProgram(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot load program: %v\n", err)
		os.Exit(1)
	}

	obj, err := searchObject(prg, off)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot find object: %v\n", err)
		os.Exit(1)
	}

	switch x := obj.(type) {
	case *types.Builtin:
		godoc(obj.Pkg().Path(), obj.Name())
	case *types.PkgName:
		godoc(x.Imported().Path())
	case *types.Const, *types.Func, *types.TypeName, *types.Var:
		if !x.Exported() {
			fmt.Fprintf(os.Stderr, "can only print documentation of exported identifiers\n")
			os.Exit(1)
		}
		godoc(obj.Pkg().Path(), obj.Name())
	default:
		fmt.Fprintf(os.Stderr, "cannot print documentation of %v\n", obj)
		os.Exit(1)
	}
}

func openWin() (*acme.Win, error) {
	id, err := strconv.Atoi(os.Getenv("winid"))
	if err != nil {
		return nil, err
	}
	return acme.Open(id, nil)
}

func selection(win *acme.Win) (filename string, off int, err error) {
	filename, err = readFilename(win)
	if err != nil {
		return "", 0, err
	}
	q0, _, err := readAddr(win)
	if err != nil {
		return "", 0, err
	}
	off, err = byteOffset(bufio.NewReader(&bodyReader{win}), q0)
	if err != nil {
		return "", 0, err
	}
	return
}

func readFilename(win *acme.Win) (string, error) {
	b, err := win.ReadAll("tag")
	if err != nil {
		return "", err
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return "", fmt.Errorf("cannot get filename from tag")
	}
	return tag[0:i], nil
}

func readAddr(win *acme.Win) (q0, q1 int, err error) {
	if _, _, err := win.ReadAddr(); err != nil {
		return 0, 0, err
	}
	if err := win.Ctl("addr=dot"); err != nil {
		return 0, 0, err
	}
	return win.ReadAddr()
}

func byteOffset(r io.RuneReader, off int) (bo int, err error) {
	for i := 0; i != off; i++ {
		_, s, err := r.ReadRune()
		if err != nil {
			return 0, err
		}
		bo += s
	}
	return
}

func loadProgram(filename string) (*loader.Program, error) {
	var conf loader.Config
	f, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
	if err != nil {
		return nil, err
	}
	if err = conf.CreateFromFilenames(f.Name.Name, filename); err != nil {
		return nil, err
	}
	return conf.Load()
}

func searchObject(prg *loader.Program, off int) (types.Object, error) {
	info := prg.Created[0]
	ident := identAtOffset(prg.Fset, info.Files[0], off)
	if ident == nil {
		return nil, fmt.Errorf("no identifier here")
	}
	if obj := info.Uses[ident]; obj != nil {
		return obj, nil
	}
	return nil, fmt.Errorf("cannot find identifier %s in file", ident.Name)
}

func identAtOffset(fset *token.FileSet, f *ast.File, off int) *ast.Ident {
	var ident *ast.Ident
	ast.Inspect(f, func(n ast.Node) bool {
		if id, ok := n.(*ast.Ident); ok {
			pos := fset.Position(id.Pos()).Offset
			if pos <= off && off < pos+len(id.Name) {
				ident = id
			}
		}
		return ident == nil
	})
	return ident
}

func godoc(args ...string) {
	c := exec.Command("godoc", args...)
	c.Stderr, c.Stdout = os.Stderr, os.Stderr
	if err := c.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "godoc failed: %v\n", err)
		os.Exit(1)
	}
}
<commit_msg>agodoc: improve error message.<commit_after>// Copyright (c) 2014 David R. Jenni. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
agodoc is a wrapper around godoc for use with Acme.
It shows the documentation of the identifier under the cursor.
*/
package main

import (
	"bufio"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"9fans.net/go/acme"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
)

type bodyReader struct{ *acme.Win }

func (r bodyReader) Read(data []byte) (int, error) {
	return r.Win.Read("body", data)
}

func main() {
	win, err := openWin()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot open window: %v\n", err)
		os.Exit(1)
	}

	filename, off, err := selection(win)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot get selection: %v\n", err)
		os.Exit(1)
	}

	prg, err := loadProgram(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot load program: %v\n", err)
		os.Exit(1)
	}

	obj, err := searchObject(prg, off)
	if err != nil {
		fmt.Fprintf(os.Stderr, "cannot find object: %v\n", err)
		os.Exit(1)
	}

	switch x := obj.(type) {
	case *types.Builtin:
		godoc(obj.Pkg().Path(), obj.Name())
	case *types.PkgName:
		godoc(x.Imported().Path())
	case *types.Const, *types.Func, *types.TypeName, *types.Var:
		if !x.Exported() {
			fmt.Fprintf(os.Stderr, "cannot print documentation of unexported identifier\n")
			os.Exit(1)
		}
		godoc(obj.Pkg().Path(), obj.Name())
	default:
		fmt.Fprintf(os.Stderr, "cannot print documentation of %v\n", obj)
		os.Exit(1)
	}
}

func openWin() (*acme.Win, error) {
	id, err := strconv.Atoi(os.Getenv("winid"))
	if err != nil {
		return nil, err
	}
	return acme.Open(id, nil)
}

func selection(win *acme.Win) (filename string, off int, err error) {
	filename, err = readFilename(win)
	if err != nil {
		return "", 0, err
	}
	q0, _, err := readAddr(win)
	if err != nil {
		return "", 0, err
	}
	off, err = byteOffset(bufio.NewReader(&bodyReader{win}), q0)
	if err != nil {
		return "", 0, err
	}
	return
}

func readFilename(win *acme.Win) (string, error) {
	b, err := win.ReadAll("tag")
	if err != nil {
		return "", err
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return "", fmt.Errorf("cannot get filename from tag")
	}
	return tag[0:i], nil
}

func readAddr(win *acme.Win) (q0, q1 int, err error) {
	if _, _, err := win.ReadAddr(); err != nil {
		return 0, 0, err
	}
	if err := win.Ctl("addr=dot"); err != nil {
		return 0, 0, err
	}
	return win.ReadAddr()
}

func byteOffset(r io.RuneReader, off int) (bo int, err error) {
	for i := 0; i != off; i++ {
		_, s, err := r.ReadRune()
		if err != nil {
			return 0, err
		}
		bo += s
	}
	return
}

func loadProgram(filename string) (*loader.Program, error) {
	var conf loader.Config
	f, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
	if err != nil {
		return nil, err
	}
	if err = conf.CreateFromFilenames(f.Name.Name, filename); err != nil {
		return nil, err
	}
	return conf.Load()
}

func searchObject(prg *loader.Program, off int) (types.Object, error) {
	info := prg.Created[0]
	ident := identAtOffset(prg.Fset, info.Files[0], off)
	if ident == nil {
		return nil, fmt.Errorf("no identifier here")
	}
	if obj := info.Uses[ident]; obj != nil {
		return obj, nil
	}
	return nil, fmt.Errorf("cannot find identifier %s in file", ident.Name)
}

func identAtOffset(fset *token.FileSet, f *ast.File, off int) *ast.Ident {
	var ident *ast.Ident
	ast.Inspect(f, func(n ast.Node) bool {
		if id, ok := n.(*ast.Ident); ok {
			pos := fset.Position(id.Pos()).Offset
			if pos <= off && off < pos+len(id.Name) {
				ident = id
			}
		}
		return ident == nil
	})
	return ident
}

func godoc(args ...string) {
	c := exec.Command("godoc", args...)
	c.Stderr, c.Stdout = os.Stderr, os.Stderr
	if err := c.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "godoc failed: %v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package app

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

var (
	db, _ = sql.Open("mysql", "root:@/golang")
	Exec = db.Exec
	Query = db.Query
	QueryRow = db.QueryRow
)

func CountRows(rows *sql.Rows) (count int) {
	for rows.Next() {
		err := rows.Scan(&count)
		CheckErr(err)
	}
	return count
}

func CountRowsUsers(rows *sql.Rows) (count int) {
	for rows.Next() {
		err := rows.Scan(&count)
		CheckErr(err)
	}
	return count
}

func CheckErr(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Update db.go<commit_after>package app

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

var (
	db, _ = sql.Open("mysql", "root:@/golang")
	Exec = db.Exec
	Query = db.Query
	QueryRow = db.QueryRow
)

func CountRows(rows *sql.Rows) (count int) {
	for rows.Next() {
		err := rows.Scan(&count)
		CheckErr(err)
	}
	return count
}

func CheckErr(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package app

import (
	"yap/nlp/format/conllu"
	"yap/nlp/format/lattice"
	"yap/nlp/format/raw"

	"yap/nlp/parser/ma"
	nlp "yap/nlp/types"
	// "yap/util"

	"fmt"
	"log"
	// "os"
	"strings"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

var (
	dictFile, inRawFile, outLatticeFile string
	maxOOVMSRPerPOS                     int = 10
	oovFile                             string
	dopeOOV                             bool
	outFormat                           string
)

func MAConfigOut() {
	log.Println("Configuration")
	log.Printf("MA Dict:\t\t%s", dictFile)
	log.Printf("Limit:\t\t%v", limit)
	log.Printf("Max OOV Msrs/POS:\t%v", maxOOVMSRPerPOS)
	log.Printf("Dope:\t\t%v", dopeOOV)
	log.Println()
	if useConllU {
		log.Printf("CoNLL-U Input:\t%s", conlluFile)
	} else {
		log.Printf("Raw Input:\t\t%s", inRawFile)
	}
	log.Printf("Output:\t\t%s", outLatticeFile)
	log.Printf("Output Format\t:%v", outFormat)
	log.Println()
}

func MA(cmd *commander.Command, args []string) error {
	useConllU = len(conlluFile) > 0
	var REQUIRED_FLAGS []string
	if useConllU {
		REQUIRED_FLAGS = []string{"dict", "conllu", "out"}
	} else {
		REQUIRED_FLAGS = []string{"dict", "raw", "out"}
	}

	VerifyFlags(cmd, REQUIRED_FLAGS)

	MAConfigOut()

	log.Println("Reading Morphological Analyzer Dictionary")
	maData := new(ma.MADict)
	if err := maData.ReadFile(dictFile); err != nil {
		panic(fmt.Sprintf("Failed reading MA dict file - %v", err))
	}
	log.Println("OOV POSs:", strings.Join(maData.TopPOS, ", "))
	maData.ComputeOOVMSRs(maxOOVMSRPerPOS)
	log.Println()
	var (
		sents      []nlp.BasicSentence
		oovVectors []interface{}
		rawOOV     interface{}
		err        error
	)
	if useConllU {
		conllSents, _, err := conllu.ReadFile(conlluFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading CoNLL-U file - %v", err))
		}
		sents = make([]nlp.BasicSentence, len(conllSents))
		for i, sent := range conllSents {
			newSent := make([]nlp.Token, len(sent.Tokens))
			for j, token := range sent.Tokens {
				newSent[j] = nlp.Token(token)
			}
			sents[i] = newSent
		}
	} else {
		sents, err = raw.ReadFile(inRawFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading raw file - %v", err))
		}
	}
	log.Println("Running Morphological Analysis")
	lattices := make([]nlp.LatticeSentence, len(sents))
	stats := new(ma.AnalyzeStats)
	stats.Init()
	maData.Init()
	maData.Stats = stats
	maData.Dope = dopeOOV
	if len(oovFile) > 0 {
		oovVectors = make([]interface{}, len(sents))
	}
	for i, sent := range sents {
		lattices[i], rawOOV = maData.Analyze(sent.Tokens())
		if oovVectors != nil {
			oovVectors[i] = rawOOV
		}
	}
	log.Println("Analyzed", stats.TotalTokens, "occurences of", len(stats.UniqTokens), "unique tokens")
	log.Println("Encountered", stats.OOVTokens, "occurences of", len(stats.UniqOOVTokens), "unknown tokens")
	output := lattice.Sentence2LatticeCorpus(lattices, nil)
	if outFormat == "ud" {
		if outJSON {
			lattice.WriteUDJSONFile(outLatticeFile, output)
		} else {
			lattice.WriteUDFile(outLatticeFile, output)
		}
	} else if outFormat == "spmrl" {
		lattice.WriteFile(outLatticeFile, output)
	} else {
		panic(fmt.Sprintf("Unknown lattice output format - %v", outFormat))
	}
	if oovVectors != nil {
		raw.WriteFile(oovFile, oovVectors)
	}
	log.Println("Wrote", len(output), "lattices")
	return nil
}

func MACmd() *commander.Command {
	cmd := &commander.Command{
		Run:       MA,
		UsageLine: "ma <file options> [arguments]",
		Short:     "run data-driven morphological analyzer on raw input",
		Long: `
run data-driven morphological analyzer on raw input

	$ ./yap ma -dict <dict file> -raw <raw file> -out <output file> [options]

`,
		Flag: *flag.NewFlagSet("ma", flag.ExitOnError),
	}
	cmd.Flag.StringVar(&dictFile, "dict", "", "Dictionary for morphological analyzer")
	cmd.Flag.StringVar(&inRawFile, "raw", "", "Input raw (tokenized) file")
	cmd.Flag.StringVar(&conlluFile, "conllu", "", "CoNLL-U-format input file")
	cmd.Flag.StringVar(&outLatticeFile, "out", "", "Output lattice file")
	cmd.Flag.StringVar(&outFormat, "format", "spmrl", "Output lattice format [spmrl|ud]")
	cmd.Flag.StringVar(&oovFile, "oov", "", "OOV File")
	cmd.Flag.IntVar(&maxOOVMSRPerPOS, "maxmsrperpos", 10, "For OOV tokens, max MSRs per POS to add")
	cmd.Flag.BoolVar(&dopeOOV, "dope", false, "Dope potential OOV tokens")
	cmd.Flag.IntVar(&limit, "limit", 0, "limit training set")
	cmd.Flag.BoolVar(&outJSON, "json", false, "Output using JSON")
	return cmd
}
<commit_msg>Fix typo in config output of ma<commit_after>package app

import (
	"yap/nlp/format/conllu"
	"yap/nlp/format/lattice"
	"yap/nlp/format/raw"

	"yap/nlp/parser/ma"
	nlp "yap/nlp/types"
	// "yap/util"

	"fmt"
	"log"
	// "os"
	"strings"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

var (
	dictFile, inRawFile, outLatticeFile string
	maxOOVMSRPerPOS                     int = 10
	oovFile                             string
	dopeOOV                             bool
	outFormat                           string
)

func MAConfigOut() {
	log.Println("Configuration")
	log.Printf("MA Dict:\t\t%s", dictFile)
	log.Printf("Limit:\t\t%v", limit)
	log.Printf("Max OOV Msrs/POS:\t%v", maxOOVMSRPerPOS)
	log.Printf("Dope:\t\t%v", dopeOOV)
	log.Println()
	if useConllU {
		log.Printf("CoNLL-U Input:\t%s", conlluFile)
	} else {
		log.Printf("Raw Input:\t\t%s", inRawFile)
	}
	log.Printf("Output:\t\t%s", outLatticeFile)
	log.Printf("Output Format:\t%v", outFormat)
	log.Println()
}

func MA(cmd *commander.Command, args []string) error {
	useConllU = len(conlluFile) > 0
	var REQUIRED_FLAGS []string
	if useConllU {
		REQUIRED_FLAGS = []string{"dict", "conllu", "out"}
	} else {
		REQUIRED_FLAGS = []string{"dict", "raw", "out"}
	}

	VerifyFlags(cmd, REQUIRED_FLAGS)

	MAConfigOut()

	log.Println("Reading Morphological Analyzer Dictionary")
	maData := new(ma.MADict)
	if err := maData.ReadFile(dictFile); err != nil {
		panic(fmt.Sprintf("Failed reading MA dict file - %v", err))
	}
	log.Println("OOV POSs:", strings.Join(maData.TopPOS, ", "))
	maData.ComputeOOVMSRs(maxOOVMSRPerPOS)
	log.Println()
	var (
		sents      []nlp.BasicSentence
		oovVectors []interface{}
		rawOOV     interface{}
		err        error
	)
	if useConllU {
		conllSents, _, err := conllu.ReadFile(conlluFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading CoNLL-U file - %v", err))
		}
		sents = make([]nlp.BasicSentence, len(conllSents))
		for i, sent := range conllSents {
			newSent := make([]nlp.Token, len(sent.Tokens))
			for j, token := range sent.Tokens {
				newSent[j] = nlp.Token(token)
			}
			sents[i] = newSent
		}
	} else {
		sents, err = raw.ReadFile(inRawFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading raw file - %v", err))
		}
	}
	log.Println("Running Morphological Analysis")
	lattices := make([]nlp.LatticeSentence, len(sents))
	stats := new(ma.AnalyzeStats)
	stats.Init()
	maData.Init()
	maData.Stats = stats
	maData.Dope = dopeOOV
	if len(oovFile) > 0 {
		oovVectors = make([]interface{}, len(sents))
	}
	for i, sent := range sents {
		lattices[i], rawOOV = maData.Analyze(sent.Tokens())
		if oovVectors != nil {
			oovVectors[i] = rawOOV
		}
	}
	log.Println("Analyzed", stats.TotalTokens, "occurences of", len(stats.UniqTokens), "unique tokens")
	log.Println("Encountered", stats.OOVTokens, "occurences of", len(stats.UniqOOVTokens), "unknown tokens")
	output := lattice.Sentence2LatticeCorpus(lattices, nil)
	if outFormat == "ud" {
		if outJSON {
			lattice.WriteUDJSONFile(outLatticeFile, output)
		} else {
			lattice.WriteUDFile(outLatticeFile, output)
		}
	} else if outFormat == "spmrl" {
		lattice.WriteFile(outLatticeFile, output)
	} else {
		panic(fmt.Sprintf("Unknown lattice output format - %v", outFormat))
	}
	if oovVectors != nil {
		raw.WriteFile(oovFile, oovVectors)
	}
	log.Println("Wrote", len(output), "lattices")
	return nil
}

func MACmd() *commander.Command {
	cmd := &commander.Command{
		Run:       MA,
		UsageLine: "ma <file options> [arguments]",
		Short:     "run data-driven morphological analyzer on raw input",
		Long: `
run data-driven morphological analyzer on raw input

	$ ./yap ma -dict <dict file> -raw <raw file> -out <output file> [options]

`,
		Flag: *flag.NewFlagSet("ma", flag.ExitOnError),
	}
	cmd.Flag.StringVar(&dictFile, "dict", "", "Dictionary for morphological analyzer")
	cmd.Flag.StringVar(&inRawFile, "raw", "", "Input raw (tokenized) file")
	cmd.Flag.StringVar(&conlluFile, "conllu", "", "CoNLL-U-format input file")
	cmd.Flag.StringVar(&outLatticeFile, "out", "", "Output lattice file")
	cmd.Flag.StringVar(&outFormat, "format", "spmrl", "Output lattice format [spmrl|ud]")
	cmd.Flag.StringVar(&oovFile, "oov", "", "OOV File")
	cmd.Flag.IntVar(&maxOOVMSRPerPOS, "maxmsrperpos", 10, "For OOV tokens, max MSRs per POS to add")
	cmd.Flag.BoolVar(&dopeOOV, "dope", false, "Dope potential OOV tokens")
	cmd.Flag.IntVar(&limit, "limit", 0, "limit training set")
	cmd.Flag.BoolVar(&outJSON, "json", false, "Output using JSON")
	return cmd
}
<|endoftext|>